all: remove `it` smartcast and replace with original variable name (#5764)

pull/5775/head
Daniel Däschle 2020-07-09 17:14:14 +02:00 committed by GitHub
parent 9511b7d0a1
commit c5dc1a33b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 210 additions and 94 deletions

View File

@ -98,9 +98,9 @@ fn (app App) gen_api_for_module_in_os(mod_name, os_name string) string {
for f in b.parsed_files {
for s in f.stmts {
if s is ast.FnDecl {
if it.is_pub {
fn_signature := it.stringify(b.table)
fn_mod := it.modname()
if s.is_pub {
fn_signature := s.stringify(b.table)
fn_mod := s.modname()
if fn_mod == mod_name {
fline := '${fn_mod}: $fn_signature'
res << fline

View File

@ -541,6 +541,32 @@ else {
println(s) // "odd"
```
#### Is check
You can check sum types using `if` like `match`ing them.
```v
struct Abc {
val string
}
struct Xyz {
foo string
}
type Alphabet = Abc | Xyz
x := Alphabet(Abc{'test'}) // sum type
if x is Abc {
// x is automatically castet to Abc and can be used here
println(x)
}
```
If you have a struct field which should be checked, there is also a way to name a alias.
```
if x.bar is MyStruct as bar {
// x.bar cannot be castet automatically, instead you say "as bar" which creates a variable with the MyStruct typing
println(bar)
}
```
### In operator
`in` allows to check whether an array or a map contains an element.

View File

@ -33,7 +33,7 @@ fn wait() {
fn (l Lander) land(w World) {
if w is Mars {
for it.dust_storm() {
for w.dust_storm() {
wait()
}
}

View File

@ -443,6 +443,7 @@ pub:
comments []Comment
pub mut:
smartcast bool // should only be true if cond is `x is sumtype`, it will be set in checker - if_expr
left_as_name string // only used in x is SumType check
}
pub struct LockExpr {

View File

@ -279,7 +279,7 @@ pub fn (node Stmt) str() string {
mut out := ''
for i, left in it.left {
if left is Ident {
var_info := it.var_info()
var_info := left.var_info()
if var_info.is_mut {
out += 'mut '
}

View File

@ -288,8 +288,8 @@ fn (b &Builder) print_warnings_and_errors() {
for file in b.parsed_files {
for stmt in file.stmts {
if stmt is ast.FnDecl {
if it.name == fn_name {
fline := it.pos.line_nr
if stmt.name == fn_name {
fline := stmt.pos.line_nr
println('$file.path:$fline:')
}
}

View File

@ -207,17 +207,17 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool {
}
ast.TypeDecl {
if stmt is ast.AliasTypeDecl {
if it.is_pub {
c.warn('type alias `$it.name` $no_pub_in_main_warning',
it.pos)
if stmt.is_pub {
c.warn('type alias `$stmt.name` $no_pub_in_main_warning',
stmt.pos)
}
} else if stmt is ast.SumTypeDecl {
if it.is_pub {
c.warn('sum type `$it.name` $no_pub_in_main_warning', it.pos)
if stmt.is_pub {
c.warn('sum type `$stmt.name` $no_pub_in_main_warning', stmt.pos)
}
} else if stmt is ast.FnTypeDecl {
if it.is_pub {
c.warn('type alias `$it.name` $no_pub_in_main_warning', it.pos)
if stmt.is_pub {
c.warn('type alias `$stmt.name` $no_pub_in_main_warning', stmt.pos)
}
}
}
@ -1266,25 +1266,25 @@ fn (mut c Checker) type_implements(typ, inter_typ table.Type, pos token.Position
// return the actual type of the expression, once the optional is handled
pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) table.Type {
if expr is ast.CallExpr {
if it.return_type.has_flag(.optional) {
if it.or_block.kind == .absent {
if expr.return_type.has_flag(.optional) {
if expr.or_block.kind == .absent {
if ret_type != table.void_type {
c.error('${it.name}() returns an option, but you missed to add an `or {}` block to it',
it.pos)
c.error('${expr.name}() returns an option, but you missed to add an `or {}` block to it',
expr.pos)
}
} else {
c.check_or_expr(it.or_block, ret_type)
c.check_or_expr(expr.or_block, ret_type)
}
// remove optional flag
// return ret_type.clear_flag(.optional)
// TODO: currently unwrapped in assign, would need to refactor assign to unwrap here
return ret_type
} else if it.or_block.kind == .block {
c.error('unexpected `or` block, the function `$it.name` does not return an optional',
it.pos)
} else if it.or_block.kind == .propagate {
c.error('unexpected `?`, the function `$it.name`, does not return an optional',
it.pos)
} else if expr.or_block.kind == .block {
c.error('unexpected `or` block, the function `$expr.name` does not return an optional',
expr.pos)
} else if expr.or_block.kind == .propagate {
c.error('unexpected `?`, the function `$expr.name`, does not return an optional',
expr.pos)
}
}
return ret_type
@ -1522,7 +1522,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
}
if assign_stmt.left.len != right_len {
if right_first is ast.CallExpr {
c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${it.name}()` returns $right_len value(s)',
c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${right_first.name}()` returns $right_len value(s)',
assign_stmt.pos)
} else {
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)',
@ -2620,27 +2620,30 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
// smartcast sumtypes when using `is`
if branch.cond is ast.InfixExpr {
infix := branch.cond as ast.InfixExpr
if infix.op == .key_is && infix.left is ast.Ident && infix.right is ast.Type {
left_expr := infix.left as ast.Ident
if infix.op == .key_is && (infix.left is ast.Ident || infix.left is ast.SelectorExpr) && infix.right is ast.Type {
right_expr := infix.right as ast.Type
if left_expr.kind == .variable {
is_variable := if infix.left is ast.Ident {
(infix.left as ast.Ident).kind == .variable
} else { true }
// Register shadow variable or `as` variable with actual type
if is_variable {
left_sym := c.table.get_type_symbol(infix.left_type)
if left_sym.kind == .sum_type {
if left_sym.kind == .sum_type && branch.left_as_name.len > 0 {
mut is_mut := false
if infix.left is ast.Ident {
is_mut = (infix.left as ast.Ident).is_mut
} else if infix.left is ast.SelectorExpr {
selector := infix.left as ast.SelectorExpr
field := c.table.struct_find_field(left_sym, selector.field_name) or { table.Field{} }
is_mut = field.is_mut
}
mut scope := c.file.scope.innermost(branch.body_pos.pos)
scope.register('it', ast.Var{
name: 'it'
scope.register(branch.left_as_name, ast.Var{
name: branch.left_as_name
typ: right_expr.typ.to_ptr()
pos: left_expr.pos
pos: infix.left.position()
is_used: true
is_mut: left_expr.is_mut
})
scope.register(left_expr.name, ast.Var{
name: left_expr.name
typ: right_expr.typ.to_ptr()
pos: left_expr.pos
is_used: true
is_mut: left_expr.is_mut
is_mut: is_mut
})
node.branches[i].smartcast = true
}

View File

@ -0,0 +1,7 @@
fn foo() string {
if true {
return ''
} else {
}
}

View File

@ -45,7 +45,7 @@ pub fn merge_comments(stmts []ast.Stmt) string {
mut res := []string{}
for s in stmts {
if s is ast.Comment {
res << it.text.trim_left('|')
res << s.text.trim_left('|')
}
}
return res.join('\n')
@ -374,11 +374,11 @@ fn (mut d Doc) generate() ?Doc {
continue
}
if stmt is ast.FnDecl {
if it.is_deprecated {
if stmt.is_deprecated {
continue
}
if it.receiver.typ != 0 {
node.attrs['parent'] = d.fmt.type_to_str(it.receiver.typ).trim_left('&')
if stmt.receiver.typ != 0 {
node.attrs['parent'] = d.fmt.type_to_str(stmt.receiver.typ).trim_left('&')
p_idx := d.contents.index_by_name(node.attrs['parent'])
if p_idx == -1 && node.attrs['parent'] != 'void' {
d.contents << DocNode{

View File

@ -246,7 +246,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
ast.AssignStmt {
for i, left in node.left {
if left is ast.Ident {
var_info := it.var_info()
var_info := left.var_info()
if var_info.is_mut {
f.write('mut ')
}
@ -660,8 +660,8 @@ pub fn (mut f Fmt) prefix_expr_cast_expr(fexpr ast.Expr) {
mut is_pe_amp_ce := false
mut ce := ast.CastExpr{}
if fexpr is ast.PrefixExpr {
if it.right is ast.CastExpr && it.op == .amp {
ce = it.right as ast.CastExpr
if fexpr.right is ast.CastExpr && fexpr.op == .amp {
ce = fexpr.right as ast.CastExpr
ce.typname = f.table.get_type_symbol(ce.typ).name
is_pe_amp_ce = true
f.expr(ce)
@ -1248,16 +1248,16 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
}
*/
if node.left is ast.Ident {
it := node.left as ast.Ident
left := node.left as ast.Ident
// `time.now()` without `time imported` is processed as a method call with `time` being
// a `node.left` expression. Import `time` automatically.
// TODO fetch all available modules
if it.name in ['time', 'os', 'strings', 'math', 'json', 'base64'] {
if it.name !in f.auto_imports {
f.auto_imports << it.name
if left.name in ['time', 'os', 'strings', 'math', 'json', 'base64'] {
if left.name !in f.auto_imports {
f.auto_imports << left.name
f.file.imports << ast.Import{
mod: it.name
alias: it.name
mod: left.name
alias: left.name
}
}
// for imp in f.file.imports {
@ -1321,7 +1321,7 @@ pub fn (mut f Fmt) match_expr(it ast.MatchExpr) {
stmt := branch.stmts[0]
if stmt is ast.ExprStmt {
// If expressions inside match branches can't be one a single line
if !expr_is_single_line(it.expr) {
if !expr_is_single_line(stmt.expr) {
single_line = false
break
}

View File

@ -1213,12 +1213,12 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
mut blank_assign := false
mut ident := ast.Ident{}
if left is ast.Ident {
ident = it
ident = *left
// id_info := ident.var_info()
// var_type = id_info.typ
blank_assign = ident.kind == .blank_ident
if ident.info is ast.IdentVar {
share := (ident.info as ast.IdentVar).share
blank_assign = left.kind == .blank_ident
if left.info is ast.IdentVar {
share := (left.info as ast.IdentVar).share
if share == .shared_t {
var_type = var_type.set_flag(.shared_f)
}
@ -1390,9 +1390,9 @@ fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) {
mut has_var := false
for lx in left {
if lx is ast.Ident {
if val.name == it.name {
if val.name == lx.name {
g.write('_var_')
g.write(it.pos.pos.str())
g.write(lx.pos.pos.str())
has_var = true
break
}
@ -2322,9 +2322,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
infix := branch.cond as ast.InfixExpr
right_type := infix.right as ast.Type
left_type := infix.left_type
left_expr := infix.left as ast.Ident
it_type := g.typ(right_type.typ)
g.write('\t$it_type* it = ($it_type*)')
g.write('\t$it_type* _sc_tmp_$branch.pos.pos = ($it_type*)')
g.expr(infix.left)
if left_type.is_ptr() {
g.write('->')
@ -2332,7 +2331,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
g.write('.')
}
g.writeln('obj;')
g.writeln('\t$it_type* $left_expr.name = it;')
g.writeln('\t$it_type* $branch.left_as_name = _sc_tmp_$branch.pos.pos;')
}
g.stmts(branch.stmts)
}

View File

@ -12,9 +12,9 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) {
for stmt in node.vweb_tmpl.stmts {
if stmt is ast.FnDecl {
// insert stmts from vweb_tmpl fn
if it.name.starts_with('main.vweb_tmpl') {
if stmt.name.starts_with('main.vweb_tmpl') {
g.inside_vweb_tmpl = true
g.stmts(it.stmts)
g.stmts(stmt.stmts)
g.inside_vweb_tmpl = false
break
}

View File

@ -686,8 +686,8 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
val := stmt.right[i]
mut is_mut := false
if left is ast.Ident {
is_mut = it.is_mut
if it.kind == .blank_ident || it.name in ['', '_'] {
is_mut = left.is_mut
if left.kind == .blank_ident || left.name in ['', '_'] {
tmp_var := g.new_tmp_var()
// TODO: Can the tmp_var declaration be omitted?
g.write('const $tmp_var = ')

View File

@ -15,7 +15,7 @@ fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) {
ast.Ident {
for expr in exprs {
if expr is ast.Ident {
if it.name == val.name {
if expr.name == val.name {
p.error_with_pos('undefined variable: `$val.name`', val.pos)
}
}
@ -49,7 +49,7 @@ fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool {
ast.Ident {
for expr in exprs {
if expr is ast.Ident {
if it.name == val_.name {
if expr.name == val_.name {
return true
}
}

View File

@ -129,13 +129,12 @@ fn (mut p Parser) vweb() ast.ComptimeCall {
// copy vars from current fn scope into vweb_tmpl scope
for stmt in file.stmts {
if stmt is ast.FnDecl {
if it.name == 'main.vweb_tmpl_$p.cur_fn_name' {
fn_decl := it
tmpl_scope := file.scope.innermost(it.body_pos.pos)
if stmt.name == 'main.vweb_tmpl_$p.cur_fn_name' {
tmpl_scope := file.scope.innermost(stmt.body_pos.pos)
for _, obj in p.scope.objects {
if obj is ast.Var {
mut v := it
v.pos = fn_decl.body_pos
mut v := obj
v.pos = stmt.body_pos
tmpl_scope.register(v.name, *v)
// set the controller action var to used
// if its unused in the template it will warn

View File

@ -555,7 +555,7 @@ fn (mut p Parser) fn_redefinition_error(name string) {
fn have_fn_main(stmts []ast.Stmt) bool {
for stmt in stmts {
if stmt is ast.FnDecl {
if it.name == 'main.main' && it.mod == 'main' {
if stmt.name == 'main.main' && stmt.mod == 'main' {
return true
}
}

View File

@ -64,6 +64,22 @@ fn (mut p Parser) if_expr() ast.IfExpr {
} else {
cond = p.expr(0)
}
mut left_as_name := ''
is_infix := cond is ast.InfixExpr
if is_infix {
infix := cond as ast.InfixExpr
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 {
p.next()
p.check_name()
} else if is_ident {
ident := infix.left as ast.Ident
ident.name
} else {
''
}
}
end_pos := p.prev_tok.position()
body_pos := p.tok.position()
p.inside_if = false
@ -77,6 +93,7 @@ fn (mut p Parser) if_expr() ast.IfExpr {
pos: start_pos.extend(end_pos)
body_pos: body_pos.extend(p.tok.position())
comments: comments
left_as_name: left_as_name
}
comments = []
if p.tok.kind != .key_else {

View File

@ -230,6 +230,10 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
}
// continue on infix expr
node = p.infix_expr(node)
// return early `if bar is SumType as b {`
if p.tok.kind == .key_as && p.inside_if {
return node
}
} else if p.tok.kind in [.inc, .dec] {
// Postfix
node = ast.PostfixExpr{

View File

@ -0,0 +1,60 @@
struct Abc {
mut:
val string
}
struct Xyz {
name string
}
type Alphabet = Abc | Xyz
fn test_if_smartcast() {
x := Alphabet(Abc{'test'})
if x is Abc {
assert x.val == 'test'
}
}
fn test_mutable() {
mut x := Alphabet(Abc{'test'})
if x is Abc {
y := Abc{}
x = &y
assert x == &y
}
}
fn test_nested_if_smartcast() {
x := Alphabet(Abc{'test'})
y := Alphabet(Xyz{'foo'})
if x is Abc {
if y is Xyz {
assert y.name == 'foo'
}
}
}
fn test_as_cast() {
x := Alphabet(Abc{'test'})
if x is Abc as test {
assert test.val == 'test'
}
}
struct Test {
abc Alphabet
}
fn test_mutable_with_struct() {
mut x := Test{Abc{'test'}}
if x.abc is Abc as test {
test.val = 'test'
assert test.val == 'test'
}
}
fn test_as_cast_with_struct() {
x := Test{Abc{'test'}}
if x.abc is Abc as test {
assert test.val == 'test'
}
}