v2 / vlib / v / checker / unused_declarations.v
140 lines · 129 sloc · 3.48 KB · dc7d47597b285c66e4887daa5cf31b3886eb433f
Raw
1module checker
2
3import v.ast
4
5@[inline]
6fn (mut c Checker) mark_fn_decl_as_referenced(fkey string) {
7 if fkey == '' {
8 return
9 }
10 c.table.used_features.referenced_fns[fkey] = true
11}
12
13@[inline]
14fn (mut c Checker) mark_const_decl_as_referenced(name string) {
15 if name == '' {
16 return
17 }
18 c.table.used_features.referenced_consts[name] = true
19}
20
21fn (mut c Checker) mark_type_str_method_as_referenced(typ ast.Type) {
22 sym := c.table.sym(c.unwrap_generic(typ))
23 if method := sym.find_method_with_generic_parent('str') {
24 c.mark_fn_decl_as_referenced(method.fkey())
25 }
26}
27
28fn (c &Checker) module_qualified_selector_const_name(node ast.SelectorExpr) string {
29 if root_ident := node.root_ident() {
30 if root_ident.name == c.mod {
31 return '${c.mod}.${node.field_name}'
32 }
33 for import_sym in c.file.imports {
34 alias := if import_sym.alias == '' {
35 import_sym.mod.all_after_last('.')
36 } else {
37 import_sym.alias
38 }
39 if root_ident.name == alias {
40 return '${import_sym.mod}.${node.field_name}'
41 }
42 }
43 }
44 return ''
45}
46
47fn (mut c Checker) check_unused_declarations(ast_files []&ast.File) {
48 if c.pref.is_repl || c.pref.is_test || c.pref.check_only || c.pref.only_check_syntax
49 || c.pref.translated || c.nr_errors > 0 || c.main_fn_decl_node.name == '' {
50 return
51 }
52 saved_file := c.file
53 for file in ast_files {
54 if file.mod.name != 'main' || file.is_test || file.is_generated || file.is_translated {
55 continue
56 }
57 c.change_current_file(file)
58 c.check_unused_declarations_in_stmts(file.stmts)
59 if c.should_abort {
60 break
61 }
62 }
63 if saved_file != unsafe { nil } {
64 c.change_current_file(saved_file)
65 }
66}
67
68fn (mut c Checker) check_unused_declarations_in_stmts(stmts []ast.Stmt) {
69 for stmt in stmts {
70 match stmt {
71 ast.ConstDecl {
72 for field in stmt.fields {
73 if c.should_warn_about_unused_const(field) {
74 c.note('unused constant: `${stripped_name(field.name)}`', field.pos)
75 }
76 }
77 }
78 ast.FnDecl {
79 if c.should_warn_about_unused_fn(stmt) {
80 name := if stmt.short_name != '' { stmt.short_name } else { stmt.get_name() }
81 c.note('unused function: `${name}`', stmt.name_pos)
82 }
83 }
84 ast.ExprStmt {
85 match stmt.expr {
86 ast.IfExpr {
87 if stmt.expr.is_comptime {
88 for branch in stmt.expr.branches {
89 if c.is_active_comptime_branch(branch.id) {
90 c.check_unused_declarations_in_stmts(branch.stmts)
91 }
92 }
93 }
94 }
95 ast.MatchExpr {
96 if stmt.expr.is_comptime {
97 for branch in stmt.expr.branches {
98 if c.is_active_comptime_branch(branch.id) {
99 c.check_unused_declarations_in_stmts(branch.stmts)
100 }
101 }
102 }
103 }
104 else {}
105 }
106 }
107 else {}
108 }
109
110 if c.should_abort {
111 return
112 }
113 }
114}
115
116fn (c &Checker) is_active_comptime_branch(branch_id int) bool {
117 if branch_id == 0 {
118 return true
119 }
120 if result := c.table.comptime_is_true['|id=${branch_id}|'] {
121 return result.val
122 }
123 return true
124}
125
126fn (c &Checker) should_warn_about_unused_const(field ast.ConstField) bool {
127 if field.is_pub || field.is_markused || field.is_exported || field.is_virtual_c {
128 return false
129 }
130 return field.name !in c.table.used_features.referenced_consts
131}
132
133fn (c &Checker) should_warn_about_unused_fn(node ast.FnDecl) bool {
134 if node.is_pub || node.is_markused || node.is_exported || node.no_body || node.is_main
135 || node.is_test || node.is_method || node.is_static_type_method || node.should_be_skipped
136 || node.short_name in ['init', 'cleanup'] {
137 return false
138 }
139 return node.fkey() !in c.table.used_features.referenced_fns
140}
141