From 7694afa44cd971e7db47adabe0fbffd7925d49f5 Mon Sep 17 00:00:00 2001 From: shadowninja55 <49539636+shadowninja55@users.noreply.github.com> Date: Tue, 13 Jul 2021 01:06:39 -0400 Subject: [PATCH] checker: check that `mut` args are lvalues (#10779) --- vlib/net/http/request_test.v | 24 ++++++++++++------------ vlib/v/checker/checker.v | 3 +++ vlib/v/checker/tests/pass_mut_lit.out | 5 +++++ vlib/v/checker/tests/pass_mut_lit.vv | 10 ++++++++++ vlib/v/tests/struct_test.v | 5 ++++- vlib/vweb/request_test.v | 24 ++++++++++++------------ 6 files changed, 46 insertions(+), 25 deletions(-) create mode 100644 vlib/v/checker/tests/pass_mut_lit.out create mode 100644 vlib/v/checker/tests/pass_mut_lit.vv diff --git a/vlib/net/http/request_test.v b/vlib/net/http/request_test.v index 3f1ff205a3..d0baf51dd0 100644 --- a/vlib/net/http/request_test.v +++ b/vlib/net/http/request_test.v @@ -28,37 +28,36 @@ fn reader(s string) &io.BufferedReader { } 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') } 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.url == '/' assert req.version == .v1_1 } 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 { - panic('did not parse: $err') - } + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n') + req := parse_request(mut reader_) or { panic('did not parse: $err') } assert req.header.custom_values('Test1') == ['a'] assert req.header.custom_values('Test2') == ['B'] } 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 { - panic('did not parse: $err') - } + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n') + req := parse_request(mut reader_) or { panic('did not parse: $err') } assert req.header.custom_values('Test1') == ['a; b'] assert req.header.custom_values('Test2') == ['c', 'd'] } 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 { - panic('did not parse: $err') - } + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc') + req := parse_request(mut reader_) or { panic('did not parse: $err') } assert req.data == 'body' } @@ -132,7 +131,8 @@ ${contents[1]} fn test_parse_large_body() ? { body := 'A'.repeat(101) // greater than max_bytes 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 == body } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0d3beae8a6..3a5a8cef22 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 { 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 { tok := call_arg.share.str() c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`', diff --git a/vlib/v/checker/tests/pass_mut_lit.out b/vlib/v/checker/tests/pass_mut_lit.out new file mode 100644 index 0000000000..09a03433a8 --- /dev/null +++ b/vlib/v/checker/tests/pass_mut_lit.out @@ -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) + | ~~~~~ diff --git a/vlib/v/checker/tests/pass_mut_lit.vv b/vlib/v/checker/tests/pass_mut_lit.vv new file mode 100644 index 0000000000..7d358f92ca --- /dev/null +++ b/vlib/v/checker/tests/pass_mut_lit.vv @@ -0,0 +1,10 @@ +struct Box { +mut: + value int +} + +fn modify(mut box Box, value int) { + box.value = value +} + +modify(mut Box{}, 10) diff --git a/vlib/v/tests/struct_test.v b/vlib/v/tests/struct_test.v index e639ed26f2..1af0a0325a 100644 --- a/vlib/v/tests/struct_test.v +++ b/vlib/v/tests/struct_test.v @@ -281,7 +281,10 @@ fn test_struct_literal_args() { bar_config({}, 10) 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.def == 10 diff --git a/vlib/vweb/request_test.v b/vlib/vweb/request_test.v index 76324d76b9..0554fc2b4e 100644 --- a/vlib/vweb/request_test.v +++ b/vlib/vweb/request_test.v @@ -28,37 +28,36 @@ fn reader(s string) &io.BufferedReader { } 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') } 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.url == '/' assert req.version == .v1_1 } 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 { - panic('did not parse: $err') - } + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n') + req := parse_request(mut reader_) or { panic('did not parse: $err') } assert req.header.custom_values('Test1') == ['a'] assert req.header.custom_values('Test2') == ['B'] } 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 { - panic('did not parse: $err') - } + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n') + req := parse_request(mut reader_) or { panic('did not parse: $err') } assert req.header.custom_values('Test1') == ['a; b'] assert req.header.custom_values('Test2') == ['c', 'd'] } 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 { - panic('did not parse: $err') - } + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc') + req := parse_request(mut reader_) or { panic('did not parse: $err') } assert req.data == 'body' } @@ -132,7 +131,8 @@ ${contents[1]} fn test_parse_large_body() ? { 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' - result := parse_request(mut reader(req)) ? + mut reader_ := reader(req) + result := parse_request(mut reader_) ? assert result.data.len == body.len assert result.data == body }