mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: remove it
smartcast and replace with original variable name (#5764)
This commit is contained in:
parent
9511b7d0a1
commit
c5dc1a33b6
@ -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
|
||||
|
46
doc/docs.md
46
doc/docs.md
@ -98,7 +98,7 @@ fn main() {
|
||||
```
|
||||
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).
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
As in many other languages (such as C, Go and Rust), `main` is an entry point.
|
||||
@ -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.
|
||||
@ -1851,9 +1877,9 @@ eprintln('$vm.name $vm.version\n $vm.description')
|
||||
|
||||
## Performance tuning
|
||||
|
||||
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
|
||||
additional hints to the C compiler, so that it can further optimize some
|
||||
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
|
||||
additional hints to the C compiler, so that it can further optimize some
|
||||
blocks of code.
|
||||
|
||||
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".
|
||||
|
||||
`[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.
|
||||
|
||||
`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
|
||||
`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
|
||||
code, with less chance of branch misprediction. In the JS backend,
|
||||
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.
|
||||
|
||||
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
|
||||
// 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
|
||||
[typedef]
|
||||
[typedef]
|
||||
struct C.Foo { }
|
||||
|
||||
// Declare a function with WINAPI
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -389,13 +389,13 @@ pub fn (i &Ident) var_info() IdentVar {
|
||||
|
||||
pub struct InfixExpr {
|
||||
pub:
|
||||
op token.Kind
|
||||
pos token.Position
|
||||
left Expr
|
||||
right Expr
|
||||
op token.Kind
|
||||
pos token.Position
|
||||
left Expr
|
||||
right Expr
|
||||
pub mut:
|
||||
left_type table.Type
|
||||
right_type table.Type
|
||||
left_type table.Type
|
||||
right_type table.Type
|
||||
}
|
||||
|
||||
pub struct PostfixExpr {
|
||||
@ -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 {
|
||||
|
@ -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 '
|
||||
}
|
||||
|
@ -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:')
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
// Register shadow variable or `as` variable with actual type
|
||||
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
|
||||
}
|
||||
|
7
vlib/v/checker/tests/returns/return_missing_simple.vv
Normal file
7
vlib/v/checker/tests/returns/return_missing_simple.vv
Normal file
@ -0,0 +1,7 @@
|
||||
fn foo() string {
|
||||
if true {
|
||||
return ''
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
@ -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{
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 = ')
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -64,9 +64,25 @@ 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
|
||||
p.inside_if = false
|
||||
stmts := p.parse_block()
|
||||
if is_or {
|
||||
p.close_scope()
|
||||
@ -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 {
|
||||
|
@ -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{
|
||||
|
60
vlib/v/tests/if_smartcast_test.v
Normal file
60
vlib/v/tests/if_smartcast_test.v
Normal 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'
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user