diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 50deb639d9..5c48d7e3a1 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -444,9 +444,9 @@ fn (t Tree) stmt(node ast.Stmt) &Node { fn (t Tree) import_module(node ast.Import) &Node { mut obj := new_object() obj.add_terse('ast_type', t.string_node('Import')) - obj.add('mod', t.string_node(node.mod)) - obj.add('alias', t.string_node(node.alias)) - obj.add('syms', t.array_node_import_symbol(node.syms)) + obj.add_terse('mod', t.string_node(node.mod)) + obj.add_terse('alias', t.string_node(node.alias)) + obj.add_terse('syms', t.array_node_import_symbol(node.syms)) obj.add('comments', t.array_node_comment(node.comments)) obj.add('next_comments', t.array_node_comment(node.next_comments)) obj.add('pos', t.position(node.pos)) @@ -458,7 +458,7 @@ fn (t Tree) import_module(node ast.Import) &Node { fn (t Tree) import_symbol(node ast.ImportSymbol) &Node { mut obj := new_object() - obj.add('name', t.string_node(node.name)) + obj.add_terse('name', t.string_node(node.name)) obj.add('pos', t.position(node.pos)) return obj } @@ -485,9 +485,10 @@ fn (t Tree) comment(node ast.Comment) &Node { fn (t Tree) const_decl(node ast.ConstDecl) &Node { mut obj := new_object() obj.add_terse('ast_type', t.string_node('ConstDecl')) - obj.add('is_pub', t.bool_node(node.is_pub)) - obj.add('is_block', t.bool_node(node.is_block)) - obj.add('fields', t.array_node_const_field(node.fields)) + obj.add_terse('is_pub', t.bool_node(node.is_pub)) + obj.add_terse('is_block', t.bool_node(node.is_block)) + obj.add_terse('fields', t.array_node_const_field(node.fields)) + obj.add_terse('attrs', t.array_node_attr(node.attrs)) obj.add('end_comments', t.array_node_comment(node.end_comments)) obj.add('pos', t.position(node.pos)) return obj @@ -496,11 +497,12 @@ fn (t Tree) const_decl(node ast.ConstDecl) &Node { fn (t Tree) const_field(node ast.ConstField) &Node { mut obj := new_object() obj.add_terse('ast_type', t.string_node('ConstField')) - obj.add('mod', t.string_node(node.mod)) - obj.add('name', t.string_node(node.name)) - obj.add('expr', t.expr(node.expr)) - obj.add('is_pub', t.bool_node(node.is_pub)) - obj.add('typ', t.type_node(node.typ)) + obj.add_terse('mod', t.string_node(node.mod)) + obj.add_terse('name', t.string_node(node.name)) + obj.add_terse('expr', t.expr(node.expr)) + obj.add_terse('is_pub', t.bool_node(node.is_pub)) + obj.add_terse('is_markused', t.bool_node(node.is_markused)) + obj.add_terse('typ', t.type_node(node.typ)) obj.add('comments', t.array_node_comment(node.comments)) obj.add('comptime_expr_value', t.comptime_expr_value(node.comptime_expr_value)) obj.add('pos', t.position(node.pos)) @@ -527,18 +529,18 @@ fn (t Tree) fn_decl(node ast.FnDecl) &Node { obj.add_terse('ast_type', t.string_node('FnDecl')) obj.add_terse('name', t.string_node(node.name)) obj.add_terse('mod', t.string_node(node.mod)) - obj.add('is_deprecated', t.bool_node(node.is_deprecated)) - obj.add('is_pub', t.bool_node(node.is_pub)) - obj.add('is_variadic', t.bool_node(node.is_variadic)) + obj.add_terse('is_deprecated', t.bool_node(node.is_deprecated)) + obj.add_terse('is_pub', t.bool_node(node.is_pub)) + obj.add_terse('is_variadic', t.bool_node(node.is_variadic)) obj.add('is_anon', t.bool_node(node.is_anon)) - obj.add('is_noreturn', t.bool_node(node.is_noreturn)) - obj.add('is_manualfree', t.bool_node(node.is_manualfree)) + obj.add_terse('is_noreturn', t.bool_node(node.is_noreturn)) + obj.add_terse('is_manualfree', t.bool_node(node.is_manualfree)) obj.add('is_main', t.bool_node(node.is_main)) obj.add('is_test', t.bool_node(node.is_test)) obj.add('is_conditional', t.bool_node(node.is_conditional)) - obj.add('is_exported', t.bool_node(node.is_exported)) + obj.add_terse('is_exported', t.bool_node(node.is_exported)) obj.add('is_keep_alive', t.bool_node(node.is_keep_alive)) - obj.add('is_unsafe', t.bool_node(node.is_unsafe)) + obj.add_terse('is_unsafe', t.bool_node(node.is_unsafe)) obj.add_terse('receiver', t.struct_field(node.receiver)) obj.add('receiver_pos', t.position(node.receiver_pos)) obj.add_terse('is_method', t.bool_node(node.is_method)) @@ -597,8 +599,8 @@ fn (t Tree) struct_decl(node ast.StructDecl) &Node { obj.add_terse('is_union', t.bool_node(node.is_union)) obj.add('pos', t.position(node.pos)) obj.add_terse('fields', t.array_node_struct_field(node.fields)) - obj.add('generic_types', t.array_node_type(node.generic_types)) - obj.add('attrs', t.array_node_attr(node.attrs)) + obj.add_terse('generic_types', t.array_node_type(node.generic_types)) + obj.add_terse('attrs', t.array_node_attr(node.attrs)) obj.add('end_comments', t.array_node_comment(node.end_comments)) obj.add_terse('embeds', t.array_node_embed(node.embeds)) return obj @@ -746,6 +748,7 @@ fn (t Tree) global_field(node ast.GlobalField) &Node { obj.add_terse('expr', t.expr(node.expr)) obj.add_terse('typ', t.type_node(node.typ)) obj.add_terse('has_expr', t.bool_node(node.has_expr)) + obj.add_terse('is_markused', t.bool_node(node.is_markused)) obj.add('comments', t.array_node_comment(node.comments)) obj.add('pos', t.position(node.pos)) obj.add('typ_pos', t.position(node.typ_pos)) diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 5194ff9ece..3b5d807425 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -128,7 +128,7 @@ pub: kind AttributeKind } -[used] +[markused] fn v_segmentation_fault_handler(signal int) { eprintln('signal 11: segmentation fault') print_backtrace() diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index df5df3851e..eeeadfc4b9 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -288,11 +288,12 @@ pub mut: // const field in const declaration group pub struct ConstField { pub: - mod string - name string - expr Expr // the value expr of field; everything after `=` - is_pub bool - pos token.Position + mod string + name string + expr Expr // the value expr of field; everything after `=` + is_pub bool + is_markused bool // an explict `[markused]` tag; the const will NOT be removed by `-skip-unused`, no matter what + pos token.Position pub mut: typ Type // the type of the const field, it can be any type in V comments []Comment // comments before current const field @@ -306,6 +307,7 @@ pub struct ConstDecl { pub: is_pub bool pos token.Position + attrs []Attr // tags like `[markused]`, valid for all the consts in the list pub mut: fields []ConstField // all the const fields in the `const (...)` block end_comments []Comment // comments that after last const field @@ -460,6 +462,7 @@ pub: is_exported bool // true for `[export: 'exact_C_name']` is_keep_alive bool // passed memory must not be freed (by GC) before function returns is_unsafe bool // true, when [unsafe] is used on a fn + is_markused bool // true, when an explict `[markused]` tag was put on a fn; `-skip-unused` will not remove that fn receiver StructField // TODO this is not a struct field receiver_pos token.Position // `(u User)` in `fn (u User) name()` position is_method bool @@ -484,8 +487,8 @@ pub mut: return_type Type return_type_pos token.Position // `string` in `fn (u User) name() string` position has_return bool - should_be_skipped bool - has_await bool // 'true' if this function uses JS.await + should_be_skipped bool // true, when -skip-unused could not find any usages of that function, starting from main + other known used functions + has_await bool // 'true' if this function uses JS.await // comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl @@ -525,7 +528,7 @@ pub mut: left_type Type // type of `user` receiver_type Type // User return_type Type - should_be_skipped bool + should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag` concrete_types []Type // concrete types, e.g. concrete_list_pos token.Position free_receiver bool // true if the receiver expression needs to be freed @@ -596,7 +599,7 @@ pub mut: // [11, 12, 13] <- cast order (smartcasts) // 12 <- the current casted type (typ) pos token.Position - is_used bool + is_used bool // whether the local variable was used in other expressions is_changed bool // to detect mutable vars that are never changed // // (for setting the position after the or block for autofree) @@ -624,10 +627,11 @@ pub: pub struct GlobalField { pub: - name string - has_expr bool - pos token.Position - typ_pos token.Position + name string + has_expr bool + pos token.Position + typ_pos token.Position + is_markused bool // an explict `[markused]` tag; the global will NOT be removed by `-skip-unused` pub mut: expr Expr typ Type @@ -638,8 +642,8 @@ pub struct GlobalDecl { pub: mod string pos token.Position - is_block bool // __global() block - attrs []Attr + is_block bool // __global() block + attrs []Attr // tags like `[markused]`, valid for all the globals in the list pub mut: fields []GlobalField end_comments []Comment diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index a5e0427ff3..39b45d747b 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -776,6 +776,7 @@ mut: } pub fn (mut f Fmt) const_decl(node ast.ConstDecl) { + f.attrs(node.attrs) if node.is_pub { f.write('pub ') } diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index 5d0102efd1..12ce4e15f6 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -336,6 +336,9 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F pref: pref } // println( all_fns.keys() ) + walker.mark_markused_fns() // tagged with `[markused]` + walker.mark_markused_consts() // tagged with `[markused]` + walker.mark_markused_globals() // tagged with `[markused]` walker.mark_exported_fns() walker.mark_root_fns(all_fn_root_names) diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 54a5a25c7f..a9307b431c 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -72,6 +72,30 @@ pub fn (mut w Walker) mark_exported_fns() { } } +pub fn (mut w Walker) mark_markused_fns() { + for _, mut func in w.all_fns { + if func.is_markused { + w.fn_decl(mut func) + } + } +} + +pub fn (mut w Walker) mark_markused_consts() { + for ckey, mut constfield in w.all_consts { + if constfield.is_markused { + w.mark_const_as_used(ckey) + } + } +} + +pub fn (mut w Walker) mark_markused_globals() { + for gkey, mut globalfield in w.all_globals { + if globalfield.is_markused { + w.mark_global_as_used(gkey) + } + } +} + pub fn (mut w Walker) stmt(node ast.Stmt) { match mut node { ast.EmptyStmt {} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 9f40dff6fb..192f759bc0 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -180,6 +180,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { mut is_noreturn := false mut is_ctor_new := false mut is_c2v_variadic := false + mut is_markused := false for fna in p.attrs { match fna.name { 'noreturn' { is_noreturn = true } @@ -193,6 +194,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { 'trusted' { is_trusted = true } 'c2v_variadic' { is_c2v_variadic = true } 'use_new' { is_ctor_new = true } + 'markused' { is_markused = true } else {} } } @@ -496,6 +498,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_test: is_test is_keep_alive: is_keep_alive is_unsafe: is_unsafe + is_markused: is_markused // attrs: p.attrs is_conditional: conditional_ctdefine_idx != -1 diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 719f311ac5..2c25c3a47e 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -13,9 +13,7 @@ import v.errors import os import hash.fnv1a -pub const ( - builtin_functions = ['print', 'println', 'eprint', 'eprintln', 'isnil', 'panic', 'exit'] -) +pub const builtin_functions = ['print', 'println', 'eprint', 'eprintln', 'isnil', 'panic', 'exit'] pub struct Parser { pref &pref.Preferences @@ -3009,6 +3007,18 @@ fn (mut p Parser) import_syms(mut parent ast.Import) { fn (mut p Parser) const_decl() ast.ConstDecl { p.top_level_statement_start() + mut attrs := []ast.Attr{} + if p.attrs.len > 0 { + attrs = p.attrs + p.attrs = [] + } + mut is_markused := false + for ga in attrs { + match ga.name { + 'markused' { is_markused = true } + else {} + } + } start_pos := p.tok.position() is_pub := p.tok.kind == .key_pub if is_pub { @@ -3055,6 +3065,7 @@ fn (mut p Parser) const_decl() ast.ConstDecl { expr: expr pos: pos.extend(expr.position()) comments: comments + is_markused: is_markused } fields << field p.table.global_scope.register(field) @@ -3073,6 +3084,7 @@ fn (mut p Parser) const_decl() ast.ConstDecl { is_pub: is_pub end_comments: comments is_block: is_block + attrs: attrs } } @@ -3105,6 +3117,15 @@ fn (mut p Parser) global_decl() ast.GlobalDecl { attrs = p.attrs p.attrs = [] } + + mut is_markused := false + for ga in attrs { + match ga.name { + 'markused' { is_markused = true } + else {} + } + } + if !p.has_globals && !p.pref.enable_globals && !p.pref.is_fmt && !p.pref.translated && !p.pref.is_livemain && !p.pref.building_v && !p.builtin_mod { p.error('use `v -enable-globals ...` to enable globals') @@ -3180,6 +3201,7 @@ fn (mut p Parser) global_decl() ast.GlobalDecl { typ_pos: typ_pos typ: typ comments: comments + is_markused: is_markused } fields << field p.table.global_scope.register(field)