checker: check error for match mut with immutable variable (fix #9704 #8976) (#13725)

pull/13728/head
yuyi 2022-03-13 15:53:29 +08:00 committed by GitHub
parent 83762fa4a4
commit 27f9bc9ba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 82 additions and 50 deletions

View File

@ -796,7 +796,7 @@ pub mut:
}
pub fn (i &Ident) var_info() IdentVar {
match mut i.info {
match i.info {
IdentVar {
return i.info
}

View File

@ -1640,7 +1640,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
mut nrt := ''
mut c_nrt := ''
ts := t.sym(typ)
match mut ts.info {
match ts.info {
Array {
mut elem_type := ts.info.elem_type
mut elem_sym := t.sym(elem_type)
@ -1744,7 +1744,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
}
else {}
}
match mut ts.info {
match ts.info {
Struct {
mut info := ts.info
info.is_generic = false

View File

@ -71,18 +71,18 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
return
}
for i, left in node.left {
if left is ast.CallExpr {
for i, mut left in node.left {
if mut left is ast.CallExpr {
// ban `foo() = 10`
c.error('cannot call function `${left.name}()` on the left side of an assignment',
left.pos)
} else if left is ast.PrefixExpr {
} else if mut left is ast.PrefixExpr {
// ban `*foo() = 10`
if left.right is ast.CallExpr && left.op == .mul {
c.error('cannot dereference a function call on the left side of an assignment, use a temporary variable',
left.pos)
}
} else if left is ast.IndexExpr {
} else if mut left is ast.IndexExpr {
if left.index is ast.RangeExpr {
c.error('cannot reassign using range expression on the left side of an assignment',
left.pos)
@ -99,8 +99,8 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
}
if node.right_types.len < node.left.len { // first type or multi return types added above
old_inside_ref_lit := c.inside_ref_lit
if left is ast.Ident {
if left.info is ast.IdentVar {
if mut left is ast.Ident {
if mut left.info is ast.IdentVar {
c.inside_ref_lit = c.inside_ref_lit || left.info.share == .shared_t
}
}

View File

@ -821,7 +821,7 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
} else if arg_sym.kind in [.struct_, .interface_, .sum_type] {
mut generic_types := []ast.Type{}
mut concrete_types := []ast.Type{}
match mut arg_sym.info {
match arg_sym.info {
ast.Struct, ast.Interface, ast.SumType {
generic_types = arg_sym.info.generic_types
concrete_types = arg_sym.info.concrete_types

View File

@ -528,7 +528,7 @@ pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int,
}
fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) {
match mut expr {
match expr {
ast.FloatLiteral {
if expr.val.f64() == 0.0 {
oper := if op_kind == .div { 'division' } else { 'modulo' }
@ -1084,10 +1084,11 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
// returns name and position of variable that needs write lock
// also sets `is_changed` to true (TODO update the name to reflect this?)
fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Pos) {
fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) {
mut to_lock := '' // name of variable that needs lock
mut pos := token.Pos{} // and its position
mut explicit_lock_needed := false
mut expr := unsafe { expr_ }
match mut expr {
ast.CastExpr {
// TODO
@ -1878,7 +1879,8 @@ fn (mut c Checker) check_loop_label(label string, pos token.Pos) {
c.loop_label = label
}
fn (mut c Checker) stmt(node ast.Stmt) {
fn (mut c Checker) stmt(node_ ast.Stmt) {
mut node := unsafe { node_ }
$if trace_checker ? {
ntype := typeof(node).replace('v.ast.', '')
eprintln('checking: ${c.file.path:-30} | pos: ${node.pos.line_str():-39} | node: $ntype | $node')
@ -2178,7 +2180,7 @@ fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
}
fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) {
match mut arg {
match arg {
ast.AsmAlias {}
ast.AsmAddressing {
if arg.scale !in [-1, 1, 2, 4, 8] {
@ -2449,12 +2451,13 @@ pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
}
// TODO node must be mut
pub fn (mut c Checker) expr(node ast.Expr) ast.Type {
pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
c.expr_level++
defer {
c.expr_level--
}
mut node := unsafe { node_ }
if c.expr_level > checker.expr_level_cutoff_limit {
c.error('checker: too many expr levels: $c.expr_level ', node.pos())
return ast.void_type
@ -3090,7 +3093,7 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
if node.tok_kind == .assign && node.is_mut {
c.error('`mut` not allowed with `=` (use `:=` to declare a variable)', node.pos)
}
if obj := node.scope.find(node.name) {
if mut obj := node.scope.find(node.name) {
match mut obj {
ast.GlobalField {
node.kind = .global
@ -3173,7 +3176,7 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
else if !name.contains('.') && node.mod != 'builtin' {
name = '${node.mod}.$node.name'
}
if obj := c.file.global_scope.find(name) {
if mut obj := c.file.global_scope.find(name) {
match mut obj {
ast.ConstField {
if !(obj.is_pub || obj.mod == c.mod || c.pref.is_test) {

View File

@ -23,6 +23,10 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
// we setting this here rather than at the end of the method
// since it is used in c.match_exprs() it saves checking twice
node.cond_type = ast.mktyp(cond_type)
if (node.cond is ast.Ident && (node.cond as ast.Ident).is_mut)
|| (node.cond is ast.SelectorExpr && (node.cond as ast.SelectorExpr).is_mut) {
c.fail_if_immutable(node.cond)
}
c.ensure_type_exists(node.cond_type, node.pos) or { return ast.void_type }
c.check_expr_opt_call(node.cond, cond_type)
cond_type_sym := c.table.sym(cond_type)
@ -307,7 +311,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
}
}
} else {
match mut cond_type_sym.info {
match cond_type_sym.info {
ast.SumType {
for v in cond_type_sym.info.variants {
v_str := c.table.type_to_str(v)

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/match_mut_with_immutable_var_err.vv:5:12: error: `i` is immutable, declare it with `mut` to make it mutable
3 | fn main() {
4 | i := Int(0)
5 | match mut i {
| ^
6 | int { i = 1 }
7 | byte { i = 2 }

View File

@ -0,0 +1,10 @@
type Int = byte | int
fn main() {
i := Int(0)
match mut i {
int { i = 1 }
byte { i = 2 }
}
println(i)
}

View File

@ -505,7 +505,8 @@ fn stmt_is_single_line(stmt ast.Stmt) bool {
//=== General Expr-related methods and helpers ===//
pub fn (mut f Fmt) expr(node ast.Expr) {
pub fn (mut f Fmt) expr(node_ ast.Expr) {
mut node := unsafe { node_ }
if f.is_debug {
eprintln('expr: ${node.pos():-42} | node: ${node.type_name():-20} | $node.str()')
}

View File

@ -107,7 +107,7 @@ fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt) string {
g.writeln('\t${metaname}.fn_name = ${ctoslit(fn_name)};')
metasrc := cnewlines(ctoslit(src))
g.writeln('\t${metaname}.src = $metasrc;')
match mut node.expr {
match node.expr {
ast.InfixExpr {
expr_op_str := ctoslit(node.expr.op.str())
expr_left_str := cnewlines(ctoslit(node.expr.left.str()))

View File

@ -178,7 +178,7 @@ fn (mut g Gen) final_gen_str(typ StrType) {
g.gen_str_for_option(typ.typ, styp, str_fn_name)
return
}
match mut sym.info {
match sym.info {
ast.Alias {
if sym.info.is_import {
g.gen_str_default(sym, styp, str_fn_name)

View File

@ -1122,7 +1122,7 @@ fn (mut g Gen) cc_type(typ ast.Type, is_prefix_struct bool) string {
sym := g.table.sym(g.unwrap_generic(typ))
mut styp := sym.cname
// TODO: this needs to be removed; cgen shouldn't resolve generic types (job of checker)
match mut sym.info {
match sym.info {
ast.Struct, ast.Interface, ast.SumType {
if sym.info.is_generic {
mut sgtyps := '_T'
@ -2733,7 +2733,7 @@ fn (mut g Gen) map_fn_ptrs(key_typ ast.TypeSymbol) (string, string, string, stri
return hash_fn, key_eq_fn, clone_fn, free_fn
}
fn (mut g Gen) expr(node ast.Expr) {
fn (mut g Gen) expr(node_ ast.Expr) {
// println('cgen expr() line_nr=$node.pos.line_nr')
old_discard_or_result := g.discard_or_result
old_is_void_expr_stmt := g.is_void_expr_stmt
@ -2744,6 +2744,7 @@ fn (mut g Gen) expr(node ast.Expr) {
g.discard_or_result = false
}
// Note: please keep the type names in the match here in alphabetical order:
mut node := unsafe { node_ }
match mut node {
ast.ComptimeType {
g.error('g.expr(): Unhandled ComptimeType', node.pos)
@ -4550,7 +4551,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
}
// sym := g.table.sym(typ)
mut name := sym.cname
match mut sym.info {
match sym.info {
ast.Struct {
if sym.info.is_generic {
continue
@ -4724,7 +4725,7 @@ fn (g &Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
}
// create list of deps
mut field_deps := []string{}
match mut sym.info {
match sym.info {
ast.ArrayFixed {
dep := g.table.sym(sym.info.elem_type).name
if dep in type_names {
@ -5576,7 +5577,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
mut name := method.name
if inter_info.parent_type.has_flag(.generic) {
parent_sym := g.table.sym(inter_info.parent_type)
match mut parent_sym.info {
match parent_sym.info {
ast.Struct, ast.Interface, ast.SumType {
name = g.generic_fn_name(parent_sym.info.concrete_types, method.name,
false)

View File

@ -1443,7 +1443,7 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) {
// We do not need to declare this variable again, so just generate `t = ...`
// instead of `string t = ...`, and we need to mark this variable as unused,
// so that it's freed after the call. (Used tmp arg vars are not freed to avoid double frees).
if x := scope.find(t) {
if mut x := scope.find(t) {
match mut x {
ast.Var { x.is_used = false }
else {}
@ -1491,8 +1491,8 @@ fn (mut g Gen) autofree_call_postgen(node_pos int) {
}
// g.doing_autofree_tmp = true
// g.write('/* postgen */')
scope := g.file.scope.innermost(node_pos)
for _, obj in scope.objects {
mut scope := g.file.scope.innermost(node_pos)
for _, mut obj in scope.objects {
match mut obj {
ast.Var {
// if var.typ == 0 {

View File

@ -501,7 +501,7 @@ fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
if sym.kind == .interface_ {
g.write('_typ $cmp_op ')
// `_Animal_Dog_index`
sub_type := match mut node.right {
sub_type := match node.right {
ast.TypeNode { node.right.typ }
ast.None { g.table.type_idxs['None__'] }
else { ast.Type(0) }

View File

@ -59,7 +59,7 @@ fn (mut g JsGen) final_gen_str(typ StrType) {
g.gen_str_for_option(typ.typ, styp, str_fn_name)
return
}
match mut sym.info {
match sym.info {
ast.Alias {
if sym.info.is_import {
g.gen_str_default(sym, styp, str_fn_name)

View File

@ -206,7 +206,7 @@ fn (mut g JsGen) final_gen_copy(typ StrType) {
}
else {}
}
match mut sym.info {
match sym.info {
ast.Alias {
g.gen_copy_for_alias(sym.info, styp, copy_fn_name)
}

View File

@ -641,8 +641,9 @@ fn (mut g JsGen) gen_alias_type_decl(node ast.AliasTypeDecl) {
g.writeln('function ${name}(val) { return val; }')
}
fn (mut g JsGen) stmt_no_semi(node ast.Stmt) {
fn (mut g JsGen) stmt_no_semi(node_ ast.Stmt) {
g.stmt_start_pos = g.out.len
mut node := unsafe { node_ }
match mut node {
ast.EmptyStmt {}
ast.AsmStmt {
@ -744,8 +745,9 @@ fn (mut g JsGen) stmt_no_semi(node ast.Stmt) {
}
}
fn (mut g JsGen) stmt(node ast.Stmt) {
fn (mut g JsGen) stmt(node_ ast.Stmt) {
g.stmt_start_pos = g.out.len
mut node := unsafe { node_ }
match mut node {
ast.EmptyStmt {}
ast.AsmStmt {
@ -853,8 +855,9 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
}
}
fn (mut g JsGen) expr(node ast.Expr) {
fn (mut g JsGen) expr(node_ ast.Expr) {
// Note: please keep the type names in the match here in alphabetical order:
mut node := unsafe { node_ }
match mut node {
ast.ComptimeType {
verror('not yet implemented')
@ -1128,7 +1131,7 @@ fn (mut g JsGen) gen_assert_metainfo(node ast.AssertStmt) string {
metasrc := src
g.writeln('${metaname}.src = "$metasrc"')
match mut node.expr {
match node.expr {
ast.InfixExpr {
expr_op_str := node.expr.op.str()
expr_left_str := node.expr.left.str()
@ -1594,7 +1597,7 @@ fn (mut g JsGen) gen_expr_stmt_no_semi(it ast.ExprStmt) {
fn (mut g JsGen) cc_type(typ ast.Type, is_prefix_struct bool) string {
sym := g.table.sym(g.unwrap_generic(typ))
mut styp := sym.cname.replace('>', '').replace('<', '')
match mut sym.info {
match sym.info {
ast.Struct, ast.Interface, ast.SumType {
if sym.info.is_generic {
mut sgtyps := '_T'

View File

@ -1209,13 +1209,13 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
if node.left is ast.InfixExpr {
g.n_error('only simple expressions are supported right now (not more than 2 operands)')
}
match mut node.left {
match node.left {
ast.Ident {
g.mov_var_to_reg(.eax, g.get_var_offset(node.left.name))
}
else {}
}
if mut node.right is ast.Ident {
if node.right is ast.Ident {
var_offset := g.get_var_offset(node.right.name)
match node.op {
.plus { g.add8_var(.eax, var_offset) }
@ -1409,9 +1409,9 @@ fn (mut g Gen) cjmp_op(op token.Kind) int {
}
fn (mut g Gen) condition(infix_expr ast.InfixExpr, neg bool) int {
match mut infix_expr.left {
match infix_expr.left {
ast.IntegerLiteral {
match mut infix_expr.right {
match infix_expr.right {
ast.IntegerLiteral {
// 3 < 4
a0 := infix_expr.left.val.int()
@ -1435,7 +1435,7 @@ fn (mut g Gen) condition(infix_expr ast.InfixExpr, neg bool) int {
}
}
ast.Ident {
match mut infix_expr.right {
match infix_expr.right {
ast.IntegerLiteral {
// var < 4
lit := infix_expr.right as ast.IntegerLiteral
@ -1515,7 +1515,7 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
infix_expr := node.cond as ast.InfixExpr
mut jump_addr := 0 // location of `jne *00 00 00 00*`
start := g.pos()
match mut infix_expr.left {
match infix_expr.left {
ast.Ident {
match infix_expr.right {
ast.Ident {

View File

@ -486,7 +486,7 @@ fn (mut g Gen) gen_forc_stmt(node ast.ForCStmt) {
match cond {
ast.InfixExpr {
// g.infix_expr(node.cond)
match mut cond.left {
match cond.left {
ast.Ident {
lit := cond.right as ast.IntegerLiteral
g.cmp_var(cond.left.name, lit.val.int())

View File

@ -96,7 +96,8 @@ pub fn (mut w Walker) mark_markused_globals() {
}
}
pub fn (mut w Walker) stmt(node ast.Stmt) {
pub fn (mut w Walker) stmt(node_ ast.Stmt) {
mut node := unsafe { node_ }
match mut node {
ast.EmptyStmt {}
ast.AsmStmt {
@ -215,7 +216,8 @@ fn (mut w Walker) exprs(exprs []ast.Expr) {
}
}
fn (mut w Walker) expr(node ast.Expr) {
fn (mut w Walker) expr(node_ ast.Expr) {
mut node := unsafe { node_ }
match mut node {
ast.EmptyExpr {
// TODO make sure this doesn't happen

View File

@ -175,7 +175,8 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
mut has_cross_var := false
mut is_static := false
mut is_volatile := false
for i, lx in left {
for i, lx_ in left {
mut lx := unsafe { lx_ }
match mut lx {
ast.Ident {
if op == .decl_assign {

View File

@ -3750,7 +3750,7 @@ fn (mut p Parser) rewind_scanner_to_current_token_in_new_mode() {
// returns true if `varname` is known
pub fn (mut p Parser) mark_var_as_used(varname string) bool {
if obj := p.scope.find(varname) {
if mut obj := p.scope.find(varname) {
match mut obj {
ast.Var {
obj.is_used = true

View File

@ -27,7 +27,7 @@ fn test_sumtype_assign() {
text: 'baz'
}
mut results := []string{}
for a in arr {
for mut a in arr {
match mut a {
Bar, Baz {
a.text = 'I am ' + a.text

View File

@ -401,7 +401,7 @@ pub fn (mut t Transformer) expr_stmt_match_expr(mut node ast.MatchExpr) ast.Expr
for mut expr in branch.exprs {
expr = t.expr(mut expr)
match mut cond {
match cond {
ast.BoolLiteral {
if expr is ast.BoolLiteral {
if cond.val == (expr as ast.BoolLiteral).val {