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

master
Delyan Angelov 2022-05-12 21:13:51 +03:00
parent 20139ad756
commit 668d1b04d2
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
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 {
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())
}
}

View File

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

View File

@ -93,6 +93,8 @@ mut:
codegen_text string
struct_init_generic_types []ast.Type
if_cond_comments []ast.Comment
script_mode bool
script_mode_start_token token.Token
}
__global codegen_files = []&ast.File{}
@ -685,12 +687,17 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
else {
p.inside_fn = true
if p.pref.is_script && !p.pref.is_test {
p.script_mode = true
p.script_mode_start_token = p.tok
p.open_scope()
mut stmts := []ast.Stmt{}
for p.tok.kind != .eof {
stmts << p.stmt(false)
}
p.close_scope()
p.script_mode = false
return ast.FnDecl{
name: 'main.main'
short_name: 'main'
@ -3276,6 +3283,9 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
p.next()
}
const_pos := p.tok.pos()
if p.disallow_declarations_in_script_mode() {
return ast.ConstDecl{}
}
p.check(.key_const)
is_block := p.tok.kind == .lpar
if is_block {
@ -3390,6 +3400,9 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
}
start_pos := p.tok.pos()
p.check(.key_global)
if p.disallow_declarations_in_script_mode() {
return ast.GlobalDecl{}
}
is_block := p.tok.kind == .lpar
if is_block {
p.next() // (
@ -3485,6 +3498,9 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
}
p.check(.key_enum)
end_pos := p.tok.pos()
if p.disallow_declarations_in_script_mode() {
return ast.EnumDecl{}
}
enum_name := p.check_name()
if enum_name.len == 1 {
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()
decl_pos := start_pos.extend(end_pos)
name_pos := p.tok.pos()
if p.disallow_declarations_in_script_mode() {
return ast.SumTypeDecl{}
}
name := p.check_name()
if name.len == 1 && name[0].is_capital() {
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) {
if p.file_base == fbase {
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()
p.check_for_impure_v(language, name_pos)
if p.disallow_declarations_in_script_mode() {
return ast.StructDecl{}
}
mut name := p.check_name()
// defer {
// if name.contains('App') {
@ -471,6 +474,9 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
}
name_pos := p.tok.pos()
p.check_for_impure_v(language, name_pos)
if p.disallow_declarations_in_script_mode() {
return ast.InterfaceDecl{}
}
modless_name := p.check_name()
if modless_name == 'IError' && p.mod != 'builtin' {
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 main() {
| ~~~~
| ~~~~~
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
2 |
3 | fn my_function() {
| ~~~~~~~~~~~
4 | println(mynum)
5 | }
5 | }

View File

@ -1,5 +1,5 @@
mynum := 10
fn main() {
fn my_function() {
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
}