v2 / vlib / v / parser / assign.v
359 lines · 350 sloc · 9.1 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module parser
5
6import v.ast
7
8fn (mut p Parser) assign_stmt() ast.Stmt {
9 mut defer_vars := p.defer_vars.clone()
10 p.defer_vars = []
11
12 exprs := p.expr_list(true)
13
14 if !(p.inside_defer && p.defer_mode == .function && p.tok.kind == .decl_assign) {
15 defer_vars << p.defer_vars
16 }
17 p.defer_vars = defer_vars
18 return p.partial_assign_stmt(exprs)
19}
20
21fn (mut p Parser) check_undefined_variables(names []string, val ast.Expr) ! {
22 p.expr_level++
23 defer {
24 p.expr_level--
25 }
26 p.check_expr_level()!
27 match val {
28 ast.Ident {
29 for name in names {
30 if name == val.name && val.kind != .blank_ident {
31 p.error_with_pos('undefined variable: `${val.name}`', val.pos)
32 return error('undefined variable: `${val.name}`')
33 }
34 }
35 }
36 ast.ArrayInit {
37 if val.has_cap {
38 p.check_undefined_variables(names, val.cap_expr)!
39 }
40 if val.has_len {
41 p.check_undefined_variables(names, val.len_expr)!
42 }
43 if val.has_init {
44 p.check_undefined_variables(names, val.init_expr)!
45 }
46 for expr in val.exprs {
47 p.check_undefined_variables(names, expr)!
48 }
49 }
50 ast.CallExpr {
51 p.check_undefined_variables(names, val.left)!
52 // arr.sort(a < b) , arr.sorted(a < b), it := [2,3,4].map(it*2), it := [2,3,4].filter(it % 2 == 0), etc.
53 if val.args.len == 1
54 && val.name in ['sort', 'sorted', 'map', 'filter', 'any', 'all', 'count'] {
55 return
56 }
57 for arg in val.args {
58 p.check_undefined_variables(names, arg.expr)!
59 }
60 }
61 ast.CastExpr {
62 p.check_undefined_variables(names, val.expr)!
63 p.check_undefined_variables(names, val.arg)!
64 }
65 ast.IndexExpr {
66 p.check_undefined_variables(names, val.left)!
67 p.check_undefined_variables(names, val.index)!
68 }
69 ast.InfixExpr {
70 p.check_undefined_variables(names, val.left)!
71 p.check_undefined_variables(names, val.right)!
72 }
73 ast.IfExpr {
74 p.check_undefined_variables(names, val.left)!
75 for branch in val.branches {
76 p.check_undefined_variables(names, branch.cond)!
77 for stmt in branch.stmts {
78 if stmt is ast.ExprStmt {
79 p.check_undefined_variables(names, stmt.expr)!
80 }
81 }
82 }
83 }
84 ast.MapInit {
85 for key in val.keys {
86 p.check_undefined_variables(names, key)!
87 }
88 for value in val.vals {
89 p.check_undefined_variables(names, value)!
90 }
91 }
92 ast.MatchExpr {
93 p.check_undefined_variables(names, val.cond)!
94 for branch in val.branches {
95 for expr in branch.exprs {
96 p.check_undefined_variables(names, expr)!
97 }
98 for stmt in branch.stmts {
99 if stmt is ast.ExprStmt {
100 p.check_undefined_variables(names, stmt.expr)!
101 }
102 }
103 }
104 }
105 ast.ParExpr {
106 p.check_undefined_variables(names, val.expr)!
107 }
108 ast.PostfixExpr {
109 p.check_undefined_variables(names, val.expr)!
110 }
111 ast.PrefixExpr {
112 p.check_undefined_variables(names, val.right)!
113 }
114 ast.SelectorExpr {
115 p.check_undefined_variables(names, val.expr)!
116 }
117 ast.StringInterLiteral {
118 for expr_ in val.exprs {
119 p.check_undefined_variables(names, expr_)!
120 }
121 }
122 ast.StructInit {
123 for init_field in val.init_fields {
124 p.check_undefined_variables(names, init_field.expr)!
125 }
126 }
127 ast.UnsafeExpr {
128 p.check_undefined_variables(names, val.expr)!
129 }
130 else {}
131 }
132}
133
134fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool {
135 // NOTE: For IndexExpr and SelectorExpr, we need to compare string representations.
136 // val_str must be computed before the match, because inside match arms `val` gets
137 // smartcast to the variant type, and calling .str() on e.g. IndexExpr would call
138 // the auto-generated struct str() instead of Expr.str().
139 // Only compute it for the types that actually need it.
140 val_str := if val is ast.IndexExpr || val is ast.SelectorExpr { val.str() } else { '' }
141 match val {
142 ast.Ident {
143 for expr in exprs {
144 if expr is ast.Ident {
145 if expr.name == val.name {
146 return true
147 }
148 }
149 }
150 }
151 ast.IndexExpr {
152 for expr in exprs {
153 if expr.str() == val_str {
154 return true
155 }
156 }
157 }
158 ast.InfixExpr {
159 return p.check_cross_variables(exprs, val.left)
160 || p.check_cross_variables(exprs, val.right)
161 }
162 ast.ParExpr {
163 return p.check_cross_variables(exprs, val.expr)
164 }
165 ast.CallExpr {
166 if p.check_cross_variables(exprs, val.left) {
167 return true
168 }
169 for arg in val.args {
170 if p.check_cross_variables(exprs, arg.expr) {
171 return true
172 }
173 }
174 }
175 ast.PrefixExpr {
176 return p.check_cross_variables(exprs, val.right)
177 }
178 ast.PostfixExpr {
179 return p.check_cross_variables(exprs, val.expr)
180 }
181 ast.SelectorExpr {
182 for expr in exprs {
183 if expr.str() == val_str {
184 return true
185 }
186 }
187 }
188 else {}
189 }
190
191 return false
192}
193
194fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt {
195 p.is_stmt_ident = false
196 op := p.tok.kind
197 if op == .power_assign {
198 p.register_auto_import('math')
199 }
200 mut pos := p.tok.pos()
201 p.next()
202 mut right := []ast.Expr{cap: left.len}
203 old_assign_rhs := p.inside_assign_rhs
204 p.inside_assign_rhs = true
205 right = p.expr_list(true)
206 p.inside_assign_rhs = old_assign_rhs
207 end_comments := p.eat_comments(same_line: true)
208 mut has_cross_var := false
209 mut is_static := false
210 mut is_volatile := false
211 for i, lx_ in left {
212 mut lx := unsafe { lx_ }
213 match mut lx {
214 ast.Ident {
215 if op == .decl_assign {
216 if lx.name.contains('.') {
217 return p.error_with_pos('non-name `${lx.name}` on left side of `:=`',
218 lx.pos)
219 }
220 if p.scope.known_var(lx.name) {
221 if !(p.pref.translated_go && lx.name in ['err', 'ok']) {
222 return p.error_with_pos('redefinition of `${lx.name}`', lx.pos)
223 }
224 }
225 mut share := unsafe { ast.ShareType(0) }
226 if mut lx.info is ast.IdentVar {
227 share = lx.info.share
228 if lx.info.is_static {
229 if !p.inside_unsafe && !p.pref.translated && !p.is_translated
230 && !p.pref.is_fmt && !p.inside_unsafe_fn {
231 return p.error_with_pos('static variables are supported only in -translated mode, `unsafe{}` blocks, or in `@[unsafe] fn`',
232 lx.pos)
233 }
234 is_static = true
235 }
236 if lx.info.is_volatile {
237 is_volatile = true
238 }
239 }
240 mut v := ast.Var{
241 name: lx.name
242 expr: if left.len == right.len { right[i] } else { ast.empty_expr }
243 share: share
244 is_mut: p.scope_var_is_mut(lx.is_mut || p.inside_for)
245 is_static: is_static
246 is_volatile: is_volatile
247 pos: lx.pos
248 is_stack_obj: p.inside_for
249 }
250 if p.prev_tok.kind == .string {
251 v.typ = ast.string_type_idx
252 } else if p.prev_tok.kind == .rsbr {
253 v.typ = ast.array_type_idx
254 }
255 if p.pref.autofree && right.len > 0 {
256 expr_for_or := if v.expr !is ast.EmptyExpr {
257 v.expr
258 } else {
259 right[0]
260 }
261 if expr_has_block_or(expr_for_or) {
262 v.is_or = true
263 }
264 }
265 obj := ast.ScopeObject(v)
266 lx.obj = obj
267 p.scope.register(obj)
268 }
269 }
270 ast.IndexExpr {
271 if op == .decl_assign {
272 return p.error_with_pos('non-name `${lx.left}[${lx.index}]` on left side of `:=`',
273 lx.pos)
274 }
275 lx.is_setter = true
276 }
277 ast.ParExpr {}
278 ast.PrefixExpr {}
279 ast.SelectorExpr {
280 if op == .decl_assign {
281 return p.error_with_pos('use assignment `=` instead of declaration `:=` when modifying struct fields',
282 pos)
283 }
284 }
285 else {}
286 }
287 }
288 if op == .decl_assign {
289 // a, b := a + 1, b
290 for r in right {
291 p.check_undefined_variables(left.map(it.str()), r) or {
292 return p.error_with_pos(err.msg(), pos)
293 }
294 }
295 } else if left.len > 1 {
296 // a, b = b, a
297 for r in right {
298 has_cross_var = p.check_cross_variables(left, r)
299 if op !in [.assign, .decl_assign] {
300 return p.unexpected_with_pos(pos, got: op.str(), expecting: ':= or = or comma')
301 }
302 if has_cross_var {
303 break
304 }
305 }
306 }
307 mut attr := ast.Attr{}
308 // This assign stmt has an attribute e.g. `x := [1,2,3] @[freed]`
309 if p.tok.kind == .at && p.tok.line_nr == p.prev_tok.line_nr {
310 p.check(.at)
311 p.check(.lsbr)
312 attrs := p.parse_attr(true)
313 if attrs.len != 1 {
314 p.error_with_pos('assignment attributes support at most one argument', p.prev_tok.pos())
315 }
316 attr = attrs[0]
317 p.check(.rsbr)
318 }
319 pos.update_last_line(p.prev_tok.line_nr)
320 p.expr_mod = ''
321 return ast.AssignStmt{
322 op: op
323 left: left
324 right: right
325 end_comments: end_comments
326 pos: pos
327 has_cross_var: has_cross_var
328 is_simple: p.inside_for && p.tok.kind == .lcbr
329 is_static: is_static
330 is_volatile: is_volatile
331 attr: attr
332 }
333}
334
335fn expr_has_block_or(expr ast.Expr) bool {
336 return match expr {
337 ast.CallExpr {
338 expr.or_block.kind == .block && expr.or_block.stmts.len > 0
339 }
340 ast.SelectorExpr {
341 expr.or_block.kind == .block && expr.or_block.stmts.len > 0
342 }
343 ast.PrefixExpr {
344 expr.or_block.kind == .block && expr.or_block.stmts.len > 0
345 }
346 ast.SqlExpr {
347 expr.or_expr.kind == .block && expr.or_expr.stmts.len > 0
348 }
349 ast.IndexExpr {
350 expr.or_expr.kind == .block && expr.or_expr.stmts.len > 0
351 }
352 ast.Ident {
353 expr.or_expr.kind == .block && expr.or_expr.stmts.len > 0
354 }
355 else {
356 false
357 }
358 }
359}
360