1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

parser: prepare for better VLS integration, more accurate parser errors (#7119)

This commit is contained in:
Daniel Däschle 2020-12-04 19:34:05 +01:00 committed by GitHub
parent 52fb7033c3
commit 47d0ed308d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 171 additions and 36 deletions

View File

@ -191,14 +191,12 @@ fn (mut f MDFile) check_examples() (int, int) {
mut should_cleanup_vfile := true mut should_cleanup_vfile := true
// eprintln('>>> checking example $vfile ...') // eprintln('>>> checking example $vfile ...')
vcontent := e.text.join('\n') vcontent := e.text.join('\n')
os.write_file(vfile, vcontent) or { os.write_file(vfile, vcontent) or { panic(err) }
panic(err)
}
mut acommands := e.command.split(' ') mut acommands := e.command.split(' ')
for command in acommands { for command in acommands {
match command { match command {
'compile' { '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 { } os.rm('x.c') or { }
if res != 0 { if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to compile')) eprintln(eline(f.path, e.sline, 0, 'example failed to compile'))
@ -210,7 +208,7 @@ fn (mut f MDFile) check_examples() (int, int) {
oks++ oks++
} }
'live' { '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 { if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live')) eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live'))
eprintln(vcontent) eprintln(vcontent)
@ -221,7 +219,7 @@ fn (mut f MDFile) check_examples() (int, int) {
oks++ oks++
} }
'failcompile' { '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 { } os.rm('x.c') or { }
if res == 0 { if res == 0 {
eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled')) eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled'))
@ -233,7 +231,7 @@ fn (mut f MDFile) check_examples() (int, int) {
oks++ oks++
} }
'oksyntax' { 'oksyntax' {
res := os.system('"$vexe" -silent -check-syntax $vfile') res := os.system('"$vexe" -w -Wfatal-errors -check-syntax $vfile')
if res != 0 { if res != 0 {
eprintln(eline(f.path, e.sline, 0, '`oksyntax` example with invalid syntax')) eprintln(eline(f.path, e.sline, 0, '`oksyntax` example with invalid syntax'))
eprintln(vcontent) eprintln(vcontent)
@ -244,7 +242,7 @@ fn (mut f MDFile) check_examples() (int, int) {
oks++ oks++
} }
'badsyntax' { 'badsyntax' {
res := os.system('"$vexe" -silent -check-syntax $vfile') res := os.system('"$vexe" -w -Wfatal-errors -check-syntax $vfile')
if res == 0 { if res == 0 {
eprintln(eline(f.path, e.sline, 0, '`badsyntax` example can be parsed fine')) eprintln(eline(f.path, e.sline, 0, '`badsyntax` example can be parsed fine'))
eprintln(vcontent) eprintln(vcontent)
@ -262,9 +260,7 @@ fn (mut f MDFile) check_examples() (int, int) {
} }
} }
if should_cleanup_vfile { if should_cleanup_vfile {
os.rm(vfile) or { os.rm(vfile) or { panic(err) }
panic(err)
}
} }
} }
return errors, oks return errors, oks

View File

@ -125,6 +125,9 @@ The build flags are shared by the build and run commands:
-W -W
Treat all warnings as errors, even in development builds. 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`. For C-specific build flags, use `v help build-c`.
See also: See also:

View File

@ -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`) In production mode (enabled by passing the `-prod` flag to v `v -prod foo.v`)
it will not compile at all (like in Go). it will not compile at all (like in Go).
<!-- this should be `failcompile`, but it compiles --> ```v failcompile
```v
fn main() { fn main() {
a := 10 a := 10
if true { if true {
@ -1736,9 +1735,9 @@ color = .green
println(color) // "green" println(color) // "green"
match color { match color {
.red { ... } .red { println('the color was red') }
.green { ... } .green { println('the color was green') }
.blue { ... } .blue { println('the color was blue') }
} }
``` ```

View File

@ -4391,6 +4391,9 @@ fn (mut c Checker) warn_or_error(message string, pos token.Position, warn bool)
return return
} }
if !warn { if !warn {
if c.pref.fatal_errors {
exit(1)
}
c.nr_errors++ c.nr_errors++
if pos.line_nr !in c.error_lines { if pos.line_nr !in c.error_lines {
err := errors.Error{ err := errors.Error{

View File

@ -11,33 +11,34 @@ fn (mut p Parser) assign_stmt() ast.Stmt {
return p.partial_assign_stmt(exprs, comments) 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 { match val {
ast.Ident { ast.Ident {
for expr in exprs { for expr in exprs {
if expr is ast.Ident { if expr is ast.Ident {
if expr.name == val.name { if expr.name == val.name {
p.error_with_pos('undefined variable: `$val.name`', val.pos) p.error_with_pos('undefined variable: `$val.name`', val.pos)
return error('undefined variable: `$val.name`')
} }
} }
} }
} }
ast.InfixExpr { ast.InfixExpr {
p.check_undefined_variables(exprs, val.left) p.check_undefined_variables(exprs, val.left) ?
p.check_undefined_variables(exprs, val.right) p.check_undefined_variables(exprs, val.right) ?
} }
ast.ParExpr { ast.ParExpr {
p.check_undefined_variables(exprs, val.expr) p.check_undefined_variables(exprs, val.expr) ?
} }
ast.PostfixExpr { ast.PostfixExpr {
p.check_undefined_variables(exprs, val.expr) p.check_undefined_variables(exprs, val.expr) ?
} }
ast.PrefixExpr { ast.PrefixExpr {
p.check_undefined_variables(exprs, val.right) p.check_undefined_variables(exprs, val.right) ?
} }
ast.StringInterLiteral { ast.StringInterLiteral {
for expr_ in val.exprs { for expr_ in val.exprs {
p.check_undefined_variables(exprs, expr_) p.check_undefined_variables(exprs, expr_) ?
} }
} }
else {} 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) has_cross_var = p.check_cross_variables(left, r)
if op !in [.assign, .decl_assign] { if op !in [.assign, .decl_assign] {
p.error('unexpected $op.str(), expecting := or = or comma') p.error('unexpected $op.str(), expecting := or = or comma')
return ast.Stmt{}
} }
if has_cross_var { if has_cross_var {
break break
@ -118,6 +120,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
if op == .decl_assign { if op == .decl_assign {
if p.scope.known_var(lx.name) { if p.scope.known_var(lx.name) {
p.error_with_pos('redefinition of `$lx.name`', lx.pos) p.error_with_pos('redefinition of `$lx.name`', lx.pos)
return ast.Stmt{}
} }
mut share := table.ShareType(0) mut share := table.ShareType(0)
if lx.info is ast.IdentVar { 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 { if !p.pref.translated {
p.error_with_pos('static variables are supported only in -translated mode', p.error_with_pos('static variables are supported only in -translated mode',
lx.pos) lx.pos)
return ast.Stmt{}
} }
is_static = true 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 { if op == .decl_assign {
p.error_with_pos('non-name `$lx.left[$lx.index]` on left side of `:=`', p.error_with_pos('non-name `$lx.left[$lx.index]` on left side of `:=`',
lx.pos) lx.pos)
return ast.Stmt{}
} }
lx.is_setter = true 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 { if op == .decl_assign {
p.error_with_pos('struct fields can only be declared during the initialization', p.error_with_pos('struct fields can only be declared during the initialization',
lx.pos) lx.pos)
return ast.Stmt{}
} }
} }
else { else {

View File

@ -51,12 +51,14 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
n := p.check_name() // skip `vweb.html()` TODO n := p.check_name() // skip `vweb.html()` TODO
if n != 'vweb' { if n != 'vweb' {
p.error(error_msg) p.error(error_msg)
return ast.ComptimeCall{}
} }
p.check(.dot) p.check(.dot)
} }
n := p.check_name() // (.name) n := p.check_name() // (.name)
if n != 'html' && n != 'tmpl' { if n != 'html' && n != 'tmpl' {
p.error(error_msg) p.error(error_msg)
return ast.ComptimeCall{}
} }
is_html := n == 'html' is_html := n == 'html'
p.check(.lpar) p.check(.lpar)
@ -85,8 +87,10 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
if !os.exists(path) { if !os.exists(path) {
if is_html { if is_html {
p.error('vweb HTML template "$path" not found') p.error('vweb HTML template "$path" not found')
return ast.ComptimeCall{}
} else { } else {
p.error('template file "$path" not found') p.error('template file "$path" not found')
return ast.ComptimeCall{}
} }
} }
// println('path is now "$path"') // println('path is now "$path"')
@ -171,6 +175,7 @@ fn (mut p Parser) comp_for() ast.CompFor {
kind = .fields kind = .fields
} else { } else {
p.error('unknown kind `$for_val`, available are: `methods` or `fields`') p.error('unknown kind `$for_val`, available are: `methods` or `fields`')
return ast.CompFor{}
} }
spos := p.tok.position() spos := p.tok.position()
stmts := p.parse_block() stmts := p.parse_block()

View File

@ -65,6 +65,7 @@ fn (mut p Parser) array_init() ast.ArrayInit {
n := p.check_name() n := p.check_name()
if n != 'init' { if n != 'init' {
p.error_with_pos('expected `init:`, not `$n`', pos) p.error_with_pos('expected `init:`, not `$n`', pos)
return ast.ArrayInit{}
} }
p.check(.colon) p.check(.colon)
has_default = true has_default = true
@ -117,6 +118,7 @@ fn (mut p Parser) array_init() ast.ArrayInit {
} }
else { else {
p.error('wrong field `$key`, expecting `len`, `cap`, or `init`') p.error('wrong field `$key`, expecting `len`, `cap`, or `init`')
return ast.ArrayInit{}
} }
} }
if p.tok.kind != .rcbr { if p.tok.kind != .rcbr {

View File

@ -111,7 +111,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg {
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
if p.tok.kind == .eof { if p.tok.kind == .eof {
p.error_with_pos('unexpected eof reached, while parsing call argument', start_pos) p.error_with_pos('unexpected eof reached, while parsing call argument', start_pos)
break return []
} }
is_shared := p.tok.kind == .key_shared is_shared := p.tok.kind == .key_shared
is_atomic := p.tok.kind == .key_atomic 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) rec_type = p.parse_type_with_mut(rec_mut)
if is_amp && rec_mut { if is_amp && rec_mut {
p.error('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`') p.error('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`')
return ast.FnDecl{}
} }
if is_shared { if is_shared {
rec_type = rec_type.set_flag(.shared_f) 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() } name = if language == .js { p.check_js_name() } else { p.check_name() }
if language == .v && !p.pref.translated && util.contains_capital(name) { if language == .v && !p.pref.translated && util.contains_capital(name) {
p.error('function names cannot contain uppercase letters, use snake_case instead') p.error('function names cannot contain uppercase letters, use snake_case instead')
return ast.FnDecl{}
} }
type_sym := p.table.get_type_symbol(rec_type) type_sym := p.table.get_type_symbol(rec_type)
// interfaces are handled in the checker, methods can not be defined on them this way // 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_) { if is_method && (type_sym.has_method(name) && type_sym.kind != .interface_) {
p.error('duplicate method `$name`') p.error('duplicate method `$name`')
return ast.FnDecl{}
} }
} }
if p.tok.kind in [.plus, .minus, .mul, .div, .mod] { 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 { for param in params {
if p.scope.known_var(param.name) { if p.scope.known_var(param.name) {
p.error_with_pos('redefinition of parameter `$param.name`', param.pos) p.error_with_pos('redefinition of parameter `$param.name`', param.pos)
break return ast.FnDecl{}
} }
p.scope.register(ast.Var{ p.scope.register(ast.Var{
name: param.name 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 // 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 { 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') p.error('cannot define new methods on non-local type $type_sym.name')
return ast.FnDecl{}
} }
// p.warn('reg method $type_sym.name . $name ()') // p.warn('reg method $type_sym.name . $name ()')
type_sym_method_idx = type_sym.register_method(table.Fn{ 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() p.close_scope()
if !no_body && are_args_type_only { if !no_body && are_args_type_only {
p.error_with_pos('functions with type only args can not have bodies', body_start_pos) p.error_with_pos('functions with type only args can not have bodies', body_start_pos)
return ast.FnDecl{}
} }
return ast.FnDecl{ return ast.FnDecl{
name: name name: name
@ -452,7 +457,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
} }
} else if is_shared || is_atomic { } else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic`or `shared`', pos) p.error_with_pos('generic object cannot be `atomic`or `shared`', pos)
break return []table.Param{}, false, false
} }
// if arg_type.is_ptr() { // if arg_type.is_ptr() {
// p.error('cannot mut') // p.error('cannot mut')
@ -473,7 +478,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
if is_variadic { if is_variadic {
p.error_with_pos('cannot use ...(variadic) with non-final parameter no $arg_no', p.error_with_pos('cannot use ...(variadic) with non-final parameter no $arg_no',
pos) pos)
break return []table.Param{}, false, false
} }
p.next() p.next()
} }
@ -488,7 +493,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
arg_no++ arg_no++
if arg_no > 1024 { if arg_no > 1024 {
p.error_with_pos('too many args', pos) p.error_with_pos('too many args', pos)
break return []table.Param{}, false, false
} }
} }
} else { } else {
@ -534,7 +539,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
} else if is_shared || is_atomic { } else if is_shared || is_atomic {
p.error_with_pos('generic object cannot be `atomic` or `shared`', p.error_with_pos('generic object cannot be `atomic` or `shared`',
pos) pos)
break return []table.Param{}, false, false
} }
typ = typ.set_nr_muls(1) typ = typ.set_nr_muls(1)
if is_shared { if is_shared {
@ -560,7 +565,7 @@ fn (mut p Parser) fn_args() ([]table.Param, bool, bool) {
if is_variadic && p.tok.kind == .comma { if is_variadic && p.tok.kind == .comma {
p.error_with_pos('cannot use ...(variadic) with non-final parameter $arg_name', p.error_with_pos('cannot use ...(variadic) with non-final parameter $arg_name',
arg_pos[i]) arg_pos[i])
break return []table.Param{}, false, false
} }
} }
if p.tok.kind != .rpar { if p.tok.kind != .rpar {

View File

@ -13,6 +13,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
p.inside_for = true p.inside_for = true
if p.tok.kind == .key_match { if p.tok.kind == .key_match {
p.error('cannot use `match` in `for` loop') p.error('cannot use `match` in `for` loop')
return ast.Stmt{}
} }
// defer { p.close_scope() } // defer { p.close_scope() }
// Infinite loop // Infinite loop
@ -29,6 +30,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
// `for i := 0; i < 10; i++ {` // `for i := 0; i < 10; i++ {`
if p.tok.kind == .key_mut { if p.tok.kind == .key_mut {
p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`') p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`')
return ast.Stmt{}
} }
mut init := ast.Stmt{} mut init := ast.Stmt{}
mut cond := p.new_true_expr() mut cond := p.new_true_expr()
@ -47,6 +49,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
// Disallow `for i := 0; i++; i < ...` // Disallow `for i := 0; i++; i < ...`
if p.tok.kind == .name && p.peek_tok.kind in [.inc, .dec] { 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') p.error('cannot use $p.tok.lit$p.peek_tok.kind as value')
return ast.Stmt{}
} }
cond = p.expr(0) cond = p.expr(0)
has_cond = true has_cond = true
@ -87,12 +90,15 @@ fn (mut p Parser) for_stmt() ast.Stmt {
val_var_name = p.check_name() val_var_name = p.check_name()
if key_var_name == val_var_name && key_var_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) 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) { if p.scope.known_var(key_var_name) {
p.error('redefinition of key iteration variable `$key_var_name`') p.error('redefinition of key iteration variable `$key_var_name`')
return ast.Stmt{}
} }
if p.scope.known_var(val_var_name) { if p.scope.known_var(val_var_name) {
p.error('redefinition of value iteration variable `$val_var_name`') p.error('redefinition of value iteration variable `$val_var_name`')
return ast.Stmt{}
} }
p.scope.register(ast.Var{ p.scope.register(ast.Var{
name: key_var_name 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) { } else if p.scope.known_var(val_var_name) {
p.error('redefinition of value iteration variable `$val_var_name`') p.error('redefinition of value iteration variable `$val_var_name`')
return ast.Stmt{}
} }
p.check(.key_in) p.check(.key_in)
if p.tok.kind == .name && p.tok.lit in [key_var_name, val_var_name] { 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') 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 // arr_expr
cond := p.expr(0) cond := p.expr(0)
@ -124,6 +132,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
}) })
if key_var_name.len > 0 { if key_var_name.len > 0 {
p.error_with_pos('cannot declare index variable with range `for`', key_var_pos) p.error_with_pos('cannot declare index variable with range `for`', key_var_pos)
return ast.Stmt{}
} }
} else { } else {
// this type will be set in checker // this type will be set in checker

View File

@ -35,6 +35,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
comments << p.eat_comments() comments << p.eat_comments()
if p.tok.kind == .key_match { if p.tok.kind == .key_match {
p.error('cannot use `match` with `if` statements') p.error('cannot use `match` with `if` statements')
return ast.IfExpr{}
} }
if p.tok.kind == .lcbr { if p.tok.kind == .lcbr {
// else { // else {
@ -82,6 +83,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
p.check(.key_if) p.check(.key_if)
if p.tok.kind == .key_match { if p.tok.kind == .key_match {
p.error('cannot use `match` with `if` statements') p.error('cannot use `match` with `if` statements')
return ast.IfExpr{}
} }
comments << p.eat_comments() comments << p.eat_comments()
mut cond := ast.Expr{} mut cond := ast.Expr{}
@ -129,6 +131,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
if is_comptime { if is_comptime {
if p.tok.kind == .key_else { if p.tok.kind == .key_else {
p.error('use `\$else` instead of `else` in compile-time `if` branches') p.error('use `\$else` instead of `else` in compile-time `if` branches')
return ast.IfExpr{}
} }
if p.peek_tok.kind == .key_else { if p.peek_tok.kind == .key_else {
p.check(.dollar) p.check(.dollar)
@ -199,6 +202,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
if p.tok.kind == .dotdot { if p.tok.kind == .dotdot {
p.error_with_pos('match only supports inclusive (`...`) ranges, not exclusive (`..`)', p.error_with_pos('match only supports inclusive (`...`) ranges, not exclusive (`..`)',
p.tok.position()) p.tok.position())
return ast.MatchExpr{}
} else if p.tok.kind == .ellipsis { } else if p.tok.kind == .ellipsis {
p.next() p.next()
expr2 := p.expr(0) expr2 := p.expr(0)
@ -283,10 +287,12 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
if has_timeout { if has_timeout {
p.error_with_pos('timeout `> t` and `else` are mutually exclusive `select` keys', p.error_with_pos('timeout `> t` and `else` are mutually exclusive `select` keys',
p.tok.position()) p.tok.position())
return ast.SelectExpr{}
} }
if has_else { if has_else {
p.error_with_pos('at most one `else` branch allowed in `select` block', p.error_with_pos('at most one `else` branch allowed in `select` block',
p.tok.position()) p.tok.position())
return ast.SelectExpr{}
} }
is_else = true is_else = true
has_else = true has_else = true
@ -295,10 +301,12 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
if has_else { if has_else {
p.error_with_pos('`else` and timeout `> t` are mutually exclusive `select` keys', p.error_with_pos('`else` and timeout `> t` are mutually exclusive `select` keys',
p.tok.position()) p.tok.position())
return ast.SelectExpr{}
} }
if has_timeout { if has_timeout {
p.error_with_pos('at most one timeout `> t` branch allowed in `select` block', p.error_with_pos('at most one timeout `> t` branch allowed in `select` block',
p.tok.position()) p.tok.position())
return ast.SelectExpr{}
} }
is_timeout = true is_timeout = true
has_timeout = true has_timeout = true
@ -318,6 +326,7 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
exprs, comments := p.expr_list() exprs, comments := p.expr_list()
if exprs.len != 1 { if exprs.len != 1 {
p.error('only one expression allowed as `select` key') p.error('only one expression allowed as `select` key')
return ast.SelectExpr{}
} }
if p.tok.kind in [.assign, .decl_assign] { if p.tok.kind in [.assign, .decl_assign] {
stmt = p.partial_assign_stmt(exprs, comments) stmt = p.partial_assign_stmt(exprs, comments)
@ -335,17 +344,20 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
ast.ExprStmt { ast.ExprStmt {
if !stmt.is_expr { if !stmt.is_expr {
p.error_with_pos('select: invalid expression', stmt.pos) p.error_with_pos('select: invalid expression', stmt.pos)
return ast.SelectExpr{}
} else { } else {
match mut stmt.expr { match mut stmt.expr {
ast.InfixExpr { ast.InfixExpr {
if stmt.expr.op != .arrow { if stmt.expr.op != .arrow {
p.error_with_pos('select key: `<-` operator expected', p.error_with_pos('select key: `<-` operator expected',
stmt.expr.pos) stmt.expr.pos)
return ast.SelectExpr{}
} }
} }
else { else {
p.error_with_pos('select key: send expression (`ch <- x`) expected', p.error_with_pos('select key: send expression (`ch <- x`) expected',
stmt.pos) stmt.pos)
return ast.SelectExpr{}
} }
} }
} }
@ -357,16 +369,19 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
if expr.op != .arrow { if expr.op != .arrow {
p.error_with_pos('select key: `<-` operator expected', p.error_with_pos('select key: `<-` operator expected',
expr.pos) expr.pos)
return ast.SelectExpr{}
} }
} }
else { else {
p.error_with_pos('select key: receive expression expected', p.error_with_pos('select key: receive expression expected',
stmt.right[0].position()) stmt.right[0].position())
return ast.SelectExpr{}
} }
} }
} }
else { else {
p.error_with_pos('select: transmission statement expected', stmt.position()) p.error_with_pos('select: transmission statement expected', stmt.position())
return ast.SelectExpr{}
} }
} }
} }

View File

@ -44,6 +44,7 @@ pub fn (mut p Parser) parse_map_type() table.Type {
// if key_type_sym.kind != .string { // if key_type_sym.kind != .string {
if key_type.idx() != table.string_type_idx { if key_type.idx() != table.string_type_idx {
p.error('maps can only have string keys for now') p.error('maps can only have string keys for now')
return 0
} }
p.check(.rsbr) p.check(.rsbr)
value_type := p.parse_type() value_type := p.parse_type()
@ -151,6 +152,7 @@ pub fn (mut p Parser) parse_type() table.Type {
} }
if p.tok.kind == .mul { if p.tok.kind == .mul {
p.error('use `&Type` instead of `*Type` when declaring references') p.error('use `&Type` instead of `*Type` when declaring references')
return 0
} }
mut nr_amps := 0 mut nr_amps := 0
// &Type // &Type
@ -167,6 +169,7 @@ pub fn (mut p Parser) parse_type() table.Type {
typ = p.parse_any_type(language, nr_muls > 0, true) typ = p.parse_any_type(language, nr_muls > 0, true)
if typ == table.void_type { if typ == table.void_type {
p.error_with_pos('use `?` instead of `?void`', pos) p.error_with_pos('use `?` instead of `?void`', pos)
return 0
} }
} }
if is_optional { 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, 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`). 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) {}`.') If you need to modify an array in a function, use a mutable argument instead: `fn foo(mut s []string) {}`.')
return 0
} }
} }
return typ 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.tok.lit in p.table.imports) {
if !p.known_import(name) { if !p.known_import(name) {
p.error('unknown module `$p.tok.lit`') p.error('unknown module `$p.tok.lit`')
return 0
} }
if p.tok.lit in p.imports { if p.tok.lit in p.imports {
p.register_used_import(p.tok.lit) 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' name = '${p.imports[name]}.$p.tok.lit'
if !p.tok.lit[0].is_capital() { if !p.tok.lit[0].is_capital() {
p.error('imported types must start with a capital letter') 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 } 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 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 // multiple return
if is_ptr { if is_ptr {
p.error('parse_type: unexpected `&` before multiple returns') p.error('parse_type: unexpected `&` before multiple returns')
return 0
} }
return p.parse_multi_return_type() 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 == '' { if name == '' {
// This means the developer is using some wrong syntax like `x: int` instead of `x int` // This means the developer is using some wrong syntax like `x: int` instead of `x int`
p.error('bad type syntax') p.error('bad type syntax')
return 0
} }
match name { match name {
'voidptr' { 'voidptr' {

View File

@ -359,6 +359,7 @@ pub fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
if c > 1000000 { if c > 1000000 {
p.error_with_pos('parsed over $c statements from fn $p.cur_fn_name, the parser is probably stuck', p.error_with_pos('parsed over $c statements from fn $p.cur_fn_name, the parser is probably stuck',
p.tok.position()) 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 != expected {
if p.tok.kind == .name { if p.tok.kind == .name {
p.error('unexpected name `$p.tok.lit`, expecting `$expected.str()`') p.error('unexpected name `$p.tok.lit`, expecting `$expected.str()`')
return
} else { } else {
p.error('unexpected `$p.tok.kind.str()`, expecting `$expected.str()`') p.error('unexpected `$p.tok.kind.str()`, expecting `$expected.str()`')
return
} }
} }
p.next() 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 { } else if p.peek_tok.kind == .name {
p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position()) 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 && } 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.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()) 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) return p.parse_multi_expr(is_top_level)
@ -761,11 +766,13 @@ fn (mut p Parser) attributes() {
attr := p.parse_attr() attr := p.parse_attr()
if p.attrs.contains(attr.name) { if p.attrs.contains(attr.name) {
p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position())) p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position()))
return
} }
if attr.is_ctdefine { if attr.is_ctdefine {
if has_ctdefine { if has_ctdefine {
p.error_with_pos('only one `[if flag]` may be applied at a time `$attr.name`', p.error_with_pos('only one `[if flag]` may be applied at a time `$attr.name`',
start_pos.extend(p.prev_tok.position())) start_pos.extend(p.prev_tok.position()))
return
} else { } else {
has_ctdefine = true has_ctdefine = true
} }
@ -777,11 +784,13 @@ fn (mut p Parser) attributes() {
break break
} }
p.error('unexpected `$p.tok.kind.str()`, expecting `;`') p.error('unexpected `$p.tok.kind.str()`, expecting `;`')
return
} }
p.next() p.next()
} }
if p.attrs.len == 0 { if p.attrs.len == 0 {
p.error_with_pos('attributes cannot be empty', p.prev_tok.position().extend(p.tok.position())) 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() name = p.check_name()
if name == 'unsafe_fn' { if name == 'unsafe_fn' {
p.error_with_pos('please use `[unsafe]` instead', p.tok.position()) p.error_with_pos('please use `[unsafe]` instead', p.tok.position())
return table.Attr{}
} else if name == 'trusted_fn' { } else if name == 'trusted_fn' {
p.error_with_pos('please use `[trusted]` instead', p.tok.position()) p.error_with_pos('please use `[trusted]` instead', p.tok.position())
return table.Attr{}
} }
if p.tok.kind == .colon { if p.tok.kind == .colon {
p.next() 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) { pub fn (mut p Parser) error_with_pos(s string, pos token.Position) {
if p.pref.fatal_errors {
exit(1)
}
mut kind := 'error:' mut kind := 'error:'
if p.pref.output_mode == .stdout { if p.pref.output_mode == .stdout {
if p.pref.is_verbose { 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] left0 := left[0]
if tok.kind == .key_mut && p.tok.kind != .decl_assign { if tok.kind == .key_mut && p.tok.kind != .decl_assign {
p.error('expecting `:=` (e.g. `mut x :=`)') p.error('expecting `:=` (e.g. `mut x :=`)')
return ast.Stmt{}
} }
if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
return p.partial_assign_stmt(left, left_comments) 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 !(left0 is ast.InfixExpr && (left0 as ast.InfixExpr).op in [.left_shift, .arrow]) && left0 !is
ast.ComptimeCall { ast.ComptimeCall {
p.error_with_pos('expression evaluated but not used', left0.position()) p.error_with_pos('expression evaluated but not used', left0.position())
return ast.Stmt{}
} }
if left.len == 1 { if left.len == 1 {
return ast.ExprStmt{ return ast.ExprStmt{
@ -980,6 +996,7 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident {
} }
} else { } else {
p.error('unexpected token `$p.tok.lit`') p.error('unexpected token `$p.tok.lit`')
return ast.Ident{}
} }
return ast.Ident{} return ast.Ident{}
} }
@ -1040,9 +1057,11 @@ pub fn (mut p Parser) name_expr() ast.Expr {
} }
'len', 'init' { 'len', 'init' {
p.error('`$key` cannot be initialized for `chan`. Did you mean `cap`?') p.error('`$key` cannot be initialized for `chan`. Did you mean `cap`?')
return ast.Expr{}
} }
else { else {
p.error('wrong field `$key`, expecting `cap`') p.error('wrong field `$key`, expecting `cap`')
return ast.Expr{}
} }
} }
last_pos = p.tok.position() last_pos = p.tok.position()
@ -1062,12 +1081,14 @@ pub fn (mut p Parser) name_expr() ast.Expr {
} else { } else {
// don't allow any other string prefix except `r`, `js` and `c` // 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`') 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` // don't allow r`byte` and c`byte`
if p.tok.lit in ['r', 'c'] && p.peek_tok.kind == .chartoken { 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)' } opt := if p.tok.lit == 'r' { '`r` (raw string)' } else { '`c` (c string)' }
p.error('cannot use $opt with `byte` and `rune`') p.error('cannot use $opt with `byte` and `rune`')
return ast.Expr{}
} }
known_var := p.mark_var_as_used(p.tok.lit) known_var := p.mark_var_as_used(p.tok.lit)
mut is_mod_cast := false mut is_mod_cast := false
@ -1321,6 +1342,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
args := p.call_args() args := p.call_args()
if is_filter && args.len != 1 { if is_filter && args.len != 1 {
p.error('needs exactly 1 argument') p.error('needs exactly 1 argument')
return ast.Expr{}
} }
p.check(.rpar) p.check(.rpar)
mut or_stmts := []ast.Stmt{} mut or_stmts := []ast.Stmt{}
@ -1486,6 +1508,7 @@ fn (mut p Parser) string_expr() ast.Expr {
p.next() p.next()
} else { } else {
p.error('format specifier may only be one letter') 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() name = p.check_name()
if module_pos.line_nr != pos.line_nr { if module_pos.line_nr != pos.line_nr {
p.error_with_pos('`module` and `$name` must be at same line', pos) p.error_with_pos('`module` and `$name` must be at same line', pos)
return ast.Module{}
} }
pos = p.tok.position() pos = p.tok.position()
if module_pos.line_nr == pos.line_nr && p.tok.kind != .comment { if module_pos.line_nr == pos.line_nr && p.tok.kind != .comment {
if p.tok.kind != .name { if p.tok.kind != .name {
p.error_with_pos('`module x` syntax error', pos) p.error_with_pos('`module x` syntax error', pos)
return ast.Module{}
} else { } else {
p.error_with_pos('`module x` can only declare one module', pos) p.error_with_pos('`module x` can only declare one module', pos)
return ast.Module{}
} }
} }
module_pos = module_pos.extend(pos) module_pos = module_pos.extend(pos)
@ -1584,10 +1610,12 @@ fn (mut p Parser) import_stmt() ast.Import {
pos := p.tok.position() pos := p.tok.position()
if p.tok.kind == .lpar { if p.tok.kind == .lpar {
p.error_with_pos('`import()` has been deprecated, use `import x` instead', pos) p.error_with_pos('`import()` has been deprecated, use `import x` instead', pos)
return ast.Import{}
} }
mut mod_name := p.check_name() mut mod_name := p.check_name()
if import_pos.line_nr != pos.line_nr { if import_pos.line_nr != pos.line_nr {
p.error_with_pos('`import` statements must be a single line', pos) p.error_with_pos('`import` statements must be a single line', pos)
return ast.Import{}
} }
mut mod_alias := mod_name mut mod_alias := mod_name
for p.tok.kind == .dot { for p.tok.kind == .dot {
@ -1595,9 +1623,11 @@ fn (mut p Parser) import_stmt() ast.Import {
pos_t := p.tok.position() pos_t := p.tok.position()
if p.tok.kind != .name { if p.tok.kind != .name {
p.error_with_pos('module syntax error, please use `x.y.z`', pos) 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 { if import_pos.line_nr != pos_t.line_nr {
p.error_with_pos('`import` and `submodule` must be at same line', pos) p.error_with_pos('`import` and `submodule` must be at same line', pos)
return ast.Import{}
} }
submod_name := p.check_name() submod_name := p.check_name()
mod_name += '.' + submod_name mod_name += '.' + submod_name
@ -1608,6 +1638,7 @@ fn (mut p Parser) import_stmt() ast.Import {
mod_alias = p.check_name() mod_alias = p.check_name()
if mod_alias == mod_name.split('.').last() { if mod_alias == mod_name.split('.').last() {
p.error_with_pos('import alias `$mod_name as $mod_alias` is redundant', p.prev_tok.position()) p.error_with_pos('import alias `$mod_name as $mod_alias` is redundant', p.prev_tok.position())
return ast.Import{}
} }
} }
mut node := 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 import_pos.line_nr == pos_t.line_nr {
if p.tok.kind !in [.lcbr, .eof, .comment] { if p.tok.kind !in [.lcbr, .eof, .comment] {
p.error_with_pos('cannot import multiple modules at a time', pos_t) p.error_with_pos('cannot import multiple modules at a time', pos_t)
return ast.Import{}
} }
} }
p.imports[mod_alias] = mod_name 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() pos_t := p.tok.position()
if p.tok.kind == .rcbr { // closed too early if p.tok.kind == .rcbr { // closed too early
p.error_with_pos('empty `$parent.mod` import set, remove `{}`', pos_t) p.error_with_pos('empty `$parent.mod` import set, remove `{}`', pos_t)
return
} }
if p.tok.kind != .name { // not a valid inner name 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', p.error_with_pos('import syntax error, please specify a valid fn or type name',
pos_t) pos_t)
return
} }
for p.tok.kind == .name { for p.tok.kind == .name {
pos := p.tok.position() pos := p.tok.position()
@ -1697,6 +1731,7 @@ fn (mut p Parser) import_syms(mut parent ast.Import) {
} }
if p.tok.kind != .rcbr { if p.tok.kind != .rcbr {
p.error_with_pos('import syntax error, no closing `}`', p.tok.position()) p.error_with_pos('import syntax error, no closing `}`', p.tok.position())
return
} }
p.next() p.next()
} }
@ -1739,6 +1774,7 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
p.check(.assign) p.check(.assign)
if p.tok.kind == .key_fn { if p.tok.kind == .key_fn {
p.error('const initializer fn literal is not a constant') p.error('const initializer fn literal is not a constant')
return ast.ConstDecl{}
} }
expr := p.expr(0) expr := p.expr(0)
field := ast.ConstField{ 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 != 'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !p.pref.enable_globals && !p.pref.is_fmt &&
p.mod !in global_enabled_mods { p.mod !in global_enabled_mods {
p.error('use `v --enable-globals ...` to enable globals') p.error('use `v --enable-globals ...` to enable globals')
return ast.GlobalDecl{}
} }
start_pos := p.tok.position() start_pos := p.tok.position()
end_pos := p.tok.position() end_pos := p.tok.position()
p.check(.key_global) p.check(.key_global)
if p.tok.kind != .lpar { if p.tok.kind != .lpar {
p.error('globals must be grouped, e.g. `__global ( a = int(1) )`') p.error('globals must be grouped, e.g. `__global ( a = int(1) )`')
return ast.GlobalDecl{}
} }
p.next() // ( p.next() // (
mut fields := []ast.GlobalField{} mut fields := []ast.GlobalField{}
@ -1819,11 +1857,13 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
typ := p.parse_type() typ := p.parse_type()
if p.tok.kind == .assign { if p.tok.kind == .assign {
p.error('global assign must have the type around the value, use `__global ( name = type(value) )`') p.error('global assign must have the type around the value, use `__global ( name = type(value) )`')
return ast.GlobalDecl{}
} }
mut expr := ast.Expr{} mut expr := ast.Expr{}
if has_expr { if has_expr {
if p.tok.kind != .lpar { if p.tok.kind != .lpar {
p.error('global assign must have a type and value, use `__global ( name = type(value) )` or `__global ( name type )`') p.error('global assign must have a type and value, use `__global ( name = type(value) )` or `__global ( name type )`')
return ast.GlobalDecl{}
} }
p.next() // ( p.next() // (
expr = p.expr(0) expr = p.expr(0)
@ -1862,6 +1902,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
if enum_name.len == 1 { if enum_name.len == 1 {
p.error_with_pos('single letter capital names are reserved for generic template types.', p.error_with_pos('single letter capital names are reserved for generic template types.',
end_pos) end_pos)
return ast.EnumDecl{}
} }
name := p.prepend_mod(enum_name) name := p.prepend_mod(enum_name)
p.check(.lcbr) p.check(.lcbr)
@ -1896,11 +1937,13 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
if is_flag { if is_flag {
if fields.len > 32 { if fields.len > 32 {
p.error('when an enum is used as bit field, it must have a max of 32 fields') 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 { for f in fields {
if f.has_expr { if f.has_expr {
p.error_with_pos('when an enum is used as a bit field, you can not assign custom values', p.error_with_pos('when an enum is used as a bit field, you can not assign custom values',
f.pos) f.pos)
return ast.EnumDecl{}
} }
} }
pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' } 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() { if name.len == 1 && name[0].is_capital() {
p.error_with_pos('single letter capital names are reserved for generic template types.', p.error_with_pos('single letter capital names are reserved for generic template types.',
decl_pos) decl_pos)
return ast.TypeDecl{}
} }
mut sum_variants := []ast.SumTypeVariant{} mut sum_variants := []ast.SumTypeVariant{}
p.check(.assign) p.check(.assign)
@ -2155,10 +2199,12 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
p.next() p.next()
if p.tok.kind != .lcbr { if p.tok.kind != .lcbr {
p.error_with_pos('please use `unsafe {`', p.tok.position()) p.error_with_pos('please use `unsafe {`', p.tok.position())
return ast.Stmt{}
} }
p.next() p.next()
if p.inside_unsafe { if p.inside_unsafe {
p.error_with_pos('already inside `unsafe` block', pos) p.error_with_pos('already inside `unsafe` block', pos)
return ast.Stmt{}
} }
if p.tok.kind == .rcbr { if p.tok.kind == .rcbr {
// `unsafe {}` // `unsafe {}`

View File

@ -50,9 +50,16 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
} }
.dollar { .dollar {
match p.peek_tok.kind { match p.peek_tok.kind {
.name { return p.vweb() } .name {
.key_if { return p.if_expr(true) } return p.vweb()
else { p.error_with_pos('unexpected `$`', p.peek_tok.position()) } }
.key_if {
return p.if_expr(true)
}
else {
p.error_with_pos('unexpected `$`', p.peek_tok.position())
return ast.Expr{}
}
} }
} }
.chartoken { .chartoken {
@ -100,6 +107,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
p.next() p.next()
if p.inside_unsafe { if p.inside_unsafe {
p.error_with_pos('already inside `unsafe` block', pos) p.error_with_pos('already inside `unsafe` block', pos)
return ast.Expr{}
} }
p.inside_unsafe = true p.inside_unsafe = true
p.check(.lcbr) p.check(.lcbr)
@ -193,9 +201,11 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
p.next() p.next()
s := if p.tok.lit != '' { '`$p.tok.lit`' } else { p.tok.kind.str() } s := if p.tok.lit != '' { '`$p.tok.lit`' } else { p.tok.kind.str() }
p.error_with_pos('unexpected $s, expecting `:`', p.tok.position()) p.error_with_pos('unexpected $s, expecting `:`', p.tok.position())
return ast.Expr{}
} else { } else {
p.error_with_pos('unexpected `$p.tok.lit`, expecting struct key', p.error_with_pos('unexpected `$p.tok.lit`, expecting struct key',
p.tok.position()) p.tok.position())
return ast.Expr{}
} }
} }
p.check(.rcbr) p.check(.rcbr)
@ -235,6 +245,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
// eof should be handled where it happens // eof should be handled where it happens
p.error_with_pos('invalid expression: unexpected $p.tok.kind.str() token', p.error_with_pos('invalid expression: unexpected $p.tok.kind.str() token',
p.tok.position()) p.tok.position())
return ast.Expr{}
} }
} }
} }

View File

@ -52,6 +52,7 @@ fn (mut p Parser) sql_expr() ast.Expr {
p.check_name() // `by` p.check_name() // `by`
} else { } else {
p.error_with_pos('use `order by` in ORM queries', order_pos) p.error_with_pos('use `order by` in ORM queries', order_pos)
return ast.Expr{}
} }
has_order = true has_order = true
order_expr = p.expr(0) order_expr = p.expr(0)
@ -139,6 +140,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
} }
else { else {
p.error('can only insert variables') 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} mut update_exprs := []ast.Expr{cap: 5}
if kind == .insert && n != 'into' { if kind == .insert && n != 'into' {
p.error('expecting `into`') p.error('expecting `into`')
return ast.SqlStmt{}
} else if kind == .update { } else if kind == .update {
if n != 'set' { if n != 'set' {
p.error('expecting `set`') p.error('expecting `set`')
return ast.SqlStmt{}
} }
for { for {
column := p.check_name() column := p.check_name()
@ -164,6 +168,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt {
} }
} else if kind == .delete && n != 'from' { } else if kind == .delete && n != 'from' {
p.error('expecting `from`') p.error('expecting `from`')
return ast.SqlStmt{}
} }
mut table_type := table.Type(0) mut table_type := table.Type(0)
mut where_expr := ast.Expr{} 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)) idx := p.table.find_type_idx(p.prepend_mod(table_name))
table_type = table.new_type(idx) 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) where_expr = p.expr(0)
} else if kind == .delete { } else if kind == .delete {
table_type = p.parse_type() table_type = p.parse_type()
sym := p.table.get_type_symbol(table_type) sym := p.table.get_type_symbol(table_type)
table_name = sym.name table_name = sym.name
p.check_sql_keyword('where') p.check_sql_keyword('where') or { return ast.SqlStmt{} }
where_expr = p.expr(0) where_expr = p.expr(0)
} }
p.check(.rcbr) 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 { if p.check_name() != name {
p.error('orm: expecting `$name`') p.error('orm: expecting `$name`')
return none
} }
return true
} }

View File

@ -45,6 +45,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
if name.len == 1 && name[0].is_capital() { if name.len == 1 && name[0].is_capital() {
p.error_with_pos('single letter capital names are reserved for generic template types.', p.error_with_pos('single letter capital names are reserved for generic template types.',
name_pos) name_pos)
return ast.StructDecl{}
} }
mut generic_types := []table.Type{} mut generic_types := []table.Type{}
if p.tok.kind == .lt { if p.tok.kind == .lt {
@ -61,13 +62,16 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
no_body := p.tok.kind != .lcbr no_body := p.tok.kind != .lcbr
if language == .v && no_body { if language == .v && no_body {
p.error('`$p.tok.lit` lacks body') p.error('`$p.tok.lit` lacks body')
return ast.StructDecl{}
} }
if language == .v && if language == .v &&
p.mod != 'builtin' && name.len > 0 && !name[0].is_capital() && !p.pref.translated { 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) p.error_with_pos('struct name `$name` must begin with capital letter', name_pos)
return ast.StructDecl{}
} }
if name.len == 1 { if name.len == 1 {
p.error_with_pos('struct names must have more than one character', name_pos) p.error_with_pos('struct names must have more than one character', name_pos)
return ast.StructDecl{}
} }
// println('struct decl $name') // println('struct decl $name')
mut ast_fields := []ast.StructField{} mut ast_fields := []ast.StructField{}
@ -100,6 +104,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
if p.tok.kind == .key_mut { if p.tok.kind == .key_mut {
if pub_mut_pos != -1 { if pub_mut_pos != -1 {
p.error('redefinition of `pub mut` section') p.error('redefinition of `pub mut` section')
return ast.StructDecl{}
} }
p.next() p.next()
pub_mut_pos = fields.len pub_mut_pos = fields.len
@ -109,6 +114,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
} else { } else {
if pub_pos != -1 { if pub_pos != -1 {
p.error('redefinition of `pub` section') p.error('redefinition of `pub` section')
return ast.StructDecl{}
} }
pub_pos = fields.len pub_pos = fields.len
is_field_pub = true is_field_pub = true
@ -119,6 +125,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
} else if p.tok.kind == .key_mut { } else if p.tok.kind == .key_mut {
if mut_pos != -1 { if mut_pos != -1 {
p.error('redefinition of `mut` section') p.error('redefinition of `mut` section')
return ast.StructDecl{}
} }
p.next() p.next()
p.check(.colon) p.check(.colon)
@ -129,6 +136,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
} else if p.tok.kind == .key_global { } else if p.tok.kind == .key_global {
if global_pos != -1 { if global_pos != -1 {
p.error('redefinition of `global` section') p.error('redefinition of `global` section')
return ast.StructDecl{}
} }
p.next() p.next()
p.check(.colon) p.check(.colon)
@ -172,6 +180,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
field_name = symbol_name field_name = symbol_name
if typ in embedded_structs { if typ in embedded_structs {
p.error_with_pos('cannot embed `$field_name` more than once', type_pos) p.error_with_pos('cannot embed `$field_name` more than once', type_pos)
return ast.StructDecl{}
} }
embedded_structs << typ embedded_structs << typ
} else { } else {
@ -275,6 +284,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
if ret == -1 { if ret == -1 {
p.error_with_pos('cannot register struct `$name`, another type with this name exists', p.error_with_pos('cannot register struct `$name`, another type with this name exists',
name_pos) name_pos)
return ast.StructDecl{}
} }
p.expr_mod = '' p.expr_mod = ''
return ast.StructDecl{ return ast.StructDecl{
@ -390,6 +400,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
if reg_idx == -1 { if reg_idx == -1 {
p.error_with_pos('cannot register interface `$interface_name`, another type with this name exists', p.error_with_pos('cannot register interface `$interface_name`, another type with this name exists',
name_pos) name_pos)
return ast.InterfaceDecl{}
} }
typ := table.new_type(reg_idx) typ := table.new_type(reg_idx)
mut ts := p.table.get_type_symbol(typ) mut ts := p.table.get_type_symbol(typ)
@ -403,9 +414,11 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
name := p.check_name() name := p.check_name()
if ts.has_method(name) { if ts.has_method(name) {
p.error_with_pos('duplicate method `$name`', method_start_pos) p.error_with_pos('duplicate method `$name`', method_start_pos)
return ast.InterfaceDecl{}
} }
if util.contains_capital(name) { if util.contains_capital(name) {
p.error('interface methods cannot contain uppercase letters, use snake_case instead') p.error('interface methods cannot contain uppercase letters, use snake_case instead')
return ast.InterfaceDecl{}
} }
// field_names << name // field_names << name
args2, _, _ := p.fn_args() // TODO merge table.Param and ast.Arg to avoid this args2, _, _ := p.fn_args() // TODO merge table.Param and ast.Arg to avoid this

View File

@ -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_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" skip_warnings bool // like C's "-w"
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error 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 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. use_color ColorOutput // whether the warnings/errors should use ANSI color escapes.
is_parallel bool is_parallel bool
@ -172,6 +173,9 @@ pub fn parse_args(args []string) (&Preferences, string) {
'-progress' { '-progress' {
// processed by testing tools in cmd/tools/modules/testing/common.v // processed by testing tools in cmd/tools/modules/testing/common.v
} }
'-Wfatal-errors' {
res.fatal_errors = true
}
'-silent' { '-silent' {
res.output_mode = .silent res.output_mode = .silent
} }

View File

@ -1236,6 +1236,9 @@ pub fn (mut s Scanner) error(msg string) {
eprintln(util.formatted_error('error:', msg, s.file_path, pos)) eprintln(util.formatted_error('error:', msg, s.file_path, pos))
exit(1) exit(1)
} else { } else {
if s.pref.fatal_errors {
exit(1)
}
s.errors << errors.Error{ s.errors << errors.Error{
file_path: s.file_path file_path: s.file_path
pos: pos pos: pos