1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

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

This commit is contained in:
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) { fn (f &Fn) mark_var_used(v Var) {
for i, vv in f.local_vars { for i, vv in f.local_vars {
if vv.name == v.name { if vv.name == v.name {
mut ptr := &f.local_vars[i] //mut ptr := &f.local_vars[i]
ptr.is_used = true //ptr.is_used = true
// / f.local_vars[i].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 // return
} }
} }
@ -86,13 +97,11 @@ fn (p mut Parser) is_sig() bool {
} }
fn new_fn(pkg string, is_public bool) *Fn { fn new_fn(pkg string, is_public bool) *Fn {
mut f := &Fn { return &Fn {
pkg: pkg pkg: pkg
local_vars: [Var{} local_vars: [Var{} ; MaxLocalVars]
; MaxLocalVars]
is_public: is_public is_public: is_public
} }
return f
} }
// Function signatures are added to the top of the .c file in the first run. // 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.scanner.line_nr = var.line_nr - 1
p.error('`$var.name` declared and not used') 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')
}
} }
} }
@ -556,6 +570,9 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin
println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut') println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut')
p.error('`$p.expr_var.name` is immutable') 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 is key_mut or a ref (&), generate & for the first arg
if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) { if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) {
method_call += '& /* ? */' method_call += '& /* ? */'

View File

@ -639,7 +639,7 @@ fn (c &V) cc_windows_cross() {
mut obj_name := c.out_name mut obj_name := c.out_name
obj_name = obj_name.replace('.exe', '') obj_name = obj_name.replace('.exe', '')
obj_name = obj_name.replace('.o.o', '.o') 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' 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 { if c.pref.show_c_cmd {
println(cmd) println(cmd)
@ -1259,7 +1259,7 @@ fn run_repl() []string {
} }
else { else {
lines << line lines << line
mut vals := s.split('\n') vals := s.split('\n')
for i:=0; i<vals.len-1; i++ { for i:=0; i<vals.len-1; i++ {
println(vals[i]) println(vals[i])
} }

View File

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