checker: add a notice for potentially incorrect smartcasts (#13644)

pull/13645/head
yuyi 2022-03-03 22:36:40 +08:00 committed by GitHub
parent 1e76cccd48
commit a98eebde7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 54 additions and 2 deletions

View File

@ -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 {}

View File

@ -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{}
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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 | }

View File

@ -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)
}
}