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
|
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
|
||||||
|
|
|
@ -124,6 +124,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`.
|
||||||
|
|
||||||
|
|
|
@ -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') }
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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' {
|
||||||
|
|
|
@ -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 {}`
|
||||||
|
|
|
@ -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{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue