From 36eae1c1758ed4270429c5f354d8107f5813adce Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 10 Aug 2020 19:05:26 +0300 Subject: [PATCH] builtin: x.vstring() instead of string(x) (#6102) --- vlib/builtin/string.v | 26 ++++++++++++++++--- vlib/builtin/string_test.v | 6 ++--- vlib/clipboard/clipboard_darwin.c.v | 2 +- vlib/clipboard/clipboard_linux.c.v | 2 +- vlib/darwin/darwin.v | 2 +- vlib/mysql/mysql.v | 8 +++--- vlib/mysql/result.v | 14 +++++----- vlib/mysql/utils.v | 11 ++++++-- vlib/net/ftp/ftp.v | 4 +-- vlib/net/http/cookie.v | 4 +-- vlib/net/urllib/urllib.v | 8 +++--- vlib/net/websocket/utils.v | 6 +---- vlib/net/websocket/ws.v | 8 +++--- vlib/os/os.v | 18 ++++++------- vlib/os/os_nix.c.v | 6 +++-- vlib/pg/pg.v | 11 ++++---- vlib/rand/rand.v | 6 ++--- vlib/strings/builder.js.v | 2 +- vlib/strings/builder.v | 2 +- vlib/strings/strings.c.v | 4 +-- vlib/strings/strings.js.v | 5 ++-- vlib/v/ast/ast.v | 9 +++++++ vlib/v/checker/checker.v | 10 +++++++ .../tests/warnings_for_string_c2v_calls.out | 14 ++++++++++ .../tests/warnings_for_string_c2v_calls.vv | 12 +++++++++ vlib/v/fmt/tests/to_string_2_forms_keep.vv | 4 +-- vlib/v/parser/pratt.v | 5 +++- vlib/v/scanner/scanner.v | 2 +- vlib/v/tests/cstrings_test.v | 2 +- vlib/vweb/vweb.v | 2 +- 30 files changed, 143 insertions(+), 72 deletions(-) create mode 100644 vlib/v/checker/tests/warnings_for_string_c2v_calls.out create mode 100644 vlib/v/checker/tests/warnings_for_string_c2v_calls.vv diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index ec96bb673f..3edb59b754 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -120,6 +120,23 @@ pub fn tos_lit(s charptr) string { } } +// byteptr.vstring() - converts a C style string to a V string. NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp byteptr) vstring() string { + return string{ + str: bp + len: unsafe {C.strlen(bp)} + } +} + +// byteptr.vstring_with_len() - converts a C style string to a V string. NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp byteptr) vstring_with_len(len int) string { + return string{ + str: bp + len: len + } +} // string.clone_static returns an independent copy of a given array // It should be used only in -autofree generated code. @@ -1429,9 +1446,10 @@ pub fn (s string) repeat(count int) string { } } unsafe { - ret[s.len * count] = 0 + new_len := s.len * count + ret[new_len] = 0 + return ret.vstring_with_len(new_len) } - return string(ret) } pub fn (s string) fields() []string { @@ -1463,7 +1481,7 @@ pub fn (s string) filter(func fn(b byte) bool) string { } unsafe { buf[new_len] = 0 - return string(buf, new_len) + return buf.vstring_with_len(new_len) } } @@ -1524,6 +1542,6 @@ pub fn (s string) strip_margin_custom(del byte) string { } unsafe { ret[count] = 0 + return ret.vstring_with_len(count) } - return string(ret) } diff --git a/vlib/builtin/string_test.v b/vlib/builtin/string_test.v index 54eca86132..552a689b80 100644 --- a/vlib/builtin/string_test.v +++ b/vlib/builtin/string_test.v @@ -502,10 +502,10 @@ fn test_bytes_to_string() { buf[3] = `l` buf[4] = `o` } - assert string(buf) == 'hello' - assert string(buf, 2) == 'he' + assert unsafe { buf.vstring() } == 'hello' + assert unsafe { buf.vstring_with_len(2) } == 'he' bytes := [`h`, `e`, `l`, `l`, `o`] - assert string(bytes, 5) == 'hello' + assert bytes.bytestr() == 'hello' } fn test_count() { diff --git a/vlib/clipboard/clipboard_darwin.c.v b/vlib/clipboard/clipboard_darwin.c.v index e4b383ea3a..80aaa3a58a 100644 --- a/vlib/clipboard/clipboard_darwin.c.v +++ b/vlib/clipboard/clipboard_darwin.c.v @@ -70,7 +70,7 @@ fn (mut cb Clipboard) get_text() string { #} #utf8_clip = [ns_clip UTF8String]; - return string(utf8_clip) + return unsafe { utf8_clip.vstring() } } pub fn new_primary() &Clipboard { diff --git a/vlib/clipboard/clipboard_linux.c.v b/vlib/clipboard/clipboard_linux.c.v index 8a0471803c..2207a7989f 100644 --- a/vlib/clipboard/clipboard_linux.c.v +++ b/vlib/clipboard/clipboard_linux.c.v @@ -309,7 +309,7 @@ fn (mut cb Clipboard) start_listener(){ C.XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property) if cb.is_supported_target(prop.actual_type) { cb.got_text = true - cb.text = string(prop.data) //TODO: return byteptr to support other mimetypes + cb.text = byteptr(prop.data).vstring() //TODO: return byteptr to support other mimetypes } cb.mutex.unlock() } diff --git a/vlib/darwin/darwin.v b/vlib/darwin/darwin.v index 32c75db2d5..3bf2d0fc8b 100644 --- a/vlib/darwin/darwin.v +++ b/vlib/darwin/darwin.v @@ -44,7 +44,7 @@ pub fn resource_path() string { if conv_result == 0 { panic('CFURLGetFileSystemRepresentation failed') } - result := string(buffer) + result := unsafe { buffer.vstring() } C.CFRelease(resource_dir_url) return result } diff --git a/vlib/mysql/mysql.v b/vlib/mysql/mysql.v index 2ea913f584..8d40069465 100644 --- a/vlib/mysql/mysql.v +++ b/vlib/mysql/mysql.v @@ -116,7 +116,7 @@ pub fn (conn Connection) escape_string(s string) string { quote := byte(39) // single quote C.mysql_real_escape_string_quote(conn.conn, to, s.str, len, quote) - return string(to) + return unsafe { to.vstring() } } // set_option is used to set extra connect options and affect behavior for a connection. @@ -175,12 +175,12 @@ pub fn (conn Connection) info() string { // get_host_info returns a string describing the connection. pub fn (conn Connection) get_host_info() string { - return string(C.mysql_get_host_info(conn.conn)) + return unsafe { C.mysql_get_host_info(conn.conn).vstring() } } // get_server_info returns the server version number as a string. pub fn (conn Connection) get_server_info() string { - return string(C.mysql_get_server_info(conn.conn)) + return unsafe { C.mysql_get_server_info(conn.conn).vstring() } } // get_server_version returns the server version number as an integer. @@ -192,7 +192,7 @@ pub fn (conn Connection) get_server_version() u64 { // get_client_info returns client version information as a string. pub fn get_client_info() string { - return string(C.mysql_get_client_info()) + return unsafe { C.mysql_get_client_info().vstring() } } // get_client_version returns client version information as an integer. diff --git a/vlib/mysql/result.v b/vlib/mysql/result.v index 1cdf0643c5..4ddeeaac23 100644 --- a/vlib/mysql/result.v +++ b/vlib/mysql/result.v @@ -34,7 +34,7 @@ pub fn (r Result) rows() []Row { if rr[i] == 0 { row.vals << '' } else { - row.vals << string(&byte(rr[i])) + row.vals << mystring( byteptr(rr[i]) ) } } rows << row @@ -64,12 +64,12 @@ pub fn (r Result) fields() []Field { orig_fields := C.mysql_fetch_fields(r.result) for i in 0..nr_cols { fields << Field{ - name: string(orig_fields[i].name) - org_name: string(orig_fields[i].org_name) - table: string(orig_fields[i].table) - org_table: string(orig_fields[i].org_table) - db: string(orig_fields[i].db) - catalog: string(orig_fields[i].catalog) + name: mystring(orig_fields[i].name) + org_name: mystring(orig_fields[i].org_name) + table: mystring(orig_fields[i].table) + org_table: mystring(orig_fields[i].org_table) + db: mystring(orig_fields[i].db) + catalog: mystring(orig_fields[i].catalog) def: resolve_nil_str(orig_fields[i].def) length: orig_fields.length max_length: orig_fields.max_length diff --git a/vlib/mysql/utils.v b/vlib/mysql/utils.v index 647c2c9718..eee5d91e4e 100644 --- a/vlib/mysql/utils.v +++ b/vlib/mysql/utils.v @@ -2,7 +2,7 @@ module mysql // get_error_msg returns error message from MySQL instance. fn get_error_msg(conn &C.MYSQL) string { - return string(C.mysql_error(conn)) + return unsafe { C.mysql_error(conn).vstring() } } // get_errno returns error number from MySQL instance. @@ -13,5 +13,12 @@ fn get_errno(conn &C.MYSQL) int { // resolve_nil_str returns empty string if passed value is a nil pointer. fn resolve_nil_str(ptr byteptr) string { if isnil(ptr) { return '' } - return string(ptr) + return unsafe { ptr.vstring() } +} + +[inline] +fn mystring(b byteptr) string { + unsafe { + return b.vstring() + } } diff --git a/vlib/net/ftp/ftp.v b/vlib/net/ftp/ftp.v index 51c9ea0fe0..c90d1e84b5 100644 --- a/vlib/net/ftp/ftp.v +++ b/vlib/net/ftp/ftp.v @@ -156,7 +156,7 @@ pub fn (ftp FTP) pwd() string { return '' } _, data := ftp.read() - spl := data.split('"') + spl := data.split('"') // " if spl.len >= 2 { return spl[1] } @@ -236,7 +236,7 @@ pub fn (ftp FTP) dir() ?[]string { } dtp.close() mut dir := []string{} - sdir := string(byteptr(list_dir.data)) + sdir := list_dir.bytestr() for lfile in sdir.split('\n') { if lfile.len > 1 { spl := lfile.split(' ') diff --git a/vlib/net/http/cookie.v b/vlib/net/http/cookie.v index 1dde3cbc35..2fac123059 100644 --- a/vlib/net/http/cookie.v +++ b/vlib/net/http/cookie.v @@ -275,7 +275,7 @@ fn sanitize(valid fn(byte) bool, v string) string { break } if ok { - return v + return v.clone() } // TODO: Use `filter` instead of this nonesense buf := v.bytes() @@ -285,7 +285,7 @@ fn sanitize(valid fn(byte) bool, v string) string { bytes.delete(i) } } - return string(bytes) + return bytes.bytestr() } fn sanitize_cookie_name(name string) string { diff --git a/vlib/net/urllib/urllib.v b/vlib/net/urllib/urllib.v index 05dd1cb825..26e61f423a 100644 --- a/vlib/net/urllib/urllib.v +++ b/vlib/net/urllib/urllib.v @@ -272,7 +272,7 @@ fn escape(s string, mode EncodingMode) string { t[i] = `+` } } - return string(t,t.len) + return t.bytestr() } upperhex := '0123456789ABCDEF' mut j := 0 @@ -293,7 +293,7 @@ fn escape(s string, mode EncodingMode) string { j++ } } - return string(t,t.len) + return t.bytestr() } // A URL represents a parsed URL (technically, a URI reference). @@ -367,7 +367,7 @@ fn (u &Userinfo) empty() bool { // string returns the encoded userinfo information in the standard form // of 'username[:password]'. -fn (u &Userinfo) string() string { +fn (u &Userinfo) str() string { if u.empty() { return '' } @@ -771,7 +771,7 @@ pub fn (u URL) str() string { buf.write('//') } if !u.user.empty() { - buf.write(u.user.string()) + buf.write(u.user.str()) buf.write('@') } if u.host != '' { diff --git a/vlib/net/websocket/utils.v b/vlib/net/websocket/utils.v index 7610cc15eb..9fde9bfc19 100644 --- a/vlib/net/websocket/utils.v +++ b/vlib/net/websocket/utils.v @@ -32,12 +32,8 @@ fn create_key_challenge_response(seckey string) string { guid := '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' sha1buf := seckey + guid hash := sha1.sum(sha1buf.bytes()) - hashstr := string(byteptr(hash.data)) + hashstr := hash.bytestr() b64 := base64.encode(hashstr) - unsafe { - sha1buf.free() - hash.free() - } return b64 } diff --git a/vlib/net/websocket/ws.v b/vlib/net/websocket/ws.v index 6d47d15c2b..74c327e715 100644 --- a/vlib/net/websocket/ws.v +++ b/vlib/net/websocket/ws.v @@ -294,7 +294,7 @@ pub fn (mut ws Client) write(payload byteptr, payload_len int, code OPCode) int } bytes_written = ws.write_to_server(fbdata, frame_len) if bytes_written == -1 { - err := string(byteptr(C.strerror(C.errno))) + err := unsafe { byteptr(C.strerror(C.errno)).vstring() } ws.log.error('write: there was an error writing data: $err') ws.send_error_event('Error writing data') goto free_data @@ -344,7 +344,7 @@ pub fn (mut ws Client) read() int { return -1 } -1 { - err := string(byteptr(C.strerror(C.errno))) + err := unsafe { byteptr(C.strerror(C.errno)).vstring() } ws.log.error('read: error reading frame. $err') ws.send_error_event('error reading frame') goto free_data @@ -564,7 +564,7 @@ pub fn (mut ws Client) read() int { code = (int(unsafe {data[header_len]}) << 8) + int(unsafe {data[header_len + 1]}) header_len += 2 payload_len -= 2 - reason = unsafe {string(&data[header_len])} + reason = unsafe { byteptr(&data[header_len]).vstring() } ws.log.info('Closing with reason: $reason & code: $code') if reason.len > 1 && !utf8.validate(reason.str, reason.len) { ws.log.error('malformed utf8 payload') @@ -654,4 +654,4 @@ fn (mut ws Client) send_control_frame(code OPCode, frame_typ string, payload []b return bytes_written } } -} \ No newline at end of file +} diff --git a/vlib/os/os.v b/vlib/os/os.v index e28e5822d4..ad4a4551c5 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -39,7 +39,7 @@ pub fn read_file(path string) ?string { mut str := malloc(fsize + 1) C.fread(str, fsize, 1, fp) str[fsize] = 0 - return string(str,fsize) + return str.vstring_with_len(fsize) } } @@ -673,7 +673,7 @@ pub fn get_raw_line() string { } offset++ } - return string(buf, offset) + return buf.vstring_with_len(offset) } } $else { max := size_t(0) @@ -858,7 +858,7 @@ pub fn executable() string { eprintln('os.executable() failed at reading /proc/self/exe to get exe path') return executable_fallback() } - return string(result) + return unsafe { result.vstring() } } $if windows { max := 512 @@ -896,7 +896,7 @@ pub fn executable() string { eprintln('os.executable() failed at calling proc_pidpath with pid: $pid . proc_pidpath returned $ret ') return executable_fallback() } - return string(result) + return unsafe { result.vstring() } } $if freebsd { mut result := vcalloc(max_path_len) @@ -905,7 +905,7 @@ pub fn executable() string { unsafe { C.sysctl(mib.data, 4, result, &size, 0, 0) } - return string(result) + return unsafe { result.vstring() } } // "Sadly there is no way to get the full path of the executed file in OpenBSD." $if openbsd {} @@ -918,7 +918,7 @@ pub fn executable() string { eprintln('os.executable() failed at reading /proc/curproc/exe to get exe path') return executable_fallback() } - return string(result,count) + return result.vstring_with_len(count) } $if dragonfly { mut result := vcalloc(max_path_len) @@ -927,7 +927,7 @@ pub fn executable() string { eprintln('os.executable() failed at reading /proc/curproc/file to get exe path') return executable_fallback() } - return string(result,count) + return unsafe { result.vstring_with_len(count) } } return executable_fallback() } @@ -1056,7 +1056,7 @@ pub fn getwd() string { if C.getcwd(charptr(buf), 512) == 0 { return '' } - return string(buf) + return unsafe { buf.vstring() } } } @@ -1079,7 +1079,7 @@ pub fn real_path(fpath string) string { return fpath } } - return string(fullpath) + return unsafe { fullpath.vstring() } } // is_abs_path returns true if `path` is absolute. diff --git a/vlib/os/os_nix.c.v b/vlib/os/os_nix.c.v index 0766d2af2f..aad83582ae 100644 --- a/vlib/os/os_nix.c.v +++ b/vlib/os/os_nix.c.v @@ -51,8 +51,10 @@ fn init_os_args(argc int, argv &&byte) []string { // mut args := []string(make(0, argc, sizeof(string))) // mut args := []string{len:argc} for i in 0 .. argc { - // args [i] = string(argv[i]) - args << unsafe {string(argv[i])} + // args [i] = argv[i].vstring() + unsafe { + args << byteptr(argv[i]).vstring() + } } return args } diff --git a/vlib/pg/pg.v b/vlib/pg/pg.v index 4a52926e09..4631e15b84 100644 --- a/vlib/pg/pg.v +++ b/vlib/pg/pg.v @@ -45,7 +45,7 @@ pub fn connect(config Config) ?DB { println("status=$status") if status != C.CONNECTION_OK { error_msg := C.PQerrorMessage(conn) - return error ('Connection to a PG database failed: ' + string(error_msg)) + return error ('Connection to a PG database failed: ' + unsafe { error_msg.vstring() } ) } return DB {conn: conn} } @@ -58,7 +58,8 @@ fn res_to_rows(res voidptr) []Row { mut row := Row{} for j in 0..nr_cols { val := C.PQgetvalue(res, i, j) - row.vals << string(val) + sval := unsafe { val.vstring() } + row.vals << sval } rows << row } @@ -100,7 +101,7 @@ pub fn (db DB) q_strings(query string) []Row { pub fn (db DB) exec(query string) []Row { res := C.PQexec(db.conn, query.str) - e := string(C.PQerrorMessage(db.conn)) + e := unsafe { C.PQerrorMessage(db.conn).vstring() } if e != '' { println('pg exec error:') println(e) @@ -118,7 +119,7 @@ fn rows_first_or_empty(rows []Row) ?Row { pub fn (db DB) exec_one(query string) ?Row { res := C.PQexec(db.conn, query.str) - e := string(C.PQerrorMessage(db.conn)) + e := unsafe { C.PQerrorMessage(db.conn).vstring() } if e != '' { return error('pg exec error: "$e"') } @@ -156,7 +157,7 @@ pub fn (db DB) exec_param(query string, param string) []Row { } fn (db DB) handle_error_or_result(res voidptr, elabel string) []Row { - e := string(C.PQerrorMessage(db.conn)) + e := unsafe { C.PQerrorMessage(db.conn).vstring() } if e != '' { println('pg $elabel error:') println(e) diff --git a/vlib/rand/rand.v b/vlib/rand/rand.v index bed2113031..ad0f1ddf45 100644 --- a/vlib/rand/rand.v +++ b/vlib/rand/rand.v @@ -141,7 +141,7 @@ pub fn string(len int) string { buf[i] = chars[intn(chars.len)] } } - return string(buf, len) + return unsafe { buf.vstring_with_len(len) } } // rand.uuid_v4 generate a completely random UUID (v4) @@ -181,7 +181,7 @@ pub fn uuid_v4() string { buf[14] = `4` buf[buflen] = 0 } - return string(buf, buflen) + return unsafe { buf.vstring_with_len(buflen) } } const( @@ -232,5 +232,5 @@ pub fn ulid_at_millisecond(unix_time_milli u64) string { unsafe{ buf[26] = 0 } - return string(buf, buflen) + return unsafe { buf.vstring_with_len(buflen) } } diff --git a/vlib/strings/builder.js.v b/vlib/strings/builder.js.v index d66483ff80..7bef516e71 100644 --- a/vlib/strings/builder.js.v +++ b/vlib/strings/builder.js.v @@ -38,7 +38,7 @@ pub fn (mut b Builder) writeln(s string) { } pub fn (b Builder) str() string { - return string(b.buf, b.len) + return unsafe { byteptr(b.buf.data).vstring_with_len(b.len) } } pub fn (mut b Builder) cut(n int) { diff --git a/vlib/strings/builder.v b/vlib/strings/builder.v index a70d30bb53..c34543c1f2 100644 --- a/vlib/strings/builder.v +++ b/vlib/strings/builder.v @@ -116,7 +116,7 @@ pub fn (mut b Builder) str() string { 'If you want to reuse a builder, call b.free() first.') } b.buf << `\0` - s := string(b.buf,b.len) + s := tos(b.buf.data, b.len) bis := b.initial_size //free(b.buf.data) b.buf = []byte{cap: bis} diff --git a/vlib/strings/strings.c.v b/vlib/strings/strings.c.v index 261d05238b..8e387e2fd6 100644 --- a/vlib/strings/strings.c.v +++ b/vlib/strings/strings.c.v @@ -10,7 +10,7 @@ pub fn repeat(c byte, n int) string { C.memset( bytes, c, n ) bytes[n] = `0` } - return string( bytes, n ) + return unsafe { bytes.vstring_with_len(n) } } // strings.repeat_string - gives you `n` repetitions of the substring `s` @@ -34,5 +34,5 @@ pub fn repeat_string(s string, n int) string { unsafe { bytes[blen] = `0` } - return string( bytes, blen ) + return unsafe { bytes.vstring_with_len(blen) } } diff --git a/vlib/strings/strings.js.v b/vlib/strings/strings.js.v index 72f66576b4..a52b47499b 100644 --- a/vlib/strings/strings.js.v +++ b/vlib/strings/strings.js.v @@ -4,9 +4,8 @@ pub fn repeat(c byte, n int) string { if n <= 0 { return '' } - mut arr := [c].repeat(n + 1) - arr[n] = `\0` - return string(arr,n) + arr := [c].repeat(n) + return arr.bytestr() } pub fn repeat_string(s string, n int) string { diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 2327cbc75a..07827215e8 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -769,6 +769,14 @@ pub: has_low bool } +// NB: &string(x) gets parsed as ast.PrefixExpr{ right: ast.CastExpr{...} } +// TODO: that is very likely a parsing bug. It should get parsed as just +// ast.CastExpr{...}, where .typname is '&string' instead. +// The current situation leads to special cases in vfmt and cgen +// (see prefix_expr_cast_expr in fmt.v, and .is_amp in cgen.v) +// .in_prexpr is also needed because of that, because the checker needs to +// show warnings about the deprecated C->V conversions `string(x)` and +// `string(x,y)`, while skipping the real pointer casts like `&string(x)`. pub struct CastExpr { pub: expr Expr // `buf` in `string(buf, n)` @@ -779,6 +787,7 @@ pub mut: typname string expr_type table.Type // `byteptr` has_arg bool + in_prexpr bool // is the parent node an ast.PrefixExpr } pub struct AssertStmt { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9f6fb29fe0..69ee63ca10 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2265,6 +2265,16 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { node.expr_type = c.expr(node.expr) from_type_sym := c.table.get_type_symbol(node.expr_type) to_type_sym := c.table.get_type_symbol(node.typ) + expr_is_ptr := node.expr_type.is_ptr() || node.expr_type.idx() in table.pointer_type_idxs + if expr_is_ptr && to_type_sym.kind == .string && !node.in_prexpr { + if node.has_arg { + c.warn('to convert a C string buffer pointer to a V string, please use x.vstring_with_len(len) instead of string(x,len)', + node.pos) + } else { + c.warn('to convert a C string buffer pointer to a V string, please use x.vstring() instead of string(x)', + node.pos) + } + } if node.expr_type == table.byte_type && to_type_sym.kind == .string { c.error('can not cast type `byte` to string, use `${node.expr.str()}.str()` instead.', node.pos) diff --git a/vlib/v/checker/tests/warnings_for_string_c2v_calls.out b/vlib/v/checker/tests/warnings_for_string_c2v_calls.out new file mode 100644 index 0000000000..e91fc26939 --- /dev/null +++ b/vlib/v/checker/tests/warnings_for_string_c2v_calls.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/warnings_for_string_c2v_calls.v:8:14: error: to convert a C string buffer pointer to a V string, please use x.vstring() instead of string(x) + 6 | p[2] = `z` + 7 | } + 8 | x := string(p) + | ^ + 9 | y := string(p, 10) + 10 | eprintln('x: $x | y: $y') +vlib/v/checker/tests/warnings_for_string_c2v_calls.v:9:14: error: to convert a C string buffer pointer to a V string, please use x.vstring_with_len(len) instead of string(x,len) + 7 | } + 8 | x := string(p) + 9 | y := string(p, 10) + | ^ + 10 | eprintln('x: $x | y: $y') + 11 | eprintln('x.len: $x.len | y.len: $y.len') diff --git a/vlib/v/checker/tests/warnings_for_string_c2v_calls.vv b/vlib/v/checker/tests/warnings_for_string_c2v_calls.vv new file mode 100644 index 0000000000..e85313803c --- /dev/null +++ b/vlib/v/checker/tests/warnings_for_string_c2v_calls.vv @@ -0,0 +1,12 @@ +fn main() { + mut p := vcalloc(20) + unsafe { + p[0] = `A` + p[1] = `B` + p[2] = `z` + } + x := string(p) + y := string(p, 10) + eprintln('x: $x | y: $y') + eprintln('x.len: $x.len | y.len: $y.len') +} diff --git a/vlib/v/fmt/tests/to_string_2_forms_keep.vv b/vlib/v/fmt/tests/to_string_2_forms_keep.vv index 6a0cbfddde..adb49bd3b0 100644 --- a/vlib/v/fmt/tests/to_string_2_forms_keep.vv +++ b/vlib/v/fmt/tests/to_string_2_forms_keep.vv @@ -5,7 +5,7 @@ fn abc() string { fullpath[1] = `b` fullpath[2] = `c` fullpath[3] = 0 - return string(fullpath) + return fullpath.vstring() } return '' } @@ -17,7 +17,7 @@ fn def() string { fullpath[1] = `b` fullpath[2] = `c` fullpath[3] = 0 - return string(fullpath, 3) + return fullpath.vstring_with_len(3) } return '' } diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 6a9084cabe..33c580a166 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -309,8 +309,11 @@ fn (mut p Parser) prefix_expr() ast.PrefixExpr { // p.warn('unsafe') // } p.next() - right := if op == .minus { p.expr(token.Precedence.call) } else { p.expr(token.Precedence.prefix) } + mut right := if op == .minus { p.expr(token.Precedence.call) } else { p.expr(token.Precedence.prefix) } p.is_amp = false + if mut right is ast.CastExpr { + right.in_prexpr = true + } return ast.PrefixExpr{ op: op right: right diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index d8e92b24d2..fb5d7212ee 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -323,7 +323,7 @@ fn filter_num_sep(txt byteptr, start, end int) string { } } b[i1] = 0 // C string compatibility - return string(b) + return b.vstring_with_len(i1) } } diff --git a/vlib/v/tests/cstrings_test.v b/vlib/v/tests/cstrings_test.v index 6ebe0c4dc5..f2742a4da2 100644 --- a/vlib/v/tests/cstrings_test.v +++ b/vlib/v/tests/cstrings_test.v @@ -10,7 +10,7 @@ fn test_cstring() { fn test_cstring_with_zeros() { rawbytes := c'\x00username\x00password' - s := string(rawbytes, 18) + s := unsafe { rawbytes.vstring_with_len(18) } h := s.bytes().hex() assert h == '00757365726e616d650070617373776f7264' } diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 805f38dd0e..e70c1cf816 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -583,7 +583,7 @@ fn readall(conn net.Socket) string { for { n := C.recv(conn.sockfd, buf, 1024, 0) m := conn.crecv(buf, 1024) - message += string( byteptr(buf), m ) + message += unsafe { byteptr(buf).vstring_with_len(m) } if message.len > max_http_post_size { break } if n == m { break } }