From 8a4691172594c9fecbe8bae6c5bcd55e14ce4aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Tue, 30 Jun 2020 14:19:22 +0200 Subject: [PATCH] checker: check if mut function arg is declared as mut (#5579) --- cmd/tools/vdoc.v | 2 +- vlib/crypto/aes/aes.v | 13 +++++++------ vlib/crypto/aes/aes_cbc.v | 8 ++++---- vlib/crypto/aes/block_generic.v | 22 +++++++++++----------- vlib/crypto/md5/md5.v | 2 +- vlib/crypto/rc4/rc4.v | 2 +- vlib/crypto/sha256/sha256.v | 2 +- vlib/v/checker/checker.v | 26 +++++++++++++++++++++----- vlib/v/checker/tests/immutable_arg.out | 6 ++++++ vlib/v/checker/tests/immutable_arg.vv | 14 ++++++++++++++ vlib/v/checker/tests/immutable_rec.out | 6 ++++++ vlib/v/checker/tests/immutable_rec.vv | 14 ++++++++++++++ vlib/v/tests/generics_test.v | 2 +- 13 files changed, 88 insertions(+), 31 deletions(-) create mode 100644 vlib/v/checker/tests/immutable_arg.out create mode 100644 vlib/v/checker/tests/immutable_arg.vv create mode 100644 vlib/v/checker/tests/immutable_rec.out create mode 100644 vlib/v/checker/tests/immutable_rec.vv diff --git a/cmd/tools/vdoc.v b/cmd/tools/vdoc.v index 3c2f08a43c..51f4ae1f1b 100644 --- a/cmd/tools/vdoc.v +++ b/cmd/tools/vdoc.v @@ -155,7 +155,7 @@ fn (mut cfg DocConfig) serve_html() { default_filename: def_name } for { - con := server.accept() or { + mut con := server.accept() or { server.close() or { } panic(err) } diff --git a/vlib/crypto/aes/aes.v b/vlib/crypto/aes/aes.v index a4e23870a0..8f21a96541 100644 --- a/vlib/crypto/aes/aes.v +++ b/vlib/crypto/aes/aes.v @@ -16,6 +16,7 @@ pub const ( // A cipher is an instance of AES encryption using a particular key. struct AesCipher { +mut: enc []u32 dec []u32 } @@ -40,7 +41,7 @@ pub fn new_cipher(key []byte) AesCipher { pub fn (c &AesCipher) block_size() int { return block_size } -pub fn (c &AesCipher) encrypt(dst, src []byte) { +pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) { if src.len < block_size { panic('crypto.aes: input not full block') } @@ -48,23 +49,23 @@ pub fn (c &AesCipher) encrypt(dst, src []byte) { panic('crypto.aes: output not full block') } // if subtle.inexact_overlap(dst[:block_size], src[:block_size]) { - if subtle.inexact_overlap(dst[..block_size], src[..block_size]) { + if subtle.inexact_overlap((*dst)[..block_size], (*src)[..block_size]) { panic('crypto.aes: invalid buffer overlap') } // for now use generic version - encrypt_block_generic(c.enc, dst, src) + encrypt_block_generic(c.enc, mut dst, src) } -pub fn (c &AesCipher) decrypt(dst, src []byte) { +pub fn (c &AesCipher) decrypt(mut dst []byte, mut src []byte) { if src.len < block_size { panic('crypto.aes: input not full block') } if dst.len < block_size { panic('crypto.aes: output not full block') } - if subtle.inexact_overlap(dst[..block_size], src[..block_size]) { + if subtle.inexact_overlap((*dst)[..block_size], (*src)[..block_size]) { panic('crypto.aes: invalid buffer overlap') } // for now use generic version - decrypt_block_generic(c.dec, dst, src) + decrypt_block_generic(c.dec, mut dst, src) } diff --git a/vlib/crypto/aes/aes_cbc.v b/vlib/crypto/aes/aes_cbc.v index 2623172dce..cbc540c4fa 100644 --- a/vlib/crypto/aes/aes_cbc.v +++ b/vlib/crypto/aes/aes_cbc.v @@ -54,7 +54,7 @@ pub fn (x &AesCbc) encrypt_blocks(mut dst []byte, src_ []byte) { if dst.len < src.len { panic('crypto.cipher: output smaller than input') } - if subtle.inexact_overlap((*dst)[..src.len], src) { + if subtle.inexact_overlap((*dst)[..src.len], src_) { panic('crypto.cipher: invalid buffer overlap') } @@ -63,7 +63,7 @@ pub fn (x &AesCbc) encrypt_blocks(mut dst []byte, src_ []byte) { for src.len > 0 { // Write the xor to dst, then encrypt in place. cipher.xor_bytes(mut (*dst)[..x.block_size], src[..x.block_size], iv) - x.b.encrypt((*dst)[..x.block_size], (*dst)[..x.block_size]) + x.b.encrypt(mut (*dst)[..x.block_size], mut (*dst)[..x.block_size]) // Move to the next block with this block as the next iv. iv = (*dst)[..x.block_size] @@ -104,7 +104,7 @@ pub fn (mut x AesCbc) decrypt_blocks(mut dst []byte, src []byte) { // Loop over all but the first block. for start > 0 { - x.b.decrypt((*dst).slice(start, end), src.slice(start, end)) + x.b.decrypt(mut (*dst).slice(start, end), mut src.slice(start, end)) cipher.xor_bytes(mut (*dst).slice(start, end), (*dst).slice(start, end), src.slice(prev, start)) end = start @@ -113,7 +113,7 @@ pub fn (mut x AesCbc) decrypt_blocks(mut dst []byte, src []byte) { } // The first block is special because it uses the saved iv. - x.b.decrypt((*dst).slice(start, end), src.slice(start, end)) + x.b.decrypt(mut (*dst).slice(start, end), mut src.slice(start, end)) cipher.xor_bytes(mut (*dst).slice(start, end), (*dst).slice(start, end), x.iv) diff --git a/vlib/crypto/aes/block_generic.v b/vlib/crypto/aes/block_generic.v index cbeeda970d..46a3b74544 100644 --- a/vlib/crypto/aes/block_generic.v +++ b/vlib/crypto/aes/block_generic.v @@ -40,7 +40,7 @@ module aes import encoding.binary // Encrypt one block from src into dst, using the expanded key xk. -fn encrypt_block_generic(xk []u32, dst, src []byte) { +fn encrypt_block_generic(xk []u32, mut dst []byte, src []byte) { _ = src[15] // early bounds check mut s0 := binary.big_endian_u32(src[..4]) mut s1 := binary.big_endian_u32(src.slice(4, 8)) @@ -85,16 +85,16 @@ fn encrypt_block_generic(xk []u32, dst, src []byte) { s3 ^= xk[k+3] _ := 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) - binary.big_endian_put_u32(mut dst.slice(12, 16), s3) + binary.big_endian_put_u32(mut (*dst)[0..4], s0) + binary.big_endian_put_u32(mut (*dst).slice(4, 8), s1) + binary.big_endian_put_u32(mut (*dst).slice(8, 12), s2) + binary.big_endian_put_u32(mut (*dst).slice(12, 16), s3) } // Decrypt one block from src into dst, using the expanded key xk. -fn decrypt_block_generic(xk []u32, dst, src []byte) { +fn decrypt_block_generic(xk []u32, mut dst []byte, src []byte) { _ = src[15] // early bounds check - mut s0 := binary.big_endian_u32(src[..4]) + mut s0 := binary.big_endian_u32(src[0..4]) mut s1 := binary.big_endian_u32(src.slice(4, 8)) mut s2 := binary.big_endian_u32(src.slice(8, 12)) mut s3 := binary.big_endian_u32(src.slice(12, 16)) @@ -137,10 +137,10 @@ fn decrypt_block_generic(xk []u32, dst, src []byte) { s3 ^= xk[k+3] _ = 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) - binary.big_endian_put_u32(mut dst.slice(12, 16), s3) + 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) + binary.big_endian_put_u32(mut (*dst).slice(12, 16), s3) } // Apply s_box0 to each byte in w. diff --git a/vlib/crypto/md5/md5.v b/vlib/crypto/md5/md5.v index 1f48adff9e..457081fb45 100644 --- a/vlib/crypto/md5/md5.v +++ b/vlib/crypto/md5/md5.v @@ -117,7 +117,7 @@ pub fn (mut d Digest) checksum() []byte { panic('d.nx != 0') } - digest := []byte{len:(size)} + mut digest := []byte{len:(size)} binary.little_endian_put_u32(mut digest, d.s[0]) binary.little_endian_put_u32(mut digest[4..], d.s[1]) diff --git a/vlib/crypto/rc4/rc4.v b/vlib/crypto/rc4/rc4.v index 798fe30119..aad864b217 100644 --- a/vlib/crypto/rc4/rc4.v +++ b/vlib/crypto/rc4/rc4.v @@ -59,7 +59,7 @@ pub fn (mut c Cipher) reset() { // xor_key_stream sets dst to the result of XORing src with the key stream. // Dst and src must overlap entirely or not at all. -pub fn (mut c Cipher) xor_key_stream(mut dst []byte, src []byte) { +pub fn (mut c Cipher) xor_key_stream(mut dst, src []byte) { if src.len == 0 { return } diff --git a/vlib/crypto/sha256/sha256.v b/vlib/crypto/sha256/sha256.v index 9665517bf9..88b080ceff 100644 --- a/vlib/crypto/sha256/sha256.v +++ b/vlib/crypto/sha256/sha256.v @@ -161,7 +161,7 @@ fn (mut d Digest) checksum() []byte { panic('d.nx != 0') } - digest := []byte{len:(size)} + mut digest := []byte{len:(size)} binary.big_endian_put_u32(mut digest, d.h[0]) binary.big_endian_put_u32(mut digest[4..], d.h[1]) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index fa0f84f90e..f667eaafbf 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -690,6 +690,17 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) { } } } + ast.CallExpr { + // TODO: should only work for builtin method + if expr.name == 'slice' { + return + } else { + c.error('cannot use function call as mut', expr.pos) + } + } + ast.ArrayInit { + return + } else { c.error('unexpected expression `${typeof(expr)}`', expr.position()) } @@ -1097,11 +1108,16 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { c.error('when forwarding a varg variable, it must be the final argument', call_expr.pos) } - if arg.is_mut && !call_arg.is_mut { - c.error('`$arg.name` is a mutable argument, you need to provide `mut`: `${call_expr.name}(mut ...)`', - call_arg.expr.position()) - } else if !arg.is_mut && call_arg.is_mut { - c.error('`$arg.name` argument is not mutable, `mut` is not needed`', call_arg.expr.position()) + if call_arg.is_mut { + c.fail_if_immutable(call_arg.expr) + if !arg.is_mut { + c.error('`$arg.name` argument is not mutable, `mut` is not needed`', call_arg.expr.position()) + } + } else { + if arg.is_mut { + c.error('`$arg.name` is a mutable argument, you need to provide `mut`: `${call_expr.name}(mut ...)`', + call_arg.expr.position()) + } } // Handle expected interface if arg_typ_sym.kind == .interface_ { diff --git a/vlib/v/checker/tests/immutable_arg.out b/vlib/v/checker/tests/immutable_arg.out new file mode 100644 index 0000000000..3583c2b847 --- /dev/null +++ b/vlib/v/checker/tests/immutable_arg.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_arg.v:13:8: error: `a` is immutable, declare it with `mut` to make it mutable + 11 | fn main() { + 12 | a := St{e: 2} + 13 | f(mut a) + | ^ + 14 | } diff --git a/vlib/v/checker/tests/immutable_arg.vv b/vlib/v/checker/tests/immutable_arg.vv new file mode 100644 index 0000000000..08e799d439 --- /dev/null +++ b/vlib/v/checker/tests/immutable_arg.vv @@ -0,0 +1,14 @@ +struct St { +mut: + e int +} + +fn f(mut x St) { + x.e++ + println(x) +} + +fn main() { + a := St{e: 2} + f(mut a) +} diff --git a/vlib/v/checker/tests/immutable_rec.out b/vlib/v/checker/tests/immutable_rec.out new file mode 100644 index 0000000000..0e918b82a1 --- /dev/null +++ b/vlib/v/checker/tests/immutable_rec.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_rec.v:13:2: error: `a` is immutable, declare it with `mut` to make it mutable + 11 | fn main() { + 12 | a := St{e: 2} + 13 | a.f() + | ^ + 14 | } diff --git a/vlib/v/checker/tests/immutable_rec.vv b/vlib/v/checker/tests/immutable_rec.vv new file mode 100644 index 0000000000..acbc0074aa --- /dev/null +++ b/vlib/v/checker/tests/immutable_rec.vv @@ -0,0 +1,14 @@ +struct St { +mut: + e int +} + +fn (mut x St) f() { + x.e++ + println(x) +} + +fn main() { + a := St{e: 2} + a.f() +} diff --git a/vlib/v/tests/generics_test.v b/vlib/v/tests/generics_test.v index 8fafd01247..6935f96b89 100644 --- a/vlib/v/tests/generics_test.v +++ b/vlib/v/tests/generics_test.v @@ -77,7 +77,7 @@ fn mut_arg2(mut x T) T { fn test_create() { create() create() - u := User{} + mut u := User{} mut_arg(mut u) mut_arg2(mut u) }