checker: check that `mut` args are lvalues (#10779)

pull/10786/head
shadowninja55 2021-07-13 01:06:39 -04:00 committed by GitHub
parent 02f0a30555
commit 7694afa44c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 46 additions and 25 deletions

View File

@ -28,37 +28,36 @@ fn reader(s string) &io.BufferedReader {
} }
fn test_parse_request_not_http() { fn test_parse_request_not_http() {
parse_request(mut reader('hello')) or { return } mut reader__ := reader('hello')
parse_request(mut reader__) or { return }
panic('should not have parsed') panic('should not have parsed')
} }
fn test_parse_request_no_headers() { fn test_parse_request_no_headers() {
req := parse_request(mut reader('GET / HTTP/1.1\r\n\r\n')) or { panic('did not parse: $err') } mut reader_ := reader('GET / HTTP/1.1\r\n\r\n')
req := parse_request(mut reader_) or { panic('did not parse: $err') }
assert req.method == .get assert req.method == .get
assert req.url == '/' assert req.url == '/'
assert req.version == .v1_1 assert req.version == .v1_1
} }
fn test_parse_request_two_headers() { fn test_parse_request_two_headers() {
req := parse_request(mut reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n')) or { mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n')
panic('did not parse: $err') req := parse_request(mut reader_) or { panic('did not parse: $err') }
}
assert req.header.custom_values('Test1') == ['a'] assert req.header.custom_values('Test1') == ['a']
assert req.header.custom_values('Test2') == ['B'] assert req.header.custom_values('Test2') == ['B']
} }
fn test_parse_request_two_header_values() { fn test_parse_request_two_header_values() {
req := parse_request(mut reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n')) or { mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n')
panic('did not parse: $err') req := parse_request(mut reader_) or { panic('did not parse: $err') }
}
assert req.header.custom_values('Test1') == ['a; b'] assert req.header.custom_values('Test1') == ['a; b']
assert req.header.custom_values('Test2') == ['c', 'd'] assert req.header.custom_values('Test2') == ['c', 'd']
} }
fn test_parse_request_body() { fn test_parse_request_body() {
req := parse_request(mut reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc')) or { mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc')
panic('did not parse: $err') req := parse_request(mut reader_) or { panic('did not parse: $err') }
}
assert req.data == 'body' assert req.data == 'body'
} }
@ -132,7 +131,8 @@ ${contents[1]}
fn test_parse_large_body() ? { fn test_parse_large_body() ? {
body := 'A'.repeat(101) // greater than max_bytes body := 'A'.repeat(101) // greater than max_bytes
req := 'GET / HTTP/1.1\r\nContent-Length: $body.len\r\n\r\n$body' req := 'GET / HTTP/1.1\r\nContent-Length: $body.len\r\n\r\n$body'
result := parse_request(mut reader(req)) ? mut reader_ := reader(req)
result := parse_request(mut reader_) ?
assert result.data.len == body.len assert result.data.len == body.len
assert result.data == body assert result.data == body
} }

View File

@ -2619,6 +2619,9 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
} }
if call_arg.is_mut && func.language == .v { if call_arg.is_mut && func.language == .v {
to_lock, pos := c.fail_if_immutable(call_arg.expr) to_lock, pos := c.fail_if_immutable(call_arg.expr)
if !call_arg.expr.is_lvalue() {
c.error('cannot pass expression as `mut`', call_arg.expr.position())
}
if !param.is_mut { if !param.is_mut {
tok := call_arg.share.str() tok := call_arg.share.str()
c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`', c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`',

View File

@ -0,0 +1,5 @@
vlib/v/checker/tests/pass_mut_lit.vv:10:12: error: cannot pass expression as `mut`
8 | }
9 |
10 | modify(mut Box{}, 10)
| ~~~~~

View File

@ -0,0 +1,10 @@
struct Box {
mut:
value int
}
fn modify(mut box Box, value int) {
box.value = value
}
modify(mut Box{}, 10)

View File

@ -281,7 +281,10 @@ fn test_struct_literal_args() {
bar_config({}, 10) bar_config({}, 10)
bar_config({ def: 4 }, 4) bar_config({ def: 4 }, 4)
c := mut_bar_config(mut { def: 10 }, 10) mut c_ := Config{
def: 10
}
c := mut_bar_config(mut c_, 10)
assert c.n == 10 assert c.n == 10
assert c.def == 10 assert c.def == 10

View File

@ -28,37 +28,36 @@ fn reader(s string) &io.BufferedReader {
} }
fn test_parse_request_not_http() { fn test_parse_request_not_http() {
parse_request(mut reader('hello')) or { return } mut reader_ := reader('hello')
parse_request(mut reader_) or { return }
panic('should not have parsed') panic('should not have parsed')
} }
fn test_parse_request_no_headers() { fn test_parse_request_no_headers() {
req := parse_request(mut reader('GET / HTTP/1.1\r\n\r\n')) or { panic('did not parse: $err') } mut reader_ := reader('GET / HTTP/1.1\r\n\r\n')
req := parse_request(mut reader_) or { panic('did not parse: $err') }
assert req.method == .get assert req.method == .get
assert req.url == '/' assert req.url == '/'
assert req.version == .v1_1 assert req.version == .v1_1
} }
fn test_parse_request_two_headers() { fn test_parse_request_two_headers() {
req := parse_request(mut reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n')) or { mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n')
panic('did not parse: $err') req := parse_request(mut reader_) or { panic('did not parse: $err') }
}
assert req.header.custom_values('Test1') == ['a'] assert req.header.custom_values('Test1') == ['a']
assert req.header.custom_values('Test2') == ['B'] assert req.header.custom_values('Test2') == ['B']
} }
fn test_parse_request_two_header_values() { fn test_parse_request_two_header_values() {
req := parse_request(mut reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n')) or { mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n')
panic('did not parse: $err') req := parse_request(mut reader_) or { panic('did not parse: $err') }
}
assert req.header.custom_values('Test1') == ['a; b'] assert req.header.custom_values('Test1') == ['a; b']
assert req.header.custom_values('Test2') == ['c', 'd'] assert req.header.custom_values('Test2') == ['c', 'd']
} }
fn test_parse_request_body() { fn test_parse_request_body() {
req := parse_request(mut reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc')) or { mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc')
panic('did not parse: $err') req := parse_request(mut reader_) or { panic('did not parse: $err') }
}
assert req.data == 'body' assert req.data == 'body'
} }
@ -132,7 +131,8 @@ ${contents[1]}
fn test_parse_large_body() ? { fn test_parse_large_body() ? {
body := 'ABCEF\r\n'.repeat(1024 * 1024) // greater than max_bytes body := 'ABCEF\r\n'.repeat(1024 * 1024) // greater than max_bytes
req := 'GET / HTTP/1.1\r\nContent-Length: $body.len\r\n\r\n$body' req := 'GET / HTTP/1.1\r\nContent-Length: $body.len\r\n\r\n$body'
result := parse_request(mut reader(req)) ? mut reader_ := reader(req)
result := parse_request(mut reader_) ?
assert result.data.len == body.len assert result.data.len == body.len
assert result.data == body assert result.data == body
} }