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 ...` left Expr // `a` in `a := if ...`
pos token.Position pos token.Position
pub mut: pub mut:
branches []IfBranch branches []IfBranch // includes all `else if` branches
is_expr bool is_expr bool
typ table.Type typ table.Type
has_else bool has_else bool

View File

@ -2368,12 +2368,15 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
return return
} }
mut is_guard := false 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 { for i, branch in node.branches {
cond := branch.cond cond := branch.cond
if cond is ast.IfGuardExpr { if cond is ast.IfGuardExpr {
if !is_guard { if !is_guard {
is_guard = true is_guard = true
guard_idx = i
guard_vars = []string{ len: node.branches.len }
g.writeln('{ /* if guard */ ') g.writeln('{ /* if guard */ ')
} }
var_name := g.new_tmp_var() var_name := g.new_tmp_var()
@ -2385,8 +2388,15 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
if i > 0 { if i > 0 {
g.write('} else ') g.write('} else ')
} }
// if last branch is `else {`
if i == node.branches.len - 1 && node.has_else { if i == node.branches.len - 1 && node.has_else {
g.writeln('{') 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 { } else {
match branch.cond as cond { match branch.cond as cond {
ast.IfGuardExpr { ast.IfGuardExpr {

View File

@ -17,6 +17,7 @@ fn (mut p Parser) if_expr() ast.IfExpr {
mut branches := []ast.IfBranch{} mut branches := []ast.IfBranch{}
mut has_else := false mut has_else := false
mut comments := []ast.Comment{} mut comments := []ast.Comment{}
mut prev_guard := false
for p.tok.kind in [.key_if, .key_else] { for p.tok.kind in [.key_if, .key_else] {
p.inside_if = true p.inside_if = true
start_pos := p.tok.position() start_pos := p.tok.position()
@ -28,26 +29,46 @@ fn (mut p Parser) if_expr() ast.IfExpr {
if p.tok.kind == .key_if { if p.tok.kind == .key_if {
p.next() p.next()
} else { } else {
// else {
has_else = true has_else = true
p.inside_if = false p.inside_if = false
end_pos := p.prev_tok.position() end_pos := p.prev_tok.position()
body_pos := p.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{ branches << ast.IfBranch{
stmts: p.parse_block() stmts: p.parse_block()
pos: start_pos.extend(end_pos) pos: start_pos.extend(end_pos)
body_pos: body_pos.extend(p.tok.position()) body_pos: body_pos.extend(p.tok.position())
comments: comments comments: comments
} }
if prev_guard {
p.close_scope()
}
comments = [] comments = []
break break
} }
} }
mut cond := ast.Expr{} mut cond := ast.Expr{}
mut is_or := false mut is_guard := false
// `if x := opt() {` // `if x := opt() {`
if p.peek_tok.kind == .decl_assign { if p.peek_tok.kind == .decl_assign {
is_or = true
p.open_scope() p.open_scope()
is_guard = true
var_pos := p.tok.position() var_pos := p.tok.position()
var_name := p.check_name() var_name := p.check_name()
p.check(.decl_assign) p.check(.decl_assign)
@ -61,13 +82,14 @@ fn (mut p Parser) if_expr() ast.IfExpr {
var_name: var_name var_name: var_name
expr: expr expr: expr
} }
prev_guard = true
} else { } else {
prev_guard = false
cond = p.expr(0) cond = p.expr(0)
} }
mut left_as_name := '' mut left_as_name := ''
is_infix := cond is ast.InfixExpr if cond is ast.InfixExpr as infix {
if is_infix { // if sum is T
infix := cond as ast.InfixExpr
is_is_cast := infix.op == .key_is is_is_cast := infix.op == .key_is
is_ident := infix.left is ast.Ident is_ident := infix.left is ast.Ident
left_as_name = if is_is_cast && p.tok.kind == .key_as { 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() body_pos := p.tok.position()
p.inside_if = false p.inside_if = false
stmts := p.parse_block() stmts := p.parse_block()
if is_or { if is_guard {
p.close_scope() p.close_scope()
} }
branches << ast.IfBranch{ branches << ast.IfBranch{

View File

@ -3,13 +3,20 @@ fn opt_err_with_code() ?string {
} }
fn test_err_with_code() { 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 { v := opt_err_with_code() or {
assert err == 'hi' assert err == 'hi'
assert errcode == 137 assert errcode == 137
return return
} }
assert false assert false
println(v) // suppress not used error _ := v
} }
fn opt_err() ?string { fn opt_err() ?string {
@ -69,7 +76,7 @@ fn test_if_else_opt() {
if _ := err_call(false) { if _ := err_call(false) {
assert false assert false
} else { } else {
assert true assert err.len != 0
} }
} }
@ -150,12 +157,12 @@ fn test_or_return() {
if _ := or_return_error() { if _ := or_return_error() {
assert false assert false
} else { } else {
assert true assert err.len != 0
} }
if _ := or_return_none() { if _ := or_return_none() {
assert false assert false
} else { } else {
assert true assert err.len == 0
} }
} }