diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index c70bed4f97..2dbfffd419 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -433,7 +433,7 @@ pub: left Expr // `a` in `a := if ...` pos token.Position pub mut: - branches []IfBranch + branches []IfBranch // includes all `else if` branches is_expr bool typ table.Type has_else bool diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index d58cefd0d1..4bb3554356 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2368,12 +2368,15 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { return } mut is_guard := false - mut guard_vars := []string{len: node.branches.len} + mut guard_idx := 0 + mut guard_vars := []string{} for i, branch in node.branches { cond := branch.cond if cond is ast.IfGuardExpr { if !is_guard { is_guard = true + guard_idx = i + guard_vars = []string{ len: node.branches.len } g.writeln('{ /* if guard */ ') } var_name := g.new_tmp_var() @@ -2385,8 +2388,15 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { if i > 0 { g.write('} else ') } + // if last branch is `else {` if i == node.branches.len - 1 && node.has_else { g.writeln('{') + // define `err` only for simple `if val := opt {...} else {` + if is_guard && guard_idx == i - 1 { + cvar_name := guard_vars[guard_idx] + g.writeln('\tstring err = ${cvar_name}.v_error;') + g.writeln('\tint errcode = ${cvar_name}.ecode;') + } } else { match branch.cond as cond { ast.IfGuardExpr { diff --git a/vlib/v/parser/if.v b/vlib/v/parser/if.v index 698484d537..8fd9715ba3 100644 --- a/vlib/v/parser/if.v +++ b/vlib/v/parser/if.v @@ -17,6 +17,7 @@ fn (mut p Parser) if_expr() ast.IfExpr { mut branches := []ast.IfBranch{} mut has_else := false mut comments := []ast.Comment{} + mut prev_guard := false for p.tok.kind in [.key_if, .key_else] { p.inside_if = true start_pos := p.tok.position() @@ -28,26 +29,46 @@ fn (mut p Parser) if_expr() ast.IfExpr { if p.tok.kind == .key_if { p.next() } else { + // else { has_else = true p.inside_if = false end_pos := p.prev_tok.position() body_pos := p.tok.position() + // only declare `err` if previous branch was an `if` guard + if prev_guard { + p.open_scope() + p.scope.register('errcode', ast.Var{ + name: 'errcode' + typ: table.int_type + pos: body_pos + is_used: true + }) + p.scope.register('err', ast.Var{ + name: 'err' + typ: table.string_type + pos: body_pos + is_used: true + }) + } branches << ast.IfBranch{ stmts: p.parse_block() pos: start_pos.extend(end_pos) body_pos: body_pos.extend(p.tok.position()) comments: comments } + if prev_guard { + p.close_scope() + } comments = [] break } } mut cond := ast.Expr{} - mut is_or := false + mut is_guard := false // `if x := opt() {` if p.peek_tok.kind == .decl_assign { - is_or = true p.open_scope() + is_guard = true var_pos := p.tok.position() var_name := p.check_name() p.check(.decl_assign) @@ -61,13 +82,14 @@ fn (mut p Parser) if_expr() ast.IfExpr { var_name: var_name expr: expr } + prev_guard = true } else { + prev_guard = false cond = p.expr(0) } mut left_as_name := '' - is_infix := cond is ast.InfixExpr - if is_infix { - infix := cond as ast.InfixExpr + if cond is ast.InfixExpr as infix { + // if sum is T is_is_cast := infix.op == .key_is is_ident := infix.left is ast.Ident left_as_name = if is_is_cast && p.tok.kind == .key_as { @@ -84,7 +106,7 @@ fn (mut p Parser) if_expr() ast.IfExpr { body_pos := p.tok.position() p.inside_if = false stmts := p.parse_block() - if is_or { + if is_guard { p.close_scope() } branches << ast.IfBranch{ diff --git a/vlib/v/tests/option_test.v b/vlib/v/tests/option_test.v index fe639a83d2..41a017001b 100644 --- a/vlib/v/tests/option_test.v +++ b/vlib/v/tests/option_test.v @@ -3,13 +3,20 @@ fn opt_err_with_code() ?string { } fn test_err_with_code() { + if w := opt_err_with_code() { + assert false + _ := w + } else { + assert err == 'hi' + assert errcode == 137 + } v := opt_err_with_code() or { assert err == 'hi' assert errcode == 137 return } assert false - println(v) // suppress not used error + _ := v } fn opt_err() ?string { @@ -69,7 +76,7 @@ fn test_if_else_opt() { if _ := err_call(false) { assert false } else { - assert true + assert err.len != 0 } } @@ -150,12 +157,12 @@ fn test_or_return() { if _ := or_return_error() { assert false } else { - assert true + assert err.len != 0 } if _ := or_return_none() { assert false } else { - assert true + assert err.len == 0 } }