parser: make script mode errors more informative, when a top level declaration is encountered, after script mode had already started

Delyan Angelov 2022-05-12 21:13:51 +03:00 committed by Jef Roosens
parent b1b10f48ad
commit b9ee4204df
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
12 changed files with 103 additions and 10 deletions

View File

@ -9,6 +9,9 @@ import v.token
pub fn (mut p Parser) expr(precedence int) ast.Expr { pub fn (mut p Parser) expr(precedence int) ast.Expr {
return p.check_expr(precedence) or { return p.check_expr(precedence) or {
if token.is_decl(p.tok.kind) && p.disallow_declarations_in_script_mode() {
return ast.empty_expr()
}
p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.pos()) p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.pos())
} }
} }

View File

@ -673,11 +673,11 @@ fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInf
fn (mut p Parser) anon_fn() ast.AnonFn { fn (mut p Parser) anon_fn() ast.AnonFn {
pos := p.tok.pos() pos := p.tok.pos()
p.check(.key_fn) p.check(.key_fn)
if p.pref.is_script && p.tok.kind == .name { if p.tok.kind == .name {
p.error_with_pos('function declarations in script mode should be before all script statements', if p.disallow_declarations_in_script_mode() {
p.tok.pos())
return ast.AnonFn{} return ast.AnonFn{}
} }
}
old_inside_defer := p.inside_defer old_inside_defer := p.inside_defer
p.inside_defer = false p.inside_defer = false
p.open_scope() p.open_scope()

View File

@ -93,6 +93,8 @@ mut:
codegen_text string codegen_text string
struct_init_generic_types []ast.Type struct_init_generic_types []ast.Type
if_cond_comments []ast.Comment if_cond_comments []ast.Comment
script_mode bool
script_mode_start_token token.Token
} }
__global codegen_files = []&ast.File{} __global codegen_files = []&ast.File{}
@ -685,12 +687,17 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
else { else {
p.inside_fn = true p.inside_fn = true
if p.pref.is_script && !p.pref.is_test { if p.pref.is_script && !p.pref.is_test {
p.script_mode = true
p.script_mode_start_token = p.tok
p.open_scope() p.open_scope()
mut stmts := []ast.Stmt{} mut stmts := []ast.Stmt{}
for p.tok.kind != .eof { for p.tok.kind != .eof {
stmts << p.stmt(false) stmts << p.stmt(false)
} }
p.close_scope() p.close_scope()
p.script_mode = false
return ast.FnDecl{ return ast.FnDecl{
name: 'main.main' name: 'main.main'
short_name: 'main' short_name: 'main'
@ -3276,6 +3283,9 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
p.next() p.next()
} }
const_pos := p.tok.pos() const_pos := p.tok.pos()
if p.disallow_declarations_in_script_mode() {
return ast.ConstDecl{}
}
p.check(.key_const) p.check(.key_const)
is_block := p.tok.kind == .lpar is_block := p.tok.kind == .lpar
if is_block { if is_block {
@ -3390,6 +3400,9 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
} }
start_pos := p.tok.pos() start_pos := p.tok.pos()
p.check(.key_global) p.check(.key_global)
if p.disallow_declarations_in_script_mode() {
return ast.GlobalDecl{}
}
is_block := p.tok.kind == .lpar is_block := p.tok.kind == .lpar
if is_block { if is_block {
p.next() // ( p.next() // (
@ -3485,6 +3498,9 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
} }
p.check(.key_enum) p.check(.key_enum)
end_pos := p.tok.pos() end_pos := p.tok.pos()
if p.disallow_declarations_in_script_mode() {
return ast.EnumDecl{}
}
enum_name := p.check_name() enum_name := p.check_name()
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.',
@ -3597,6 +3613,9 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
end_pos := p.tok.pos() end_pos := p.tok.pos()
decl_pos := start_pos.extend(end_pos) decl_pos := start_pos.extend(end_pos)
name_pos := p.tok.pos() name_pos := p.tok.pos()
if p.disallow_declarations_in_script_mode() {
return ast.SumTypeDecl{}
}
name := p.check_name() name := p.check_name()
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.',
@ -3889,6 +3908,15 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
} }
} }
fn (mut p Parser) disallow_declarations_in_script_mode() bool {
if p.script_mode {
p.note_with_pos('script mode started here', p.script_mode_start_token.pos())
p.error_with_pos('all definitions must occur before code in script mode', p.tok.pos())
return true
}
return false
}
fn (mut p Parser) trace(fbase string, message string) { fn (mut p Parser) trace(fbase string, message string) {
if p.file_base == fbase { if p.file_base == fbase {
println('> p.trace | ${fbase:-10s} | $message') println('> p.trace | ${fbase:-10s} | $message')

View File

@ -36,6 +36,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
} }
name_pos := p.tok.pos() name_pos := p.tok.pos()
p.check_for_impure_v(language, name_pos) p.check_for_impure_v(language, name_pos)
if p.disallow_declarations_in_script_mode() {
return ast.StructDecl{}
}
mut name := p.check_name() mut name := p.check_name()
// defer { // defer {
// if name.contains('App') { // if name.contains('App') {
@ -471,6 +474,9 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
} }
name_pos := p.tok.pos() name_pos := p.tok.pos()
p.check_for_impure_v(language, name_pos) p.check_for_impure_v(language, name_pos)
if p.disallow_declarations_in_script_mode() {
return ast.InterfaceDecl{}
}
modless_name := p.check_name() modless_name := p.check_name()
if modless_name == 'IError' && p.mod != 'builtin' { if modless_name == 'IError' && p.mod != 'builtin' {
p.error_with_pos('cannot register interface `IError`, it is builtin interface type', p.error_with_pos('cannot register interface `IError`, it is builtin interface type',

View File

@ -0,0 +1,12 @@
vlib/v/parser/tests/invalid_enum_decl_script_err.vv:1:1: notice: script mode started here
1 | mynum := 10
| ~~~~~
2 |
3 | enum MyEnum {
vlib/v/parser/tests/invalid_enum_decl_script_err.vv:3:1: error: all definitions must occur before code in script mode
1 | mynum := 10
2 |
3 | enum MyEnum {
| ~~~~
4 | aa
5 | bb

View File

@ -0,0 +1,7 @@
mynum := 10
enum MyEnum {
aa
bb
cc
}

View File

@ -1,7 +1,12 @@
vlib/v/parser/tests/invalid_fn_decl_script_err.vv:3:4: error: function declarations in script mode should be before all script statements vlib/v/parser/tests/invalid_fn_decl_script_err.vv:1:1: notice: script mode started here
1 | mynum := 10
| ~~~~~
2 |
3 | fn my_function() {
vlib/v/parser/tests/invalid_fn_decl_script_err.vv:3:4: error: all definitions must occur before code in script mode
1 | mynum := 10 1 | mynum := 10
2 | 2 |
3 | fn main() { 3 | fn my_function() {
| ~~~~ | ~~~~~~~~~~~
4 | println(mynum) 4 | println(mynum)
5 | } 5 | }

View File

@ -1,5 +1,5 @@
mynum := 10 mynum := 10
fn main() { fn my_function() {
println(mynum) println(mynum)
} }

View File

@ -0,0 +1,11 @@
vlib/v/parser/tests/invalid_interface_decl_script_err.vv:1:1: notice: script mode started here
1 | mynum := 10
| ~~~~~
2 |
3 | interface IUniversal {
vlib/v/parser/tests/invalid_interface_decl_script_err.vv:3:1: error: all definitions must occur before code in script mode
1 | mynum := 10
2 |
3 | interface IUniversal {
| ~~~~~~~~~
4 | }

View File

@ -0,0 +1,4 @@
mynum := 10
interface IUniversal {
}

View File

@ -0,0 +1,12 @@
vlib/v/parser/tests/invalid_struct_decl_script_err.vv:1:1: notice: script mode started here
1 | mynum := 10
| ~~~~~
2 |
3 | struct Abc {
vlib/v/parser/tests/invalid_struct_decl_script_err.vv:3:1: error: all definitions must occur before code in script mode
1 | mynum := 10
2 |
3 | struct Abc {
| ~~~~~~
4 | x int
5 | }

View File

@ -0,0 +1,5 @@
mynum := 10
struct Abc {
x int
}