diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v index d9b75563ce..9bf4fae4df 100644 --- a/cmd/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -235,7 +235,7 @@ fn find_working_diff_command() ?string { 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' + ' is_all: $f.is_all' + ' is_worker: $f.is_worker' + ' is_debug: $f.is_debug' + ' }' } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 2c1c027167..a94583c2ec 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -3,10 +3,8 @@ // that can be found in the LICENSE file. module ast -import ( - v.token - v.table -) +import v.token +import v.table pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl @@ -95,9 +93,9 @@ mut: // module declaration pub struct Module { pub: - name string - path string - expr Expr + name string + path string + expr Expr is_skipped bool // module main can be skipped in single file programs } @@ -162,7 +160,7 @@ pub struct StructInitField { pub: name string expr Expr - pos token.Position + pos token.Position mut: typ table.Type expected_type table.Type @@ -189,7 +187,7 @@ pub struct AnonFn { pub: decl FnDecl mut: - typ table.Type + typ table.Type } pub struct FnDecl { @@ -228,7 +226,7 @@ mut: args []CallArg expected_arg_types []table.Type is_c bool - is_js bool + is_js bool or_block OrExpr left_type table.Type // type of `user` receiver_type table.Type // User @@ -326,7 +324,7 @@ pub: tok_kind token.Kind mod string pos token.Position - is_mut bool + is_mut bool mut: name string kind IdentKind @@ -537,11 +535,11 @@ pub struct EnumField { pub struct EnumDecl { pub: - name string - is_pub bool - fields []EnumField - // vals []string + name string + is_pub bool + fields []EnumField // default_exprs []Expr + pos token.Position } pub struct AliasTypeDecl { @@ -549,6 +547,7 @@ pub: name string is_pub bool parent_type table.Type + pos token.Position } pub struct SumTypeDecl { @@ -556,6 +555,7 @@ pub: name string is_pub bool sub_types []table.Type + pos token.Position } pub struct FnTypeDecl { @@ -563,6 +563,7 @@ pub: name string is_pub bool typ table.Type + pos token.Position } // TODO: handle this differently @@ -720,30 +721,22 @@ pub: [inline] pub fn expr_is_blank_ident(expr Expr) bool { match expr { - Ident { - return it.kind == .blank_ident - } - else { - return false - } + Ident { return it.kind == .blank_ident } + else { return false } } } [inline] pub fn expr_is_call(expr Expr) bool { return match expr { - CallExpr { - true - } - else { - false - } + CallExpr { true } + else { false } } } fn (expr Expr) position() token.Position { // all uncommented have to be implemented - match mut expr { + match var expr { ArrayInit { return it.pos } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6a851e2e4a..63eceba190 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -66,11 +66,8 @@ pub fn (c mut Checker) check_files(ast_files []ast.File) { for file in ast_files { c.check(file) if file.mod.name == 'main' { - if fn_decl := get_main_fn_decl(file) { + if c.check_file_in_main(file) { 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 { - if stmt is ast.FnDecl { - fn_decl := stmt as ast.FnDecl - if fn_decl.name == 'main' { - return fn_decl + match stmt { + ast.ConstDecl { + if it.is_pub { + 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) { diff --git a/vlib/v/checker/tests/inout/checker_error_test.v b/vlib/v/checker/tests/inout/checker_error_test.v index a7c3350515..d565f86420 100644 --- a/vlib/v/checker/tests/inout/checker_error_test.v +++ b/vlib/v/checker/tests/inout/checker_error_test.v @@ -24,7 +24,8 @@ fn test_all() { os.cp(path, program) or { panic(err) } - res := os.exec('$vexe $program') or { + // -prod so that warn are errors + res := os.exec('$vexe -prod $program') or { panic(err) } mut expected := os.read_file(program.replace('.v', '') + '.out') or { diff --git a/vlib/v/checker/tests/inout/no_pub_in_main.out b/vlib/v/checker/tests/inout/no_pub_in_main.out new file mode 100644 index 0000000000..bba28e48db --- /dev/null +++ b/vlib/v/checker/tests/inout/no_pub_in_main.out @@ -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| } diff --git a/vlib/v/checker/tests/inout/no_pub_in_main.vv b/vlib/v/checker/tests/inout/no_pub_in_main.vv new file mode 100644 index 0000000000..76d6ee0114 --- /dev/null +++ b/vlib/v/checker/tests/inout/no_pub_in_main.vv @@ -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 +} diff --git a/vlib/v/checker/tests/inout/pub_fn_main.out b/vlib/v/checker/tests/inout/pub_fn_main.out deleted file mode 100644 index a0d7830153..0000000000 --- a/vlib/v/checker/tests/inout/pub_fn_main.out +++ /dev/null @@ -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| } diff --git a/vlib/v/checker/tests/inout/pub_fn_main.vv b/vlib/v/checker/tests/inout/pub_fn_main.vv deleted file mode 100644 index 0f47c312c0..0000000000 --- a/vlib/v/checker/tests/inout/pub_fn_main.vv +++ /dev/null @@ -1,3 +0,0 @@ -pub fn main() { - println('Hello world !') -} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 2b519216bf..f9461a2cc2 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -85,7 +85,7 @@ pub fn (var p Parser) call_args() []ast.CallArg { fn (var p Parser) fn_decl() ast.FnDecl { // p.table.clear_vars() - pos := p.tok.position() + start_pos := p.tok.position() p.open_scope() is_deprecated := p.attr == 'deprecated' is_pub := p.tok.kind == .key_pub @@ -165,9 +165,11 @@ fn (var p Parser) fn_decl() ast.FnDecl { typ: arg.typ }) } + var end_pos := p.prev_tok.position() // Return type var return_type := table.void_type if p.tok.kind.is_start_of_type() { + end_pos = p.tok.position() return_type = p.parse_type() } // Register @@ -229,7 +231,7 @@ fn (var p Parser) fn_decl() ast.FnDecl { is_c: is_c is_js: is_js no_body: no_body - pos: pos + pos: start_pos.extend(end_pos) is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index a9c49a02e2..1d882fbf64 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -945,11 +945,12 @@ fn (var p Parser) import_stmt() []ast.Import { } fn (var p Parser) const_decl() ast.ConstDecl { + start_pos := p.tok.position() is_pub := p.tok.kind == .key_pub if is_pub { p.next() } - pos := p.tok.position() + end_pos := p.tok.position() p.check(.key_const) if p.tok.kind != .lpar { 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) return ast.ConstDecl{ - pos: pos + pos: start_pos.extend(end_pos) fields: fields is_pub: is_pub } @@ -1000,14 +1001,10 @@ fn (var p Parser) return_stmt() ast.Return { break } } - last_pos := exprs.last().position() + end_pos := exprs.last().position() return ast.Return{ exprs: exprs - pos: token.Position{ - line_nr: first_pos.line_nr - pos: first_pos.pos - len: last_pos.pos - first_pos.pos + last_pos.len - } + pos: first_pos.extend(end_pos) } } @@ -1054,10 +1051,12 @@ fn (var p Parser) global_decl() ast.GlobalDecl { fn (var p Parser) enum_decl() ast.EnumDecl { is_pub := p.tok.kind == .key_pub + start_pos := p.tok.position() if is_pub { p.next() } p.check(.key_enum) + end_pos := p.tok.position() name := p.prepend_mod(p.check_name()) p.check(.lcbr) var vals := []string @@ -1101,15 +1100,19 @@ fn (var p Parser) enum_decl() ast.EnumDecl { name: name is_pub: is_pub fields: fields + pos: start_pos.extend(end_pos) } } fn (var p Parser) type_decl() ast.TypeDecl { + start_pos := p.tok.position() is_pub := p.tok.kind == .key_pub if is_pub { p.next() } p.check(.key_type) + end_pos := p.tok.position() + decl_pos := start_pos.extend(end_pos) name := p.check_name() var sum_variants := []table.Type if p.tok.kind == .assign { @@ -1123,6 +1126,7 @@ fn (var p Parser) type_decl() ast.TypeDecl { name: fn_name is_pub: is_pub 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` @@ -1149,6 +1153,7 @@ fn (var p Parser) type_decl() ast.TypeDecl { name: name is_pub: is_pub sub_types: sum_variants + pos: decl_pos } } // type MyType int @@ -1166,6 +1171,7 @@ fn (var p Parser) type_decl() ast.TypeDecl { name: name is_pub: is_pub parent_type: parent_type + pos: decl_pos } } diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 62f19a1126..1b9efaea23 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -8,7 +8,7 @@ import v.table import v.token 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 if is_pub { p.next() @@ -30,6 +30,7 @@ fn (var p Parser) struct_decl() ast.StructDecl { if !is_c && !is_js && no_body { p.error('`$p.tok.lit` lacks body') } + end_pos := p.tok.position() var name := p.check_name() // println('struct decl $name') var ast_fields := []ast.StructField @@ -37,7 +38,6 @@ fn (var p Parser) struct_decl() ast.StructDecl { var mut_pos := -1 var pub_pos := -1 var pub_mut_pos := -1 - var last_pos := token.Position{} if !no_body { p.check(.lcbr) for p.tok.kind != .rcbr { @@ -113,7 +113,6 @@ fn (var p Parser) struct_decl() ast.StructDecl { } // println('struct field $ti.name $field_name') } - last_pos = p.tok.position() p.check(.rcbr) } 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.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{ name: name is_pub: is_pub fields: ast_fields - pos: pos + pos: start_pos.extend(end_pos) mut_pos: mut_pos pub_pos: pub_pos pub_mut_pos: pub_mut_pos diff --git a/vlib/v/token/position.v b/vlib/v/token/position.v index ecc0272c6e..8d953a60ac 100644 --- a/vlib/v/token/position.v +++ b/vlib/v/token/position.v @@ -14,6 +14,13 @@ pub fn (pos Position) str() string { 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] pub fn (tok &Token) position() Position { return Position{