diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 4acc01cbff..ec1c08de53 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1646,6 +1646,7 @@ fn (t Tree) or_expr(node ast.OrExpr) &Node { obj.add_terse('stmts', t.array_node_stmt(node.stmts)) obj.add_terse('kind', t.enum_node(node.kind)) obj.add('pos', t.pos(node.pos)) + obj.add('scope', t.number_node(int(node.scope))) return obj } diff --git a/vlib/builtin/sorted_map.v b/vlib/builtin/sorted_map.v index 7be2dca4a9..114dcdc556 100644 --- a/vlib/builtin/sorted_map.v +++ b/vlib/builtin/sorted_map.v @@ -14,6 +14,10 @@ module builtin // The number for `degree` has been picked through vigor- // ous benchmarking but can be changed to any number > 1. // `degree` determines the maximum length of each node. +// TODO: the @[markused] tag here is needed as a workaround for +// compilation with `-cc clang -usecache`; added in https://github.com/vlang/v/pull/25034 + +@[markused] const degree = 6 const mid_index = degree - 1 const max_len = 2 * degree - 1 diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index f760b1db2f..a043690777 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -935,6 +935,7 @@ pub mut: is_tmp bool // for tmp for loop vars, so that autofree can skip them is_auto_heap bool // value whose address goes out of scope is_stack_obj bool // may be pointer to stack value (`mut` or `&` arg and not @[heap] struct) + is_special bool // err, it, a, b vars (ignore not useds) } // used for smartcasting only @@ -1912,10 +1913,12 @@ pub enum OrKind { // `or { ... }` pub struct OrExpr { pub: - kind OrKind - pos token.Pos + kind OrKind + pos token.Pos + scope &Scope = unsafe { nil } pub mut: - stmts []Stmt + err_used bool + stmts []Stmt } /* diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 4fe72a2307..faea0f1d00 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -113,6 +113,7 @@ mut: type_level int // to avoid infinite recursion segfaults due to compiler bugs in ensure_type_exists ensure_generic_type_level int // to avoid infinite recursion segfaults in ensure_generic_type_specify_type_names cur_orm_ts ast.TypeSymbol + cur_or_expr &ast.OrExpr = unsafe { nil } cur_anon_fn &ast.AnonFn = unsafe { nil } vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path** loop_labels []string // filled, when inside labelled for loops: `a_label: for x in 0..10 {` @@ -299,7 +300,7 @@ pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) { for _, obj in sc.objects { match obj { ast.Var { - if !obj.is_used && obj.name[0] != `_` { + if !obj.is_special && !obj.is_used && obj.name[0] != `_` { if !c.pref.translated && !c.file.is_translated { if obj.is_arg { if c.pref.show_unused_params { @@ -310,11 +311,11 @@ pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) { } } } - if obj.is_mut && !obj.is_changed && !c.is_builtin_mod && obj.name != 'it' { - // if obj.is_mut && !obj.is_changed && !c.is_builtin { //TODO C error bad field not checked - // c.warn('`$obj.name` is declared as mutable, but it was never changed', - // obj.pos) - } + // if obj.is_mut && !obj.is_changed && !c.is_builtin_mod && obj.name != 'it' { + // if obj.is_mut && !obj.is_changed && !c.is_builtin { //TODO C error bad field not checked + // c.warn('`$obj.name` is declared as mutable, but it was never changed', + // obj.pos) + // } } else {} } @@ -1338,7 +1339,10 @@ fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast. } } } else { + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &expr.or_block c.check_or_expr(expr.or_block, ret_type, expr_ret_type, expr) + c.cur_or_expr = last_cur_or_expr } return ret_type.clear_flag(.result) } else { @@ -1365,7 +1369,10 @@ fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast. } } else { if expr.or_block.kind != .absent { + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &expr.or_block c.check_or_expr(expr.or_block, ret_type, expr.typ, expr) + c.cur_or_expr = last_cur_or_expr } } return ret_type.clear_flag(.result) @@ -1389,8 +1396,11 @@ fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast. if return_none_or_error { c.check_expr_option_or_result_call(expr.or_expr, c.table.cur_fn.return_type) } else { + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &expr.or_expr c.check_or_expr(expr.or_expr, ret_type, ret_type.set_flag(.result), expr) + c.cur_or_expr = last_cur_or_expr } } else if expr.left is ast.SelectorExpr && expr.left_type.has_option_or_result() { with_modifier_kind := if expr.left_type.has_flag(.option) { @@ -1464,6 +1474,10 @@ fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return } // allow `f() or {}` return + } else if !node.err_used { + if err_var := node.scope.find_var('err') { + c.cur_or_expr.err_used = err_var.is_used + } } mut valid_stmts := node.stmts.filter(it !is ast.SemicolonStmt) mut last_stmt := if valid_stmts.len > 0 { valid_stmts.last() } else { node.stmts.last() } @@ -1809,7 +1823,10 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { unwrapped_typ := c.unwrap_generic(node.typ) c.expected_or_type = unwrapped_typ.clear_option_and_result() c.stmts_ending_with_expression(mut node.or_block.stmts, c.expected_or_type) + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &node.or_block c.check_or_expr(node.or_block, unwrapped_typ, c.expected_or_type, node) + c.cur_or_expr = last_cur_or_expr c.expected_or_type = ast.void_type } return field.typ @@ -4079,7 +4096,10 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { unwrapped_typ := typ.clear_option_and_result() c.expected_or_type = unwrapped_typ c.stmts_ending_with_expression(mut node.or_expr.stmts, c.expected_or_type) + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &node.or_expr c.check_or_expr(node.or_expr, typ, c.expected_or_type, node) + c.cur_or_expr = last_cur_or_expr return unwrapped_typ } return typ @@ -4194,7 +4214,10 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { unwrapped_typ := typ.clear_option_and_result() c.expected_or_type = unwrapped_typ c.stmts_ending_with_expression(mut node.or_expr.stmts, c.expected_or_type) + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &node.or_expr c.check_or_expr(node.or_expr, typ, c.expected_or_type, node) + c.cur_or_expr = last_cur_or_expr return unwrapped_typ } return typ @@ -4256,7 +4279,10 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { unwrapped_typ := typ.clear_option_and_result() c.expected_or_type = unwrapped_typ c.stmts_ending_with_expression(mut node.or_expr.stmts, c.expected_or_type) + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &node.or_expr c.check_or_expr(node.or_expr, typ, c.expected_or_type, node) + c.cur_or_expr = last_cur_or_expr } return typ } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index da4386d498..4bde2672c1 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -783,7 +783,10 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type { if node.or_block.kind == .block { old_inside_or_block_value := c.inside_or_block_value c.inside_or_block_value = true + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &node.or_block c.check_or_expr(node.or_block, typ, c.expected_or_type, node) + c.cur_or_expr = last_cur_or_expr c.inside_or_block_value = old_inside_or_block_value } c.expected_or_type = old_expected_or_type @@ -3819,10 +3822,11 @@ fn scope_register_a_b(mut s ast.Scope, pos token.Pos, typ ast.Type) { fn scope_register_var_name(mut s ast.Scope, pos token.Pos, typ ast.Type, name string) { s.register(ast.Var{ - name: name - pos: pos - typ: typ - is_used: true + name: name + pos: pos + typ: typ + is_used: false + is_special: true }) } diff --git a/vlib/v/checker/orm.v b/vlib/v/checker/orm.v index 396f36e8d8..f437cc1a00 100644 --- a/vlib/v/checker/orm.v +++ b/vlib/v/checker/orm.v @@ -192,8 +192,10 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { if node.is_insert { node.typ = ast.int_type } - + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &node.or_expr c.check_orm_or_expr(mut node) + c.cur_or_expr = last_cur_or_expr if node.is_insert { return ast.int_type @@ -211,8 +213,10 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) ast.Type { for mut line in node.lines { c.sql_stmt_line(mut line) } - + last_cur_or_expr := c.cur_or_expr + c.cur_or_expr = &node.or_expr c.check_orm_or_expr(mut node) + c.cur_or_expr = last_cur_or_expr return ast.void_type } @@ -605,7 +609,6 @@ fn (mut c Checker) check_orm_or_expr(mut expr ORMExpr) { return } } - return_type := if mut expr is ast.SqlExpr { expr.typ } else { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 4fcab54a8c..9d7607f000 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -7177,7 +7177,10 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty } if or_block.kind == .block { g.or_expr_return_type = return_type.clear_option_and_result() - g.writeln('\tIError err = ${cvar_name}${tmp_op}err;') + if !g.pref.skip_unused || or_block.err_used + || (g.fn_decl != unsafe { nil } && (g.fn_decl.is_main || g.fn_decl.is_test)) { + g.writeln('\tIError err = ${cvar_name}${tmp_op}err;') + } g.inside_or_block = true defer { diff --git a/vlib/v/gen/c/if.v b/vlib/v/gen/c/if.v index b2463b8212..3cd40fd2b9 100644 --- a/vlib/v/gen/c/if.v +++ b/vlib/v/gen/c/if.v @@ -310,8 +310,12 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { g.writeln('{') // define `err` for the last branch after a `if val := opt {...}' guard if is_guard && guard_idx == i - 1 { - cvar_name := guard_vars[guard_idx] - g.writeln('\tIError err = ${cvar_name}.err;') + if err_var := branch.scope.find_var('err') { + if !g.pref.skip_unused || err_var.is_used { + cvar_name := guard_vars[guard_idx] + g.writeln('\tIError err = ${cvar_name}.err;') + } + } } } else if branch.cond is ast.IfGuardExpr { mut var_name := guard_vars[i] diff --git a/vlib/v/gen/c/orm.v b/vlib/v/gen/c/orm.v index a4b44c0141..cbe2a450ac 100644 --- a/vlib/v/gen/c/orm.v +++ b/vlib/v/gen/c/orm.v @@ -1124,7 +1124,7 @@ fn (mut g Gen) write_orm_select(node ast.SqlExpr, connection_var_name string, re field_var := '${tmp}.${c_name(field.name)}' field_c_typ := g.styp(final_field_typ) if sym.kind == .struct && sym.name != 'time.Time' { - mut sub := node.sub_structs[int(final_field_typ)] + mut sub := node.sub_structs[int(final_field_typ)] or { continue } mut where_expr := sub.where_expr as ast.InfixExpr mut ident := where_expr.right as ast.Ident primitive_type_index := g.table.find_type('orm.Primitive') @@ -1161,7 +1161,7 @@ fn (mut g Gen) write_orm_select(node ast.SqlExpr, connection_var_name string, re } else { verror('missing fkey attribute') } - sub := node.sub_structs[final_field_typ] + sub := node.sub_structs[final_field_typ] or { continue } if sub.has_where { mut where_expr := sub.where_expr as ast.InfixExpr mut left_where_expr := where_expr.left as ast.Ident diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index f259e4d168..33e2bc2207 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -536,10 +536,11 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr { mut or_kind := ast.OrKind.absent mut or_pos := p.tok.pos() mut or_stmts := []ast.Stmt{} + mut or_scope := &ast.Scope(unsafe { nil }) if p.tok.kind == .key_orelse { // `$method() or {}`` or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) + or_stmts, or_pos, or_scope = p.or_block(.with_err_var) } return ast.ComptimeCall{ left: left @@ -554,6 +555,7 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr { stmts: or_stmts kind: or_kind pos: or_pos + scope: or_scope } } } diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 59771c23aa..1f4292771f 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -727,11 +727,12 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo fn (mut p Parser) gen_or_block() ast.OrExpr { if p.tok.kind == .key_orelse { // `foo() or {}`` - or_stmts, or_pos := p.or_block(.with_err_var) + or_stmts, or_pos, or_scope := p.or_block(.with_err_var) return ast.OrExpr{ kind: ast.OrKind.block stmts: or_stmts pos: or_pos + scope: or_scope } } else if p.tok.kind in [.question, .not] { or_pos := p.tok.pos() @@ -820,11 +821,12 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { mut or_stmts := []ast.Stmt{} mut or_kind := ast.OrKind.absent mut or_pos := p.tok.pos() + mut or_scope := &ast.Scope(unsafe { nil }) // allow `x := <-ch or {...}` to handle closed channel if op == .arrow { if p.tok.kind == .key_orelse { or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) + or_stmts, or_pos, or_scope = p.or_block(.with_err_var) } if p.tok.kind == .question { p.next() @@ -845,6 +847,7 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { stmts: or_stmts kind: or_kind pos: or_pos + scope: or_scope } } } @@ -911,6 +914,7 @@ fn (mut p Parser) prefix_expr() ast.Expr { mut or_stmts := []ast.Stmt{} mut or_kind := ast.OrKind.absent mut or_pos := p.tok.pos() + mut or_scope := &ast.Scope(unsafe { nil }) // allow `x := <-ch or {...}` to handle closed channel if op == .arrow { if mut right is ast.SelectorExpr { @@ -919,7 +923,7 @@ fn (mut p Parser) prefix_expr() ast.Expr { right.or_block = ast.OrExpr{} } else if p.tok.kind == .key_orelse { or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) + or_stmts, or_pos, or_scope = p.or_block(.with_err_var) } else if p.tok.kind == .question { p.next() or_kind = .propagate_option @@ -935,6 +939,7 @@ fn (mut p Parser) prefix_expr() ast.Expr { stmts: or_stmts kind: or_kind pos: or_pos + scope: or_scope } } } diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 9c2eaa2768..71169f2e12 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -74,10 +74,11 @@ fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr { mut pos := first_pos.extend(last_pos) mut or_stmts := []ast.Stmt{} // TODO: remove unnecessary allocations by just using .absent mut or_pos := p.tok.pos() + mut or_scope := &ast.Scope(unsafe { nil }) if p.tok.kind == .key_orelse { // `foo() or {}`` or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) + or_stmts, or_pos, or_scope = p.or_block(.with_err_var) } if p.tok.kind in [.question, .not] { is_not := p.tok.kind == .not @@ -108,6 +109,7 @@ fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr { stmts: or_stmts kind: or_kind pos: or_pos + scope: or_scope } scope: p.scope comments: comments diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index 0327d306f2..624044ac82 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -53,8 +53,9 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr { name: 'err' typ: ast.error_type pos: p.tok.pos() - is_used: true + is_used: false is_stack_obj: true + is_special: true }) } branches << ast.IfBranch{ diff --git a/vlib/v/parser/orm.v b/vlib/v/parser/orm.v index 130545a647..5ef4751446 100644 --- a/vlib/v/parser/orm.v +++ b/vlib/v/parser/orm.v @@ -185,10 +185,11 @@ fn (mut p Parser) parse_sql_or_block() ast.OrExpr { mut stmts := []ast.Stmt{} mut kind := ast.OrKind.absent mut pos := p.tok.pos() + mut or_scope := &ast.Scope(unsafe { nil }) if p.tok.kind == .key_orelse { kind = .block - stmts, pos = p.or_block(.with_err_var) + stmts, pos, or_scope = p.or_block(.with_err_var) } else if p.tok.kind == .not { kind = .propagate_result p.next() @@ -198,6 +199,7 @@ fn (mut p Parser) parse_sql_or_block() ast.OrExpr { stmts: stmts kind: kind pos: pos + scope: or_scope } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 61c6a18c56..8a2f8b9d25 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1232,13 +1232,14 @@ fn (mut p Parser) ident(language ast.Language) ast.Ident { mut or_kind := ast.OrKind.absent mut or_stmts := []ast.Stmt{} mut or_pos := token.Pos{} + mut or_scope := &ast.Scope(unsafe { nil }) if allowed_cases && p.tok.kind == .question && p.peek_tok.kind != .lpar { // var?, not var?( or_kind = ast.OrKind.propagate_option p.check(.question) } else if allowed_cases && p.tok.kind == .key_orelse { or_kind = ast.OrKind.block - or_stmts, or_pos = p.or_block(.no_err_var) + or_stmts, or_pos, or_scope = p.or_block(.no_err_var) } else if is_following_concrete_types { // `generic_fn[int]` concrete_types = p.parse_concrete_types() @@ -1281,6 +1282,7 @@ fn (mut p Parser) ident(language ast.Language) ast.Ident { kind: or_kind stmts: or_stmts pos: or_pos + scope: or_scope } concrete_types: concrete_types } @@ -1752,7 +1754,7 @@ enum OrBlockErrVarMode { with_err_var } -fn (mut p Parser) or_block(err_var_mode OrBlockErrVarMode) ([]ast.Stmt, token.Pos) { +fn (mut p Parser) or_block(err_var_mode OrBlockErrVarMode) ([]ast.Stmt, token.Pos, &ast.Scope) { was_inside_or_expr := p.inside_or_expr defer { p.inside_or_expr = was_inside_or_expr @@ -1762,6 +1764,7 @@ fn (mut p Parser) or_block(err_var_mode OrBlockErrVarMode) ([]ast.Stmt, token.Po mut pos := p.tok.pos() p.next() p.open_scope() + or_scope := p.scope defer { p.close_scope() } @@ -1771,14 +1774,15 @@ fn (mut p Parser) or_block(err_var_mode OrBlockErrVarMode) ([]ast.Stmt, token.Po name: 'err' typ: ast.error_type pos: p.tok.pos() - is_used: true + is_used: false is_stack_obj: true + is_special: true }) } stmts := p.parse_block_no_scope(false) pos = pos.extend(p.prev_tok.pos()) - return stmts, pos + return stmts, pos, or_scope } fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { @@ -1802,11 +1806,12 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { mut or_kind_high := ast.OrKind.absent mut or_stmts_high := []ast.Stmt{} mut or_pos_high := token.Pos{} + mut or_scope := &ast.Scope(unsafe { nil }) if !p.or_is_handled { // a[..end] or {...} if p.tok.kind == .key_orelse { - or_stmts_high, or_pos_high = p.or_block(.no_err_var) + or_stmts_high, or_pos_high, or_scope = p.or_block(.no_err_var) return ast.IndexExpr{ left: left pos: pos_high @@ -1821,6 +1826,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { kind: .block stmts: or_stmts_high pos: or_pos_high + scope: or_scope } is_gated: is_gated } @@ -1870,11 +1876,11 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { mut or_kind_low := ast.OrKind.absent mut or_stmts_low := []ast.Stmt{} mut or_pos_low := token.Pos{} - + mut or_scope := &ast.Scope(unsafe { nil }) if !p.or_is_handled { // a[start..end] or {...} if p.tok.kind == .key_orelse { - or_stmts_low, or_pos_low = p.or_block(.no_err_var) + or_stmts_low, or_pos_low, or_scope = p.or_block(.no_err_var) return ast.IndexExpr{ left: left pos: pos_low @@ -1890,6 +1896,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { kind: .block stmts: or_stmts_low pos: or_pos_low + scope: or_scope } is_gated: is_gated } @@ -1930,10 +1937,11 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { mut or_kind := ast.OrKind.absent mut or_stmts := []ast.Stmt{} mut or_pos := token.Pos{} + mut or_scope := &ast.Scope(unsafe { nil }) if !p.or_is_handled { // a[i] or { ... } if p.tok.kind == .key_orelse { - or_stmts, or_pos = p.or_block(.no_err_var) + or_stmts, or_pos, or_scope = p.or_block(.no_err_var) return ast.IndexExpr{ left: left index: expr @@ -1942,6 +1950,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr { kind: .block stmts: or_stmts pos: or_pos + scope: or_scope } is_gated: is_gated } @@ -2079,9 +2088,10 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { mut or_kind := ast.OrKind.absent mut or_stmts := []ast.Stmt{} mut or_pos := token.Pos{} + mut or_scope := &ast.Scope(unsafe { nil }) if p.tok.kind == .key_orelse { or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) + or_stmts, or_pos, or_scope = p.or_block(.with_err_var) } else if p.tok.kind == .not { or_kind = .propagate_result or_pos = p.tok.pos() @@ -2101,6 +2111,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { kind: or_kind stmts: or_stmts pos: or_pos + scope: or_scope } scope: p.scope next_token: p.tok.kind