parser: prepare for better VLS integration, more accurate parser errors (#7119)
parent
52fb7033c3
commit
47d0ed308d
|
@ -191,14 +191,12 @@ fn (mut f MDFile) check_examples() (int, int) {
|
|||
mut should_cleanup_vfile := true
|
||||
// eprintln('>>> checking example $vfile ...')
|
||||
vcontent := e.text.join('\n')
|
||||
os.write_file(vfile, vcontent) or {
|
||||
panic(err)
|
||||
}
|
||||
os.write_file(vfile, vcontent) or { panic(err) }
|
||||
mut acommands := e.command.split(' ')
|
||||
for command in acommands {
|
||||
match command {
|
||||
'compile' {
|
||||
res := os.system('"$vexe" -silent -o x.c $vfile')
|
||||
res := os.system('"$vexe" -w -Wfatal-errors -o x.c $vfile')
|
||||
os.rm('x.c') or { }
|
||||
if res != 0 {
|
||||
eprintln(eline(f.path, e.sline, 0, 'example failed to compile'))
|
||||
|
@ -210,7 +208,7 @@ fn (mut f MDFile) check_examples() (int, int) {
|
|||
oks++
|
||||
}
|
||||
'live' {
|
||||
res := os.system('"$vexe" -silent -live -o x.c $vfile')
|
||||
res := os.system('"$vexe" -w -Wfatal-errors -live -o x.c $vfile')
|
||||
if res != 0 {
|
||||
eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live'))
|
||||
eprintln(vcontent)
|
||||
|
@ -221,7 +219,7 @@ fn (mut f MDFile) check_examples() (int, int) {
|
|||
oks++
|
||||
}
|
||||
'failcompile' {
|
||||
res := os.system('"$vexe" -silent -o x.c $vfile')
|
||||
res := os.system('"$vexe" -w -Wfatal-errors -o x.c $vfile')
|
||||
os.rm('x.c') or { }
|
||||
if res == 0 {
|
||||
eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled'))
|
||||
|
@ -233,7 +231,7 @@ fn (mut f MDFile) check_examples() (int, int) {
|
|||
oks++
|
||||
}
|
||||
'oksyntax' {
|
||||
res := os.system('"$vexe" -silent -check-syntax $vfile')
|
||||
res := os.system('"$vexe" -w -Wfatal-errors -check-syntax $vfile')
|
||||
if res != 0 {
|
||||
eprintln(eline(f.path, e.sline, 0, '`oksyntax` example with invalid syntax'))
|
||||
eprintln(vcontent)
|
||||
|
@ -244,7 +242,7 @@ fn (mut f MDFile) check_examples() (int, int) {
|
|||
oks++
|
||||
}
|
||||
'badsyntax' {
|
||||
res := os.system('"$vexe" -silent -check-syntax $vfile')
|
||||
res := os.system('"$vexe" -w -Wfatal-errors -check-syntax $vfile')
|
||||
if res == 0 {
|
||||
eprintln(eline(f.path, e.sline, 0, '`badsyntax` example can be parsed fine'))
|
||||
eprintln(vcontent)
|
||||
|
@ -262,9 +260,7 @@ fn (mut f MDFile) check_examples() (int, int) {
|
|||
}
|
||||
}
|
||||
if should_cleanup_vfile {
|
||||
os.rm(vfile) or {
|
||||
panic(err)
|
||||
}
|
||||
os.rm(vfile) or { panic(err) }
|
||||
}
|
||||
}
|
||||
return errors, oks
|
||||
|
|
|
@ -125,6 +125,9 @@ The build flags are shared by the build and run commands:
|
|||
-W
|
||||
Treat all warnings as errors, even in development builds.
|
||||
|
||||
-Wfatal-errors
|
||||
Unconditionally exit with exit(1) after the first error. Useful for scripts/tooling that calls V.
|
||||
|
||||
For C-specific build flags, use `v help build-c`.
|
||||
|
||||
See also:
|
||||
|
|
|
@ -297,8 +297,7 @@ In development mode the compiler will warn you that you haven't used the variabl
|
|||
In production mode (enabled by passing the `-prod` flag to v – `v -prod foo.v`)
|
||||
it will not compile at all (like in Go).
|
||||
|
||||
<!-- this should be `failcompile`, but it compiles -->
|
||||
```v
|
||||
```v failcompile
|
||||
fn main() {
|
||||
a := 10
|
||||
if true {
|
||||
|
@ -1736,9 +1735,9 @@ color = .green
|
|||
println(color) // "green"
|
||||
|
||||
match color {
|
||||
.red { ... }
|
||||
.green { ... }
|
||||
.blue { ... }
|
||||
.red { println('the color was red') }
|
||||
.green { println('the color was green') }
|
||||
.blue { println('the color was blue') }
|
||||
}
|
||||
|
||||
```
|
||||
|
|
|
@ -4391,6 +4391,9 @@ fn (mut c Checker) warn_or_error(message string, pos token.Position, warn bool)
|
|||
return
|
||||
}
|
||||
if !warn {
|
||||
if c.pref.fatal_errors {
|
||||
exit(1)
|
||||
}
|
||||
c.nr_errors++
|
||||
if pos.line_nr !in c.error_lines {
|
||||
err := errors.Error{
|
||||
|
|
|
@ -11,33 +11,34 @@ fn (mut p Parser) assign_stmt() ast.Stmt {
|
|||
return p.partial_assign_stmt(exprs, comments)
|
||||
}
|
||||
|
||||
fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) {
|
||||
fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) ? {
|
||||
match val {
|
||||
ast.Ident {
|
||||
for expr in exprs {
|
||||
if expr is ast.Ident {
|
||||
if expr.name == val.name {
|
||||
p.error_with_pos('undefined variable: `$val.name`', val.pos)
|
||||
return error('undefined variable: `$val.name`')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.InfixExpr {
|
||||
p.check_undefined_variables(exprs, val.left)
|
||||
p.check_undefined_variables(exprs, val.right)
|
||||
p.check_undefined_variables(exprs, val.left) ?
|
||||
p.check_undefined_variables(exprs, val.right) ?
|
||||
}
|
||||
ast.ParExpr {
|
||||
p.check_undefined_variables(exprs, val.expr)
|
||||
p.check_undefined_variables(exprs, val.expr) ?
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
p.check_undefined_variables(exprs, val.expr)
|
||||
p.check_undefined_variables(exprs, val.expr) ?
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
p.check_undefined_variables(exprs, val.right)
|
||||
p.check_undefined_variables(exprs, val.right) ?
|
||||
}
|
||||
ast.StringInterLiteral {
|
||||
for expr_ in val.exprs {
|
||||
p.check_undefined_variables(exprs, expr_)
|
||||
p.check_undefined_variables(exprs, expr_) ?
|
||||
}
|
||||
}
|
||||
else {}
|
||||
|
@ -105,6 +106,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
|
|||
has_cross_var = p.check_cross_variables(left, r)
|
||||
if op !in [.assign, .decl_assign] {
|
||||
p.error('unexpected $op.str(), expecting := or = or comma')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
if has_cross_var {
|
||||
break
|
||||
|
@ -118,6 +120,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
|
|||
if op == .decl_assign {
|
||||
if p.scope.known_var(lx.name) {
|
||||
p.error_with_pos('redefinition of `$lx.name`', lx.pos)
|
||||
return ast.Stmt{}
|
||||
}
|
||||
mut share := table.ShareType(0)
|
||||
if lx.info is ast.IdentVar {
|
||||
|
@ -127,6 +130,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
|
|||
if !p.pref.translated {
|
||||
p.error_with_pos('static variables are supported only in -translated mode',
|
||||
lx.pos)
|
||||
return ast.Stmt{}
|
||||
}
|
||||
is_static = true
|
||||
}
|
||||
|
@ -159,6 +163,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
|
|||
if op == .decl_assign {
|
||||
p.error_with_pos('non-name `$lx.left[$lx.index]` on left side of `:=`',
|
||||
lx.pos)
|
||||
return ast.Stmt{}
|
||||
}
|
||||
lx.is_setter = true
|
||||
}
|
||||
|
@ -168,6 +173,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
|
|||
if op == .decl_assign {
|
||||
p.error_with_pos('struct fields can only be declared during the initialization',
|
||||
lx.pos)
|
||||
return ast.Stmt{}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -51,12 +51,14 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
|
|||
n := p.check_name() // skip `vweb.html()` TODO
|
||||
if n != 'vweb' {
|
||||
p.error(error_msg)
|
||||
return ast.ComptimeCall{}
|
||||
}
|
||||
p.check(.dot)
|
||||
}
|
||||
n := p.check_name() // (.name)
|
||||
if n != 'html' && n != 'tmpl' {
|
||||
p.error(error_msg)
|
||||
return ast.ComptimeCall{}
|
||||
}
|
||||
is_html := n == 'html'
|
||||
p.check(.lpar)
|
||||
|
@ -85,8 +87,10 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
|
|||
if !os.exists(path) {
|
||||
if is_html {
|
||||
p.error('vweb HTML template "$path" not found')
|
||||
return ast.ComptimeCall{}
|
||||
} else {
|
||||
p.error('template file "$path" not found')
|
||||
return ast.ComptimeCall{}
|
||||
}
|
||||
}
|
||||
// println('path is now "$path"')
|
||||
|
@ -171,6 +175,7 @@ fn (mut p Parser) comp_for() ast.CompFor {
|
|||
kind = .fields
|
||||
} else {
|
||||
p.error('unknown kind `$for_val`, available are: `methods` or `fields`')
|
||||
return ast.CompFor{}
|
||||
}
|
||||
spos := p.tok.position()
|
||||
stmts := p.parse_block()
|
||||
|
|
|
@ -65,6 +65,7 @@ fn (mut p Parser) array_init() ast.ArrayInit {
|
|||
n := p.check_name()
|
||||
if n != 'init' {
|
||||
p.error_with_pos('expected `init:`, not `$n`', pos)
|
||||
return ast.ArrayInit{}
|
||||
}
|
||||
p.check(.colon)
|
||||
has_default = true
|
||||
|
@ -117,6 +118,7 @@ fn (mut p Parser) array_init() ast.ArrayInit {
|
|||
}
|
||||
else {
|
||||
p.error('wrong field `$key`, expecting `len`, `cap`, or `init`')
|
||||
return ast.ArrayInit{}
|
||||
}
|
||||
}
|
||||
if p.tok.kind != .rcbr {
|
||||
|
|
|
@ -111,7 +111,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg {
|
|||
for p.tok.kind != .rpar {
|
||||
if p.tok.kind == .eof {
|
||||
p.error_with_pos('unexpected eof reached, while parsing call argument', start_pos)
|
||||
break
|
||||
return []
|
||||
}
|
||||
is_shared := p.tok.kind == .key_shared
|
||||
is_atomic := p.tok.kind == .key_atomic
|
||||
|
@ -197,6 +197,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
rec_type = p.parse_type_with_mut(rec_mut)
|
||||
if is_amp && rec_mut {
|
||||
p.error('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`')
|
||||
return ast.FnDecl{}
|
||||
}
|
||||
if is_shared {
|
||||
rec_type = rec_type.set_flag(.shared_f)
|
||||
|
@ -220,11 +221,13 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
name = if language == .js { p.check_js_name() } else { p.check_name() }
|
||||
if language == .v && !p.pref.translated && util.contains_capital(name) {
|
||||
p.error('function names cannot contain uppercase letters, use snake_case instead')
|
||||
return ast.FnDecl{}
|
||||
}
|
||||
type_sym := p.table.get_type_symbol(rec_type)
|
||||
// interfaces are handled in the checker, methods can not be defined on them this way
|
||||
if is_method && (type_sym.has_method(name) && type_sym.kind != .interface_) {
|
||||
p.error('duplicate method `$name`')
|
||||
return ast.FnDecl{}
|
||||
}
|
||||
}
|
||||
if p.tok.kind in [.plus, .minus, .mul, .div, .mod] {
|
||||
|
@ -245,7 +248,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
for param in params {
|
||||
if p.scope.known_var(param.name) {
|
||||
p.error_with_pos('redefinition of parameter `$param.name`', param.pos)
|
||||
break
|
||||
return ast.FnDecl{}
|
||||
}
|
||||
p.scope.register(ast.Var{
|
||||
name: param.name
|
||||
|
@ -274,6 +277,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
// we could also check if kind is .array, .array_fixed, .map instead of mod.len
|
||||
if type_sym.mod.len > 0 && type_sym.mod != p.mod && type_sym.language == .v {
|
||||
p.error('cannot define new methods on non-local type $type_sym.name')
|
||||
return ast.FnDecl{}
|
||||
}
|
||||
// p.warn('reg method $type_sym.name . $name ()')
|
||||
type_sym_method_idx = type_sym.register_method(table.Fn{
|
||||
|
@ -329,6 +333,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
p.close_scope()
|
||||
if !no_body && are_args_type_only {
|
||||
p.error_with_pos('functions with type only args can not have bodies', body_start_pos)
|
||||
return ast.FnDecl{}
|
||||
}
|
||||
return ast.FnDecl{
|
||||
name: name
|
||||
|
@ -452,7 +457,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
|
|||
}
|
||||
} else if is_shared || is_atomic {
|
||||
p.error_with_pos('generic object cannot be `atomic`or `shared`', pos)
|
||||
break
|
||||
return []table.Param{}, false, false
|
||||
}
|
||||
// if arg_type.is_ptr() {
|
||||
// p.error('cannot mut')
|
||||
|
@ -473,7 +478,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
|
|||
if is_variadic {
|
||||
p.error_with_pos('cannot use ...(variadic) with non-final parameter no $arg_no',
|
||||
pos)
|
||||
break
|
||||
return []table.Param{}, false, false
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
|
@ -488,7 +493,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
|
|||
arg_no++
|
||||
if arg_no > 1024 {
|
||||
p.error_with_pos('too many args', pos)
|
||||
break
|
||||
return []table.Param{}, false, false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -534,7 +539,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
|
|||
} else if is_shared || is_atomic {
|
||||
p.error_with_pos('generic object cannot be `atomic` or `shared`',
|
||||
pos)
|
||||
break
|
||||
return []table.Param{}, false, false
|
||||
}
|
||||
typ = typ.set_nr_muls(1)
|
||||
if is_shared {
|
||||
|
@ -560,7 +565,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
|
|||
if is_variadic && p.tok.kind == .comma {
|
||||
p.error_with_pos('cannot use ...(variadic) with non-final parameter $arg_name',
|
||||
arg_pos[i])
|
||||
break
|
||||
return []table.Param{}, false, false
|
||||
}
|
||||
}
|
||||
if p.tok.kind != .rpar {
|
||||
|
|
|
@ -13,6 +13,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
|
|||
p.inside_for = true
|
||||
if p.tok.kind == .key_match {
|
||||
p.error('cannot use `match` in `for` loop')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
// defer { p.close_scope() }
|
||||
// Infinite loop
|
||||
|
@ -29,6 +30,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
|
|||
// `for i := 0; i < 10; i++ {`
|
||||
if p.tok.kind == .key_mut {
|
||||
p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
mut init := ast.Stmt{}
|
||||
mut cond := p.new_true_expr()
|
||||
|
@ -47,6 +49,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
|
|||
// Disallow `for i := 0; i++; i < ...`
|
||||
if p.tok.kind == .name && p.peek_tok.kind in [.inc, .dec] {
|
||||
p.error('cannot use $p.tok.lit$p.peek_tok.kind as value')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
cond = p.expr(0)
|
||||
has_cond = true
|
||||
|
@ -87,12 +90,15 @@ fn (mut p Parser) for_stmt() ast.Stmt {
|
|||
val_var_name = p.check_name()
|
||||
if key_var_name == val_var_name && key_var_name != '_' {
|
||||
p.error_with_pos('key and value in a for loop cannot be the same', val_var_pos)
|
||||
return ast.Stmt{}
|
||||
}
|
||||
if p.scope.known_var(key_var_name) {
|
||||
p.error('redefinition of key iteration variable `$key_var_name`')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
if p.scope.known_var(val_var_name) {
|
||||
p.error('redefinition of value iteration variable `$val_var_name`')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
p.scope.register(ast.Var{
|
||||
name: key_var_name
|
||||
|
@ -101,10 +107,12 @@ fn (mut p Parser) for_stmt() ast.Stmt {
|
|||
})
|
||||
} else if p.scope.known_var(val_var_name) {
|
||||
p.error('redefinition of value iteration variable `$val_var_name`')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
p.check(.key_in)
|
||||
if p.tok.kind == .name && p.tok.lit in [key_var_name, val_var_name] {
|
||||
p.error('in a `for x in array` loop, the key or value iteration variable `$p.tok.lit` can not be the same as the array variable')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
// arr_expr
|
||||
cond := p.expr(0)
|
||||
|
@ -124,6 +132,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
|
|||
})
|
||||
if key_var_name.len > 0 {
|
||||
p.error_with_pos('cannot declare index variable with range `for`', key_var_pos)
|
||||
return ast.Stmt{}
|
||||
}
|
||||
} else {
|
||||
// this type will be set in checker
|
||||
|
|
|
@ -35,6 +35,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
|
|||
comments << p.eat_comments()
|
||||
if p.tok.kind == .key_match {
|
||||
p.error('cannot use `match` with `if` statements')
|
||||
return ast.IfExpr{}
|
||||
}
|
||||
if p.tok.kind == .lcbr {
|
||||
// else {
|
||||
|
@ -82,6 +83,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
|
|||
p.check(.key_if)
|
||||
if p.tok.kind == .key_match {
|
||||
p.error('cannot use `match` with `if` statements')
|
||||
return ast.IfExpr{}
|
||||
}
|
||||
comments << p.eat_comments()
|
||||
mut cond := ast.Expr{}
|
||||
|
@ -129,6 +131,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
|
|||
if is_comptime {
|
||||
if p.tok.kind == .key_else {
|
||||
p.error('use `\$else` instead of `else` in compile-time `if` branches')
|
||||
return ast.IfExpr{}
|
||||
}
|
||||
if p.peek_tok.kind == .key_else {
|
||||
p.check(.dollar)
|
||||
|
@ -199,6 +202,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
|||
if p.tok.kind == .dotdot {
|
||||
p.error_with_pos('match only supports inclusive (`...`) ranges, not exclusive (`..`)',
|
||||
p.tok.position())
|
||||
return ast.MatchExpr{}
|
||||
} else if p.tok.kind == .ellipsis {
|
||||
p.next()
|
||||
expr2 := p.expr(0)
|
||||
|
@ -283,10 +287,12 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
|
|||
if has_timeout {
|
||||
p.error_with_pos('timeout `> t` and `else` are mutually exclusive `select` keys',
|
||||
p.tok.position())
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
if has_else {
|
||||
p.error_with_pos('at most one `else` branch allowed in `select` block',
|
||||
p.tok.position())
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
is_else = true
|
||||
has_else = true
|
||||
|
@ -295,10 +301,12 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
|
|||
if has_else {
|
||||
p.error_with_pos('`else` and timeout `> t` are mutually exclusive `select` keys',
|
||||
p.tok.position())
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
if has_timeout {
|
||||
p.error_with_pos('at most one timeout `> t` branch allowed in `select` block',
|
||||
p.tok.position())
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
is_timeout = true
|
||||
has_timeout = true
|
||||
|
@ -318,6 +326,7 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
|
|||
exprs, comments := p.expr_list()
|
||||
if exprs.len != 1 {
|
||||
p.error('only one expression allowed as `select` key')
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
if p.tok.kind in [.assign, .decl_assign] {
|
||||
stmt = p.partial_assign_stmt(exprs, comments)
|
||||
|
@ -335,17 +344,20 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
|
|||
ast.ExprStmt {
|
||||
if !stmt.is_expr {
|
||||
p.error_with_pos('select: invalid expression', stmt.pos)
|
||||
return ast.SelectExpr{}
|
||||
} else {
|
||||
match mut stmt.expr {
|
||||
ast.InfixExpr {
|
||||
if stmt.expr.op != .arrow {
|
||||
p.error_with_pos('select key: `<-` operator expected',
|
||||
stmt.expr.pos)
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
}
|
||||
else {
|
||||
p.error_with_pos('select key: send expression (`ch <- x`) expected',
|
||||
stmt.pos)
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,16 +369,19 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
|
|||
if expr.op != .arrow {
|
||||
p.error_with_pos('select key: `<-` operator expected',
|
||||
expr.pos)
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
}
|
||||
else {
|
||||
p.error_with_pos('select key: receive expression expected',
|
||||
stmt.right[0].position())
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
p.error_with_pos('select: transmission statement expected', stmt.position())
|
||||
return ast.SelectExpr{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ pub fn (mut p Parser) parse_map_type() table.Type {
|
|||
// if key_type_sym.kind != .string {
|
||||
if key_type.idx() != table.string_type_idx {
|
||||
p.error('maps can only have string keys for now')
|
||||
return 0
|
||||
}
|
||||
p.check(.rsbr)
|
||||
value_type := p.parse_type()
|
||||
|
@ -151,6 +152,7 @@ pub fn (mut p Parser) parse_type() table.Type {
|
|||
}
|
||||
if p.tok.kind == .mul {
|
||||
p.error('use `&Type` instead of `*Type` when declaring references')
|
||||
return 0
|
||||
}
|
||||
mut nr_amps := 0
|
||||
// &Type
|
||||
|
@ -167,6 +169,7 @@ pub fn (mut p Parser) parse_type() table.Type {
|
|||
typ = p.parse_any_type(language, nr_muls > 0, true)
|
||||
if typ == table.void_type {
|
||||
p.error_with_pos('use `?` instead of `?void`', pos)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
if is_optional {
|
||||
|
@ -184,6 +187,7 @@ pub fn (mut p Parser) parse_type() table.Type {
|
|||
p.error('V arrays are already references behind the scenes,
|
||||
there is no need to use a reference to an array (e.g. use `[]string` instead of `&[]string`).
|
||||
If you need to modify an array in a function, use a mutable argument instead: `fn foo(mut s []string) {}`.')
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return typ
|
||||
|
@ -200,6 +204,7 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool, check
|
|||
// /if !(p.tok.lit in p.table.imports) {
|
||||
if !p.known_import(name) {
|
||||
p.error('unknown module `$p.tok.lit`')
|
||||
return 0
|
||||
}
|
||||
if p.tok.lit in p.imports {
|
||||
p.register_used_import(p.tok.lit)
|
||||
|
@ -210,6 +215,7 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool, check
|
|||
name = '${p.imports[name]}.$p.tok.lit'
|
||||
if !p.tok.lit[0].is_capital() {
|
||||
p.error('imported types must start with a capital letter')
|
||||
return 0
|
||||
}
|
||||
} else if p.expr_mod != '' && !p.in_generic_params { // p.expr_mod is from the struct and not from the generic parameter
|
||||
name = p.expr_mod + '.' + name
|
||||
|
@ -231,6 +237,7 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool, check
|
|||
// multiple return
|
||||
if is_ptr {
|
||||
p.error('parse_type: unexpected `&` before multiple returns')
|
||||
return 0
|
||||
}
|
||||
return p.parse_multi_return_type()
|
||||
}
|
||||
|
@ -248,6 +255,7 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool, check
|
|||
if name == '' {
|
||||
// This means the developer is using some wrong syntax like `x: int` instead of `x int`
|
||||
p.error('bad type syntax')
|
||||
return 0
|
||||
}
|
||||
match name {
|
||||
'voidptr' {
|
||||
|
|
|
@ -359,6 +359,7 @@ pub fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
|
|||
if c > 1000000 {
|
||||
p.error_with_pos('parsed over $c statements from fn $p.cur_fn_name, the parser is probably stuck',
|
||||
p.tok.position())
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -396,8 +397,10 @@ fn (mut p Parser) check(expected token.Kind) {
|
|||
if p.tok.kind != expected {
|
||||
if p.tok.kind == .name {
|
||||
p.error('unexpected name `$p.tok.lit`, expecting `$expected.str()`')
|
||||
return
|
||||
} else {
|
||||
p.error('unexpected `$p.tok.kind.str()`, expecting `$expected.str()`')
|
||||
return
|
||||
}
|
||||
}
|
||||
p.next()
|
||||
|
@ -645,9 +648,11 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
|||
}
|
||||
} else if p.peek_tok.kind == .name {
|
||||
p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position())
|
||||
return ast.Stmt{}
|
||||
} else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr &&
|
||||
p.peek_tok.kind in [.rcbr, .eof] && !p.mark_var_as_used(p.tok.lit) {
|
||||
p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position())
|
||||
return ast.Stmt{}
|
||||
}
|
||||
}
|
||||
return p.parse_multi_expr(is_top_level)
|
||||
|
@ -761,11 +766,13 @@ fn (mut p Parser) attributes() {
|
|||
attr := p.parse_attr()
|
||||
if p.attrs.contains(attr.name) {
|
||||
p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position()))
|
||||
return
|
||||
}
|
||||
if attr.is_ctdefine {
|
||||
if has_ctdefine {
|
||||
p.error_with_pos('only one `[if flag]` may be applied at a time `$attr.name`',
|
||||
start_pos.extend(p.prev_tok.position()))
|
||||
return
|
||||
} else {
|
||||
has_ctdefine = true
|
||||
}
|
||||
|
@ -777,11 +784,13 @@ fn (mut p Parser) attributes() {
|
|||
break
|
||||
}
|
||||
p.error('unexpected `$p.tok.kind.str()`, expecting `;`')
|
||||
return
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
if p.attrs.len == 0 {
|
||||
p.error_with_pos('attributes cannot be empty', p.prev_tok.position().extend(p.tok.position()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -808,8 +817,10 @@ fn (mut p Parser) parse_attr() table.Attr {
|
|||
name = p.check_name()
|
||||
if name == 'unsafe_fn' {
|
||||
p.error_with_pos('please use `[unsafe]` instead', p.tok.position())
|
||||
return table.Attr{}
|
||||
} else if name == 'trusted_fn' {
|
||||
p.error_with_pos('please use `[trusted]` instead', p.tok.position())
|
||||
return table.Attr{}
|
||||
}
|
||||
if p.tok.kind == .colon {
|
||||
p.next()
|
||||
|
@ -841,6 +852,9 @@ pub fn (mut p Parser) warn(s string) {
|
|||
}
|
||||
|
||||
pub fn (mut p Parser) error_with_pos(s string, pos token.Position) {
|
||||
if p.pref.fatal_errors {
|
||||
exit(1)
|
||||
}
|
||||
mut kind := 'error:'
|
||||
if p.pref.output_mode == .stdout {
|
||||
if p.pref.is_verbose {
|
||||
|
@ -902,6 +916,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
|
|||
left0 := left[0]
|
||||
if tok.kind == .key_mut && p.tok.kind != .decl_assign {
|
||||
p.error('expecting `:=` (e.g. `mut x :=`)')
|
||||
return ast.Stmt{}
|
||||
}
|
||||
if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
|
||||
return p.partial_assign_stmt(left, left_comments)
|
||||
|
@ -910,6 +925,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
|
|||
!(left0 is ast.InfixExpr && (left0 as ast.InfixExpr).op in [.left_shift, .arrow]) && left0 !is
|
||||
ast.ComptimeCall {
|
||||
p.error_with_pos('expression evaluated but not used', left0.position())
|
||||
return ast.Stmt{}
|
||||
}
|
||||
if left.len == 1 {
|
||||
return ast.ExprStmt{
|
||||
|
@ -980,6 +996,7 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident {
|
|||
}
|
||||
} else {
|
||||
p.error('unexpected token `$p.tok.lit`')
|
||||
return ast.Ident{}
|
||||
}
|
||||
return ast.Ident{}
|
||||
}
|
||||
|
@ -1040,9 +1057,11 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
}
|
||||
'len', 'init' {
|
||||
p.error('`$key` cannot be initialized for `chan`. Did you mean `cap`?')
|
||||
return ast.Expr{}
|
||||
}
|
||||
else {
|
||||
p.error('wrong field `$key`, expecting `cap`')
|
||||
return ast.Expr{}
|
||||
}
|
||||
}
|
||||
last_pos = p.tok.position()
|
||||
|
@ -1062,12 +1081,14 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
} else {
|
||||
// don't allow any other string prefix except `r`, `js` and `c`
|
||||
p.error('only `c`, `r`, `js` are recognized string prefixes, but you tried to use `$p.tok.lit`')
|
||||
return ast.Expr{}
|
||||
}
|
||||
}
|
||||
// don't allow r`byte` and c`byte`
|
||||
if p.tok.lit in ['r', 'c'] && p.peek_tok.kind == .chartoken {
|
||||
opt := if p.tok.lit == 'r' { '`r` (raw string)' } else { '`c` (c string)' }
|
||||
p.error('cannot use $opt with `byte` and `rune`')
|
||||
return ast.Expr{}
|
||||
}
|
||||
known_var := p.mark_var_as_used(p.tok.lit)
|
||||
mut is_mod_cast := false
|
||||
|
@ -1321,6 +1342,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
|||
args := p.call_args()
|
||||
if is_filter && args.len != 1 {
|
||||
p.error('needs exactly 1 argument')
|
||||
return ast.Expr{}
|
||||
}
|
||||
p.check(.rpar)
|
||||
mut or_stmts := []ast.Stmt{}
|
||||
|
@ -1486,6 +1508,7 @@ fn (mut p Parser) string_expr() ast.Expr {
|
|||
p.next()
|
||||
} else {
|
||||
p.error('format specifier may only be one letter')
|
||||
return ast.Expr{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1544,13 +1567,16 @@ fn (mut p Parser) module_decl() ast.Module {
|
|||
name = p.check_name()
|
||||
if module_pos.line_nr != pos.line_nr {
|
||||
p.error_with_pos('`module` and `$name` must be at same line', pos)
|
||||
return ast.Module{}
|
||||
}
|
||||
pos = p.tok.position()
|
||||
if module_pos.line_nr == pos.line_nr && p.tok.kind != .comment {
|
||||
if p.tok.kind != .name {
|
||||
p.error_with_pos('`module x` syntax error', pos)
|
||||
return ast.Module{}
|
||||
} else {
|
||||
p.error_with_pos('`module x` can only declare one module', pos)
|
||||
return ast.Module{}
|
||||
}
|
||||
}
|
||||
module_pos = module_pos.extend(pos)
|
||||
|
@ -1584,10 +1610,12 @@ fn (mut p Parser) import_stmt() ast.Import {
|
|||
pos := p.tok.position()
|
||||
if p.tok.kind == .lpar {
|
||||
p.error_with_pos('`import()` has been deprecated, use `import x` instead', pos)
|
||||
return ast.Import{}
|
||||
}
|
||||
mut mod_name := p.check_name()
|
||||
if import_pos.line_nr != pos.line_nr {
|
||||
p.error_with_pos('`import` statements must be a single line', pos)
|
||||
return ast.Import{}
|
||||
}
|
||||
mut mod_alias := mod_name
|
||||
for p.tok.kind == .dot {
|
||||
|
@ -1595,9 +1623,11 @@ fn (mut p Parser) import_stmt() ast.Import {
|
|||
pos_t := p.tok.position()
|
||||
if p.tok.kind != .name {
|
||||
p.error_with_pos('module syntax error, please use `x.y.z`', pos)
|
||||
return ast.Import{}
|
||||
}
|
||||
if import_pos.line_nr != pos_t.line_nr {
|
||||
p.error_with_pos('`import` and `submodule` must be at same line', pos)
|
||||
return ast.Import{}
|
||||
}
|
||||
submod_name := p.check_name()
|
||||
mod_name += '.' + submod_name
|
||||
|
@ -1608,6 +1638,7 @@ fn (mut p Parser) import_stmt() ast.Import {
|
|||
mod_alias = p.check_name()
|
||||
if mod_alias == mod_name.split('.').last() {
|
||||
p.error_with_pos('import alias `$mod_name as $mod_alias` is redundant', p.prev_tok.position())
|
||||
return ast.Import{}
|
||||
}
|
||||
}
|
||||
mut node := ast.Import{
|
||||
|
@ -1623,6 +1654,7 @@ fn (mut p Parser) import_stmt() ast.Import {
|
|||
if import_pos.line_nr == pos_t.line_nr {
|
||||
if p.tok.kind !in [.lcbr, .eof, .comment] {
|
||||
p.error_with_pos('cannot import multiple modules at a time', pos_t)
|
||||
return ast.Import{}
|
||||
}
|
||||
}
|
||||
p.imports[mod_alias] = mod_name
|
||||
|
@ -1639,10 +1671,12 @@ fn (mut p Parser) import_syms(mut parent ast.Import) {
|
|||
pos_t := p.tok.position()
|
||||
if p.tok.kind == .rcbr { // closed too early
|
||||
p.error_with_pos('empty `$parent.mod` import set, remove `{}`', pos_t)
|
||||
return
|
||||
}
|
||||
if p.tok.kind != .name { // not a valid inner name
|
||||
p.error_with_pos('import syntax error, please specify a valid fn or type name',
|
||||
pos_t)
|
||||
return
|
||||
}
|
||||
for p.tok.kind == .name {
|
||||
pos := p.tok.position()
|
||||
|
@ -1697,6 +1731,7 @@ fn (mut p Parser) import_syms(mut parent ast.Import) {
|
|||
}
|
||||
if p.tok.kind != .rcbr {
|
||||
p.error_with_pos('import syntax error, no closing `}`', p.tok.position())
|
||||
return
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
|
@ -1739,6 +1774,7 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
|
|||
p.check(.assign)
|
||||
if p.tok.kind == .key_fn {
|
||||
p.error('const initializer fn literal is not a constant')
|
||||
return ast.ConstDecl{}
|
||||
}
|
||||
expr := p.expr(0)
|
||||
field := ast.ConstField{
|
||||
|
@ -1795,12 +1831,14 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
|
|||
p.mod != 'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !p.pref.enable_globals && !p.pref.is_fmt &&
|
||||
p.mod !in global_enabled_mods {
|
||||
p.error('use `v --enable-globals ...` to enable globals')
|
||||
return ast.GlobalDecl{}
|
||||
}
|
||||
start_pos := p.tok.position()
|
||||
end_pos := p.tok.position()
|
||||
p.check(.key_global)
|
||||
if p.tok.kind != .lpar {
|
||||
p.error('globals must be grouped, e.g. `__global ( a = int(1) )`')
|
||||
return ast.GlobalDecl{}
|
||||
}
|
||||
p.next() // (
|
||||
mut fields := []ast.GlobalField{}
|
||||
|
@ -1819,11 +1857,13 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
|
|||
typ := p.parse_type()
|
||||
if p.tok.kind == .assign {
|
||||
p.error('global assign must have the type around the value, use `__global ( name = type(value) )`')
|
||||
return ast.GlobalDecl{}
|
||||
}
|
||||
mut expr := ast.Expr{}
|
||||
if has_expr {
|
||||
if p.tok.kind != .lpar {
|
||||
p.error('global assign must have a type and value, use `__global ( name = type(value) )` or `__global ( name type )`')
|
||||
return ast.GlobalDecl{}
|
||||
}
|
||||
p.next() // (
|
||||
expr = p.expr(0)
|
||||
|
@ -1862,6 +1902,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||
if enum_name.len == 1 {
|
||||
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
||||
end_pos)
|
||||
return ast.EnumDecl{}
|
||||
}
|
||||
name := p.prepend_mod(enum_name)
|
||||
p.check(.lcbr)
|
||||
|
@ -1896,11 +1937,13 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||
if is_flag {
|
||||
if fields.len > 32 {
|
||||
p.error('when an enum is used as bit field, it must have a max of 32 fields')
|
||||
return ast.EnumDecl{}
|
||||
}
|
||||
for f in fields {
|
||||
if f.has_expr {
|
||||
p.error_with_pos('when an enum is used as a bit field, you can not assign custom values',
|
||||
f.pos)
|
||||
return ast.EnumDecl{}
|
||||
}
|
||||
}
|
||||
pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' }
|
||||
|
@ -1950,6 +1993,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
if name.len == 1 && name[0].is_capital() {
|
||||
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
||||
decl_pos)
|
||||
return ast.TypeDecl{}
|
||||
}
|
||||
mut sum_variants := []ast.SumTypeVariant{}
|
||||
p.check(.assign)
|
||||
|
@ -2155,10 +2199,12 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
|
|||
p.next()
|
||||
if p.tok.kind != .lcbr {
|
||||
p.error_with_pos('please use `unsafe {`', p.tok.position())
|
||||
return ast.Stmt{}
|
||||
}
|
||||
p.next()
|
||||
if p.inside_unsafe {
|
||||
p.error_with_pos('already inside `unsafe` block', pos)
|
||||
return ast.Stmt{}
|
||||
}
|
||||
if p.tok.kind == .rcbr {
|
||||
// `unsafe {}`
|
||||
|
|
|
@ -50,9 +50,16 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
}
|
||||
.dollar {
|
||||
match p.peek_tok.kind {
|
||||
.name { return p.vweb() }
|
||||
.key_if { return p.if_expr(true) }
|
||||
else { p.error_with_pos('unexpected `$`', p.peek_tok.position()) }
|
||||
.name {
|
||||
return p.vweb()
|
||||
}
|
||||
.key_if {
|
||||
return p.if_expr(true)
|
||||
}
|
||||
else {
|
||||
p.error_with_pos('unexpected `$`', p.peek_tok.position())
|
||||
return ast.Expr{}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chartoken {
|
||||
|
@ -100,6 +107,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
p.next()
|
||||
if p.inside_unsafe {
|
||||
p.error_with_pos('already inside `unsafe` block', pos)
|
||||
return ast.Expr{}
|
||||
}
|
||||
p.inside_unsafe = true
|
||||
p.check(.lcbr)
|
||||
|
@ -193,9 +201,11 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
p.next()
|
||||
s := if p.tok.lit != '' { '`$p.tok.lit`' } else { p.tok.kind.str() }
|
||||
p.error_with_pos('unexpected $s, expecting `:`', p.tok.position())
|
||||
return ast.Expr{}
|
||||
} else {
|
||||
p.error_with_pos('unexpected `$p.tok.lit`, expecting struct key',
|
||||
p.tok.position())
|
||||
return ast.Expr{}
|
||||
}
|
||||
}
|
||||
p.check(.rcbr)
|
||||
|
@ -235,6 +245,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
// eof should be handled where it happens
|
||||
p.error_with_pos('invalid expression: unexpected $p.tok.kind.str() token',
|
||||
p.tok.position())
|
||||
return ast.Expr{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ fn (mut p Parser) sql_expr() ast.Expr {
|
|||
p.check_name() // `by`
|
||||
} else {
|
||||
p.error_with_pos('use `order by` in ORM queries', order_pos)
|
||||
return ast.Expr{}
|
||||
}
|
||||
has_order = true
|
||||
order_expr = p.expr(0)
|
||||
|
@ -139,6 +140,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
|
|||
}
|
||||
else {
|
||||
p.error('can only insert variables')
|
||||
return ast.SqlStmt{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,9 +149,11 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
|
|||
mut update_exprs := []ast.Expr{cap: 5}
|
||||
if kind == .insert && n != 'into' {
|
||||
p.error('expecting `into`')
|
||||
return ast.SqlStmt{}
|
||||
} else if kind == .update {
|
||||
if n != 'set' {
|
||||
p.error('expecting `set`')
|
||||
return ast.SqlStmt{}
|
||||
}
|
||||
for {
|
||||
column := p.check_name()
|
||||
|
@ -164,6 +168,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
|
|||
}
|
||||
} else if kind == .delete && n != 'from' {
|
||||
p.error('expecting `from`')
|
||||
return ast.SqlStmt{}
|
||||
}
|
||||
mut table_type := table.Type(0)
|
||||
mut where_expr := ast.Expr{}
|
||||
|
@ -179,13 +184,13 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
|
|||
idx := p.table.find_type_idx(p.prepend_mod(table_name))
|
||||
table_type = table.new_type(idx)
|
||||
}
|
||||
p.check_sql_keyword('where')
|
||||
p.check_sql_keyword('where') or { return ast.SqlStmt{} }
|
||||
where_expr = p.expr(0)
|
||||
} else if kind == .delete {
|
||||
table_type = p.parse_type()
|
||||
sym := p.table.get_type_symbol(table_type)
|
||||
table_name = sym.name
|
||||
p.check_sql_keyword('where')
|
||||
p.check_sql_keyword('where') or { return ast.SqlStmt{} }
|
||||
where_expr = p.expr(0)
|
||||
}
|
||||
p.check(.rcbr)
|
||||
|
@ -202,8 +207,10 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut p Parser) check_sql_keyword(name string) {
|
||||
fn (mut p Parser) check_sql_keyword(name string) ?bool {
|
||||
if p.check_name() != name {
|
||||
p.error('orm: expecting `$name`')
|
||||
return none
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
if name.len == 1 && name[0].is_capital() {
|
||||
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
||||
name_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
mut generic_types := []table.Type{}
|
||||
if p.tok.kind == .lt {
|
||||
|
@ -61,13 +62,16 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
no_body := p.tok.kind != .lcbr
|
||||
if language == .v && no_body {
|
||||
p.error('`$p.tok.lit` lacks body')
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
if language == .v &&
|
||||
p.mod != 'builtin' && name.len > 0 && !name[0].is_capital() && !p.pref.translated {
|
||||
p.error_with_pos('struct name `$name` must begin with capital letter', name_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
if name.len == 1 {
|
||||
p.error_with_pos('struct names must have more than one character', name_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
// println('struct decl $name')
|
||||
mut ast_fields := []ast.StructField{}
|
||||
|
@ -100,6 +104,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
if p.tok.kind == .key_mut {
|
||||
if pub_mut_pos != -1 {
|
||||
p.error('redefinition of `pub mut` section')
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
p.next()
|
||||
pub_mut_pos = fields.len
|
||||
|
@ -109,6 +114,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
} else {
|
||||
if pub_pos != -1 {
|
||||
p.error('redefinition of `pub` section')
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
pub_pos = fields.len
|
||||
is_field_pub = true
|
||||
|
@ -119,6 +125,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
} else if p.tok.kind == .key_mut {
|
||||
if mut_pos != -1 {
|
||||
p.error('redefinition of `mut` section')
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
p.next()
|
||||
p.check(.colon)
|
||||
|
@ -129,6 +136,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
} else if p.tok.kind == .key_global {
|
||||
if global_pos != -1 {
|
||||
p.error('redefinition of `global` section')
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
p.next()
|
||||
p.check(.colon)
|
||||
|
@ -172,6 +180,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
field_name = symbol_name
|
||||
if typ in embedded_structs {
|
||||
p.error_with_pos('cannot embed `$field_name` more than once', type_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
embedded_structs << typ
|
||||
} else {
|
||||
|
@ -275,6 +284,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
if ret == -1 {
|
||||
p.error_with_pos('cannot register struct `$name`, another type with this name exists',
|
||||
name_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
p.expr_mod = ''
|
||||
return ast.StructDecl{
|
||||
|
@ -390,6 +400,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
if reg_idx == -1 {
|
||||
p.error_with_pos('cannot register interface `$interface_name`, another type with this name exists',
|
||||
name_pos)
|
||||
return ast.InterfaceDecl{}
|
||||
}
|
||||
typ := table.new_type(reg_idx)
|
||||
mut ts := p.table.get_type_symbol(typ)
|
||||
|
@ -403,9 +414,11 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
name := p.check_name()
|
||||
if ts.has_method(name) {
|
||||
p.error_with_pos('duplicate method `$name`', method_start_pos)
|
||||
return ast.InterfaceDecl{}
|
||||
}
|
||||
if util.contains_capital(name) {
|
||||
p.error('interface methods cannot contain uppercase letters, use snake_case instead')
|
||||
return ast.InterfaceDecl{}
|
||||
}
|
||||
// field_names << name
|
||||
args2, _, _ := p.fn_args() // TODO merge table.Param and ast.Arg to avoid this
|
||||
|
|
|
@ -123,6 +123,7 @@ pub mut:
|
|||
skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js)
|
||||
skip_warnings bool // like C's "-w"
|
||||
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error
|
||||
fatal_errors bool // unconditionally exit after the first error with exit(1)
|
||||
reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them
|
||||
use_color ColorOutput // whether the warnings/errors should use ANSI color escapes.
|
||||
is_parallel bool
|
||||
|
@ -172,6 +173,9 @@ pub fn parse_args(args []string) (&Preferences, string) {
|
|||
'-progress' {
|
||||
// processed by testing tools in cmd/tools/modules/testing/common.v
|
||||
}
|
||||
'-Wfatal-errors' {
|
||||
res.fatal_errors = true
|
||||
}
|
||||
'-silent' {
|
||||
res.output_mode = .silent
|
||||
}
|
||||
|
|
|
@ -1236,6 +1236,9 @@ pub fn (mut s Scanner) error(msg string) {
|
|||
eprintln(util.formatted_error('error:', msg, s.file_path, pos))
|
||||
exit(1)
|
||||
} else {
|
||||
if s.pref.fatal_errors {
|
||||
exit(1)
|
||||
}
|
||||
s.errors << errors.Error{
|
||||
file_path: s.file_path
|
||||
pos: pos
|
||||
|
|
Loading…
Reference in New Issue