parser: fail when assigning to _ with :=

pull/5275/head
Enzo Baldisserri 2020-06-08 00:47:04 +02:00 committed by GitHub
parent 36edd6295f
commit 11b7b97311
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 137 additions and 111 deletions

View File

@ -24,7 +24,7 @@ fn main() {
for len in str_lens {
end_pos := start_pos + len
str := string(bytepile[start_pos..end_pos],len)
_ = wyhash.wyhash_c(&str.str, u64(str.len), 1)
wyhash.wyhash_c(&str.str, u64(str.len), 1)
start_pos = end_pos
}
t1 := time.ticks()
@ -34,7 +34,7 @@ fn main() {
for len in str_lens {
end_pos := start_pos + len
str := string(bytepile[start_pos..end_pos],len)
_ = wyhash.sum64_string(str, 1)
wyhash.sum64_string(str, 1)
start_pos = end_pos
}
t2 := time.ticks()
@ -44,7 +44,7 @@ fn main() {
for len in str_lens {
end_pos := start_pos + len
str := string(bytepile[start_pos..end_pos],len)
_ = fnv1a.sum64_string(str)
fnv1a.sum64_string(str)
start_pos = end_pos
}
t3 := time.ticks()

View File

@ -56,7 +56,7 @@ fn main() {
}
}
_ := os.exec('v cmd/tools/vup.v') or {
os.exec('v cmd/tools/vup.v') or {
panic(err)
}
show_current_v_version(vexe)

View File

@ -27,7 +27,7 @@ pub fn (mut app App) json_endpoint() {
pub fn (mut app App) index() {
app.cnt++
show:= true
show := true
//app.vweb.text('Hello world from vweb')
hello := 'Hello world from vweb'
numbers := [1,2,3]

View File

@ -593,7 +593,7 @@ fn test_array_str() {
numbers := [1, 2, 3]
assert numbers == [1,2,3]
numbers2 := [numbers, [4, 5, 6]] // dup str() bug
_=numbers2
_ = numbers2
assert true
assert numbers.str() == '[1, 2, 3]'
// QTODO

View File

@ -84,7 +84,7 @@ fn encrypt_block_generic(xk []u32, dst, src []byte) {
s2 ^= xk[k+2]
s3 ^= xk[k+3]
_ = dst[15] // early bounds check
_ := dst[15] // early bounds check
binary.big_endian_put_u32(mut dst[..4], s0)
binary.big_endian_put_u32(mut dst.slice(4, 8), s1)
binary.big_endian_put_u32(mut dst.slice(8, 12), s2)

View File

@ -36,7 +36,7 @@ pub fn start_reloader(mut r live.LiveReloadInfo) {
// If that fails, the program would crash anyway, just provide
// an error message to the user and exit:
r.reloads++
_ := compile_and_reload_shared_lib(mut r) or {
compile_and_reload_shared_lib(mut r) or {
eprintln( err )
exit(1)
}

View File

@ -136,9 +136,7 @@ pub fn (ftp FTP) login(user, passwd string) bool {
}
return false
}
code, data = ftp.read()
// TODO Replace `data` with `_`
_ := data
code, _ = ftp.read()
if code == logged_in {
return true
}

View File

@ -150,10 +150,10 @@ pub fn listen(port int) ?Socket {
s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) or {
return error(err)
}
_ = s.bind(port) or {
s.bind(port) or {
return error(err)
}
_ = s.listen() or {
s.listen() or {
return error(err)
}
return s
@ -212,7 +212,7 @@ pub fn dial(address string, port int) ?Socket {
s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) or {
return error(err)
}
_ = s.connect(address, port) or {
s.connect(address, port) or {
return error(err)
}
return s

View File

@ -4,7 +4,7 @@ fn start_socket_udp_server() {
bufsize := 1024
bytes := [1024]byte
s := net.socket_udp() or { panic(err) }
_ = s.bind( 9876 ) or { panic(err) }
s.bind( 9876 ) or { panic(err) }
println('Waiting for udp packets:')
for {
res := s.crecv(bytes, bufsize)

View File

@ -26,7 +26,7 @@ fn test_rand_r_seed_update() {
seed := 10
for _ in 0 .. rnd_count {
prev_seed := seed
_ := rand.rand_r(&seed)
_ = rand.rand_r(&seed)
assert prev_seed != seed
}
}

View File

@ -12,7 +12,7 @@ fn test_parse() {
fn test_parse_invalid() {
s := 'Invalid time string'
_ := time.parse(s) or {
time.parse(s) or {
assert true
return
}
@ -38,7 +38,7 @@ fn test_parse_rfc2822() {
fn test_parse_rfc2822_invalid() {
s3 := 'Thu 12 Foo 2019 06:07:45 +0800'
_ := time.parse_rfc2822(s3) or {
time.parse_rfc2822(s3) or {
assert true
return
}
@ -71,4 +71,4 @@ fn test_rfc8601_parse_cest() {
assert t.minute == 38
assert t.second == 6
assert t.microsecond == 15959
}
}

View File

@ -801,14 +801,6 @@ pub fn expr_is_blank_ident(expr Expr) bool {
}
}
[inline]
pub fn expr_is_call(expr Expr) bool {
return match expr {
CallExpr { true }
else { false }
}
}
pub fn (expr Expr) position() token.Position {
// all uncommented have to be implemented
match mut expr {

View File

@ -668,25 +668,36 @@ fn (mut c Checker) assign_expr(mut assign_expr ast.AssignExpr) {
c.expected_type = table.void_type
left_type := c.unwrap_generic(c.expr(assign_expr.left))
c.expected_type = left_type
if ast.expr_is_blank_ident(assign_expr.left) {
c.expected_type = table.Type(0)
}
assign_expr.left_type = left_type
// println('setting exp type to $c.expected_type $t.name')
right_type := c.check_expr_opt_call(assign_expr.val, c.unwrap_generic(c.expr(assign_expr.val)))
assign_expr.right_type = right_type
right := c.table.get_type_symbol(right_type)
left := c.table.get_type_symbol(left_type)
if ast.expr_is_blank_ident(assign_expr.left) {
return
match assign_expr.left {
ast.Ident {
if it.kind == .blank_ident {
if assign_expr.op != .assign {
c.error('cannot modify blank `_` variable', it.pos)
}
return
}
}
ast.PrefixExpr {
// Do now allow `*x = y` outside `unsafe`
if it.op == .mul && !c.inside_unsafe {
c.error('modifying variables via deferencing can only be done in `unsafe` blocks',
assign_expr.pos)
}
}
else {}
}
// Make sure the variable is mutable
c.fail_if_immutable(assign_expr.left)
// Do now allow `*x = y` outside `unsafe`
if assign_expr.left is ast.PrefixExpr {
p := assign_expr.left as ast.PrefixExpr
if p.op == .mul && !c.inside_unsafe {
c.error('modifying variables via deferencing can only be done in `unsafe` blocks',
assign_expr.pos)
}
}
// Single side check
match assign_expr.op {
.assign {} // No need to do single side check for =. But here put it first for speed.
@ -1318,7 +1329,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
right_first := assign_stmt.right[0]
mut right_len := assign_stmt.right.len
if right_first is ast.CallExpr || right_first is ast.IfExpr || right_first is ast.MatchExpr {
right_type0 := c.expr(assign_stmt.right[0])
right_type0 := c.expr(right_first)
assign_stmt.right_types = [right_type0]
right_type_sym0 := c.table.get_type_symbol(right_type0)
if right_type0 == table.void_type {
@ -1332,12 +1343,11 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
call_expr := assign_stmt.right[0] as ast.CallExpr
c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${call_expr.name}()` returns $right_len value(s)',
assign_stmt.pos)
return
} else {
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)',
assign_stmt.pos)
return
}
return
}
} else if assign_stmt.left.len != right_len {
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $assign_stmt.right.len value(s)',
@ -1354,20 +1364,19 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
c.check_expr_opt_call(assign_stmt.right[i], assign_stmt.right_types[i])
}
mut val_type := assign_stmt.right_types[i]
// check variable name for beginning with capital letter 'Abc'
is_decl := assign_stmt.op == .decl_assign
if is_decl && ident.name != '_' {
c.check_valid_snake_case(ident.name, 'variable name', ident.pos)
}
if assign_stmt.op == .decl_assign {
val_type = c.table.mktyp(val_type)
}
mut ident_var_info := ident.var_info()
if assign_stmt.op == .assign {
is_decl := assign_stmt.op == .decl_assign
if is_decl {
if ident.kind != .blank_ident {
// check variable name for beginning with capital letter 'Abc'
c.check_valid_snake_case(ident.name, 'variable name', ident.pos)
}
val_type = c.table.mktyp(val_type)
} else {
c.fail_if_immutable(ident)
var_type := c.expr(ident)
assign_stmt.left_types << var_type
if !c.check_types(val_type, var_type) {
if ident.kind != .blank_ident && !c.check_types(val_type, var_type) {
val_type_sym := c.table.get_type_symbol(val_type)
var_type_sym := c.table.get_type_symbol(var_type)
c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`',

View File

@ -0,0 +1,5 @@
vlib/v/checker/tests/blank_modify.v:2:2: error: cannot modify blank `_` variable
1 | fn main() {
2 | _ += 1
| ^
3 | }

View File

@ -0,0 +1,3 @@
fn main() {
_ += 1
}

View File

@ -1,5 +1,5 @@
vlib/v/checker/tests/if_expr_last_stmt.v:4:7: error: `if` expression requires an expression as the last statement of every branch
2 | _ := if true {
2 | _ = if true {
3 | 1
4 | } else if false {
| ~~~~~~~~~~~~~

View File

@ -1,5 +1,5 @@
fn main() {
_ := if true {
_ = if true {
1
} else if false {
} else {

View File

@ -1,5 +1,5 @@
vlib/v/checker/tests/if_expr_no_else.v:2:10: error: `if` expression needs `else` clause
vlib/v/checker/tests/if_expr_no_else.v:2:9: error: `if` expression needs `else` clause
1 | fn main() {
2 | _ := if true { 1 }
| ~~
2 | _ = if true { 1 }
| ~~
3 | }

View File

@ -1,3 +1,3 @@
fn main() {
_ := if true { 1 }
_ = if true { 1 }
}

View File

@ -2,5 +2,5 @@ vlib/v/checker/tests/incorrect_name_variable.v:2:2: error: variable name `_abc`
1 | fn main() {
2 | _abc := 1
| ~~~~
3 | _ := _abc
3 | _ = _abc
4 | }

View File

@ -1,4 +1,4 @@
fn main() {
_abc := 1
_ := _abc
_ = _abc
}

View File

@ -10,7 +10,7 @@ fn main() {
'string'
}
}
_ := match x {
_ = match x {
int {
'int'
}
@ -24,7 +24,7 @@ fn main() {
'else'
}
}
_ := match x {
_ = match x {
int {
'int'
}

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/no_interface_instantiation_a.v:4:10: error: cannot instantiate interface `Speaker`
vlib/v/checker/tests/no_interface_instantiation_a.v:4:9: error: cannot instantiate interface `Speaker`
2 |
3 | fn main() {
4 | _ := Speaker{}
| ~~~~~~~~~
4 | _ = Speaker{}
| ~~~~~~~~~
5 | }

View File

@ -1,5 +1,5 @@
interface Speaker {}
fn main() {
_ := Speaker{}
_ = Speaker{}
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/selector_expr_assign.v:7:6: error: struct fields can only be declared during the initialization
5 | fn main() {
6 | abc := Abc{}
7 | abc.a := 2
| ^
8 | }

View File

@ -0,0 +1,8 @@
struct Abc {
a int
}
fn main() {
abc := Abc{}
abc.a := 2
}

View File

@ -1,7 +1,7 @@
vlib/v/checker/tests/struct_unknown_field.v:8:9: error: unknown field `bar` in struct literal of type `Test`
vlib/v/checker/tests/struct_unknown_field.v:8:3: error: unknown field `bar` in struct literal of type `Test`
6 | t := Test{
7 | foo: true
8 | bar: false
| ~~~~~~~~~~
9 | }
10 | _ = t
10 | _ = t

View File

@ -4,8 +4,8 @@ struct Test {
fn main() {
t := Test{
foo: true
bar: false
}
_ = t
foo: true
bar: false
}
_ = t
}

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/unexpected_or.v:6:7: error: unexpected `or` block, the function `ret_zero` does not return an optional
vlib/v/checker/tests/unexpected_or.v:6:6: error: unexpected `or` block, the function `ret_zero` does not return an optional
4 |
5 | fn main() {
6 | _ := ret_zero() or { 1 }
| ~~~~~~~~~~
6 | _ = ret_zero() or { 1 }
| ~~~~~~~~~~
7 | }

View File

@ -3,5 +3,5 @@ fn ret_zero() int {
}
fn main() {
_ := ret_zero() or { 1 }
_ = ret_zero() or { 1 }
}

View File

@ -1,7 +1,7 @@
vlib/v/checker/tests/void_fn_as_value.v:5:8: error: unknown function: x
vlib/v/checker/tests/void_fn_as_value.v:5:7: error: unknown function: x
3 | fn main() {
4 | mut a := 'aa'
5 | a += x('a','b')
| ~~~~~~~~~~
6 | mut b := 'abcdef'
7 | _ = b
4 | mut a := 'aa'
5 | a += x('a','b')
| ~~~~~~~~~~
6 | mut b := 'abcdef'
7 | _ = b

View File

@ -1,8 +1,8 @@
module main
fn main() {
mut a := 'aa'
a += x('a','b')
mut b := 'abcdef'
_ = b
mut a := 'aa'
a += x('a','b')
mut b := 'abcdef'
_ = b
}

View File

@ -3,5 +3,5 @@ fn ret_void() ?void {
}
fn main() {
_ := ret_void() or { panic('$err') }
_ = ret_void() or { panic('$err') }
}

View File

@ -341,7 +341,7 @@ pub fn generate(input_path string, pub_only, with_comments bool) ?Doc {
mut doc := new(input_path)
doc.pub_only = pub_only
doc.with_comments = with_comments
_ = doc.generate() or {
doc.generate() or {
return error(err)
}
return doc

View File

@ -48,7 +48,7 @@ fn main() {
v := "done"
{
_ := "block"
_ = "block"
}
pos := POSITION.go_back

View File

@ -92,8 +92,7 @@ fn (mut p Parser) partial_assign_stmt(known_lhs []ast.Ident) ast.Stmt {
}
}
for i, ident in idents {
known_var := p.scope.known_var(ident.name)
if !is_decl && !known_var {
if !is_decl && ident.kind != .blank_ident && !p.scope.known_var(ident.name) {
p.error('unknown variable `$ident.name`')
}
if is_decl && ident.kind != .blank_ident {
@ -132,12 +131,9 @@ pub fn (mut p Parser) assign_expr(left ast.Expr) ast.AssignExpr {
pos := p.tok.position()
p.next()
val := p.expr(0)
match left {
ast.IndexExpr {
// it.mark_as_setter()
it.is_setter = true
}
else {}
if left is ast.IndexExpr {
mut index_expr := left as ast.IndexExpr
index_expr.is_setter = true
}
node := ast.AssignExpr{
left: left

View File

@ -729,7 +729,17 @@ fn (mut p Parser) parse_multi_expr() ast.Stmt {
if p.tok.kind == .decl_assign || (p.tok.kind == .assign && collected.len > 1) {
mut idents := []ast.Ident{}
for c in collected {
idents << c as ast.Ident
match c {
ast.Ident {
idents << it
}
ast.SelectorExpr {
p.error_with_pos('struct fields can only be declared during the initialization', it.pos)
}
else {
p.error_with_pos('unexpected `${typeof(c)}`', c.position())
}
}
}
return p.partial_assign_stmt(idents)
} else if p.tok.kind.is_assign() {
@ -789,19 +799,18 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident {
if p.expr_mod.len > 0 {
name = '${p.expr_mod}.$name'
}
mut ident := ast.Ident{
return ast.Ident{
kind: .unresolved
name: name
language: language
mod: p.mod
pos: pos
}
ident.is_mut = is_mut
ident.info = ast.IdentVar{
is_mut: is_mut
is_static: is_static
info: ast.IdentVar{
is_mut: is_mut
is_static: is_static
}
}
return ident
} else {
p.error('unexpected token `$p.tok.lit`')
}

View File

@ -264,7 +264,7 @@ pub fn (mut t Table) register_type_symbol(typ TypeSymbol) int {
}
pub fn (t &Table) known_type(name string) bool {
_ = t.find_type(name) or {
t.find_type(name) or {
return false
}
return true

View File

@ -66,7 +66,7 @@ fn test_array_int_full_options() {
assert a.str() == "[0, 0]"
b := []int{len: 10, cap: 100, init: 1} // this creates an array with 10 one and initial capacity 100 elements, value given
_ := b.clone() // discard result variable, sample
_ = b.clone() // discard result variable, sample
println('array b: length: ${b.len}, capacity: ${b.cap}, content: $b')
assert b.len == 10
assert b.cap == 100
@ -74,7 +74,7 @@ fn test_array_int_full_options() {
assert b.str() == "[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]"
mut c := []int{len: 2, cap: 10, init: 1} // this creates an array with 2 one and initial capacity 10 elements, value given
_ := c.clone() // discard result variable, sample
_ = c.clone() // discard result variable, sample
println('array c: length: ${c.len}, capacity: ${c.cap}, content: $c')
assert c.len == 2
assert c.cap == 10
@ -109,7 +109,7 @@ fn test_array_string_full_options() {
assert a.str() == "['', '']"
b := []string{len: 10, cap: 100, init: 'b'} // this creates an array with 10 'b', initial capacity 100 elements, value given
_ := b.clone() // discard result variable, sample
_ = b.clone() // discard result variable, sample
println('array b: length: ${b.len}, capacity: ${b.cap}') // ok
println('array b: length: ${b.len}, capacity: ${b.cap}, content: $b')
assert b.len == 10
@ -118,7 +118,7 @@ fn test_array_string_full_options() {
assert b.str() == "['b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']"
mut c := []string{len: 2, cap: 10, init: 'c'} // this creates an array with 2 'c' and initial capacity 10 elements, value given
_ := c.clone() // discard result variable, sample
_ = c.clone() // discard result variable, sample
println('array c: length: ${c.len}, capacity: ${c.cap}, content: $c')
assert c.len == 2
assert c.cap == 10

View File

@ -21,7 +21,7 @@ fn high_fn_multi_return(a int, b fn (c []int, d []string) ([]int, []string)) {
}
fn high_fn_return_single_anon() (fn(int)f32) {
_ := 1
_ = 1
correct := fn(n int)f32 {
return f32(n * n)
}
@ -29,7 +29,7 @@ fn high_fn_return_single_anon() (fn(int)f32) {
}
fn high_fn_return_multi_anons() (fn(int)f32, fn(int)string) {
// parsing trap
_ := fn(n int)byte {
_ = fn(n int)byte {
return 0x00
}
correct_second := fn(n int)string {
@ -39,7 +39,7 @@ fn high_fn_return_multi_anons() (fn(int)f32, fn(int)string) {
return f32(n * n)
}
// parsing trap
_ := fn(n int)[]int {
_ = fn(n int)[]int {
return [n]
}
return correct_first, correct_second

View File

@ -35,7 +35,7 @@ fn test_decode() {
assert data.license == 'GPL-2.0'
assert data.dependencies[0] == 'hello'
assert data.unknown['test'][0] == 'foo'
_ := vmod.decode('') or {
vmod.decode('') or {
assert err == 'vmod: no content.'
exit(0)
}