do not allow declaring a mutable variable if it's never modified

pull/1308/head
Alexander Medvednikov 2019-07-25 13:16:17 +02:00
parent dbf027acb8
commit 9ccd3bde01
4 changed files with 47 additions and 19 deletions

View File

@ -49,9 +49,20 @@ fn (f mut Fn) open_scope() {
fn (f &Fn) mark_var_used(v Var) {
for i, vv in f.local_vars {
if vv.name == v.name {
mut ptr := &f.local_vars[i]
ptr.is_used = true
// / f.local_vars[i].is_used = true
//mut ptr := &f.local_vars[i]
//ptr.is_used = true
f.local_vars[i].is_used = true
return
}
}
}
fn (f &Fn) mark_var_changed(v Var) {
for i, vv in f.local_vars {
if vv.name == v.name {
//mut ptr := &f.local_vars[i]
//ptr.is_used = true
f.local_vars[i].is_changed = true
// return
}
}
@ -86,13 +97,11 @@ fn (p mut Parser) is_sig() bool {
}
fn new_fn(pkg string, is_public bool) *Fn {
mut f := &Fn {
return &Fn {
pkg: pkg
local_vars: [Var{}
; MaxLocalVars]
local_vars: [Var{} ; MaxLocalVars]
is_public: is_public
}
return f
}
// Function signatures are added to the top of the .c file in the first run.
@ -444,6 +453,11 @@ fn (p mut Parser) check_unused_variables() {
p.scanner.line_nr = var.line_nr - 1
p.error('`$var.name` declared and not used')
}
if !var.is_changed && var.is_mut && !p.pref.is_repl && !var.is_arg && !p.pref.translated && var.name != '_' {
p.scanner.line_nr = var.line_nr - 1
p.error('`$var.name` is declared mutable, but it was never changed')
}
}
}
@ -555,7 +569,10 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
if receiver.is_mut && !p.expr_var.is_mut {
println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut')
p.error('`$p.expr_var.name` is immutable')
}
}
if !p.expr_var.is_changed {
p.cur_fn.mark_var_changed(p.expr_var)
}
// if receiver is key_mut or a ref (&), generate & for the first arg
if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) {
method_call += '& /* ? */'

View File

@ -639,7 +639,7 @@ fn (c &V) cc_windows_cross() {
mut obj_name := c.out_name
obj_name = obj_name.replace('.exe', '')
obj_name = obj_name.replace('.o.o', '.o')
mut include := '-I $winroot/include '
include := '-I $winroot/include '
cmd := 'clang -o $obj_name -w $include -DUNICODE -D_UNICODE -m32 -c -target x86_64-win32 $ModPath/$c.out_name_c'
if c.pref.show_c_cmd {
println(cmd)
@ -1259,7 +1259,7 @@ fn run_repl() []string {
}
else {
lines << line
mut vals := s.split('\n')
vals := s.split('\n')
for i:=0; i<vals.len-1; i++ {
println(vals[i])
}

View File

@ -27,6 +27,7 @@ mut:
access_mod AccessMod
is_global bool // __global (translated from C only)
is_used bool
is_changed bool
scope_level int
}
@ -708,7 +709,7 @@ fn (p mut Parser) check_space(expected Token) {
fn (p mut Parser) check(expected Token) {
if p.tok != expected {
println('check()')
mut s := 'expected `${expected.str()}` but got `${p.strtok()}`'
s := 'expected `${expected.str()}` but got `${p.strtok()}`'
p.next()
println('next token = `${p.strtok()}`')
print_backtrace()
@ -1158,6 +1159,9 @@ fn (p mut Parser) assign_statement(v Var, ph int, is_map bool) {
if !v.is_mut && !v.is_arg && !p.pref.translated && !v.is_global{
p.error('`$v.name` is immutable')
}
if !v.is_changed {
p.cur_fn.mark_var_changed(v)
}
is_str := v.typ == 'string'
switch tok {
case Token.assign:
@ -1263,10 +1267,9 @@ fn (p mut Parser) var_decl() {
is_mut: is_mut
is_alloc: p.is_alloc
})
mut cgen_typ := typ
if !or_else {
gen_name := p.table.var_cgen_name(name)
mut nt_gen := p.table.cgen_name_type_pair(gen_name, cgen_typ) + '='
mut nt_gen := p.table.cgen_name_type_pair(gen_name, typ) + '='
if is_static {
nt_gen = 'static $nt_gen'
}
@ -1502,7 +1505,7 @@ fn (p mut Parser) name_expr() string {
return cfn.typ
}
// Constant
mut c := p.table.find_const(name)
c := p.table.find_const(name)
if c.name != '' && ptr && !c.is_global {
p.error('cannot take the address of constant `$c.name`')
}
@ -1614,6 +1617,9 @@ fn (p mut Parser) var_expr(v Var) string {
if !v.is_mut && !v.is_arg && !p.pref.translated {
p.error('`$v.name` is immutable')
}
if !v.is_changed {
p.cur_fn.mark_var_changed(v)
}
if typ != 'int' {
if !p.pref.translated && !is_number_type(typ) {
p.error('cannot ++/-- value of type `$typ`')
@ -1649,7 +1655,7 @@ fn (p &Parser) fileis(s string) bool {
// user.company.name => `str_typ` is `Company`
fn (p mut Parser) dot(str_typ string, method_ph int) string {
p.check(.dot)
mut field_name := p.lit
field_name := p.lit
p.fgen(field_name)
p.log('dot() field_name=$field_name typ=$str_typ')
//if p.fileis('main.v') {
@ -1713,7 +1719,7 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string {
return field.typ
}
// method
mut method := p.table.find_method(typ, field_name)
method := p.table.find_method(typ, field_name)
p.fn_call(method, method_ph, '', str_typ)
// Methods returning `array` should return `array_string`
if method.typ == 'array' && typ.name.starts_with('array_') {
@ -1956,6 +1962,9 @@ fn (p mut Parser) expression() string {
if !p.expr_var.is_mut && !p.pref.translated {
p.error('`$p.expr_var.name` is immutable (can\'t <<)')
}
if !p.expr_var.is_changed {
p.cur_fn.mark_var_changed(p.expr_var)
}
expr_type := p.expression()
// Two arrays of the same type?
push_array := typ == expr_type
@ -3092,6 +3101,7 @@ fn (p mut Parser) for_st() {
typ: 'int'
// parent_fn: p.cur_fn
is_mut: true
is_changed: true
}
p.register_var(i_var)
p.genln(';\nfor (int $i = 0; $i < $tmp .len; $i ++) {')
@ -3102,6 +3112,7 @@ fn (p mut Parser) for_st() {
name: i
typ: 'string'
is_mut: true
is_changed: true
}
p.register_var(i_var)
p.genln('array_string keys_$tmp = map_keys(& $tmp ); ')
@ -3159,6 +3170,7 @@ fn (p mut Parser) for_st() {
name: val
typ: var_type
ptr: typ.contains('*')
is_changed: true
}
p.register_var(val_var)
i := p.get_tmp()
@ -3348,7 +3360,7 @@ fn (p mut Parser) go_statement() {
p.next()
p.check(.dot)
typ := p.table.find_type(v.typ)
mut method := p.table.find_method(typ, p.lit)
method := p.table.find_method(typ, p.lit)
p.async_fn_call(method, 0, var_name, v.typ)
}
// Normal function

View File

@ -695,11 +695,10 @@ fn (table &Table) qualify_module(mod string, file_path string) string {
}
fn new_file_import_table(file_path string) *FileImportTable {
mut t := &FileImportTable{
return &FileImportTable{
file_path: file_path
imports: map[string]string{}
}
return t
}
fn (fit &FileImportTable) known_import(mod string) bool {