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 f in b.parsed_files {
for s in f.stmts { for s in f.stmts {
if s is ast.FnDecl { if s is ast.FnDecl {
if it.is_pub { if s.is_pub {
fn_signature := it.stringify(b.table) fn_signature := s.stringify(b.table)
fn_mod := it.modname() fn_mod := s.modname()
if fn_mod == mod_name { if fn_mod == mod_name {
fline := '${fn_mod}: $fn_signature' fline := '${fn_mod}: $fn_signature'
res << fline res << fline

View File

@ -98,7 +98,7 @@ fn main() {
``` ```
Save that snippet into a file `hello.v` . Now do: `v run hello.v` . Save that snippet into a file `hello.v` . Now do: `v run hello.v` .
> That is assuming you have symlinked your V with `v symlink`, as described > That is assuming you have symlinked your V with `v symlink`, as described
[here](https://github.com/vlang/v/blob/master/README.md#symlinking). [here](https://github.com/vlang/v/blob/master/README.md#symlinking).
If you have not yet, you have to type the path to V manually. If you have not yet, you have to type the path to V manually.
@ -108,7 +108,7 @@ Congratulations - you just wrote your first V program, and executed it!
See `v help` for all supported commands. See `v help` for all supported commands.
In the above example, you can see that functions are declared with `fn`. In the above example, you can see that functions are declared with `fn`.
The return type goes after the function name. In this case `main` doesn't The return type goes after the function name. In this case `main` doesn't
return anything, so the return type can be omitted. return anything, so the return type can be omitted.
As in many other languages (such as C, Go and Rust), `main` is an entry point. As in many other languages (such as C, Go and Rust), `main` is an entry point.
@ -541,6 +541,32 @@ else {
println(s) // "odd" 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 operator
`in` allows to check whether an array or a map contains an element. `in` allows to check whether an array or a map contains an element.
@ -1851,9 +1877,9 @@ eprintln('$vm.name $vm.version\n $vm.description')
## Performance tuning ## Performance tuning
The generated C code is usually fast enough, when you compile your code The generated C code is usually fast enough, when you compile your code
with `-prod`. There are some situations though, where you may want to give with `-prod`. There are some situations though, where you may want to give
additional hints to the C compiler, so that it can further optimize some additional hints to the C compiler, so that it can further optimize some
blocks of code. blocks of code.
NB: These are *rarely* needed, and should not be used, unless you NB: These are *rarely* needed, and should not be used, unless you
@ -1862,11 +1888,11 @@ To cite gcc's documentation: "programmers are notoriously bad at predicting
how their programs actually perform". how their programs actually perform".
`[inline]` - you can tag functions with `[inline]`, so the C compiler will `[inline]` - you can tag functions with `[inline]`, so the C compiler will
try to inline them, which in some cases, may be beneficial for performance, try to inline them, which in some cases, may be beneficial for performance,
but may impact the size of your executable. but may impact the size of your executable.
`if _likely_(bool expression) {` this hints the C compiler, that the passed `if _likely_(bool expression) {` this hints the C compiler, that the passed
boolean expression is very likely to be true, so it can generate assembly boolean expression is very likely to be true, so it can generate assembly
code, with less chance of branch misprediction. In the JS backend, code, with less chance of branch misprediction. In the JS backend,
that does nothing. that does nothing.
@ -2106,7 +2132,7 @@ On Unix-like platforms, the file can be run directly after making it executable
V has several attributes that modify the behavior of functions and structs. V has several attributes that modify the behavior of functions and structs.
An attribute is specified inside `[]` right before the function/struct declaration and applies only to the following definition. An attribute is specified inside `[]` right before the function/struct declaration and applies only to the following definition.
```v ```v
// Calling this function will result in a deprecation warning // Calling this function will result in a deprecation warning
@ -2132,7 +2158,7 @@ fn bar() {
} }
// For C interop only, tells V that the following struct is defined with `typedef struct` in C // For C interop only, tells V that the following struct is defined with `typedef struct` in C
[typedef] [typedef]
struct C.Foo { } struct C.Foo { }
// Declare a function with WINAPI // Declare a function with WINAPI

View File

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

View File

@ -389,13 +389,13 @@ pub fn (i &Ident) var_info() IdentVar {
pub struct InfixExpr { pub struct InfixExpr {
pub: pub:
op token.Kind op token.Kind
pos token.Position pos token.Position
left Expr left Expr
right Expr right Expr
pub mut: pub mut:
left_type table.Type left_type table.Type
right_type table.Type right_type table.Type
} }
pub struct PostfixExpr { pub struct PostfixExpr {
@ -443,6 +443,7 @@ pub:
comments []Comment comments []Comment
pub mut: pub mut:
smartcast bool // should only be true if cond is `x is sumtype`, it will be set in checker - if_expr 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 { pub struct LockExpr {

View File

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

View File

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

View File

@ -207,17 +207,17 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool {
} }
ast.TypeDecl { ast.TypeDecl {
if stmt is ast.AliasTypeDecl { if stmt is ast.AliasTypeDecl {
if it.is_pub { if stmt.is_pub {
c.warn('type alias `$it.name` $no_pub_in_main_warning', c.warn('type alias `$stmt.name` $no_pub_in_main_warning',
it.pos) stmt.pos)
} }
} else if stmt is ast.SumTypeDecl { } else if stmt is ast.SumTypeDecl {
if it.is_pub { if stmt.is_pub {
c.warn('sum type `$it.name` $no_pub_in_main_warning', it.pos) c.warn('sum type `$stmt.name` $no_pub_in_main_warning', stmt.pos)
} }
} else if stmt is ast.FnTypeDecl { } else if stmt is ast.FnTypeDecl {
if it.is_pub { if stmt.is_pub {
c.warn('type alias `$it.name` $no_pub_in_main_warning', it.pos) 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 // 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 { pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) table.Type {
if expr is ast.CallExpr { if expr is ast.CallExpr {
if it.return_type.has_flag(.optional) { if expr.return_type.has_flag(.optional) {
if it.or_block.kind == .absent { if expr.or_block.kind == .absent {
if ret_type != table.void_type { if ret_type != table.void_type {
c.error('${it.name}() returns an option, but you missed to add an `or {}` block to it', c.error('${expr.name}() returns an option, but you missed to add an `or {}` block to it',
it.pos) expr.pos)
} }
} else { } else {
c.check_or_expr(it.or_block, ret_type) c.check_or_expr(expr.or_block, ret_type)
} }
// remove optional flag // remove optional flag
// return ret_type.clear_flag(.optional) // return ret_type.clear_flag(.optional)
// TODO: currently unwrapped in assign, would need to refactor assign to unwrap here // TODO: currently unwrapped in assign, would need to refactor assign to unwrap here
return ret_type return ret_type
} else if it.or_block.kind == .block { } else if expr.or_block.kind == .block {
c.error('unexpected `or` block, the function `$it.name` does not return an optional', c.error('unexpected `or` block, the function `$expr.name` does not return an optional',
it.pos) expr.pos)
} else if it.or_block.kind == .propagate { } else if expr.or_block.kind == .propagate {
c.error('unexpected `?`, the function `$it.name`, does not return an optional', c.error('unexpected `?`, the function `$expr.name`, does not return an optional',
it.pos) expr.pos)
} }
} }
return ret_type 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 assign_stmt.left.len != right_len {
if right_first is ast.CallExpr { 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) assign_stmt.pos)
} else { } else {
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)', 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` // smartcast sumtypes when using `is`
if branch.cond is ast.InfixExpr { if branch.cond is ast.InfixExpr {
infix := branch.cond as ast.InfixExpr infix := branch.cond as ast.InfixExpr
if infix.op == .key_is && infix.left is ast.Ident && infix.right is ast.Type { if infix.op == .key_is && (infix.left is ast.Ident || infix.left is ast.SelectorExpr) && infix.right is ast.Type {
left_expr := infix.left as ast.Ident
right_expr := infix.right as ast.Type right_expr := infix.right as ast.Type
if left_expr.kind == .variable { is_variable := if infix.left is ast.Ident {
// Register shadow variable or `as` variable with actual type (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) 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) mut scope := c.file.scope.innermost(branch.body_pos.pos)
scope.register('it', ast.Var{ scope.register(branch.left_as_name, ast.Var{
name: 'it' name: branch.left_as_name
typ: right_expr.typ.to_ptr() typ: right_expr.typ.to_ptr()
pos: left_expr.pos pos: infix.left.position()
is_used: true is_used: true
is_mut: left_expr.is_mut is_mut: 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
}) })
node.branches[i].smartcast = true 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{} mut res := []string{}
for s in stmts { for s in stmts {
if s is ast.Comment { if s is ast.Comment {
res << it.text.trim_left('|') res << s.text.trim_left('|')
} }
} }
return res.join('\n') return res.join('\n')
@ -374,11 +374,11 @@ fn (mut d Doc) generate() ?Doc {
continue continue
} }
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
if it.is_deprecated { if stmt.is_deprecated {
continue continue
} }
if it.receiver.typ != 0 { if stmt.receiver.typ != 0 {
node.attrs['parent'] = d.fmt.type_to_str(it.receiver.typ).trim_left('&') node.attrs['parent'] = d.fmt.type_to_str(stmt.receiver.typ).trim_left('&')
p_idx := d.contents.index_by_name(node.attrs['parent']) p_idx := d.contents.index_by_name(node.attrs['parent'])
if p_idx == -1 && node.attrs['parent'] != 'void' { if p_idx == -1 && node.attrs['parent'] != 'void' {
d.contents << DocNode{ d.contents << DocNode{

View File

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

View File

@ -1213,12 +1213,12 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
mut blank_assign := false mut blank_assign := false
mut ident := ast.Ident{} mut ident := ast.Ident{}
if left is ast.Ident { if left is ast.Ident {
ident = it ident = *left
// id_info := ident.var_info() // id_info := ident.var_info()
// var_type = id_info.typ // var_type = id_info.typ
blank_assign = ident.kind == .blank_ident blank_assign = left.kind == .blank_ident
if ident.info is ast.IdentVar { if left.info is ast.IdentVar {
share := (ident.info as ast.IdentVar).share share := (left.info as ast.IdentVar).share
if share == .shared_t { if share == .shared_t {
var_type = var_type.set_flag(.shared_f) 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 mut has_var := false
for lx in left { for lx in left {
if lx is ast.Ident { if lx is ast.Ident {
if val.name == it.name { if val.name == lx.name {
g.write('_var_') g.write('_var_')
g.write(it.pos.pos.str()) g.write(lx.pos.pos.str())
has_var = true has_var = true
break break
} }
@ -2322,9 +2322,8 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
infix := branch.cond as ast.InfixExpr infix := branch.cond as ast.InfixExpr
right_type := infix.right as ast.Type right_type := infix.right as ast.Type
left_type := infix.left_type left_type := infix.left_type
left_expr := infix.left as ast.Ident
it_type := g.typ(right_type.typ) 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) g.expr(infix.left)
if left_type.is_ptr() { if left_type.is_ptr() {
g.write('->') g.write('->')
@ -2332,7 +2331,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
g.write('.') g.write('.')
} }
g.writeln('obj;') 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) 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 { for stmt in node.vweb_tmpl.stmts {
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
// insert stmts from vweb_tmpl fn // 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.inside_vweb_tmpl = true
g.stmts(it.stmts) g.stmts(stmt.stmts)
g.inside_vweb_tmpl = false g.inside_vweb_tmpl = false
break break
} }

View File

@ -686,8 +686,8 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
val := stmt.right[i] val := stmt.right[i]
mut is_mut := false mut is_mut := false
if left is ast.Ident { if left is ast.Ident {
is_mut = it.is_mut is_mut = left.is_mut
if it.kind == .blank_ident || it.name in ['', '_'] { if left.kind == .blank_ident || left.name in ['', '_'] {
tmp_var := g.new_tmp_var() tmp_var := g.new_tmp_var()
// TODO: Can the tmp_var declaration be omitted? // TODO: Can the tmp_var declaration be omitted?
g.write('const $tmp_var = ') 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 { ast.Ident {
for expr in exprs { for expr in exprs {
if expr is ast.Ident { 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) 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 { ast.Ident {
for expr in exprs { for expr in exprs {
if expr is ast.Ident { if expr is ast.Ident {
if it.name == val_.name { if expr.name == val_.name {
return true 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 // copy vars from current fn scope into vweb_tmpl scope
for stmt in file.stmts { for stmt in file.stmts {
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
if it.name == 'main.vweb_tmpl_$p.cur_fn_name' { if stmt.name == 'main.vweb_tmpl_$p.cur_fn_name' {
fn_decl := it tmpl_scope := file.scope.innermost(stmt.body_pos.pos)
tmpl_scope := file.scope.innermost(it.body_pos.pos)
for _, obj in p.scope.objects { for _, obj in p.scope.objects {
if obj is ast.Var { if obj is ast.Var {
mut v := it mut v := obj
v.pos = fn_decl.body_pos v.pos = stmt.body_pos
tmpl_scope.register(v.name, *v) tmpl_scope.register(v.name, *v)
// set the controller action var to used // set the controller action var to used
// if its unused in the template it will warn // 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 { fn have_fn_main(stmts []ast.Stmt) bool {
for stmt in stmts { for stmt in stmts {
if stmt is ast.FnDecl { if stmt is ast.FnDecl {
if it.name == 'main.main' && it.mod == 'main' { if stmt.name == 'main.main' && stmt.mod == 'main' {
return true return true
} }
} }

View File

@ -64,9 +64,25 @@ fn (mut p Parser) if_expr() ast.IfExpr {
} else { } else {
cond = p.expr(0) 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() end_pos := p.prev_tok.position()
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_or {
p.close_scope() p.close_scope()
@ -77,6 +93,7 @@ fn (mut p Parser) if_expr() ast.IfExpr {
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
left_as_name: left_as_name
} }
comments = [] comments = []
if p.tok.kind != .key_else { 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 // continue on infix expr
node = p.infix_expr(node) 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] { } else if p.tok.kind in [.inc, .dec] {
// Postfix // Postfix
node = ast.PostfixExpr{ 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'
}
}