v2 / vlib / v / checker / visible_mutation.v
361 lines · 344 sloc · 9.3 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1module checker
2
3import v.ast
4
5enum RootMutationVisibility {
6 none
7 direct
8 private_path
9 public_path
10}
11
12@[inline]
13fn is_visible_root_mutation(vis RootMutationVisibility) bool {
14 return vis in [.direct, .public_path]
15}
16
17fn (mut c Checker) visible_param_mutation_cache_key(func ast.Fn, param_idx int) string {
18 return '${func.fkey()}|${param_idx}'
19}
20
21fn (mut c Checker) fn_has_visible_mutation_for_param(func ast.Fn, param_idx int) bool {
22 if param_idx < 0 || param_idx >= func.params.len || !func.params[param_idx].is_mut {
23 return false
24 }
25 cache_key := c.visible_param_mutation_cache_key(func, param_idx)
26 if cache_key in c.visible_param_mutation_cache {
27 return c.visible_param_mutation_cache[cache_key]
28 }
29 if cache_key in c.visible_param_mutation_in_progress {
30 return true
31 }
32 if func.source_fn == unsafe { nil } || func.no_body || func.language != .v {
33 c.visible_param_mutation_cache[cache_key] = true
34 return true
35 }
36 fn_decl := unsafe { &ast.FnDecl(func.source_fn) }
37 if fn_decl == unsafe { nil } || param_idx >= fn_decl.params.len {
38 c.visible_param_mutation_cache[cache_key] = true
39 return true
40 }
41 c.visible_param_mutation_in_progress[cache_key] = true
42 res := c.fn_decl_has_visible_mutation_for_param(fn_decl, param_idx)
43 c.visible_param_mutation_in_progress.delete(cache_key)
44 c.visible_param_mutation_cache[cache_key] = res
45 return res
46}
47
48fn (mut c Checker) fn_decl_has_visible_mutation_for_param(fn_decl &ast.FnDecl, param_idx int) bool {
49 if param_idx < 0 || param_idx >= fn_decl.params.len || !fn_decl.params[param_idx].is_mut {
50 return false
51 }
52 root_name := fn_decl.params[param_idx].name
53 root_type := fn_decl.params[param_idx].typ
54 for stmt in fn_decl.stmts {
55 if c.stmt_has_visible_mutation(stmt, root_name, root_type) {
56 return true
57 }
58 }
59 return false
60}
61
62fn (mut c Checker) stmt_has_visible_mutation(stmt ast.Stmt, root_name string, root_type ast.Type) bool {
63 match stmt {
64 ast.FnDecl {
65 return false
66 }
67 ast.ExprStmt {
68 return c.expr_has_visible_mutation(stmt.expr, root_name, root_type)
69 }
70 ast.AssignStmt {
71 for left_expr in stmt.left {
72 if is_visible_root_mutation(c.expr_mutation_visibility(left_expr, root_name,
73 root_type))
74 {
75 return true
76 }
77 }
78 if c.assign_stmt_aliases_visible_state(stmt, root_name, root_type) {
79 return true
80 }
81 }
82 else {}
83 }
84
85 return c.node_children_have_visible_mutation(ast.Node(stmt), root_name, root_type)
86}
87
88fn (mut c Checker) expr_has_visible_mutation(expr ast.Expr, root_name string, root_type ast.Type) bool {
89 match expr {
90 ast.AnonFn, ast.LambdaExpr {
91 return false
92 }
93 ast.CallExpr {
94 if c.call_has_visible_root_mutation(expr, root_name, root_type) {
95 return true
96 }
97 }
98 ast.PrefixExpr {
99 if expr.op == .amp
100 && is_visible_root_mutation(c.expr_mutation_visibility(expr.right, root_name, root_type)) {
101 return true
102 }
103 }
104 ast.PostfixExpr {
105 if is_visible_root_mutation(c.expr_mutation_visibility(expr.expr, root_name, root_type)) {
106 return true
107 }
108 }
109 ast.InfixExpr {
110 if expr.op == .left_shift
111 && is_visible_root_mutation(c.expr_mutation_visibility(expr.left, root_name, root_type)) {
112 return true
113 }
114 }
115 else {}
116 }
117
118 return c.node_children_have_visible_mutation(ast.Node(expr), root_name, root_type)
119}
120
121fn (mut c Checker) node_children_have_visible_mutation(node ast.Node, root_name string, root_type ast.Type) bool {
122 for child in node.children() {
123 match child {
124 ast.Expr {
125 if c.expr_has_visible_mutation(child, root_name, root_type) {
126 return true
127 }
128 }
129 ast.Stmt {
130 if child is ast.FnDecl {
131 continue
132 }
133 if c.stmt_has_visible_mutation(child, root_name, root_type) {
134 return true
135 }
136 }
137 ast.CallArg {
138 if c.expr_has_visible_mutation(child.expr, root_name, root_type) {
139 return true
140 }
141 }
142 ast.IfBranch {
143 if c.expr_has_visible_mutation(child.cond, root_name, root_type) {
144 return true
145 }
146 for stmt in child.stmts {
147 if c.stmt_has_visible_mutation(stmt, root_name, root_type) {
148 return true
149 }
150 }
151 }
152 ast.MatchBranch {
153 for branch_expr in child.exprs {
154 if c.expr_has_visible_mutation(branch_expr, root_name, root_type) {
155 return true
156 }
157 }
158 for stmt in child.stmts {
159 if c.stmt_has_visible_mutation(stmt, root_name, root_type) {
160 return true
161 }
162 }
163 }
164 ast.SelectBranch {
165 if c.stmt_has_visible_mutation(child.stmt, root_name, root_type) {
166 return true
167 }
168 for stmt in child.stmts {
169 if c.stmt_has_visible_mutation(stmt, root_name, root_type) {
170 return true
171 }
172 }
173 }
174 else {}
175 }
176 }
177 return false
178}
179
180fn (mut c Checker) expr_mutation_visibility(expr ast.Expr, root_name string, root_type ast.Type) RootMutationVisibility {
181 mut reduced := expr
182 reduced = reduced.remove_par()
183 current := reduced
184 if current is ast.Ident {
185 return if current.name == root_name {
186 .direct
187 } else {
188 .none
189 }
190 }
191 if current is ast.SelectorExpr {
192 mut parent_expr := current.expr
193 parent_expr = parent_expr.remove_par()
194 parent := parent_expr
195 if parent is ast.Ident && parent.name == root_name {
196 root_sym := c.table.final_sym(c.unwrap_generic(root_type))
197 field := c.table.find_field_with_embeds(root_sym, current.field_name) or {
198 return .public_path
199 }
200 return if field.is_pub { .public_path } else { .private_path }
201 }
202 return c.expr_mutation_visibility(parent, root_name, root_type)
203 }
204 if current is ast.IndexExpr {
205 return c.expr_mutation_visibility(current.left, root_name, root_type)
206 }
207 if current is ast.PrefixExpr {
208 return c.expr_mutation_visibility(current.right, root_name, root_type)
209 }
210 if current is ast.PostfixExpr {
211 return c.expr_mutation_visibility(current.expr, root_name, root_type)
212 }
213 if current is ast.CastExpr {
214 return c.expr_mutation_visibility(current.expr, root_name, root_type)
215 }
216 if current is ast.CallExpr {
217 if current.is_method {
218 return c.expr_mutation_visibility(current.left, root_name, root_type)
219 }
220 }
221 return .none
222}
223
224fn (mut c Checker) call_has_visible_root_mutation(node ast.CallExpr, root_name string, root_type ast.Type) bool {
225 mut called_fn := ast.Fn{}
226 mut has_called_fn := false
227 if func := c.find_called_fn(node) {
228 called_fn = func
229 has_called_fn = true
230 }
231 if node.is_method {
232 left_vis := c.expr_mutation_visibility(node.left, root_name, root_type)
233 if has_called_fn {
234 if called_fn.params.len > 0 && called_fn.params[0].is_mut {
235 match left_vis {
236 .direct {
237 if c.fn_has_visible_mutation_for_param(called_fn, 0) {
238 return true
239 }
240 }
241 .public_path {
242 return true
243 }
244 else {}
245 }
246 }
247 } else if is_visible_root_mutation(left_vis) {
248 return true
249 }
250 }
251 if !has_called_fn {
252 for arg in node.args {
253 if arg.is_mut
254 && is_visible_root_mutation(c.expr_mutation_visibility(arg.expr, root_name, root_type)) {
255 return true
256 }
257 }
258 return false
259 }
260 for i, arg in node.args {
261 if !arg.is_mut {
262 continue
263 }
264 param_idx := c.call_arg_param_index(called_fn, i)
265 arg_vis := c.expr_mutation_visibility(arg.expr, root_name, root_type)
266 if param_idx < 0 || param_idx >= called_fn.params.len {
267 if is_visible_root_mutation(arg_vis) {
268 return true
269 }
270 continue
271 }
272 if !called_fn.params[param_idx].is_mut {
273 continue
274 }
275 match arg_vis {
276 .direct {
277 if c.fn_has_visible_mutation_for_param(called_fn, param_idx) {
278 return true
279 }
280 }
281 .public_path {
282 return true
283 }
284 else {}
285 }
286 }
287 return false
288}
289
290fn (mut c Checker) find_called_fn(node ast.CallExpr) ?ast.Fn {
291 if node.is_method {
292 mut candidate_types := []ast.Type{}
293 for typ in [node.receiver_type, node.left_type, c.unwrap_generic(node.receiver_type),
294 c.unwrap_generic(node.left_type)] {
295 if typ != 0 && typ !in candidate_types {
296 candidate_types << typ
297 }
298 }
299 for typ in candidate_types {
300 sym := c.table.sym(c.unwrap_generic(typ))
301 if method := c.table.find_method(sym, node.name) {
302 return method
303 }
304 if method := c.table.find_method_with_embeds(sym, node.name) {
305 return method
306 }
307 }
308 return none
309 }
310 return c.table.find_fn(node.name)
311}
312
313fn (c &Checker) call_arg_param_index(func ast.Fn, arg_idx int) int {
314 offset := if func.is_method { 1 } else { 0 }
315 if func.is_variadic && func.params.len > 0 && arg_idx + offset >= func.params.len - 1 {
316 return func.params.len - 1
317 }
318 param_idx := arg_idx + offset
319 if param_idx >= func.params.len {
320 return -1
321 }
322 return param_idx
323}
324
325fn (mut c Checker) assign_stmt_aliases_visible_state(node ast.AssignStmt, root_name string, root_type ast.Type) bool {
326 mut pair_count := node.left.len
327 if node.right.len < pair_count {
328 pair_count = node.right.len
329 }
330 for i in 0 .. pair_count {
331 right_vis := c.expr_mutation_visibility(node.right[i], root_name, root_type)
332 if !is_visible_root_mutation(right_vis) {
333 continue
334 }
335 right_type := if i < node.right_types.len {
336 node.right_types[i]
337 } else {
338 ast.no_type
339 }
340 if !c.type_may_share_mutable_storage(right_type) {
341 continue
342 }
343 mut left_expr := node.left[i]
344 left_expr = left_expr.remove_par()
345 if left_expr is ast.Ident && left_expr.is_mut() {
346 return true
347 }
348 }
349 return false
350}
351
352fn (mut c Checker) type_may_share_mutable_storage(typ ast.Type) bool {
353 if typ == 0 || typ == ast.no_type {
354 return false
355 }
356 unwrapped := c.unwrap_generic(typ)
357 if unwrapped.is_any_kind_of_pointer() || unwrapped.has_flag(.shared_f) {
358 return true
359 }
360 return c.table.final_sym(unwrapped).kind in [.array, .map, .chan, .interface, .thread, .function]
361}
362