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:
parent
dbf027acb8
commit
9ccd3bde01
@ -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 += '& /* ? */'
|
||||||
|
@ -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])
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user