diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index cf17c1eeba..2d1e78ead8 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -92,6 +92,7 @@ pub mut: inside_comptime_for_field bool skip_flags bool // should `#flag` and `#include` be skipped fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc + smartcast_mut_pos token.Pos ct_cond_stack []ast.Expr mut: stmt_level int // the nesting level inside each stmts list; @@ -1677,8 +1678,13 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { } } else { if sym.info is ast.Struct { + if c.smartcast_mut_pos != token.Pos{} { + c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value', + c.smartcast_mut_pos) + } suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name)) c.error(suggestion.say(unknown_field_msg), node.pos) + return ast.void_type } // >> Hack to allow old style custom error implementations @@ -1693,7 +1699,10 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { return method.return_type } // <<< - + if c.smartcast_mut_pos != token.Pos{} { + c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value', + c.smartcast_mut_pos) + } c.error(unknown_field_msg, node.pos) } return ast.void_type @@ -3215,7 +3224,7 @@ pub fn (mut c Checker) concat_expr(mut node ast.ConcatExpr) ast.Type { } // smartcast takes the expression with the current type which should be smartcasted to the target type in the given scope -fn (c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) { +fn (mut c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) { sym := c.table.sym(cur_type) to_type := if sym.kind == .interface_ { to_type_.ref() } else { to_type_ } match expr { @@ -3250,6 +3259,8 @@ fn (c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mu pos: expr.pos orig_type: orig_type }) + } else { + c.smartcast_mut_pos = expr.pos } } ast.Ident { @@ -3277,6 +3288,8 @@ fn (c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mu smartcasts: smartcasts orig_type: orig_type }) + } else if is_mut && !expr.is_mut { + c.smartcast_mut_pos = expr.pos } } else {} diff --git a/vlib/v/checker/for.v b/vlib/v/checker/for.v index f9cc7a7fe7..b4209e23ea 100644 --- a/vlib/v/checker/for.v +++ b/vlib/v/checker/for.v @@ -3,6 +3,7 @@ module checker import v.ast +import v.token fn (mut c Checker) for_c_stmt(node ast.ForCStmt) { c.in_for_count++ @@ -164,4 +165,7 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) { c.stmts(node.stmts) c.loop_label = prev_loop_label c.in_for_count-- + if c.smartcast_mut_pos != token.Pos{} { + c.smartcast_mut_pos = token.Pos{} + } } diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index 362326718a..68d02b92f8 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -4,6 +4,7 @@ module checker import v.ast import v.pref +import v.token pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { if_kind := if node.is_comptime { '\$if' } else { 'if' } @@ -145,6 +146,9 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { } else { c.stmts(branch.stmts) } + if c.smartcast_mut_pos != token.Pos{} { + c.smartcast_mut_pos = token.Pos{} + } } if expr_required { if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt { diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index df107fdfab..d43005e281 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -3,6 +3,7 @@ module checker import v.ast import v.pref import v.util +import v.token import strings pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { @@ -32,6 +33,9 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { } else { c.stmts(branch.stmts) } + if c.smartcast_mut_pos != token.Pos{} { + c.smartcast_mut_pos = token.Pos{} + } if node.is_expr { if branch.stmts.len > 0 { // ignore last statement - workaround diff --git a/vlib/v/checker/tests/incorrect_smartcast_err.out b/vlib/v/checker/tests/incorrect_smartcast_err.out new file mode 100644 index 0000000000..e414220886 --- /dev/null +++ b/vlib/v/checker/tests/incorrect_smartcast_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/incorrect_smartcast_err.vv:10:5: notice: smartcasting requires either an immutable value, or an explicit mut keyword before the value + 8 | fn main(){ + 9 | mut example := IExample(Example{field:"test"}) + 10 | if example is Example{ + | ~~~~~~~ + 11 | println(example.field) + 12 | } +vlib/v/checker/tests/incorrect_smartcast_err.vv:11:19: error: type `IExample` has no field named `field` + 9 | mut example := IExample(Example{field:"test"}) + 10 | if example is Example{ + 11 | println(example.field) + | ~~~~~ + 12 | } + 13 | } diff --git a/vlib/v/checker/tests/incorrect_smartcast_err.vv b/vlib/v/checker/tests/incorrect_smartcast_err.vv new file mode 100644 index 0000000000..7e77d69948 --- /dev/null +++ b/vlib/v/checker/tests/incorrect_smartcast_err.vv @@ -0,0 +1,13 @@ +interface IExample{ +} + +struct Example{ + field string +} + +fn main(){ + mut example := IExample(Example{field:"test"}) + if example is Example{ + println(example.field) + } +}