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

checker: check unsafe array assign (fix #9651) (#15515)

This commit is contained in:
yuyi 2022-08-25 13:52:13 +08:00 committed by GitHub
parent 86496aa191
commit c662431cfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 84 additions and 48 deletions

View File

@ -321,7 +321,7 @@ fn take_screenshots(opt Options, app AppConfig) ![]string {
os.setenv('$k', rv, true) os.setenv('$k', rv, true)
} }
mut flags := app.capture.flags.join(' ') flags := app.capture.flags.join(' ')
result := opt.verbose_execute('${os.quoted_path(v_exe)} $flags -d gg_record run ${os.quoted_path(app.abs_path)}') result := opt.verbose_execute('${os.quoted_path(v_exe)} $flags -d gg_record run ${os.quoted_path(app.abs_path)}')
if result.exit_code != 0 { if result.exit_code != 0 {
return error('Failed taking screenshot of `$app.abs_path`:\n$result.output') return error('Failed taking screenshot of `$app.abs_path`:\n$result.output')
@ -336,7 +336,7 @@ fn take_screenshots(opt Options, app AppConfig) ![]string {
existing_screenshots := get_app_screenshots(out_path, app)! existing_screenshots := get_app_screenshots(out_path, app)!
mut flags := app.capture.flags flags := app.capture.flags
mut p_app := os.new_process(app.abs_path) mut p_app := os.new_process(app.abs_path)
p_app.set_args(flags) p_app.set_args(flags)

View File

@ -86,7 +86,7 @@ fn main() {
if module_names.len == 0 && os.exists('./v.mod') { if module_names.len == 0 && os.exists('./v.mod') {
println('Detected v.mod file inside the project directory. Using it...') println('Detected v.mod file inside the project directory. Using it...')
manifest := vmod.from_file('./v.mod') or { panic(err) } manifest := vmod.from_file('./v.mod') or { panic(err) }
module_names = manifest.dependencies module_names = manifest.dependencies.clone()
} }
mut source := Source.vpm mut source := Source.vpm
if '--once' in options { if '--once' in options {

View File

@ -154,7 +154,7 @@ fn (r &Repl) current_source_code(should_add_temp_lines bool, not_add_print bool)
if !not_add_print { if !not_add_print {
lines = r.vstartup_lines.filter(!it.starts_with('print')) lines = r.vstartup_lines.filter(!it.starts_with('print'))
} else { } else {
lines = r.vstartup_lines lines = r.vstartup_lines.clone()
} }
all_lines << lines all_lines << lines
} }

View File

@ -83,7 +83,7 @@ pub fn (mut list LinkedList<T>) pop() ?T {
return error('Linked list is empty') return error('Linked list is empty')
} }
mut node := list.head mut node := list.head
mut to_return := node.data mut to_return := unsafe { node.data }
if unsafe { node.next == 0 } { if unsafe { node.next == 0 } {
// first node case // first node case
// set to null // set to null
@ -92,7 +92,7 @@ pub fn (mut list LinkedList<T>) pop() ?T {
for unsafe { node.next.next != 0 } { for unsafe { node.next.next != 0 } {
node = node.next node = node.next
} }
to_return = node.next.data to_return = unsafe { node.next.data }
// set to null // set to null
node.next = unsafe { nil } node.next = unsafe { nil }
} }

View File

@ -55,8 +55,8 @@ fn newton_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient [
q.inc() q.inc()
r -= b r -= b
} }
quotient = q.digits quotient = q.digits.clone()
remainder = r.digits remainder = r.digits.clone()
shrink_tail_zeros(mut remainder) shrink_tail_zeros(mut remainder)
} }
@ -241,7 +241,7 @@ fn toom3_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u3
result := (((pinf.lshift(s) + t2).lshift(s) + t1).lshift(s) + tm1).lshift(s) + p0 result := (((pinf.lshift(s) + t2).lshift(s) + t1).lshift(s) + tm1).lshift(s) + p0
storage = result.digits storage = result.digits.clone()
} }
[inline] [inline]

View File

