v2 / vlib / v / checker / return.v
754 lines · 727 sloc · 21.32 KB · bc76d3b61f496e216e51c6454e3317e13d586f7c
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module checker
4
5import v.ast
6
7// error_type_name returns a proper type name reference for error messages
8// ? => Option type
9// ! => Result type
10// others => type `name`
11fn (mut c Checker) error_type_name(exp_type ast.Type) string {
12 return if exp_type == ast.void_type.set_flag(.result) {
13 'Result type'
14 } else if exp_type == ast.void_type.set_flag(.option) {
15 'Option type'
16 } else {
17 'type `${c.table.type_to_str(exp_type)}`'
18 }
19}
20
21@[inline]
22fn (mut c Checker) error_unaliased_type_name(exp_type ast.Type) string {
23 return c.error_type_name(c.table.unaliased_type(exp_type))
24}
25
26// TODO: non deferred
27fn (mut c Checker) return_stmt(mut node ast.Return) {
28 if c.table.cur_fn == unsafe { nil } {
29 return
30 }
31 prev_inside_return := c.inside_return
32 c.inside_return = true
33 defer {
34 c.inside_return = prev_inside_return
35 }
36
37 // check `defer_stmts` in return, to ensure the same behavior with `cgen`
38 old_inside_defer := c.inside_defer
39 c.inside_defer = true
40 for i := c.table.cur_fn.defer_stmts.len - 1; i >= 0; i-- {
41 c.stmts(mut c.table.cur_fn.defer_stmts[i].stmts)
42 }
43 c.inside_defer = old_inside_defer
44
45 c.expected_type = c.table.cur_fn.return_type
46 mut expected_type := c.unwrap_generic(c.expected_type)
47 if expected_type != 0 && c.table.sym(expected_type).kind == .alias {
48 unaliased_type := c.table.unaliased_type(expected_type)
49 if unaliased_type.has_option_or_result() {
50 expected_type = unaliased_type
51 }
52 }
53 expected_type_sym := c.table.sym(expected_type)
54 if expected_type_sym.info is ast.ArrayFixed {
55 c.table.find_or_register_array_fixed(expected_type_sym.info.elem_type,
56 expected_type_sym.info.size, expected_type_sym.info.size_expr, true)
57 }
58 if node.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type {
59 c.error('unexpected argument, current function does not return anything',
60 node.exprs[0].pos())
61 return
62 } else if node.exprs.len > 1 && c.table.cur_fn.return_type == ast.void_type.set_flag(.option) {
63 c.error('can only return `none` from an Option-only return function', node.exprs[0].pos())
64 return
65 } else if node.exprs.len > 1 && c.table.cur_fn.return_type == ast.void_type.set_flag(.result) {
66 c.error('functions with Result-only return types can only return an error',
67 node.exprs[0].pos())
68 return
69 } else if node.exprs.len == 0 && !(c.expected_type == ast.void_type
70 || expected_type_sym.kind == .void) {
71 stype := c.table.type_to_str(expected_type)
72 arg := if expected_type_sym.kind == .multi_return { 'arguments' } else { 'argument' }
73 c.error('expected `${stype}` ${arg}', node.pos)
74 return
75 }
76 if node.exprs.len == 0 {
77 return
78 }
79 exp_is_option := expected_type.has_flag(.option)
80 exp_is_result := expected_type.has_flag(.result)
81 mut expected_types := [expected_type]
82 if expected_type_sym.info is ast.MultiReturn {
83 expected_types = expected_type_sym.info.types.clone()
84 if c.table.cur_concrete_types.len > 0 {
85 expected_types = expected_types.map(c.unwrap_generic(it))
86 c.table.used_features.comptime_syms[c.table.find_or_register_multi_return(expected_types)] = true
87 }
88 }
89 mut got_types := []ast.Type{}
90 mut expr_idxs := []int{}
91 for i, mut expr in node.exprs {
92 mut typ := c.expr(mut expr)
93 if typ == 0 {
94 return
95 }
96 // Handle `return unsafe { none }`
97 if mut expr is ast.UnsafeExpr && expr.expr is ast.None {
98 c.error('cannot return `none` in unsafe block', expr.expr.pos)
99 }
100 if typ == ast.void_type {
101 c.error('`${expr}` used as value', node.pos)
102 return
103 }
104 // Unpack multi return types
105 sym := c.table.sym(typ)
106 if sym.kind == .multi_return {
107 if i > 0 || i != node.exprs.len - 1 {
108 c.error('cannot use multi-return with other return types', expr.pos())
109 }
110 for t in sym.mr_info().types {
111 got_types << t
112 expr_idxs << i
113 }
114 } else {
115 if mut expr is ast.Ident && expr.obj is ast.Var {
116 if expr.obj.smartcasts.len > 0 {
117 typ = c.unwrap_generic(c.visible_var_type_for_read(expr.obj))
118 }
119 if expr.obj.ct_type_var != .no_comptime {
120 typ = c.type_resolver.get_type_or_default(expr, typ)
121 }
122 if expr.obj.expr is ast.IfGuardExpr {
123 if var := expr.scope.find_var(expr.name) {
124 typ = var.typ
125 }
126 }
127 } else if mut expr is ast.SelectorExpr {
128 if expr.expr_type != 0 {
129 scope_field := expr.scope.find_struct_field(smartcast_selector_expr_str(expr),
130 expr.expr_type, expr.field_name)
131 if scope_field != unsafe { nil } && scope_field.smartcasts.len > 0 {
132 typ = c.unwrap_generic(c.exposed_smartcast_type(scope_field.orig_type,
133 scope_field.smartcasts.last(), scope_field.is_mut))
134 }
135 }
136 }
137 got_types << typ
138 expr_idxs << i
139 }
140 }
141 node.types = got_types
142 $if debug_manualfree ? {
143 cfn := c.table.cur_fn
144 if cfn.is_manualfree {
145 pnames := cfn.params.map(it.name)
146 for expr in node.exprs {
147 if expr is ast.Ident {
148 if expr.name in pnames {
149 c.note('returning a parameter in a fn marked with `@[manualfree]` can cause double freeing in the caller',
150 node.pos)
151 }
152 }
153 }
154 }
155 }
156 // allow `none` & `error` return types for function that returns option
157 option_type_idx := c.table.type_idxs['_option']
158 result_type_idx := c.table.type_idxs['_result']
159 got_types_0_idx := got_types[0].idx()
160 if exp_is_option && got_types_0_idx == ast.error_type_idx {
161 c.error('Option and Result types have been split, use `!Foo` to return errors', node.pos)
162 } else if exp_is_result && got_types_0_idx == ast.none_type_idx {
163 c.error('Option and Result types have been split, use `?` to return none', node.pos)
164 }
165 expected_fn_return_type_has_option := c.table.cur_fn.return_type.has_flag(.option)
166 expected_fn_return_type_has_result := c.table.cur_fn.return_type.has_flag(.result)
167 if exp_is_option && expected_fn_return_type_has_result {
168 if got_types_0_idx == ast.none_type_idx {
169 c.error('cannot use `none` as ${c.error_type_name(c.table.unaliased_type(c.table.cur_fn.return_type))} in return argument',
170 node.pos)
171 return
172 }
173 return
174 }
175 if exp_is_result && expected_fn_return_type_has_option {
176 c.error('expecting to return a ?Type, but you are returning ${c.error_type_name(expected_type)} instead',
177 node.pos)
178 return
179 }
180 if (exp_is_option
181 && got_types_0_idx in [ast.none_type_idx, ast.error_type_idx, option_type_idx])
182 || (exp_is_result && got_types_0_idx in [ast.error_type_idx, result_type_idx]) {
183 return
184 }
185 if expected_types.len > 0 && expected_types.len != got_types.len {
186 // `fn foo() !(int, string) { return Err{} }`
187 if (exp_is_option || exp_is_result) && node.exprs.len == 1 {
188 mut expr_ := node.exprs[0]
189 got_type := c.expr(mut expr_)
190 got_type_sym := c.table.sym(got_type)
191 if got_type_sym.kind == .struct && c.type_implements(got_type, ast.error_type, node.pos) {
192 node.exprs[0] = ast.CastExpr{
193 expr: node.exprs[0]
194 typname: 'IError'
195 typ: ast.error_type
196 expr_type: got_type
197 pos: node.pos
198 }
199 node.types[0] = ast.error_type
200 return
201 }
202 }
203 arg := if expected_types.len == 1 { 'argument' } else { 'arguments' }
204 midx := int_max(0, int_min(expected_types.len, expr_idxs.len - 1))
205 mismatch_pos := node.exprs[expr_idxs[midx]].pos()
206 c.error('expected ${expected_types.len} ${arg}, but got ${got_types.len}', mismatch_pos)
207 return
208 }
209 for i, exp_type in expected_types {
210 exprv := node.exprs[expr_idxs[i]]
211 if exprv is ast.Ident && exprv.or_expr.kind == .propagate_option {
212 if exp_type.has_flag(.option) {
213 c.warn('unwrapping option is redundant as the function returns option', node.pos)
214 } else {
215 c.error('should not unwrap option var on return, it could be none', node.pos)
216 }
217 }
218 got_type := c.unwrap_generic(got_types[i])
219 if got_type.has_flag(.option) && (!exp_type.has_flag(.option)
220 || !c.check_types(got_type.clear_flag(.option), exp_type.clear_flag(.option))) {
221 c.error('cannot use `${c.table.type_to_str(got_type)}` as ${c.error_type_name(exp_type)} in return argument',
222 exprv.pos())
223 }
224 if got_type.has_flag(.result) && (!exp_type.has_flag(.result)
225 || c.table.type_to_str(got_type) != c.table.type_to_str(exp_type)) {
226 c.error('cannot use `${c.table.type_to_str(got_type)}` as ${c.error_type_name(exp_type)} in return argument',
227 exprv.pos())
228 }
229 if exprv is ast.ComptimeCall && exprv.kind == .tmpl
230 && c.table.final_sym(exp_type).kind != .string {
231 c.error('cannot use `string` as type `${c.table.type_to_str(exp_type)}` in return argument',
232 exprv.pos)
233 }
234 if exprv !is ast.ComptimeCall {
235 got_type_sym := c.table.sym(got_type)
236 exp_type_sym := c.table.sym(exp_type)
237 if c.check_types(got_type, exp_type) {
238 if exp_type.is_unsigned() && got_type.is_int_literal() {
239 if exprv is ast.IntegerLiteral {
240 var := (node.exprs[expr_idxs[i]] as ast.IntegerLiteral).val
241 if var[0] == `-` {
242 c.note('cannot use a negative value as value of ${c.error_type_name(exp_type)} in return argument',
243 exprv.pos)
244 }
245 }
246 }
247 } else {
248 if c.pref.skip_unused && got_types[i].has_flag(.generic) {
249 c.table.used_features.comptime_syms[got_type] = true
250 }
251 if exp_type_sym.kind == .interface
252 || (exp_type_sym.kind == .generic_inst && exp_type_sym.info is ast.GenericInst
253 && c.table.type_symbols[exp_type_sym.info.parent_idx].kind == .interface) {
254 if c.type_implements(got_type, exp_type, node.pos) {
255 if !got_type.is_any_kind_of_pointer() && got_type_sym.kind != .interface
256 && !c.inside_unsafe {
257 c.mark_as_referenced(mut &node.exprs[expr_idxs[i]], true)
258 }
259 }
260 continue
261 }
262 exp_final_sym := c.table.final_sym(exp_type)
263 got_final_sym := c.table.final_sym(got_type)
264 if exp_final_sym.kind == .array_fixed && got_final_sym.kind == .array_fixed {
265 got_arr_sym := c.table.sym(c.cast_to_fixed_array_ret(got_type, got_final_sym))
266 if (exp_final_sym.info as ast.ArrayFixed).is_compatible(got_arr_sym.info as ast.ArrayFixed) {
267 continue
268 }
269 }
270 // `fn foo() !int { return Err{} }`
271 if expected_fn_return_type_has_result && got_type_sym.kind == .struct
272 && c.type_implements(got_type, ast.error_type, node.pos) {
273 node.exprs[expr_idxs[i]] = ast.CastExpr{
274 expr: exprv
275 typname: 'IError'
276 typ: ast.error_type
277 expr_type: got_type
278 pos: node.pos
279 }
280 node.types[expr_idxs[i]] = ast.error_type
281 continue
282 }
283 got_type_name := if got_type_sym.kind == .function {
284 '${c.table.type_to_str(got_type)}'
285 } else {
286 got_type_sym.name
287 }
288 // ignore generic lambda return in this phase
289 if c.inside_lambda && exp_type.has_flag(.generic) {
290 continue
291 }
292 if exp_final_sym.info is ast.ArrayFixed
293 && c.array_fixed_has_unresolved_size(exp_final_sym.info) {
294 continue
295 }
296 // In generic functions, scope variable types can be stale from a
297 // different instantiation pass. Skip the error if the original
298 // return type is generic — the cgen resolves types per-instantiation.
299 if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.return_type.has_flag(.generic)
300 && c.table.cur_concrete_types.len > 0 {
301 continue
302 }
303
304 c.error('cannot use `${got_type_name}` as ${c.error_type_name(exp_type)} in return argument',
305 exprv.pos())
306 }
307 }
308 if exprv is ast.Ident {
309 if exprv.obj is ast.Var && exprv.obj.smartcasts.len > 0 {
310 orig_sym := c.table.final_sym(exprv.obj.orig_type)
311 if orig_sym.kind == .interface {
312 continue
313 }
314 }
315 }
316 if got_type.is_any_kind_of_pointer() && !exp_type.is_any_kind_of_pointer()
317 && !c.table.unaliased_type(exp_type).is_any_kind_of_pointer() {
318 if exprv.is_auto_deref_var() {
319 continue
320 }
321 c.add_error_detail('use `return *pointer` instead of `return pointer`, and just `return value` instead of `return &value`')
322 c.error('fn `${c.table.cur_fn.name}` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_type)}` instead',
323 exprv.pos())
324 }
325 if exp_type.is_any_kind_of_pointer() && !got_type.is_any_kind_of_pointer()
326 && !c.table.unaliased_type(got_type).is_any_kind_of_pointer()
327 && got_type != ast.int_literal_type && !c.pref.translated && !c.file.is_translated {
328 if exprv.is_auto_deref_var() {
329 continue
330 }
331 if c.table.final_sym(exp_type).kind == .interface
332 && c.table.final_sym(got_type).kind == .interface
333 && exp_type.deref().idx() == got_type.idx() {
334 continue
335 }
336 c.error('fn `${c.table.cur_fn.name}` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_type)}` instead',
337 exprv.pos())
338 }
339 if exp_type.is_ptr() && got_type.is_ptr() {
340 r_expr := node.exprs[expr_idxs[i]]
341 if r_expr is ast.Ident {
342 mut ident_expr := r_expr
343 c.fail_if_stack_struct_action_outside_unsafe(mut ident_expr, 'returned')
344 } else if r_expr is ast.PrefixExpr && r_expr.op == .amp {
345 // &var
346 if r_expr.right is ast.Ident {
347 mut ident_expr := r_expr.right
348 c.fail_if_stack_struct_action_outside_unsafe(mut ident_expr, 'returned')
349 }
350 }
351 }
352 }
353 if exp_is_option && node.exprs.len > 0 {
354 expr0 := node.exprs[0]
355 if expr0 is ast.CallExpr {
356 if expr0.or_block.kind == .propagate_option && node.exprs.len == 1 {
357 v_name := if expr0.is_static_method {
358 expr0.name.all_before('__static__') + '.' + expr0.name.all_after('__static__')
359 } else {
360 expr0.name
361 }
362 c.error('`?` is not needed, use `return ${v_name}()`', expr0.pos)
363 }
364 }
365 }
366}
367
368fn (mut c Checker) find_unreachable_statements_after_noreturn_calls(stmts []ast.Stmt) {
369 mut prev_stmt_was_noreturn_call := false
370 for stmt in stmts {
371 if stmt is ast.ExprStmt {
372 if stmt.expr is ast.CallExpr {
373 if prev_stmt_was_noreturn_call {
374 c.warn('unreachable code after a @[noreturn] call', stmt.pos)
375 return
376 }
377 prev_stmt_was_noreturn_call = stmt.expr.is_noreturn
378 }
379 } else {
380 prev_stmt_was_noreturn_call = false
381 }
382 }
383}
384
385// Note: has_top_return/1 should be called on *already checked* stmts,
386// which do have their stmt.expr.is_noreturn set properly:
387fn (mut c Checker) has_top_return(stmts []ast.Stmt) bool {
388 for stmt in stmts {
389 match stmt {
390 ast.Return {
391 return true
392 }
393 ast.Block {
394 if c.has_top_return(stmt.stmts) {
395 return true
396 }
397 }
398 ast.ExprStmt {
399 if c.expr_never_falls_through(stmt.expr) {
400 return true
401 }
402 }
403 else {}
404 }
405 }
406 return false
407}
408
409fn (mut c Checker) expr_never_falls_through(expr ast.Expr) bool {
410 match expr {
411 ast.CallExpr {
412 // do not ignore panic() calls on non checked stmts
413 return expr.is_noreturn
414 || (expr.is_method == false && expr.name == 'panic')
415 || c.call_expr_propagates_always_error(expr)
416 }
417 ast.ComptimeCall {
418 return expr.kind == .compile_error
419 }
420 ast.LockExpr {
421 return c.has_top_return(expr.stmts)
422 }
423 ast.IfExpr {
424 return c.has_top_return_in_if_expr(expr)
425 }
426 ast.MatchExpr {
427 return c.has_top_return_in_match_expr(expr)
428 }
429 else {}
430 }
431
432 return false
433}
434
435fn (mut c Checker) call_expr_propagates_always_error(node ast.CallExpr) bool {
436 if node.should_be_skipped || node.or_block.kind != .propagate_result {
437 return false
438 }
439 mut func := ast.Fn{}
440 if node.is_method {
441 if node.left_type == 0 {
442 return false
443 }
444 left_sym := c.table.sym(c.unwrap_generic(node.left_type))
445 if left_sym.kind == .placeholder {
446 return false
447 }
448 func = c.table.find_method(left_sym, node.name) or { return false }
449 } else {
450 if node.name == '' {
451 return false
452 }
453 func = c.table.find_fn(node.name) or { return false }
454 }
455 return c.fn_always_errors(func)
456}
457
458fn (mut c Checker) fn_always_errors(func ast.Fn) bool {
459 if func.name == '' || func.source_fn == unsafe { nil } || func.no_body || func.language != .v
460 || !func.return_type.has_flag(.result) {
461 return false
462 }
463 fkey := func.fkey()
464 if fkey in c.always_error_fn_cache {
465 return c.always_error_fn_cache[fkey]
466 }
467 if fkey in c.always_error_fn_in_progress {
468 return false
469 }
470 c.always_error_fn_in_progress[fkey] = true
471 defer {
472 c.always_error_fn_in_progress.delete(fkey)
473 }
474 fn_decl := unsafe { &ast.FnDecl(func.source_fn) }
475 result := c.stmts_always_error(fn_decl.stmts)
476 c.always_error_fn_cache[fkey] = result
477 return result
478}
479
480fn (mut c Checker) stmts_always_error(stmts []ast.Stmt) bool {
481 for stmt in stmts {
482 if c.stmt_always_errors(stmt) {
483 return true
484 }
485 }
486 return false
487}
488
489fn (mut c Checker) stmt_always_errors(stmt ast.Stmt) bool {
490 match stmt {
491 ast.Return {
492 return c.return_stmt_always_errors(stmt)
493 }
494 ast.Block {
495 return c.stmts_always_error(stmt.stmts)
496 }
497 ast.ExprStmt {
498 return c.expr_always_errors(stmt.expr)
499 }
500 ast.ForStmt {
501 return stmt.is_inf && stmt.stmts.len == 0
502 }
503 else {}
504 }
505
506 return false
507}
508
509fn (mut c Checker) expr_always_errors(expr ast.Expr) bool {
510 match expr {
511 ast.CallExpr {
512 return expr.is_noreturn || c.call_expr_propagates_always_error(expr)
513 }
514 ast.ComptimeCall {
515 return expr.kind == .compile_error
516 }
517 ast.IfExpr {
518 return c.if_expr_always_errors(expr)
519 }
520 ast.MatchExpr {
521 return c.match_expr_always_errors(expr)
522 }
523 ast.LockExpr {
524 return c.stmts_always_error(expr.stmts)
525 }
526 else {}
527 }
528
529 return false
530}
531
532fn (mut c Checker) return_stmt_always_errors(node ast.Return) bool {
533 if node.types.len == 1 && c.table.unaliased_type(node.types[0]).idx() == ast.error_type_idx {
534 return true
535 }
536 return false
537}
538
539fn (mut c Checker) has_top_return_in_if_expr(if_expr ast.IfExpr) bool {
540 if if_expr.branches.len < 2 || !if_expr.has_else {
541 return false
542 }
543 for branch in if_expr.branches {
544 if !c.has_top_return(branch.stmts) {
545 return false
546 }
547 }
548 return true
549}
550
551fn (mut c Checker) if_expr_always_errors(if_expr ast.IfExpr) bool {
552 if if_expr.branches.len < 2 || !if_expr.has_else {
553 return false
554 }
555 for branch in if_expr.branches {
556 if !c.stmts_always_error(branch.stmts) {
557 return false
558 }
559 }
560 return true
561}
562
563fn (mut c Checker) has_top_return_in_match_expr(match_expr ast.MatchExpr) bool {
564 if match_expr.branches.len == 0 {
565 return false
566 }
567 if match_expr.is_comptime {
568 mut has_else := false
569 for branch in match_expr.branches {
570 if branch.is_else {
571 has_else = true
572 break
573 }
574 for expr in branch.exprs {
575 if expr is ast.Ident && expr.name == '\$else' {
576 has_else = true
577 break
578 }
579 }
580 if has_else {
581 break
582 }
583 }
584 if has_else {
585 for branch in match_expr.branches {
586 if !c.has_top_return(branch.stmts) {
587 return false
588 }
589 }
590 return true
591 }
592 return false
593 }
594 for branch in match_expr.branches {
595 if !c.has_top_return(branch.stmts) {
596 return false
597 }
598 }
599 return true
600}
601
602fn (mut c Checker) match_expr_always_errors(match_expr ast.MatchExpr) bool {
603 if match_expr.branches.len == 0 {
604 return false
605 }
606 if match_expr.is_comptime {
607 mut has_else := false
608 for branch in match_expr.branches {
609 if branch.is_else {
610 has_else = true
611 break
612 }
613 for expr in branch.exprs {
614 if expr is ast.Ident && expr.name == '\$else' {
615 has_else = true
616 break
617 }
618 }
619 if has_else {
620 break
621 }
622 }
623 if !has_else {
624 return false
625 }
626 }
627 for branch in match_expr.branches {
628 if !c.stmts_always_error(branch.stmts) {
629 return false
630 }
631 }
632 return true
633}
634
635fn (mut c Checker) check_noreturn_fn_decl(mut node ast.FnDecl) {
636 if !node.is_noreturn {
637 return
638 }
639 if node.no_body {
640 return
641 }
642 if node.return_type != ast.void_type {
643 c.error('[noreturn] functions cannot have return types', node.pos)
644 }
645 if uses_return_stmt(node.stmts) {
646 c.error('[noreturn] functions cannot use return statements', node.pos)
647 }
648 mut pos := node.pos
649 mut is_valid_end_of_noreturn_fn := false
650 if node.stmts.len != 0 {
651 last_stmt := node.stmts.last()
652 match last_stmt {
653 ast.ExprStmt {
654 if last_stmt.expr is ast.CallExpr {
655 if last_stmt.expr.should_be_skipped {
656 c.error('@[noreturn] functions cannot end with a skippable `@[if ..]` call',
657 last_stmt.pos)
658 return
659 }
660 if last_stmt.expr.is_noreturn {
661 is_valid_end_of_noreturn_fn = true
662 }
663 }
664 }
665 ast.ForStmt {
666 if last_stmt.is_inf && last_stmt.stmts.len == 0 {
667 is_valid_end_of_noreturn_fn = true
668 }
669 }
670 else {}
671 }
672
673 if !is_valid_end_of_noreturn_fn {
674 pos = last_stmt.pos
675 }
676 }
677 if !is_valid_end_of_noreturn_fn {
678 c.error('@[noreturn] functions should end with a call to another @[noreturn] function, or with an infinite `for {}` loop',
679 pos)
680 }
681}
682
683fn uses_return_stmt(stmts []ast.Stmt) bool {
684 if stmts.len == 0 {
685 return false
686 }
687 for stmt in stmts {
688 match stmt {
689 ast.Return {
690 return true
691 }
692 ast.Block {
693 if uses_return_stmt(stmt.stmts) {
694 return true
695 }
696 }
697 ast.ExprStmt {
698 match stmt.expr {
699 ast.CallExpr {
700 if uses_return_stmt(stmt.expr.or_block.stmts) {
701 return true
702 }
703 }
704 ast.MatchExpr {
705 for b in stmt.expr.branches {
706 if uses_return_stmt(b.stmts) {
707 return true
708 }
709 }
710 }
711 ast.SelectExpr {
712 for b in stmt.expr.branches {
713 if uses_return_stmt(b.stmts) {
714 return true
715 }
716 }
717 }
718 ast.IfExpr {
719 for b in stmt.expr.branches {
720 if uses_return_stmt(b.stmts) {
721 return true
722 }
723 }
724 }
725 else {}
726 }
727 }
728 ast.ForStmt {
729 if uses_return_stmt(stmt.stmts) {
730 return true
731 }
732 }
733 ast.ForCStmt {
734 if uses_return_stmt(stmt.stmts) {
735 return true
736 }
737 }
738 ast.ForInStmt {
739 if uses_return_stmt(stmt.stmts) {
740 return true
741 }
742 }
743 else {}
744 }
745 }
746 return false
747}
748
749fn is_noreturn_callexpr(expr ast.Expr) bool {
750 if expr is ast.CallExpr {
751 return expr.is_noreturn
752 }
753 return false
754}
755