From 2f1810634e11a8bce9d8d651d19bb8ef1fa15f79 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 13 Mar 2021 18:20:46 +0300 Subject: [PATCH] checker: do not allow direct initialization of builtin types (s := string{}) --- vlib/encoding/base64/base64.v | 20 ++++++++++--------- vlib/os/os_c.v | 14 +++++++------ vlib/v/ast/ast.v | 1 - vlib/v/checker/checker.v | 10 ++++++++++ .../checker/tests/check_generic_int_init.out | 2 +- vlib/v/tests/comptime_if_expr_test.v | 11 +++++----- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/vlib/encoding/base64/base64.v b/vlib/encoding/base64/base64.v index cea93a4aec..710e84dfe7 100644 --- a/vlib/encoding/base64/base64.v +++ b/vlib/encoding/base64/base64.v @@ -24,7 +24,7 @@ pub fn decode(data string) []byte { unsafe { buffer := malloc(size) n := decode_in_buffer(data, buffer) - return array{element_size: 1, data: buffer, len: n, cap: size} + return buffer.vbytes(n) } } @@ -70,7 +70,8 @@ fn alloc_and_encode(src byteptr, len int) string { // the a base64 url encoded `string` passed in `data`. pub fn url_decode(data string) []byte { mut result := data.replace_each(['-', '+', '_', '/']) - match result.len % 4 { // Pad with trailing '='s + match result.len % 4 { + // Pad with trailing '='s 2 { result += '==' } // 2 pad chars 3 { result += '=' } // 1 pad char else {} // no padding @@ -81,7 +82,8 @@ pub fn url_decode(data string) []byte { // url_decode_str is the string variant of url_decode pub fn url_decode_str(data string) string { mut result := data.replace_each(['-', '+', '_', '/']) - match result.len % 4 { // Pad with trailing '='s + match result.len % 4 { + // Pad with trailing '='s 2 { result += '==' } // 2 pad chars 3 { result += '=' } // 1 pad char else {} // no padding @@ -132,19 +134,19 @@ pub fn decode_in_buffer(data &string, buffer byteptr) int { mut char_c := 0 mut char_d := 0 if i < input_length { - char_a = index[unsafe { d[i] }] + char_a = base64.index[unsafe { d[i] }] i++ } if i < input_length { - char_b = index[unsafe { d[i] }] + char_b = base64.index[unsafe { d[i] }] i++ } if i < input_length { - char_c = index[unsafe { d[i] }] + char_c = base64.index[unsafe { d[i] }] i++ } if i < input_length { - char_d = index[unsafe { d[i] }] + char_d = base64.index[unsafe { d[i] }] i++ } @@ -180,7 +182,7 @@ fn encode_from_buffer(dest byteptr, src byteptr, src_len int) int { mut d := src mut b := dest - mut etable := byteptr(enc_table.str) + mut etable := byteptr(base64.enc_table.str) for i < input_length { mut octet_a := 0 mut octet_b := 0 @@ -210,7 +212,7 @@ fn encode_from_buffer(dest byteptr, src byteptr, src_len int) int { j += 4 } - padding_length := ending_table[input_length % 3] + padding_length := base64.ending_table[input_length % 3] for i = 0; i < padding_length; i++ { unsafe { b[output_length - 1 - i] = `=` diff --git a/vlib/os/os_c.v b/vlib/os/os_c.v index 7cd7eaecb0..86c156ff24 100644 --- a/vlib/os/os_c.v +++ b/vlib/os/os_c.v @@ -483,7 +483,7 @@ pub fn read_file_array(path string) []T { a := T{} tsize := int(sizeof(a)) // prepare for reading, get current file size - mut fp := vfopen(path, 'rb') or { return array{} } + mut fp := vfopen(path, 'rb') or { return []T{} } C.fseek(fp, 0, C.SEEK_END) fsize := C.ftell(fp) C.rewind(fp) @@ -492,11 +492,13 @@ pub fn read_file_array(path string) []T { buf := unsafe { malloc(fsize) } C.fread(buf, fsize, 1, fp) C.fclose(fp) - return array{ - element_size: tsize - data: buf - len: len - cap: len + return unsafe { + array{ + element_size: tsize + data: buf + len: len + cap: len + } } } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index be4f34ac28..03eff5149d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1248,7 +1248,6 @@ pub fn (expr Expr) is_blank_ident() bool { pub fn (expr Expr) position() token.Position { // all uncommented have to be implemented match expr { - // KEKW2 AnonFn { return expr.decl.pos } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 23fead181a..5798040c55 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -463,6 +463,16 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { utyp := c.unwrap_generic(struct_init.typ) c.ensure_type_exists(utyp, struct_init.pos) or {} type_sym := c.table.get_type_symbol(utyp) + // Make sure the first letter is capital, do not allow e.g. `x := string{}`, + // but `x := T{}` is ok. + if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v + && c.cur_generic_types.len == 0 { + pos := type_sym.name.last_index('.') or { -1 } + first_letter := type_sym.name[pos + 1] + if !first_letter.is_capital() { + c.error('cannot initialize builtin type `$type_sym.name`', struct_init.pos) + } + } if type_sym.kind == .sum_type && struct_init.fields.len == 1 { sexpr := struct_init.fields[0].expr.str() c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`', diff --git a/vlib/v/checker/tests/check_generic_int_init.out b/vlib/v/checker/tests/check_generic_int_init.out index 8542862d5d..2306d077c5 100644 --- a/vlib/v/checker/tests/check_generic_int_init.out +++ b/vlib/v/checker/tests/check_generic_int_init.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/check_generic_int_init.vv:2:9: error: type `int` is private +vlib/v/checker/tests/check_generic_int_init.vv:2:9: error: cannot initialize builtin type `int` 1 | fn test() T { 2 | return T{} | ~~~ diff --git a/vlib/v/tests/comptime_if_expr_test.v b/vlib/v/tests/comptime_if_expr_test.v index 9e65f994a8..868d298fa3 100644 --- a/vlib/v/tests/comptime_if_expr_test.v +++ b/vlib/v/tests/comptime_if_expr_test.v @@ -31,7 +31,7 @@ fn test_ct_expressions() { fn generic_t_is() O { $if O is string { - return 'It\'s a string!' + return "It's a string!" } $else { return O{} } @@ -41,17 +41,17 @@ fn generic_t_is() O { struct GenericTIsTest {} fn test_generic_t_is() { - assert generic_t_is() == 'It\'s a string!' + assert generic_t_is() == "It's a string!" assert generic_t_is() == GenericTIsTest{} } fn generic_t_is2() ?T { $if T is string { - return 'It\'s a string!' + return "It's a string!" } $else { return T{} } - } +} fn test_generic_t_is2() { res := generic_t_is2() or { @@ -62,7 +62,7 @@ fn test_generic_t_is2() { assert false GenericTIsTest{} } - assert res == 'It\'s a string!' + assert res == "It's a string!" assert res2 == GenericTIsTest{} } @@ -123,4 +123,3 @@ fn test_generic_t_is_with_else_if() { x := generic_t_is_with_else_if() assert x == ['name', 'age'] } -