@ -66,7 +66,7 @@ pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, wher
stmt.bind_res(fields, dataptr, lens, num_fields) stmt.bind_res(fields, dataptr, lens, num_fields)
mut row := 0 mut row := 0
mut types := config.types mut types := config.types.clone()
mut field_types := []FieldType{} mut field_types := []FieldType{}
if config.is_count { if config.is_count {
types = [orm.type_idx['u64']] types = [orm.type_idx['u64']]

View File

@ -54,13 +54,13 @@ fn (mut btree BTree) add_children(tag Tag) int {
btree.all_tags << tag btree.all_tags << tag
if btree.all_tags.len > 1 { if btree.all_tags.len > 1 {
for btree.childrens.len <= btree.node_pointer { for btree.childrens.len <= btree.node_pointer {
mut temp_array := btree.childrens mut temp_array := btree.childrens.clone()
temp_array << []int{} temp_array << []int{}
btree.childrens = temp_array btree.childrens = temp_array
} }
btree.childrens[btree.node_pointer] << btree.all_tags.len - 1 btree.childrens[btree.node_pointer] << btree.all_tags.len - 1
for btree.parents.len < btree.all_tags.len { for btree.parents.len < btree.all_tags.len {
mut temp_array := btree.parents mut temp_array := btree.parents.clone()
temp_array << 0 temp_array << 0
btree.parents = temp_array btree.parents = temp_array
} }

View File

@ -62,7 +62,7 @@ fn (raw_ver RawVersion) coerce() ?Version {
} }
fn (raw_ver RawVersion) complete() RawVersion { fn (raw_ver RawVersion) complete() RawVersion {
mut raw_ints := raw_ver.raw_ints mut raw_ints := raw_ver.raw_ints.clone()
for raw_ints.len < 3 { for raw_ints.len < 3 {
raw_ints << '0' raw_ints << '0'
} }

View File

@ -399,7 +399,7 @@ pub fn (t &Table) get_embeds(sym &TypeSymbol, options GetEmbedsOptions) [][]Type
if unalias_sym.info is Struct { if unalias_sym.info is Struct {
for embed in unalias_sym.info.embeds { for embed in unalias_sym.info.embeds {
embed_sym := t.sym(embed) embed_sym := t.sym(embed)
mut preceding := options.preceding mut preceding := options.preceding.clone()
preceding << embed preceding << embed
embeds << t.get_embeds(embed_sym, preceding: preceding) embeds << t.get_embeds(embed_sym, preceding: preceding)
} }
@ -1932,7 +1932,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
} }
} }
} }
mut all_methods := ts.methods mut all_methods := unsafe { ts.methods }
for imethod in imethods { for imethod in imethods {
for mut method in all_methods { for mut method in all_methods {
if imethod.name == method.name { if imethod.name == method.name {
@ -2097,7 +2097,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
} }
sym.register_method(method) sym.register_method(method)
} }
mut all_methods := parent.methods mut all_methods := unsafe { parent.methods }
for imethod in imethods { for imethod in imethods {
for mut method in all_methods { for mut method in all_methods {
if imethod.name == method.name { if imethod.name == method.name {

View File

@ -338,13 +338,29 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
} }
left_sym := c.table.sym(left_type_unwrapped) left_sym := c.table.sym(left_type_unwrapped)
right_sym := c.table.sym(right_type_unwrapped) right_sym := c.table.sym(right_type_unwrapped)
if left_sym.kind == .array && !c.inside_unsafe && node.op in [.assign, .decl_assign]
&& right_sym.kind == .array && left is ast.Ident && !left.is_blank_ident() old_assign_error_condition := left_sym.kind == .array && !c.inside_unsafe
&& right is ast.Ident { && node.op in [.assign, .decl_assign] && right_sym.kind == .array && left is ast.Ident
&& !left.is_blank_ident() && right is ast.Ident
if old_assign_error_condition {
// Do not allow `a = b`, only `a = b.clone()` // Do not allow `a = b`, only `a = b.clone()`
c.error('use `array2 $node.op.str() array1.clone()` instead of `array2 $node.op.str() array1` (or use `unsafe`)', c.error('use `array2 $node.op.str() array1.clone()` instead of `array2 $node.op.str() array1` (or use `unsafe`)',
node.pos) node.pos)
} }
// Do not allow `a = val.array_field`, only `a = val.array_field.clone()`
// TODO: turn this warning into an error after 2022/09/24
// TODO: and remove the less strict check from above.
if left_sym.kind == .array && !c.inside_unsafe && right_sym.kind == .array
&& left is ast.Ident && !left.is_blank_ident() && right in [ast.Ident, ast.SelectorExpr]
&& ((node.op == .decl_assign && (left as ast.Ident).is_mut)
|| node.op == .assign) {
// no point to show the notice, if the old error was already shown:
if !old_assign_error_condition {
mut_str := if node.op == .decl_assign { 'mut ' } else { '' }
c.note('use `${mut_str}array2 $node.op.str() array1.clone()` instead of `${mut_str}array2 $node.op.str() array1` (or use `unsafe`)',
node.pos)
}
}
if left_sym.kind == .array && right_sym.kind == .array { if left_sym.kind == .array && right_sym.kind == .array {
// `mut arr := [u8(1),2,3]` // `mut arr := [u8(1),2,3]`
// `arr = [byte(4),5,6]` // `arr = [byte(4),5,6]`

View File

@ -756,8 +756,8 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
mut concrete_types := []ast.Type{} mut concrete_types := []ast.Type{}
match arg_sym.info { match arg_sym.info {
ast.Struct, ast.Interface, ast.SumType { ast.Struct, ast.Interface, ast.SumType {
generic_types = arg_sym.info.generic_types generic_types = arg_sym.info.generic_types.clone()
concrete_types = arg_sym.info.concrete_types concrete_types = arg_sym.info.concrete_types.clone()
} }
else {} else {}
} }

View File

@ -542,7 +542,7 @@ pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int,
mut ares := []ast.InterfaceEmbedding{} mut ares := []ast.InterfaceEmbedding{}
for ie in iface_embeds { for ie in iface_embeds {
if iface_decl := c.table.interfaces[ie.typ] { if iface_decl := c.table.interfaces[ie.typ] {
mut list := iface_decl.embeds mut list := iface_decl.embeds.clone()
if !iface_decl.are_embeds_expanded { if !iface_decl.are_embeds_expanded {
list = c.expand_iface_embeds(idecl, level + 1, iface_decl.embeds) list = c.expand_iface_embeds(idecl, level + 1, iface_decl.embeds)
c.table.interfaces[ie.typ].embeds = list c.table.interfaces[ie.typ].embeds = list

View File

@ -30,7 +30,7 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
exp_is_result := expected_type.has_flag(.result) exp_is_result := expected_type.has_flag(.result)
mut expected_types := [expected_type] mut expected_types := [expected_type]
if expected_type_sym.info is ast.MultiReturn { if expected_type_sym.info is ast.MultiReturn {
expected_types = expected_type_sym.info.types expected_types = expected_type_sym.info.types.clone()
if c.table.cur_concrete_types.len > 0 { if c.table.cur_concrete_types.len > 0 {
expected_types = expected_types.map(c.unwrap_generic(it)) expected_types = expected_types.map(c.unwrap_generic(it))
} }

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/array_assign_err.vv:8:8: notice: use `mut array2 := array1.clone()` instead of `mut array2 := array1` (or use `unsafe`)
6 | fn main() {
7 | a := Dumb{[1, 2, 3]}
8 | mut b := a.v // I expect a compiler error
| ~~
9 | for _ in 0 .. 100 {
10 | b << [4, 5, 6]

View File

@ -0,0 +1,13 @@
struct Dumb {
mut:
v []int
}
fn main() {
a := Dumb{[1, 2, 3]}
mut b := a.v // I expect a compiler error
for _ in 0 .. 100 {
b << [4, 5, 6]
}
println(a) // prints [4,5,6] in this case, but I've also seen crashes
}

View File

@ -10,10 +10,10 @@ vlib/v/checker/tests/array_or_map_assign_err.vv:5:5: error: use `array2 = array1
4 | mut a3 := []int{} 4 | mut a3 := []int{}
5 | a3 = a1 5 | a3 = a1
| ^ | ^
6 | 6 |
7 | m1 := {'one': 1} 7 | m1 := {'one': 1}
vlib/v/checker/tests/array_or_map_assign_err.vv:8:8: error: cannot copy map: call `move` or `clone` method (or use a reference) vlib/v/checker/tests/array_or_map_assign_err.vv:8:8: error: cannot copy map: call `move` or `clone` method (or use a reference)
6 | 6 |
7 | m1 := {'one': 1} 7 | m1 := {'one': 1}
8 | m2 := m1 8 | m2 := m1
| ~~ | ~~
@ -24,10 +24,10 @@ vlib/v/checker/tests/array_or_map_assign_err.vv:10:7: error: cannot copy map: ca
9 | mut m3 := map[string]int{} 9 | mut m3 := map[string]int{}
10 | m3 = m1 10 | m3 = m1
| ~~ | ~~
11 | 11 |
12 | _ = a2 12 | _ = a2
vlib/v/checker/tests/array_or_map_assign_err.vv:25:8: error: cannot copy map: call `move` or `clone` method (or use a reference) vlib/v/checker/tests/array_or_map_assign_err.vv:25:8: error: cannot copy map: call `move` or `clone` method (or use a reference)
23 | 23 |
24 | fn foo(mut m map[string]int) { 24 | fn foo(mut m map[string]int) {
25 | m2 := m 25 | m2 := m
| ^ | ^

View File

@ -1160,12 +1160,12 @@ pub fn (mut f Fmt) interface_decl(node ast.InterfaceDecl) {
immut_fields := if node.mut_pos < 0 { node.fields } else { node.fields[..node.mut_pos] } immut_fields := if node.mut_pos < 0 { node.fields } else { node.fields[..node.mut_pos] }
mut_fields := if node.mut_pos < 0 { []ast.StructField{} } else { node.fields[node.mut_pos..] } mut_fields := if node.mut_pos < 0 { []ast.StructField{} } else { node.fields[node.mut_pos..] }
mut immut_methods := node.methods mut immut_methods := node.methods.clone()
mut mut_methods := []ast.FnDecl{} mut mut_methods := []ast.FnDecl{}
for i, method in node.methods { for i, method in node.methods {
if method.params[0].is_mut { if method.params[0].is_mut {
immut_methods = node.methods[..i] immut_methods = node.methods[..i].clone()
mut_methods = node.methods[i..] mut_methods = node.methods[i..].clone()
break break
} }
} }

View File

@ -1030,7 +1030,7 @@ fn (mut g Gen) register_result(t ast.Type) string {
fn (mut g Gen) write_optionals() { fn (mut g Gen) write_optionals() {
mut done := []string{} mut done := []string{}
rlock g.done_optionals { rlock g.done_optionals {
done = g.done_optionals done = g.done_optionals.clone()
} }
for base, styp in g.optionals { for base, styp in g.optionals {
if base in done { if base in done {
@ -5814,7 +5814,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
} }
} }
} }
mut methods := st_sym.methods mut methods := st_sym.methods.clone()
method_names := methods.map(it.name) method_names := methods.map(it.name)
match st_sym.info { match st_sym.info {
ast.Struct, ast.Interface, ast.SumType { ast.Struct, ast.Interface, ast.SumType {

View File

@ -1077,12 +1077,12 @@ pub fn (mut f Gen) interface_decl(node ast.InterfaceDecl) {
immut_fields := if node.mut_pos < 0 { node.fields } else { node.fields[..node.mut_pos] } immut_fields := if node.mut_pos < 0 { node.fields } else { node.fields[..node.mut_pos] }
mut_fields := if node.mut_pos < 0 { []ast.StructField{} } else { node.fields[node.mut_pos..] } mut_fields := if node.mut_pos < 0 { []ast.StructField{} } else { node.fields[node.mut_pos..] }
mut immut_methods := node.methods mut immut_methods := node.methods.clone()
mut mut_methods := []ast.FnDecl{} mut mut_methods := []ast.FnDecl{}
for i, method in node.methods { for i, method in node.methods {
if method.params[0].is_mut { if method.params[0].is_mut {
immut_methods = node.methods[..i] immut_methods = node.methods[..i].clone()
mut_methods = node.methods[i..] mut_methods = node.methods[i..].clone()
break break
} }
} }

View File

@ -675,7 +675,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
g.push_pub_var(name) g.push_pub_var(name)
} }
} }
mut args := it.params args := it.params
g.fn_args(args, it.is_variadic) g.fn_args(args, it.is_variadic)
g.writeln(') {') g.writeln(') {')
@ -808,7 +808,7 @@ fn (mut g JsGen) gen_anon_fn(mut fun ast.AnonFn) {
g.write('return function (') g.write('return function (')
mut args := it.params args := it.params
g.fn_args(args, it.is_variadic) g.fn_args(args, it.is_variadic)
g.writeln(') {') g.writeln(') {')

View File

@ -6,7 +6,7 @@ module parser
import v.ast import v.ast
fn (mut p Parser) assign_stmt() ast.Stmt { fn (mut p Parser) assign_stmt() ast.Stmt {
mut defer_vars := p.defer_vars mut defer_vars := p.defer_vars.clone()
p.defer_vars = []ast.Ident{} p.defer_vars = []ast.Ident{}
exprs, comments := p.expr_list() exprs, comments := p.expr_list()

View File

@ -728,7 +728,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
tmp := p.label_names tmp := p.label_names
p.label_names = [] p.label_names = []
stmts = p.parse_block_no_scope(false) stmts = p.parse_block_no_scope(false)
label_names = p.label_names label_names = p.label_names.clone()
p.label_names = tmp p.label_names = tmp
} }
p.cur_fn_name = keep_fn_name p.cur_fn_name = keep_fn_name

View File

@ -327,9 +327,9 @@ pub fn (mut p Parser) parse() &ast.File {
} }
p.scope.end_pos = p.tok.pos p.scope.end_pos = p.tok.pos
mut errors := p.errors mut errors := p.errors.clone()
mut warnings := p.warnings mut warnings := p.warnings.clone()
mut notices := p.notices mut notices := p.notices.clone()
if p.pref.check_only { if p.pref.check_only {
errors << p.scanner.errors errors << p.scanner.errors
@ -2001,7 +2001,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
tok := p.tok tok := p.tok
mut pos := tok.pos() mut pos := tok.pos()
mut defer_vars := p.defer_vars mut defer_vars := p.defer_vars.clone()
p.defer_vars = []ast.Ident{} p.defer_vars = []ast.Ident{}
left, left_comments := p.expr_list() left, left_comments := p.expr_list()
@ -3374,7 +3374,7 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
p.top_level_statement_start() p.top_level_statement_start()
mut attrs := []ast.Attr{} mut attrs := []ast.Attr{}
if p.attrs.len > 0 { if p.attrs.len > 0 {
attrs = p.attrs attrs = p.attrs.clone()
p.attrs = [] p.attrs = []
} }
mut is_markused := false mut is_markused := false
@ -3491,7 +3491,7 @@ fn (mut p Parser) return_stmt() ast.Return {
fn (mut p Parser) global_decl() ast.GlobalDecl { fn (mut p Parser) global_decl() ast.GlobalDecl {
mut attrs := []ast.Attr{} mut attrs := []ast.Attr{}
if p.attrs.len > 0 { if p.attrs.len > 0 {
attrs = p.attrs attrs = p.attrs.clone()
p.attrs = [] p.attrs = []
} }

View File

@ -64,7 +64,7 @@ struct Efg {
fn test_shared_auto_init_array() { fn test_shared_auto_init_array() {
e := Efg{} e := Efg{}
shared a := e.a shared a := unsafe { e.a }
lock a { lock a {
a << 23.0625 a << 23.0625
a << -133.25 a << -133.25
@ -81,11 +81,11 @@ fn test_shared_array_in_struct() {
a: [1.25, 2.75, 7, 13.0625] a: [1.25, 2.75, 7, 13.0625]
i: 12 i: 12
} }
shared t := x.a shared t := unsafe { x.a }
lock t { lock t {
t[2] = -1.125 t[2] = -1.125
} }
shared tt := x.a shared tt := unsafe { x.a }
v := rlock tt { v := rlock tt {
tt[3] tt[3]
} }