all: `if` guard optional: define `err` in `else` branch (#5853)

pull/5868/head
Nick Treleaven 2020-07-17 18:10:01 +01:00 committed by GitHub
parent ea322bdd97
commit 39f90e25f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 12 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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{

View File

@ -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
}
}