checker: disallow pub in main

pull/4498/head
Enzo Baldisserri 2020-04-19 00:07:57 +02:00 committed by GitHub
parent de9f302412
commit 57c142b993
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 193 additions and 67 deletions

View File

@ -235,7 +235,7 @@ fn find_working_diff_command() ?string {
return error('no working diff command found') return error('no working diff command found')
} }
pub fn (f FormatOptions) str() string { fn (f FormatOptions) str() string {
return 'FormatOptions{ is_l: $f.is_l' + ' is_w: $f.is_w' + ' is_diff: $f.is_diff' + ' is_verbose: $f.is_verbose' + return 'FormatOptions{ is_l: $f.is_l' + ' is_w: $f.is_w' + ' is_diff: $f.is_diff' + ' is_verbose: $f.is_verbose' +
' is_all: $f.is_all' + ' is_worker: $f.is_worker' + ' is_debug: $f.is_debug' + ' }' ' is_all: $f.is_all' + ' is_worker: $f.is_worker' + ' is_debug: $f.is_debug' + ' }'
} }

View File

@ -3,10 +3,8 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module ast module ast
import ( import v.token
v.token import v.table
v.table
)
pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl
@ -95,9 +93,9 @@ mut:
// module declaration // module declaration
pub struct Module { pub struct Module {
pub: pub:
name string name string
path string path string
expr Expr expr Expr
is_skipped bool // module main can be skipped in single file programs is_skipped bool // module main can be skipped in single file programs
} }
@ -162,7 +160,7 @@ pub struct StructInitField {
pub: pub:
name string name string
expr Expr expr Expr
pos token.Position pos token.Position
mut: mut:
typ table.Type typ table.Type
expected_type table.Type expected_type table.Type
@ -189,7 +187,7 @@ pub struct AnonFn {
pub: pub:
decl FnDecl decl FnDecl
mut: mut:
typ table.Type typ table.Type
} }
pub struct FnDecl { pub struct FnDecl {
@ -228,7 +226,7 @@ mut:
args []CallArg args []CallArg
expected_arg_types []table.Type expected_arg_types []table.Type
is_c bool is_c bool
is_js bool is_js bool
or_block OrExpr or_block OrExpr
left_type table.Type // type of `user` left_type table.Type // type of `user`
receiver_type table.Type // User receiver_type table.Type // User
@ -326,7 +324,7 @@ pub:
tok_kind token.Kind tok_kind token.Kind
mod string mod string
pos token.Position pos token.Position
is_mut bool is_mut bool
mut: mut:
name string name string
kind IdentKind kind IdentKind
@ -537,11 +535,11 @@ pub struct EnumField {
pub struct EnumDecl { pub struct EnumDecl {
pub: pub:
name string name string
is_pub bool is_pub bool
fields []EnumField fields []EnumField
// vals []string
// default_exprs []Expr // default_exprs []Expr
pos token.Position
} }
pub struct AliasTypeDecl { pub struct AliasTypeDecl {
@ -549,6 +547,7 @@ pub:
name string name string
is_pub bool is_pub bool
parent_type table.Type parent_type table.Type
pos token.Position
} }
pub struct SumTypeDecl { pub struct SumTypeDecl {
@ -556,6 +555,7 @@ pub:
name string name string
is_pub bool is_pub bool
sub_types []table.Type sub_types []table.Type
pos token.Position
} }
pub struct FnTypeDecl { pub struct FnTypeDecl {
@ -563,6 +563,7 @@ pub:
name string name string
is_pub bool is_pub bool
typ table.Type typ table.Type
pos token.Position
} }
// TODO: handle this differently // TODO: handle this differently
@ -720,30 +721,22 @@ pub:
[inline] [inline]
pub fn expr_is_blank_ident(expr Expr) bool { pub fn expr_is_blank_ident(expr Expr) bool {
match expr { match expr {
Ident { Ident { return it.kind == .blank_ident }
return it.kind == .blank_ident else { return false }
}
else {
return false
}
} }
} }
[inline] [inline]
pub fn expr_is_call(expr Expr) bool { pub fn expr_is_call(expr Expr) bool {
return match expr { return match expr {
CallExpr { CallExpr { true }
true else { false }
}
else {
false
}
} }
} }
fn (expr Expr) position() token.Position { fn (expr Expr) position() token.Position {
// all uncommented have to be implemented // all uncommented have to be implemented
match mut expr { match var expr {
ArrayInit { ArrayInit {
return it.pos return it.pos
} }

View File

@ -66,11 +66,8 @@ pub fn (c mut Checker) check_files(ast_files []ast.File) {
for file in ast_files { for file in ast_files {
c.check(file) c.check(file)
if file.mod.name == 'main' { if file.mod.name == 'main' {
if fn_decl := get_main_fn_decl(file) { if c.check_file_in_main(file) {
has_main_fn = true has_main_fn = true
if fn_decl.is_pub {
c.error('function `main` cannot be declared public', fn_decl.pos)
}
} }
} }
} }
@ -87,16 +84,71 @@ pub fn (c mut Checker) check_files(ast_files []ast.File) {
} }
} }
fn get_main_fn_decl(file ast.File) ?ast.FnDecl { const (
no_pub_in_main_warning = 'in module main cannot be declared public'
)
// do checks specific to files in main module
// returns `true` if a main function is in the file
fn (c mut Checker) check_file_in_main(file ast.File) bool {
mut has_main_fn := false
for stmt in file.stmts { for stmt in file.stmts {
if stmt is ast.FnDecl { match stmt {
fn_decl := stmt as ast.FnDecl ast.ConstDecl {
if fn_decl.name == 'main' { if it.is_pub {
return fn_decl c.warn('const $no_pub_in_main_warning', it.pos)
}
} }
ast.ConstField {
if it.is_pub {
c.warn('const field `$it.name` $no_pub_in_main_warning', it.pos)
}
}
ast.EnumDecl {
if it.is_pub {
c.warn('enum `$it.name` $no_pub_in_main_warning', it.pos)
}
}
ast.FnDecl {
if it.name == 'main' {
has_main_fn = true
if it.is_pub {
c.error('function `main` cannot be declared public', it.pos)
}
} else {
if it.is_pub {
c.warn('function `$it.name` $no_pub_in_main_warning', it.pos)
}
}
}
ast.StructDecl {
if it.is_pub {
c.warn('struct `$it.name` $no_pub_in_main_warning', it.pos)
}
}
ast.TypeDecl {
type_decl := stmt as ast.TypeDecl
if type_decl is ast.AliasTypeDecl {
alias_decl := type_decl as ast.AliasTypeDecl
if alias_decl.is_pub {
c.warn('type alias `$alias_decl.name` $no_pub_in_main_warning', alias_decl.pos)
}
} else if type_decl is ast.SumTypeDecl {
sum_decl := type_decl as ast.SumTypeDecl
if sum_decl.is_pub {
c.warn('sum type `$sum_decl.name` $no_pub_in_main_warning', sum_decl.pos)
}
} else if type_decl is ast.FnTypeDecl {
fn_decl := type_decl as ast.FnTypeDecl
if fn_decl.is_pub {
c.warn('type alias `$fn_decl.name` $no_pub_in_main_warning', fn_decl.pos)
}
}
}
else {}
} }
} }
return none return has_main_fn
} }
pub fn (c mut Checker) struct_decl(decl ast.StructDecl) { pub fn (c mut Checker) struct_decl(decl ast.StructDecl) {

View File

@ -24,7 +24,8 @@ fn test_all() {
os.cp(path, program) or { os.cp(path, program) or {
panic(err) panic(err)
} }
res := os.exec('$vexe $program') or { // -prod so that warn are errors
res := os.exec('$vexe -prod $program') or {
panic(err) panic(err)
} }
mut expected := os.read_file(program.replace('.v', '') + '.out') or { mut expected := os.read_file(program.replace('.v', '') + '.out') or {

View File

@ -0,0 +1,49 @@
vlib/v/checker/tests/inout/no_pub_in_main.v:3:1: error: type alias `Integer` in module main cannot be declared public
1| module main
2|
3| pub type Integer = int
~~~~~~~~~~~~~~~~
4|
5| pub type Float = f32 | f64
vlib/v/checker/tests/inout/no_pub_in_main.v:5:1: error: sum type `Float` in module main cannot be declared public
3| pub type Integer = int
4|
5| pub type Float = f32 | f64
~~~~~~~~~~~~~~
6|
7| // Buggy ATM
vlib/v/checker/tests/inout/no_pub_in_main.v:10:1: error: enum `Color` in module main cannot be declared public
8| // pub type Fn = fn () int
9|
10| pub enum Color {
~~~~~~~~~~~~~~
11| red
12| green
vlib/v/checker/tests/inout/no_pub_in_main.v:16:1: error: const in module main cannot be declared public
14| }
15|
16| pub const (
~~~~~~~~~
17| w = 'world'
18| )
vlib/v/checker/tests/inout/no_pub_in_main.v:20:1: error: function `my_fn` in module main cannot be declared public
18| )
19|
20| pub fn my_fn() int {
~~~~~~~~~~~~~~~~~~
21| return 1
22| }
vlib/v/checker/tests/inout/no_pub_in_main.v:24:1: error: function `main` cannot be declared public
22| }
23|
24| pub fn main() {
~~~~~~~~~~~~~
25| println('main')
26| }
vlib/v/checker/tests/inout/no_pub_in_main.v:28:1: error: struct `MyStruct` in module main cannot be declared public
26| }
27|
28| pub struct MyStruct {
~~~~~~~~~~~~~~~~~~~
29| field int
30| }

View File

@ -0,0 +1,30 @@
module main
pub type Integer = int
pub type Float = f32 | f64
// Buggy ATM
// pub type Fn = fn () int
pub enum Color {
red
green
blue
}
pub const (
w = 'world'
)
pub fn my_fn() int {
return 1
}
pub fn main() {
println('main')
}
pub struct MyStruct {
field int
}

View File

@ -1,5 +0,0 @@
vlib/v/checker/tests/inout/pub_fn_main.v:1:1: error: function `main` cannot be declared public
1| pub fn main() {
~~~
2| println('Hello world !')
3| }

View File

@ -1,3 +0,0 @@
pub fn main() {
println('Hello world !')
}

View File

@ -85,7 +85,7 @@ pub fn (var p Parser) call_args() []ast.CallArg {
fn (var p Parser) fn_decl() ast.FnDecl { fn (var p Parser) fn_decl() ast.FnDecl {
// p.table.clear_vars() // p.table.clear_vars()
pos := p.tok.position() start_pos := p.tok.position()
p.open_scope() p.open_scope()
is_deprecated := p.attr == 'deprecated' is_deprecated := p.attr == 'deprecated'
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
@ -165,9 +165,11 @@ fn (var p Parser) fn_decl() ast.FnDecl {
typ: arg.typ typ: arg.typ
}) })
} }
var end_pos := p.prev_tok.position()
// Return type // Return type
var return_type := table.void_type var return_type := table.void_type
if p.tok.kind.is_start_of_type() { if p.tok.kind.is_start_of_type() {
end_pos = p.tok.position()
return_type = p.parse_type() return_type = p.parse_type()
} }
// Register // Register
@ -229,7 +231,7 @@ fn (var p Parser) fn_decl() ast.FnDecl {
is_c: is_c is_c: is_c
is_js: is_js is_js: is_js
no_body: no_body no_body: no_body
pos: pos pos: start_pos.extend(end_pos)
is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
} }
} }

View File

@ -945,11 +945,12 @@ fn (var p Parser) import_stmt() []ast.Import {
} }
fn (var p Parser) const_decl() ast.ConstDecl { fn (var p Parser) const_decl() ast.ConstDecl {
start_pos := p.tok.position()
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
if is_pub { if is_pub {
p.next() p.next()
} }
pos := p.tok.position() end_pos := p.tok.position()
p.check(.key_const) p.check(.key_const)
if p.tok.kind != .lpar { if p.tok.kind != .lpar {
p.error('consts must be grouped, e.g.\nconst (\n\ta = 1\n)') p.error('consts must be grouped, e.g.\nconst (\n\ta = 1\n)')
@ -975,7 +976,7 @@ fn (var p Parser) const_decl() ast.ConstDecl {
} }
p.check(.rpar) p.check(.rpar)
return ast.ConstDecl{ return ast.ConstDecl{
pos: pos pos: start_pos.extend(end_pos)
fields: fields fields: fields
is_pub: is_pub is_pub: is_pub
} }
@ -1000,14 +1001,10 @@ fn (var p Parser) return_stmt() ast.Return {
break break
} }
} }
last_pos := exprs.last().position() end_pos := exprs.last().position()
return ast.Return{ return ast.Return{
exprs: exprs exprs: exprs
pos: token.Position{ pos: first_pos.extend(end_pos)
line_nr: first_pos.line_nr
pos: first_pos.pos
len: last_pos.pos - first_pos.pos + last_pos.len
}
} }
} }
@ -1054,10 +1051,12 @@ fn (var p Parser) global_decl() ast.GlobalDecl {
fn (var p Parser) enum_decl() ast.EnumDecl { fn (var p Parser) enum_decl() ast.EnumDecl {
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
start_pos := p.tok.position()
if is_pub { if is_pub {
p.next() p.next()
} }
p.check(.key_enum) p.check(.key_enum)
end_pos := p.tok.position()
name := p.prepend_mod(p.check_name()) name := p.prepend_mod(p.check_name())
p.check(.lcbr) p.check(.lcbr)
var vals := []string var vals := []string
@ -1101,15 +1100,19 @@ fn (var p Parser) enum_decl() ast.EnumDecl {
name: name name: name
is_pub: is_pub is_pub: is_pub
fields: fields fields: fields
pos: start_pos.extend(end_pos)
} }
} }
fn (var p Parser) type_decl() ast.TypeDecl { fn (var p Parser) type_decl() ast.TypeDecl {
start_pos := p.tok.position()
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
if is_pub { if is_pub {
p.next() p.next()
} }
p.check(.key_type) p.check(.key_type)
end_pos := p.tok.position()
decl_pos := start_pos.extend(end_pos)
name := p.check_name() name := p.check_name()
var sum_variants := []table.Type var sum_variants := []table.Type
if p.tok.kind == .assign { if p.tok.kind == .assign {
@ -1123,6 +1126,7 @@ fn (var p Parser) type_decl() ast.TypeDecl {
name: fn_name name: fn_name
is_pub: is_pub is_pub: is_pub
typ: fn_type typ: fn_type
pos: decl_pos
} }
} }
first_type := p.parse_type() // need to parse the first type before we can check if it's `type A = X | Y` first_type := p.parse_type() // need to parse the first type before we can check if it's `type A = X | Y`
@ -1149,6 +1153,7 @@ fn (var p Parser) type_decl() ast.TypeDecl {
name: name name: name
is_pub: is_pub is_pub: is_pub
sub_types: sum_variants sub_types: sum_variants
pos: decl_pos
} }
} }
// type MyType int // type MyType int
@ -1166,6 +1171,7 @@ fn (var p Parser) type_decl() ast.TypeDecl {
name: name name: name
is_pub: is_pub is_pub: is_pub
parent_type: parent_type parent_type: parent_type
pos: decl_pos
} }
} }

