checker: add a notice for potentially incorrect smartcasts (#13644)
parent
1e76cccd48
commit
a98eebde7f
|
@ -92,6 +92,7 @@ pub mut:
|
||||||
inside_comptime_for_field bool
|
inside_comptime_for_field bool
|
||||||
skip_flags bool // should `#flag` and `#include` be skipped
|
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
|
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
|
ct_cond_stack []ast.Expr
|
||||||
mut:
|
mut:
|
||||||
stmt_level int // the nesting level inside each stmts list;
|
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 {
|
} else {
|
||||||
if sym.info is ast.Struct {
|
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))
|
suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name))
|
||||||
c.error(suggestion.say(unknown_field_msg), node.pos)
|
c.error(suggestion.say(unknown_field_msg), node.pos)
|
||||||
|
return ast.void_type
|
||||||
}
|
}
|
||||||
|
|
||||||
// >> Hack to allow old style custom error implementations
|
// >> 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
|
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)
|
c.error(unknown_field_msg, node.pos)
|
||||||
}
|
}
|
||||||
return ast.void_type
|
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
|
// 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)
|
sym := c.table.sym(cur_type)
|
||||||
to_type := if sym.kind == .interface_ { to_type_.ref() } else { to_type_ }
|
to_type := if sym.kind == .interface_ { to_type_.ref() } else { to_type_ }
|
||||||
match expr {
|
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
|
pos: expr.pos
|
||||||
orig_type: orig_type
|
orig_type: orig_type
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
c.smartcast_mut_pos = expr.pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
|
@ -3277,6 +3288,8 @@ fn (c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mu
|
||||||
smartcasts: smartcasts
|
smartcasts: smartcasts
|
||||||
orig_type: orig_type
|
orig_type: orig_type
|
||||||
})
|
})
|
||||||
|
} else if is_mut && !expr.is_mut {
|
||||||
|
c.smartcast_mut_pos = expr.pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {}
|
else {}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
module checker
|
module checker
|
||||||
|
|
||||||
import v.ast
|
import v.ast
|
||||||
|
import v.token
|
||||||
|
|
||||||
fn (mut c Checker) for_c_stmt(node ast.ForCStmt) {
|
fn (mut c Checker) for_c_stmt(node ast.ForCStmt) {
|
||||||
c.in_for_count++
|
c.in_for_count++
|
||||||
|
@ -164,4 +165,7 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
c.loop_label = prev_loop_label
|
c.loop_label = prev_loop_label
|
||||||
c.in_for_count--
|
c.in_for_count--
|
||||||
|
if c.smartcast_mut_pos != token.Pos{} {
|
||||||
|
c.smartcast_mut_pos = token.Pos{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ module checker
|
||||||
|
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.pref
|
import v.pref
|
||||||
|
import v.token
|
||||||
|
|
||||||
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
||||||
if_kind := if node.is_comptime { '\$if' } else { 'if' }
|
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 {
|
} else {
|
||||||
c.stmts(branch.stmts)
|
c.stmts(branch.stmts)
|
||||||
}
|
}
|
||||||
|
if c.smartcast_mut_pos != token.Pos{} {
|
||||||
|
c.smartcast_mut_pos = token.Pos{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if expr_required {
|
if expr_required {
|
||||||
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
||||||
|
|
|
@ -3,6 +3,7 @@ module checker
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.pref
|
import v.pref
|
||||||
import v.util
|
import v.util
|
||||||
|
import v.token
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
|
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 {
|
} else {
|
||||||
c.stmts(branch.stmts)
|
c.stmts(branch.stmts)
|
||||||
}
|
}
|
||||||
|
if c.smartcast_mut_pos != token.Pos{} {
|
||||||
|
c.smartcast_mut_pos = token.Pos{}
|
||||||
|
}
|
||||||
if node.is_expr {
|
if node.is_expr {
|
||||||
if branch.stmts.len > 0 {
|
if branch.stmts.len > 0 {
|
||||||
// ignore last statement - workaround
|
// ignore last statement - workaround
|
||||||
|
|
|
@ -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 | }
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue