| 1 | // Copyright (c) 2019-2024 Felipe Pena. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license that can be found in the LICENSE file. |
| 3 | module checker |
| 4 | |
| 5 | import v.ast |
| 6 | |
| 7 | @[inline] |
| 8 | fn (mut c Checker) markused_comptime_call(check bool, key string) { |
| 9 | if check { |
| 10 | c.table.used_features.comptime_calls[key] = true |
| 11 | } |
| 12 | } |
| 13 | |
| 14 | fn comptime_call_last_arg_type(arg ast.CallArg) ast.Type { |
| 15 | return if arg.expr is ast.ArrayDecompose { |
| 16 | arg.expr.expr_type |
| 17 | } else { |
| 18 | arg.typ |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | fn (mut c Checker) markused_assertstmt_auto_str(mut node ast.AssertStmt) { |
| 23 | if !c.table.used_features.auto_str && !c.is_builtin_mod && mut node.expr is ast.InfixExpr { |
| 24 | if !c.table.sym(c.unwrap_generic(node.expr.left_type)).has_method('str') { |
| 25 | c.table.used_features.auto_str = true |
| 26 | return |
| 27 | } |
| 28 | if !c.table.sym(c.unwrap_generic(node.expr.right_type)).has_method('str') { |
| 29 | c.table.used_features.auto_str = true |
| 30 | } |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | fn (mut c Checker) markused_dumpexpr(mut node ast.DumpExpr) { |
| 35 | if c.is_builtin_mod { |
| 36 | return |
| 37 | } |
| 38 | unwrapped_type := c.unwrap_generic(node.expr_type) |
| 39 | if node.expr_type.has_flag(.generic) { |
| 40 | c.table.used_features.comptime_syms[unwrapped_type] = true |
| 41 | } |
| 42 | if !c.table.sym(unwrapped_type).has_method('str') { |
| 43 | c.table.used_features.auto_str = true |
| 44 | if node.expr_type.is_ptr() { |
| 45 | c.table.used_features.auto_str_ptr = true |
| 46 | } |
| 47 | if !c.table.used_features.auto_str_arr { |
| 48 | c.table.used_features.auto_str_arr = c.table.final_sym(unwrapped_type).kind == .array |
| 49 | } |
| 50 | } else { |
| 51 | c.table.used_features.print_types[node.expr_type.idx()] = true |
| 52 | } |
| 53 | c.table.used_features.print_types[ast.int_type_idx] = true |
| 54 | } |
| 55 | |
| 56 | @[inline] |
| 57 | fn (mut c Checker) markused_used_maps(check bool) { |
| 58 | if check { |
| 59 | c.table.used_features.used_maps++ |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | fn (mut c Checker) markused_castexpr(mut _ ast.CastExpr, _ ast.Type, mut final_to_sym ast.TypeSymbol) { |
| 64 | if c.is_builtin_mod { |
| 65 | return |
| 66 | } |
| 67 | if c.table.used_features.used_maps == 0 && mut final_to_sym.info is ast.SumType { |
| 68 | if final_to_sym.info.variants.any(c.table.final_sym(it).kind == .map) { |
| 69 | c.table.used_features.used_maps++ |
| 70 | } |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | fn (mut c Checker) markused_comptimecall(mut node ast.ComptimeCall) { |
| 75 | c.markused_comptime_call(true, |
| 76 | '${int(c.unwrap_generic(c.comptime.comptime_for_method.receiver_type))}.${c.comptime.comptime_for_method.name}') |
| 77 | if c.inside_anon_fn { |
| 78 | // $method passed to anon fn, mark all methods as used |
| 79 | sym := c.table.sym(c.unwrap_generic(node.left_type)) |
| 80 | for m in sym.get_methods() { |
| 81 | c.table.used_features.comptime_calls['${int(c.unwrap_generic(m.receiver_type))}.${m.name}'] = true |
| 82 | if node.args.len > 0 && m.params.len > 0 { |
| 83 | last_param := m.params.last().typ |
| 84 | last_arg_type := comptime_call_last_arg_type(node.args.last()) |
| 85 | if (last_param.is_int() || last_param.is_float() |
| 86 | || last_param.is_bool()) && c.table.final_sym(last_arg_type).kind == .array { |
| 87 | c.table.used_features.comptime_calls['${ast.string_type_idx}.${c.table.type_to_str(m.params.last().typ)}'] = true |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | } else { |
| 92 | m := c.comptime.comptime_for_method |
| 93 | if node.args.len > 0 && m.params.len > 0 { |
| 94 | last_param := m.params.last().typ |
| 95 | last_arg_type := comptime_call_last_arg_type(node.args.last()) |
| 96 | if (last_param.is_int() || last_param.is_float() || last_param.is_bool()) |
| 97 | && c.table.final_sym(last_arg_type).kind == .array { |
| 98 | c.table.used_features.comptime_calls['${ast.string_type_idx}.${c.table.type_to_str(m.params.last().typ)}'] = true |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | fn (mut c Checker) markused_comptimefor(mut _ ast.ComptimeFor, unwrapped_expr_type ast.Type) { |
| 105 | if c.table.used_features.used_maps == 0 { |
| 106 | final_sym := c.table.final_sym(unwrapped_expr_type) |
| 107 | if final_sym.info is ast.Map { |
| 108 | c.table.used_features.used_maps++ |
| 109 | } else if final_sym.info is ast.SumType { |
| 110 | if final_sym.info.variants.any(c.table.final_sym(it).kind == .map) { |
| 111 | c.table.used_features.used_maps++ |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | fn (mut c Checker) markused_call_expr(left_type ast.Type, mut node ast.CallExpr) { |
| 118 | if left_type != 0 && left_type.is_ptr() && !c.table.used_features.auto_str_ptr |
| 119 | && node.name == 'str' { |
| 120 | c.table.used_features.auto_str_ptr = true |
| 121 | if !c.table.used_features.auto_str_arr { |
| 122 | c.table.used_features.auto_str_arr = c.table.final_sym(left_type).kind == .array |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | fn (mut c Checker) markused_print_call(mut node ast.CallExpr) { |
| 128 | if !c.is_builtin_mod && c.mod != 'math.bits' && node.args[0].expr !is ast.StringLiteral { |
| 129 | arg_typ := c.unwrap_generic(node.args[0].typ) |
| 130 | if arg_typ == 0 { |
| 131 | return |
| 132 | } |
| 133 | if (node.args[0].expr is ast.CallExpr && node.args[0].expr.is_method |
| 134 | && node.args[0].expr.name == 'str') |
| 135 | || !c.table.sym(arg_typ).has_method('str') { |
| 136 | c.table.used_features.auto_str = true |
| 137 | } else { |
| 138 | c.mark_type_str_method_as_referenced(arg_typ) |
| 139 | if arg_typ.has_option_or_result() { |
| 140 | c.table.used_features.print_options = true |
| 141 | } |
| 142 | c.table.used_features.print_types[arg_typ.idx()] = true |
| 143 | if !c.table.used_features.auto_str_ptr && node.args[0].expr is ast.Ident { |
| 144 | var_obj := node.args[0].expr.obj |
| 145 | if var_obj is ast.Var { |
| 146 | if var_obj.orig_type != 0 { |
| 147 | fsym := c.table.final_sym(var_obj.orig_type) |
| 148 | if fsym.kind == .interface { |
| 149 | c.table.used_features.auto_str_ptr = true |
| 150 | } else if fsym.kind == .array { |
| 151 | c.table.used_features.auto_str_arr = true |
| 152 | } |
| 153 | return |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | if arg_typ.is_ptr() { |
| 159 | c.table.used_features.auto_str_ptr = true |
| 160 | } |
| 161 | if !c.table.used_features.auto_str_arr || !c.table.used_features.auto_str_ptr { |
| 162 | sym := c.table.final_sym(arg_typ) |
| 163 | if sym.kind == .array { |
| 164 | c.table.used_features.auto_str_arr = true |
| 165 | } else if sym.info is ast.Struct { |
| 166 | if !c.table.used_features.auto_str_ptr { |
| 167 | c.table.used_features.auto_str_ptr = sym.info.fields.any(it.typ.is_ptr() |
| 168 | || it.typ.is_pointer()) |
| 169 | } |
| 170 | c.table.used_features.auto_str_arr = |
| 171 | sym.info.fields.any(c.table.final_sym(it.typ).kind == .array) |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | fn (mut c Checker) markused_method_call(mut node ast.CallExpr, mut left_expr ast.Expr, left_type ast.Type) { |
| 178 | if !left_type.has_flag(.generic) && mut left_expr is ast.Ident { |
| 179 | if left_expr.obj is ast.Var && left_expr.obj.ct_type_var == .smartcast { |
| 180 | c.table.used_features.comptime_calls['${int(left_type)}.${node.name}'] = true |
| 181 | } |
| 182 | if c.table.sym(left_type).kind == .alias { |
| 183 | c.table.used_features.comptime_calls['${int(left_type)}.${node.name}'] = true |
| 184 | // Alias method calls can also auto-resolve to pointer receivers in cgen. |
| 185 | if !left_type.is_ptr() { |
| 186 | c.table.used_features.comptime_calls['${int(left_type.ref())}.${node.name}'] = true |
| 187 | } |
| 188 | } |
| 189 | } else if left_type.has_flag(.generic) { |
| 190 | unwrapped_left := c.unwrap_generic(left_type) |
| 191 | c.table.used_features.comptime_calls['${int(unwrapped_left)}.${node.name}'] = true |
| 192 | // Generic method calls can resolve to pointer receivers during cgen (`x.name()` -> `(&x).name()`). |
| 193 | // Mark both forms so skip-unused does not drop pointer receiver methods. |
| 194 | if !unwrapped_left.is_ptr() { |
| 195 | c.table.used_features.comptime_calls['${int(unwrapped_left.ref())}.${node.name}'] = true |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | fn (mut c Checker) markused_string_inter_lit(mut _ ast.StringInterLiteral, ftyp ast.Type) { |
| 201 | if c.is_builtin_mod { |
| 202 | return |
| 203 | } |
| 204 | if !c.table.sym(ftyp).has_method('str') { |
| 205 | c.table.used_features.auto_str = true |
| 206 | } else { |
| 207 | c.mark_type_str_method_as_referenced(ftyp) |
| 208 | c.table.used_features.print_types[ftyp.idx()] = true |
| 209 | } |
| 210 | if ftyp.is_ptr() { |
| 211 | c.table.used_features.auto_str_ptr = true |
| 212 | } |
| 213 | if !c.table.used_features.auto_str_arr { |
| 214 | c.table.used_features.auto_str_arr = c.table.final_sym(ftyp).kind == .array |
| 215 | } |
| 216 | if ftyp.has_option_or_result() { |
| 217 | c.table.used_features.print_options = true |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | fn (mut c Checker) markused_array_method(check bool, method_name string) { |
| 222 | if !check { |
| 223 | return |
| 224 | } |
| 225 | match method_name { |
| 226 | '' { // array init |
| 227 | } |
| 228 | 'first' { |
| 229 | c.table.used_features.arr_first = true |
| 230 | } |
| 231 | 'last' { |
| 232 | c.table.used_features.arr_last = true |
| 233 | } |
| 234 | 'pop_left' { |
| 235 | c.table.used_features.arr_pop_left = true |
| 236 | } |
| 237 | 'pop' { |
| 238 | c.table.used_features.arr_pop = true |
| 239 | } |
| 240 | 'delete' { |
| 241 | c.table.used_features.arr_delete = true |
| 242 | } |
| 243 | 'map' { |
| 244 | c.table.used_features.arr_map = true |
| 245 | } |
| 246 | else {} |
| 247 | } |
| 248 | } |
| 249 | |