View File

@ -8,7 +8,7 @@ import v.table
import v.token import v.token
fn (var p Parser) struct_decl() ast.StructDecl { fn (var p Parser) struct_decl() ast.StructDecl {
first_pos := p.tok.position() start_pos := p.tok.position()
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
if is_pub { if is_pub {
p.next() p.next()
@ -30,6 +30,7 @@ fn (var p Parser) struct_decl() ast.StructDecl {
if !is_c && !is_js && no_body { if !is_c && !is_js && no_body {
p.error('`$p.tok.lit` lacks body') p.error('`$p.tok.lit` lacks body')
} }
end_pos := p.tok.position()
var name := p.check_name() var name := p.check_name()
// println('struct decl $name') // println('struct decl $name')
var ast_fields := []ast.StructField var ast_fields := []ast.StructField
@ -37,7 +38,6 @@ fn (var p Parser) struct_decl() ast.StructDecl {
var mut_pos := -1 var mut_pos := -1
var pub_pos := -1 var pub_pos := -1
var pub_mut_pos := -1 var pub_mut_pos := -1
var last_pos := token.Position{}
if !no_body { if !no_body {
p.check(.lcbr) p.check(.lcbr)
for p.tok.kind != .rcbr { for p.tok.kind != .rcbr {
@ -113,7 +113,6 @@ fn (var p Parser) struct_decl() ast.StructDecl {
} }
// println('struct field $ti.name $field_name') // println('struct field $ti.name $field_name')
} }
last_pos = p.tok.position()
p.check(.rcbr) p.check(.rcbr)
} }
if is_c { if is_c {
@ -145,16 +144,11 @@ fn (var p Parser) struct_decl() ast.StructDecl {
p.error('cannot register type `$name`, another type with this name exists') p.error('cannot register type `$name`, another type with this name exists')
} }
p.expr_mod = '' p.expr_mod = ''
pos := token.Position{
line_nr: first_pos.line_nr
pos: first_pos.pos
len: last_pos.pos - first_pos.pos + last_pos.len
}
return ast.StructDecl{ return ast.StructDecl{
name: name name: name
is_pub: is_pub is_pub: is_pub
fields: ast_fields fields: ast_fields
pos: pos pos: start_pos.extend(end_pos)
mut_pos: mut_pos mut_pos: mut_pos
pub_pos: pub_pos pub_pos: pub_pos
pub_mut_pos: pub_mut_pos pub_mut_pos: pub_mut_pos

View File

@ -14,6 +14,13 @@ pub fn (pos Position) str() string {
return 'Position{ line_nr: $pos.line_nr, pos: $pos.pos, len: $pos.len }' return 'Position{ line_nr: $pos.line_nr, pos: $pos.pos, len: $pos.len }'
} }
pub fn (pos Position) extend(end Position) Position {
return {
pos |
len: end.pos - pos.pos + end.len
}
}
[inline] [inline]
pub fn (tok &Token) position() Position { pub fn (tok &Token) position() Position {
return Position{ return Position{