From 9d0a5942acc0f75c500efb5d9cff98ebeb2630b4 Mon Sep 17 00:00:00 2001 From: Tim Basel Date: Fri, 11 Feb 2022 14:52:33 +0100 Subject: [PATCH] builtin: change IError `msg` and `code` to methods + fix vlib, add a deprecation notice for the old usages (#13041) --- vlib/builtin/js/builtin.v | 76 +++++++-- vlib/builtin/option.c.v | 1 - vlib/builtin/option.v | 83 +++++++--- vlib/crypto/bcrypt/bcrypt_test.v | 2 +- vlib/crypto/rand/rand.v | 7 +- vlib/flag/flag.v | 46 +++--- vlib/flag/flag_test.v | 10 +- vlib/io/util/util.v | 4 +- vlib/json/json_decode_test.v | 2 +- vlib/json/json_test.v | 4 +- vlib/mysql/mysql.v | 3 +- vlib/net/errors.v | 2 +- vlib/net/http/header.v | 12 +- vlib/net/http/request.v | 17 +- vlib/net/http/server.v | 2 +- vlib/net/tcp.v | 8 +- vlib/net/urllib/urllib.v | 10 +- vlib/net/websocket/websocket_server.v | 2 +- vlib/os/file.c.v | 22 ++- vlib/os/file_test.v | 4 +- vlib/os/os.v | 17 +- vlib/os/os_test.v | 4 +- vlib/os/signal_test.v | 2 +- vlib/semver/range.v | 8 +- vlib/semver/semver.v | 17 +- vlib/sqlite/sqlite.v | 3 +- vlib/sync/channel_opt_propagate_test.v | 2 +- vlib/time/parse.v | 7 +- vlib/toml/checker/checker.v | 4 +- vlib/toml/input/input.v | 2 +- vlib/toml/scanner/scanner.v | 2 +- .../tests/alexcrichton.toml-rs-tests_test.v | 6 +- vlib/toml/tests/burntsushi.toml-test_test.v | 6 +- vlib/toml/tests/iarna.toml-spec-tests_test.v | 8 +- vlib/toml/tests/toml_bom_test.v | 4 +- vlib/v/ast/ast.v | 2 +- vlib/v/builder/builder.v | 2 +- vlib/v/builder/cbuilder/cbuilder.v | 5 +- vlib/v/builder/compile.v | 2 +- vlib/v/builder/rebuilding.v | 2 +- vlib/v/checker/assign.v | 2 +- vlib/v/checker/checker.v | 76 +++++---- vlib/v/checker/comptime.v | 8 +- vlib/v/checker/containers.v | 4 +- vlib/v/checker/fn.v | 16 +- vlib/v/checker/if.v | 2 +- vlib/v/checker/match.v | 2 +- vlib/v/checker/struct.v | 4 +- vlib/v/doc/doc_private_fn_test.v | 6 +- vlib/v/fmt/fmt_keep_test.v | 6 +- vlib/v/fmt/tests/match_keep.vv | 6 +- vlib/v/gen/c/assert.v | 3 +- vlib/v/gen/c/cgen.v | 15 +- vlib/v/gen/c/cmain.v | 10 +- vlib/v/gen/c/comptime.v | 2 +- vlib/v/gen/js/comptime.v | 2 +- vlib/v/gen/js/js.v | 3 +- vlib/v/parser/assign.v | 2 +- vlib/v/parser/parse_type.v | 1 + vlib/v/parser/sql.v | 2 +- vlib/v/pkgconfig/pkgconfig_test.v | 4 +- vlib/v/scanner/scanner.v | 2 +- vlib/v/tests/closure_generator_test.v | 2 +- vlib/v/tests/fn_multiple_returns_test.v | 4 +- vlib/v/tests/if_expr_of_optional_test.v | 2 +- vlib/v/tests/inout/os.vv | 2 +- vlib/v/tests/option_2_test.v | 2 +- vlib/v/tests/option_test.v | 16 +- vlib/v/tests/option_void_test.v | 8 +- vlib/v/tests/prod_test.v | 2 +- vlib/v/tests/repl/repl_test.v | 2 +- vlib/v/tests/repl/runner/runner.v | 2 +- vlib/v/tests/struct_test.v | 2 +- vlib/v/tests/vmod_parser_test.v | 2 +- vlib/v/transformer/transformer.v | 145 ++++++++++-------- vlib/vweb/parse.v | 2 +- vlib/vweb/tests/vweb_test.v | 12 +- vlib/vweb/vweb.v | 4 +- vlib/x/json2/decoder.v | 6 +- vlib/x/json2/decoder_test.v | 2 +- 80 files changed, 493 insertions(+), 324 deletions(-) diff --git a/vlib/builtin/js/builtin.v b/vlib/builtin/js/builtin.v index 843c515d9..a9e7e2602 100644 --- a/vlib/builtin/js/builtin.v +++ b/vlib/builtin/js/builtin.v @@ -14,41 +14,85 @@ pub fn panic(s string) { // IError holds information about an error instance pub interface IError { + // >> Hack to allow old style custom error implementations + // TODO: remove once deprecation period for `IError` methods has ended msg string - code int + code int // << + msg() string + code() int } -// Error is the default implementation of IError, that is returned by e.g. `error()` +pub fn (err IError) str() string { + return match err { + None__ { + 'none' + } + Error { + err.msg() + } + MessageError { + err.msg() + } + else { + // >> Hack to allow old style custom error implementations + // TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included) + if !isnil(err.msg) { + '$err.type_name(): $err.msg' + } else { + // << + '$err.type_name(): $err.msg()' + } + } + } +} + +// Error is the empty default implementation of `IError`. pub struct Error { -pub: + // >> Hack to allow old style custom error implementations + // TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included) msg string code int + /// << } -struct None__ { +pub fn (err Error) msg() string { + return '' +} + +pub fn (err Error) code() int { + return 0 +} + +// MessageError is the default implementation of the `IError` interface that is returned by the `error()` function +struct MessageError { +pub: msg string code int } +pub fn (err MessageError) msg() string { + return err.msg +} + +pub fn (err MessageError) code() int { + return err.code +} + +pub const none__ = IError(&None__{}) + +struct None__ { + Error +} + fn (_ None__) str() string { return 'none' } -pub const none__ = IError(None__{'', 0}) - pub struct Option { state byte err IError = none__ } -pub fn (err IError) str() string { - return match err { - None__ { 'none' } - Error { err.msg } - else { 'Error: $err.msg' } - } -} - pub fn (o Option) str() string { if o.state == 0 { return 'Option{ ok }' @@ -69,7 +113,7 @@ fn trace_error(x string) { [inline] pub fn error(message string) IError { // trace_error(message) - return &Error{ + return &MessageError{ msg: message } } @@ -79,7 +123,7 @@ pub fn error(message string) IError { [inline] pub fn error_with_code(message string, code int) IError { // trace_error('$message | code: $code') - return &Error{ + return &MessageError{ msg: message code: code } diff --git a/vlib/builtin/option.c.v b/vlib/builtin/option.c.v index 464cf87df..1e04b2648 100644 --- a/vlib/builtin/option.c.v +++ b/vlib/builtin/option.c.v @@ -8,7 +8,6 @@ struct C.IError { [unsafe] pub fn (ie &IError) free() { unsafe { - ie.msg.free() cie := &C.IError(ie) free(cie._object) } diff --git a/vlib/builtin/option.v b/vlib/builtin/option.v index 4f06c4f5d..d368f3a96 100644 --- a/vlib/builtin/option.v +++ b/vlib/builtin/option.v @@ -5,30 +5,79 @@ module builtin // IError holds information about an error instance pub interface IError { + // >> Hack to allow old style custom error implementations + // TODO: remove once deprecation period for `IError` methods has ended msg string - code int + code int // << + msg() string + code() int +} + +pub fn (err IError) str() string { + return match err { + None__ { + 'none' + } + Error { + err.msg() + } + MessageError { + err.msg() + } + else { + // >> Hack to allow old style custom error implementations + // TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included) + if !isnil(err.msg) { + '$err.type_name(): $err.msg' + } else { + // << + '$err.type_name(): $err.msg()' + } + } + } } -// Error is the default implementation of IError, that is returned by e.g. `error()` +// Error is the empty default implementation of `IError`. pub struct Error { + // >> Hack to allow old style custom error implementations + // TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included) + msg string + code int + /// << +} + +pub fn (err Error) msg() string { + return '' +} + +pub fn (err Error) code() int { + return 0 +} + +// MessageError is the default implementation of the `IError` interface that is returned by the `error()` function +struct MessageError { pub: msg string code int } -pub fn (err IError) str() string { - return match err { - None__ { 'none' } - Error { err.msg } - else { '$err.type_name(): $err.msg' } - } +pub fn (err MessageError) msg() string { + return err.msg +} + +pub fn (err MessageError) code() int { + return err.code +} + +[unsafe] +pub fn (err &MessageError) free() { + unsafe { err.msg.free() } } const none__ = IError(&None__{}) struct None__ { - msg string - code int + Error } fn (_ None__) str() string { @@ -45,7 +94,7 @@ fn trace_error(x string) { [inline] pub fn error(message string) IError { trace_error(message) - return &Error{ + return &MessageError{ msg: message } } @@ -55,7 +104,7 @@ pub fn error(message string) IError { [inline] pub fn error_with_code(message string, code int) IError { trace_error('$message | code: $code') - return &Error{ + return &MessageError{ msg: message code: code } @@ -78,16 +127,6 @@ fn opt_ok(data voidptr, mut option Option, size int) { } } -[unsafe] -pub fn (e &Error) free() { - unsafe { e.msg.free() } -} - -[unsafe] -pub fn (n &None__) free() { - unsafe { n.msg.free() } -} - pub fn (_ none) str() string { return 'none' } diff --git a/vlib/crypto/bcrypt/bcrypt_test.v b/vlib/crypto/bcrypt/bcrypt_test.v index b59a3a9d5..772fe0862 100644 --- a/vlib/crypto/bcrypt/bcrypt_test.v +++ b/vlib/crypto/bcrypt/bcrypt_test.v @@ -6,6 +6,6 @@ fn test_crypto_bcrypt() { bcrypt.compare_hash_and_password('password'.bytes(), hash.bytes()) or { panic(err) } bcrypt.compare_hash_and_password('password2'.bytes(), hash.bytes()) or { - assert err == error('mismatched hash and password') + assert err.msg() == 'mismatched hash and password' } } diff --git a/vlib/crypto/rand/rand.v b/vlib/crypto/rand/rand.v index c60f8915c..b21e96923 100644 --- a/vlib/crypto/rand/rand.v +++ b/vlib/crypto/rand/rand.v @@ -5,6 +5,9 @@ module rand struct ReadError { - msg string = 'crypto.rand.read() error reading random bytes' - code int + Error +} + +pub fn (err ReadError) msg() string { + return 'crypto.rand.read() error reading random bytes' } diff --git a/vlib/flag/flag.v b/vlib/flag/flag.v index 56b7fffc9..d1427ce6e 100644 --- a/vlib/flag/flag.v +++ b/vlib/flag/flag.v @@ -11,23 +11,28 @@ pub: } struct UnkownFlagError { - msg string - code int + Error + flag string } -struct MinimumArgsCountError { - msg string - code int +fn (err UnkownFlagError) msg() string { + return 'Unknown flag `$err.flag`' } -struct MaximumArgsCountError { - msg string - code int +struct ArgsCountError { + Error + got int + want int } -struct NoArgsExpectedError { - msg string - code int +fn (err ArgsCountError) msg() string { + if err.want == 0 { + return 'Expected no arguments, but got $err.got' + } else if err.got > err.want { + return 'Expected at most $err.want arguments, but got $err.got' + } else { + return 'Expected at least $err.want arguments, but got $err.got' + } } // free frees the resources associated with a given Flag @@ -616,24 +621,27 @@ pub fn (mut fs FlagParser) finalize() ?[]string { for a in remaining { if (a.len >= 2 && a[..2] == '--') || (a.len == 2 && a[0] == `-`) { return IError(&UnkownFlagError{ - msg: 'Unknown flag `$a`' + flag: a }) } } } if remaining.len < fs.min_free_args && fs.min_free_args > 0 { - return IError(&MinimumArgsCountError{ - msg: 'Expected at least $fs.min_free_args arguments, but given $remaining.len' + return IError(&ArgsCountError{ + want: fs.min_free_args + got: remaining.len }) } if remaining.len > fs.max_free_args && fs.max_free_args > 0 { - return IError(&MaximumArgsCountError{ - msg: 'Expected at most $fs.max_free_args arguments, but given $remaining.len' + return IError(&ArgsCountError{ + want: fs.max_free_args + got: remaining.len }) } if remaining.len > 0 && fs.max_free_args == 0 && fs.min_free_args == 0 { - return IError(&NoArgsExpectedError{ - msg: 'Expected no arguments, but given $remaining.len' + return IError(&ArgsCountError{ + want: 0 + got: remaining.len }) } remaining << fs.all_after_dashdash @@ -647,7 +655,7 @@ pub fn (mut fs FlagParser) finalize() ?[]string { // you want more control over the error handling. pub fn (mut fs FlagParser) remaining_parameters() []string { return fs.finalize() or { - eprintln(err.msg) + eprintln(err.msg()) println(fs.usage()) exit(1) } diff --git a/vlib/flag/flag_test.v b/vlib/flag/flag_test.v index be52e9788..501293aef 100644 --- a/vlib/flag/flag_test.v +++ b/vlib/flag/flag_test.v @@ -137,7 +137,7 @@ fn test_finalize_returns_error_for_unknown_flags_long() { mut fp := flag.new_flag_parser(['--known', '--unknown']) fp.bool('known', 0, false, '') finalized := fp.finalize() or { - assert err.msg == 'Unknown flag `--unknown`' + assert err.msg() == 'Unknown flag `--unknown`' return } assert finalized.len < 0 // expect error to be returned @@ -147,7 +147,7 @@ fn test_finalize_returns_error_for_unknown_flags_short() { mut fp := flag.new_flag_parser(['--known', '-x']) fp.bool('known', 0, false, '') finalized := fp.finalize() or { - assert err.msg == 'Unknown flag `-x`' + assert err.msg() == 'Unknown flag `-x`' return } assert finalized.len < 0 // expect error to be returned @@ -210,7 +210,7 @@ fn test_error_for_to_few_free_args() ? { mut fp1 := flag.new_flag_parser(['a', 'b', 'c']) fp1.limit_free_args(5, 6) ? args := fp1.finalize() or { - assert err.msg.starts_with('Expected at least 5 arguments') + assert err.msg().starts_with('Expected at least 5 arguments') return } assert args.len < 0 // expect an error and need to use args @@ -220,7 +220,7 @@ fn test_error_for_to_much_free_args() ? { mut fp1 := flag.new_flag_parser(['a', 'b', 'c']) fp1.limit_free_args(1, 2) ? args := fp1.finalize() or { - assert err.msg.starts_with('Expected at most 2 arguments') + assert err.msg().starts_with('Expected at most 2 arguments') return } assert args.len < 0 // expect an error and need to use args @@ -230,7 +230,7 @@ fn test_could_expect_no_free_args() ? { mut fp1 := flag.new_flag_parser(['a']) fp1.limit_free_args(0, 0) ? args := fp1.finalize() or { - assert err.msg.starts_with('Expected no arguments') + assert err.msg().starts_with('Expected no arguments') return } assert args.len < 0 // expect an error and need to use args diff --git a/vlib/io/util/util.v b/vlib/io/util/util.v index c3d2144bf..845579af4 100644 --- a/vlib/io/util/util.v +++ b/vlib/io/util/util.v @@ -24,7 +24,7 @@ pub fn temp_file(tfo TempFileOptions) ?(os.File, string) { ' could not create temporary file in "$d". Please ensure write permissions.') } d = d.trim_right(os.path_separator) - prefix, suffix := prefix_and_suffix(tfo.pattern) or { return error(@FN + ' ' + err.msg) } + prefix, suffix := prefix_and_suffix(tfo.pattern) or { return error(@FN + ' $err.msg()') } for retry := 0; retry < util.retries; retry++ { path := os.join_path(d, prefix + random_number() + suffix) mut mode := 'rw+' @@ -57,7 +57,7 @@ pub fn temp_dir(tdo TempFileOptions) ?string { ' could not create temporary directory "$d". Please ensure write permissions.') } d = d.trim_right(os.path_separator) - prefix, suffix := prefix_and_suffix(tdo.pattern) or { return error(@FN + ' ' + err.msg) } + prefix, suffix := prefix_and_suffix(tdo.pattern) or { return error(@FN + ' $err.msg()') } for retry := 0; retry < util.retries; retry++ { path := os.join_path(d, prefix + random_number() + suffix) os.mkdir_all(path) or { continue } diff --git a/vlib/json/json_decode_test.v b/vlib/json/json_decode_test.v index 8d52fc2b5..56cbcad7e 100644 --- a/vlib/json/json_decode_test.v +++ b/vlib/json/json_decode_test.v @@ -14,7 +14,7 @@ mut: fn test_json_decode_fails_to_decode_unrecognised_array_of_dicts() { data := '[{"twins":[{"id":123,"seed":"abcde","pubkey":"xyzasd"},{"id":456,"seed":"dfgdfgdfgd","pubkey":"skjldskljh45sdf"}]}]' json.decode(TestTwins, data) or { - assert err.msg == "expected field 'twins' is missing" + assert err.msg() == "expected field 'twins' is missing" return } assert false diff --git a/vlib/json/json_test.v b/vlib/json/json_test.v index 8ceaed96e..37c38c168 100644 --- a/vlib/json/json_test.v +++ b/vlib/json/json_test.v @@ -333,7 +333,7 @@ fn test_errors() { data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":{"name":"Donlon"},"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' json.decode(Data, data) or { println(err) - assert err.msg.starts_with('Json element is not an array:') + assert err.msg().starts_with('Json element is not an array:') return } assert false @@ -342,7 +342,7 @@ fn test_errors() { data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":[{"name":"Donlon"},{"name":"Termanches"}],"name":"KU"}],"users":[{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}],"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' json.decode(Data, data) or { println(err) - assert err.msg.starts_with('Json element is not an object:') + assert err.msg().starts_with('Json element is not an object:') return } assert false diff --git a/vlib/mysql/mysql.v b/vlib/mysql/mysql.v index 90e7008e8..a0d1bbe95 100644 --- a/vlib/mysql/mysql.v +++ b/vlib/mysql/mysql.v @@ -20,8 +20,7 @@ pub enum ConnectionFlag { } struct SQLError { - msg string - code int + MessageError } // TODO: Documentation diff --git a/vlib/net/errors.v b/vlib/net/errors.v index 7899eca8e..e050c1245 100644 --- a/vlib/net/errors.v +++ b/vlib/net/errors.v @@ -21,7 +21,7 @@ pub const ( ) pub fn socket_error_message(potential_code int, s string) ?int { - return socket_error(potential_code) or { return error('$err.msg; $s') } + return socket_error(potential_code) or { return error('$err.msg(); $s') } } pub fn socket_error(potential_code int) ?int { diff --git a/vlib/net/http/header.v b/vlib/net/http/header.v index d2c5f2555..7f4a19eac 100644 --- a/vlib/net/http/header.v +++ b/vlib/net/http/header.v @@ -627,18 +627,25 @@ fn (mut h Header) add_key(key string) { // Custom error struct for invalid header tokens struct HeaderKeyError { - msg string + Error code int header string invalid_char byte } +pub fn (err HeaderKeyError) msg() string { + return "Invalid header key: '$err.header'" +} + +pub fn (err HeaderKeyError) code() int { + return err.code +} + // is_valid checks if the header token contains all valid bytes fn is_valid(header string) ? { for _, c in header { if int(c) >= 128 || !is_token(c) { return IError(HeaderKeyError{ - msg: "Invalid header key: '$header'" code: 1 header: header invalid_char: c @@ -647,7 +654,6 @@ fn is_valid(header string) ? { } if header.len == 0 { return IError(HeaderKeyError{ - msg: "Invalid header key: '$header'" code: 2 header: header invalid_char: 0 diff --git a/vlib/net/http/request.v b/vlib/net/http/request.v index 83f7c6837..0373beb42 100644 --- a/vlib/net/http/request.v +++ b/vlib/net/http/request.v @@ -261,15 +261,20 @@ pub: } pub struct UnexpectedExtraAttributeError { -pub: - msg string - code int + Error + attributes []string +} + +pub fn (err UnexpectedExtraAttributeError) msg() string { + return 'Encountered unexpected extra attributes: $err.attributes' } pub struct MultiplePathAttributesError { -pub: - msg string = 'Expected at most one path attribute' - code int + Error +} + +pub fn (err MultiplePathAttributesError) msg() string { + return 'Expected at most one path attribute' } // multipart_form_body converts form and file data into a multipart/form diff --git a/vlib/net/http/server.v b/vlib/net/http/server.v index cf278e30b..6b808d58d 100644 --- a/vlib/net/http/server.v +++ b/vlib/net/http/server.v @@ -48,7 +48,7 @@ pub fn (mut s Server) listen_and_serve() ? { break } mut conn := s.listener.accept() or { - if err.msg != 'net: op timed out' { + if err.msg() != 'net: op timed out' { eprintln('accept() failed: $err; skipping') } continue diff --git a/vlib/net/tcp.v b/vlib/net/tcp.v index 043e84aa0..92305a144 100644 --- a/vlib/net/tcp.v +++ b/vlib/net/tcp.v @@ -21,13 +21,13 @@ mut: pub fn dial_tcp(address string) ?&TcpConn { addrs := resolve_addrs_fuzzy(address, .tcp) or { - return error('$err.msg; could not resolve address $address in dial_tcp') + return error('$err.msg(); could not resolve address $address in dial_tcp') } // Very simple dialer for addr in addrs { mut s := new_tcp_socket(addr.family()) or { - return error('$err.msg; could not create new tcp socket in dial_tcp') + return error('$err.msg(); could not create new tcp socket in dial_tcp') } s.connect(addr) or { // Connection failed @@ -215,10 +215,10 @@ mut: } pub fn listen_tcp(family AddrFamily, saddr string) ?&TcpListener { - s := new_tcp_socket(family) or { return error('$err.msg; could not create new socket') } + s := new_tcp_socket(family) or { return error('$err.msg(); could not create new socket') } addrs := resolve_addrs(saddr, family, .tcp) or { - return error('$err.msg; could not resolve address $saddr') + return error('$err.msg(); could not resolve address $saddr') } // TODO(logic to pick here) diff --git a/vlib/net/urllib/urllib.v b/vlib/net/urllib/urllib.v index e9a53a607..521770372 100644 --- a/vlib/net/urllib/urllib.v +++ b/vlib/net/urllib/urllib.v @@ -410,7 +410,7 @@ fn split_by_scheme(rawurl string) ?[]string { } fn get_scheme(rawurl string) ?string { - split := split_by_scheme(rawurl) or { return err.msg } + split := split_by_scheme(rawurl) or { return err.msg() } return split[0] } @@ -593,9 +593,9 @@ fn parse_host(host string) ?string { // We do impose some restrictions on the zone, to avoid stupidity // like newlines. if zone := host[..i].index('%25') { - host1 := unescape(host[..zone], .encode_host) or { return err.msg } - host2 := unescape(host[zone..i], .encode_zone) or { return err.msg } - host3 := unescape(host[i..], .encode_host) or { return err.msg } + host1 := unescape(host[..zone], .encode_host) or { return err.msg() } + host2 := unescape(host[zone..i], .encode_zone) or { return err.msg() } + host3 := unescape(host[i..], .encode_host) or { return err.msg() } return host1 + host2 + host3 } if idx := host.last_index(':') { @@ -606,7 +606,7 @@ fn parse_host(host string) ?string { } } } - h := unescape(host, .encode_host) or { return err.msg } + h := unescape(host, .encode_host) or { return err.msg() } return h // host = h // return host diff --git a/vlib/net/websocket/websocket_server.v b/vlib/net/websocket/websocket_server.v index 99af3e0eb..f241e4575 100644 --- a/vlib/net/websocket/websocket_server.v +++ b/vlib/net/websocket/websocket_server.v @@ -122,7 +122,7 @@ fn (mut s Server) serve_client(mut c Client) ? { } s.setup_callbacks(mut server_client) c.listen() or { - s.logger.error(err.msg) + s.logger.error(err.msg()) return err } } diff --git a/vlib/os/file.c.v b/vlib/os/file.c.v index b81c72ceb..878cc0ac8 100644 --- a/vlib/os/file.c.v +++ b/vlib/os/file.c.v @@ -478,22 +478,28 @@ pub fn (mut f File) flush() { C.fflush(f.cfile) } -pub struct ErrFileNotOpened { - msg string = 'os: file not opened' - code int +pub struct FileNotOpenedError { + Error } -pub struct ErrSizeOfTypeIs0 { - msg string = 'os: size of type is 0' - code int +pub fn (err FileNotOpenedError) msg() string { + return 'os: file not opened' +} + +pub struct SizeOfTypeIs0Error { + Error +} + +pub fn (err SizeOfTypeIs0Error) msg() string { + return 'os: size of type is 0' } fn error_file_not_opened() IError { - return IError(&ErrFileNotOpened{}) + return IError(&FileNotOpenedError{}) } fn error_size_of_type_0() IError { - return IError(&ErrSizeOfTypeIs0{}) + return IError(&SizeOfTypeIs0Error{}) } // read_struct reads a single struct of type `T` diff --git a/vlib/os/file_test.v b/vlib/os/file_test.v index 3757642f2..793705666 100644 --- a/vlib/os/file_test.v +++ b/vlib/os/file_test.v @@ -273,7 +273,7 @@ fn test_write_raw_at_negative_pos() ? { if _ := f.write_raw_at(another_point, -1) { assert false } - f.write_raw_at(another_point, -234) or { assert err.msg == 'Invalid argument' } + f.write_raw_at(another_point, -234) or { assert err.msg() == 'Invalid argument' } f.close() } @@ -328,7 +328,7 @@ fn test_read_raw_at_negative_pos() ? { if _ := f.read_raw_at(-1) { assert false } - f.read_raw_at(-234) or { assert err.msg == 'Invalid argument' } + f.read_raw_at(-234) or { assert err.msg() == 'Invalid argument' } f.close() } diff --git a/vlib/os/os.v b/vlib/os/os.v index 0d6b67b25..c52ffa0c1 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -150,12 +150,12 @@ pub fn rmdir_all(path string) ? { for item in items { fullpath := join_path_single(path, item) if is_dir(fullpath) && !is_link(fullpath) { - rmdir_all(fullpath) or { ret_err = err.msg } + rmdir_all(fullpath) or { ret_err = err.msg() } } else { - rm(fullpath) or { ret_err = err.msg } + rm(fullpath) or { ret_err = err.msg() } } } - rmdir(path) or { ret_err = err.msg } + rmdir(path) or { ret_err = err.msg() } if ret_err.len > 0 { return error(ret_err) } @@ -404,13 +404,16 @@ fn executable_fallback() string { return exepath } -pub struct ErrExecutableNotFound { - msg string = 'os: failed to find executable' - code int +pub struct ExecutableNotFoundError { + Error +} + +pub fn (err ExecutableNotFoundError) msg() string { + return 'os: failed to find executable' } fn error_failed_to_find_executable() IError { - return IError(&ErrExecutableNotFound{}) + return IError(&ExecutableNotFoundError{}) } // find_exe_path walks the environment PATH, just like most shell do, it returns diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 3d5d906cb..6324e5415 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -35,7 +35,7 @@ fn test_open_file() { filename := './test1.txt' hello := 'hello world!' os.open_file(filename, 'r+', 0o666) or { - assert err.msg == 'No such file or directory' + assert err.msg() == 'No such file or directory' os.File{} } mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) } @@ -51,7 +51,7 @@ fn test_open_file_binary() { filename := './test1.dat' hello := 'hello \n world!' os.open_file(filename, 'r+', 0o666) or { - assert err.msg == 'No such file or directory' + assert err.msg() == 'No such file or directory' os.File{} } mut file := os.open_file(filename, 'wb+', 0o666) or { panic(err) } diff --git a/vlib/os/signal_test.v b/vlib/os/signal_test.v index 1c56540c8..87f5b9b2a 100644 --- a/vlib/os/signal_test.v +++ b/vlib/os/signal_test.v @@ -20,7 +20,7 @@ fn test_signal_opt_invalid_argument() { assert false } os.signal_opt(.kill, default_handler) or { - assert err.msg == 'Invalid argument' + assert err.msg() == 'Invalid argument' assert err.code == 22 } } diff --git a/vlib/semver/range.v b/vlib/semver/range.v index fd0a76999..2f77bb732 100644 --- a/vlib/semver/range.v +++ b/vlib/semver/range.v @@ -29,14 +29,8 @@ struct Range { comparator_sets []ComparatorSet } -struct InvalidComparatorCountError { - msg string - code int -} - struct InvalidComparatorFormatError { - msg string - code int + MessageError } fn (r Range) satisfies(ver Version) bool { diff --git a/vlib/semver/semver.v b/vlib/semver/semver.v index dac89c705..57089b05b 100644 --- a/vlib/semver/semver.v +++ b/vlib/semver/semver.v @@ -20,13 +20,20 @@ pub enum Increment { } struct EmptyInputError { - msg string = 'Empty input' - code int + Error +} + +pub fn (err EmptyInputError) msg() string { + return 'Empty input' } struct InvalidVersionFormatError { - msg string - code int + Error + input string +} + +pub fn (err InvalidVersionFormatError) msg() string { + return 'Invalid version format for input "$err.input"' } // * Constructor. @@ -38,7 +45,7 @@ pub fn from(input string) ?Version { raw_version := parse(input) version := raw_version.validate() or { return IError(&InvalidVersionFormatError{ - msg: 'Invalid version format for input "$input"' + input: input }) } return version diff --git a/vlib/sqlite/sqlite.v b/vlib/sqlite/sqlite.v index 8238dcca6..0f8d590c6 100644 --- a/vlib/sqlite/sqlite.v +++ b/vlib/sqlite/sqlite.v @@ -33,8 +33,7 @@ struct Stmt { } struct SQLError { - msg string - code int + MessageError } // diff --git a/vlib/sync/channel_opt_propagate_test.v b/vlib/sync/channel_opt_propagate_test.v index a7e26fe85..20cb19627 100644 --- a/vlib/sync/channel_opt_propagate_test.v +++ b/vlib/sync/channel_opt_propagate_test.v @@ -14,7 +14,7 @@ fn do_rec_calc_send(chs []chan i64, mut sem sync.Semaphore) { mut msg := '' for { mut s := get_val_from_chan(chs[0]) or { - msg = err.msg + msg = err.msg() break } s++ diff --git a/vlib/time/parse.v b/vlib/time/parse.v index 0a2b5102d..dd1184c21 100644 --- a/vlib/time/parse.v +++ b/vlib/time/parse.v @@ -4,13 +4,16 @@ module time pub struct TimeParseError { - msg string + Error code int } +pub fn (err TimeParseError) msg() string { + return 'Invalid time format code: $err.code' +} + fn error_invalid_time(code int) IError { return TimeParseError{ - msg: 'Invalid time format code: $code' code: code } } diff --git a/vlib/toml/checker/checker.v b/vlib/toml/checker/checker.v index 630e462b5..1bdf5ca1a 100644 --- a/vlib/toml/checker/checker.v +++ b/vlib/toml/checker/checker.v @@ -477,14 +477,14 @@ fn (c Checker) check_quoted_escapes(q ast.Quoted) ? { c.check_unicode_escape(s.text[pos..pos + 11]) or { st := s.state() return error(@MOD + '.' + @STRUCT + '.' + @FN + - ' escaped Unicode is invalid. $err.msg.capitalize() ($st.line_nr,$st.col) in ...${c.excerpt(q.pos)}...') + ' escaped Unicode is invalid. $err.msg().capitalize() ($st.line_nr,$st.col) in ...${c.excerpt(q.pos)}...') } } else { pos := s.state().pos c.check_unicode_escape(s.text[pos..]) or { st := s.state() return error(@MOD + '.' + @STRUCT + '.' + @FN + - ' escaped Unicode is invalid. $err.msg.capitalize() ($st.line_nr,$st.col) in ...${c.excerpt(q.pos)}...') + ' escaped Unicode is invalid. $err.msg().capitalize() ($st.line_nr,$st.col) in ...${c.excerpt(q.pos)}...') } } } diff --git a/vlib/toml/input/input.v b/vlib/toml/input/input.v index cc5439636..c2490c0cc 100644 --- a/vlib/toml/input/input.v +++ b/vlib/toml/input/input.v @@ -47,7 +47,7 @@ pub fn (c Config) read_input() ?string { if os.is_file(c.file_path) { text = os.read_file(c.file_path) or { return error(@MOD + '.' + @STRUCT + '.' + @FN + - ' Could not read "$c.file_path": "$err.msg"') + ' Could not read "$c.file_path": "$err.msg()"') } } return text diff --git a/vlib/toml/scanner/scanner.v b/vlib/toml/scanner/scanner.v index e61e4a4db..0bda8081f 100644 --- a/vlib/toml/scanner/scanner.v +++ b/vlib/toml/scanner/scanner.v @@ -55,7 +55,7 @@ pub fn new_scanner(config Config) ?&Scanner { if os.is_file(file_path) { text = os.read_file(file_path) or { return error(@MOD + '.' + @STRUCT + '.' + @FN + - ' Could not read "$file_path": "$err.msg"') + ' Could not read "$file_path": "$err.msg()"') } } mut s := &Scanner{ diff --git a/vlib/toml/tests/alexcrichton.toml-rs-tests_test.v b/vlib/toml/tests/alexcrichton.toml-rs-tests_test.v index bc708159e..9a8142d2a 100644 --- a/vlib/toml/tests/alexcrichton.toml-rs-tests_test.v +++ b/vlib/toml/tests/alexcrichton.toml-rs-tests_test.v @@ -155,12 +155,12 @@ fn test_alexcrichton_toml_rs() ? { v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or { contents := os.read_file(v_toml_json_path) ? - panic(err.msg + '\n$contents') + panic(err.msg() + '\n$contents') } alexcrichton_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', alexcrichton_toml_json_path]) or { contents := os.read_file(v_toml_json_path) ? - panic(err.msg + '\n$contents') + panic(err.msg() + '\n$contents') } assert alexcrichton_normalized_json == v_normalized_json @@ -199,7 +199,7 @@ fn test_alexcrichton_toml_rs() ? { assert false } else { if !hide_oks { - println(' $err.msg') + println(' $err.msg()') } assert true } diff --git a/vlib/toml/tests/burntsushi.toml-test_test.v b/vlib/toml/tests/burntsushi.toml-test_test.v index 656e161ac..418d6ea47 100644 --- a/vlib/toml/tests/burntsushi.toml-test_test.v +++ b/vlib/toml/tests/burntsushi.toml-test_test.v @@ -141,11 +141,11 @@ fn test_burnt_sushi_tomltest() ? { v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or { contents := os.read_file(v_toml_json_path) ? - panic(err.msg + '\n$contents') + panic(err.msg() + '\n$contents') } bs_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', bs_toml_json_path]) or { contents := os.read_file(v_toml_json_path) ? - panic(err.msg + '\n$contents') + panic(err.msg() + '\n$contents') } assert bs_normalized_json == v_normalized_json @@ -182,7 +182,7 @@ fn test_burnt_sushi_tomltest() ? { assert false } else { if !hide_oks { - println(' $err.msg') + println(' $err.msg()') } assert true } diff --git a/vlib/toml/tests/iarna.toml-spec-tests_test.v b/vlib/toml/tests/iarna.toml-spec-tests_test.v index 31d7d269c..278642f8a 100644 --- a/vlib/toml/tests/iarna.toml-spec-tests_test.v +++ b/vlib/toml/tests/iarna.toml-spec-tests_test.v @@ -181,7 +181,7 @@ fn test_iarna_toml_spec_tests() ? { // NOTE there's known errors with the python convertion method. // For now we just ignore them as it's a broken tool - not a wrong test-case. // Uncomment this print to see/check them. - // eprintln(err.msg + '\n$contents') + // eprintln(err.msg() + '\n$contents') e++ println('ERR [${i + 1}/$valid_test_files.len] "$valid_test_file" EXCEPTION [$e/$valid_value_exceptions.len]...') continue @@ -208,12 +208,12 @@ fn test_iarna_toml_spec_tests() ? { v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or { contents := os.read_file(v_toml_json_path) ? - panic(err.msg + '\n$contents') + panic(err.msg() + '\n$contents') } cmd := [jq, '-S', '-f "$jq_normalize_path"', iarna_toml_json_path] iarna_normalized_json := run(cmd) or { contents := os.read_file(v_toml_json_path) ? - panic(err.msg + '\n$contents\n\ncmd: ${cmd.join(' ')}') + panic(err.msg() + '\n$contents\n\ncmd: ${cmd.join(' ')}') } assert iarna_normalized_json == v_normalized_json @@ -250,7 +250,7 @@ fn test_iarna_toml_spec_tests() ? { assert false } else { if !hide_oks { - println(' $err.msg') + println(' $err.msg()') } assert true } diff --git a/vlib/toml/tests/toml_bom_test.v b/vlib/toml/tests/toml_bom_test.v index 9111bd3bd..fda33f63a 100644 --- a/vlib/toml/tests/toml_bom_test.v +++ b/vlib/toml/tests/toml_bom_test.v @@ -37,13 +37,13 @@ fn test_toml_with_bom() { // Re-cycle bad_toml_doc mut bad_toml_doc := empty_toml_document bad_toml_doc = toml.parse(toml_text_with_utf16_bom) or { - println(' $err.msg') + println(' $err.msg()') assert true empty_toml_document } bad_toml_doc = toml.parse(toml_text_with_utf32_bom) or { - println(' $err.msg') + println(' $err.msg()') assert true empty_toml_document } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 95245f5ca..295e70969 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -183,7 +183,6 @@ pub: pub struct StringInterLiteral { pub: vals []string - exprs []Expr fwidths []int precisions []int pluss []bool @@ -191,6 +190,7 @@ pub: fmt_poss []token.Pos pos token.Pos pub mut: + exprs []Expr expr_types []Type fmts []byte need_fmts []bool // an explicit non-default fmt required, e.g. `x` diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 00f660498..8b88d8cad 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -69,7 +69,7 @@ pub fn new_builder(pref &pref.Preferences) Builder { pref: pref table: table checker: checker.new_checker(table, pref) - transformer: transformer.new_transformer(pref) + transformer: transformer.new_transformer_with_table(table, pref) compiled_dir: compiled_dir cached_msvc: msvc } diff --git a/vlib/v/builder/cbuilder/cbuilder.v b/vlib/v/builder/cbuilder/cbuilder.v index abe694f13..48a551e94 100644 --- a/vlib/v/builder/cbuilder/cbuilder.v +++ b/vlib/v/builder/cbuilder/cbuilder.v @@ -36,11 +36,12 @@ pub fn compile_c(mut b builder.Builder) { pub fn gen_c(mut b builder.Builder, v_files []string) string { b.front_and_middle_stages(v_files) or { - if err.code != 9999 { - builder.verror(err.msg) + if err.code() != 9999 { + builder.verror(err.msg()) } return '' } + util.timing_start('C GEN') res := c.gen(b.parsed_files, b.table, b.pref) util.timing_measure('C GEN') diff --git a/vlib/v/builder/compile.v b/vlib/v/builder/compile.v index 79ebf5497..9f719f7a1 100644 --- a/vlib/v/builder/compile.v +++ b/vlib/v/builder/compile.v @@ -22,7 +22,7 @@ pub fn compile(command string, pref &pref.Preferences, backend_cb FnBackend) { } os.is_writable_folder(output_folder) or { // An early error here, is better than an unclear C error later: - verror(err.msg) + verror(err.msg()) } // Construct the V object from command line arguments mut b := new_builder(pref) diff --git a/vlib/v/builder/rebuilding.v b/vlib/v/builder/rebuilding.v index 80893ae18..40dd90265 100644 --- a/vlib/v/builder/rebuilding.v +++ b/vlib/v/builder/rebuilding.v @@ -181,7 +181,7 @@ fn (mut b Builder) rebuild_cached_module(vexe string, imp_path string) string { } b.v_build_module(vexe, imp_path) rebuilded_o := b.pref.cache_manager.exists('.o', imp_path) or { - panic('could not rebuild cache module for $imp_path, error: $err.msg') + panic('could not rebuild cache module for $imp_path, error: $err.msg()') } return rebuilded_o } diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index ddc7cfaec..5edb95adf 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -535,7 +535,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { node.pos) } } else { - c.error('cannot assign to `$left`: $err.msg', right.pos()) + c.error('cannot assign to `$left`: $err.msg()', right.pos()) } } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index f753880ec..9abcf04d6 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -612,7 +612,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { if left_sym.kind !in [.sum_type, .interface_] { elem_type := right_final.array_info().elem_type c.check_expected(left_type, elem_type) or { - c.error('left operand to `$node.op` does not match the array element type: $err.msg', + c.error('left operand to `$node.op` does not match the array element type: $err.msg()', left_right_pos) } } @@ -620,7 +620,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { .map { map_info := right_final.map_info() c.check_expected(left_type, map_info.key_type) or { - c.error('left operand to `$node.op` does not match the map key type: $err.msg', + c.error('left operand to `$node.op` does not match the map key type: $err.msg()', left_right_pos) } node.left_type = map_info.key_type @@ -1306,6 +1306,15 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to // Verify methods for imethod in imethods { method := c.table.find_method_with_embeds(typ_sym, imethod.name) or { + // >> Hack to allow old style custom error implementations + // TODO: remove once deprecation period for `IError` methods has ended + if inter_sym.name == 'IError' && (imethod.name == 'msg' || imethod.name == 'code') { + c.note("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`. The usage of fields is being deprecated in favor of methods.", + pos) + continue + } + // << + typ_sym.find_method_with_generic_parent(imethod.name) or { c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`", pos) @@ -1343,8 +1352,15 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to } // voidptr is an escape hatch, it should be allowed to be passed if utyp != ast.voidptr_type { - c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`", - pos) + // >> Hack to allow old style custom error implementations + // TODO: remove once deprecation period for `IError` methods has ended + if inter_sym.name == 'IError' && (ifield.name == 'msg' || ifield.name == 'code') { + // do nothing, necessary warnings are already printed + } else { + // << + c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`", + pos) + } } } inter_sym.info.types << utyp @@ -1571,15 +1587,15 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { has_field = true mut embed_types := []ast.Type{} field, embed_types = c.table.find_field_from_embeds(sym, field_name) or { - if err.msg != '' { - c.error(err.msg, node.pos) + if err.msg() != '' { + c.error(err.msg(), node.pos) } has_field = false ast.StructField{}, []ast.Type{} } node.from_embed_types = embed_types if sym.kind in [.aggregate, .sum_type] { - unknown_field_msg = err.msg + unknown_field_msg = err.msg() } } if !c.inside_unsafe { @@ -1600,8 +1616,8 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { has_field = true mut embed_types := []ast.Type{} field, embed_types = c.table.find_field_from_embeds(gs, field_name) or { - if err.msg != '' { - c.error(err.msg, node.pos) + if err.msg() != '' { + c.error(err.msg(), node.pos) } has_field = false ast.StructField{}, []ast.Type{} @@ -1636,6 +1652,20 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name)) c.error(suggestion.say(unknown_field_msg), node.pos) } + + // >> Hack to allow old style custom error implementations + // TODO: remove once deprecation period for `IError` methods has ended + if sym.name == 'IError' && (field_name == 'msg' || field_name == 'code') { + method := c.table.find_method(sym, field_name) or { + c.error('invalid `IError` interface implementation: $err', node.pos) + return ast.void_type + } + c.note('the `.$field_name` field on `IError` is deprecated, use `.${field_name}()` instead.', + node.pos) + return method.return_type + } + // <<< + c.error(unknown_field_msg, node.pos) } return ast.void_type @@ -2128,7 +2158,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { if flag.contains('@VROOT') { // c.note(checker.vroot_is_deprecated_message, node.pos) vroot := util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } node.val = 'include $vroot' @@ -2143,7 +2173,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { } if flag.contains('@VMODROOT') { vroot := util.resolve_vmodroot(flag, c.file.path) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } node.val = 'include $vroot' @@ -2152,7 +2182,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { } if flag.contains('\$env(') { env := util.resolve_env_value(flag, true) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } node.main = env @@ -2171,15 +2201,15 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { '--cflags --libs $node.main'.split(' ') } mut m := pkgconfig.main(args) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } cflags := m.run() or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } c.table.parse_cflag(cflags, c.mod, c.pref.compile_defines_all) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } } @@ -2189,7 +2219,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { if flag.contains('@VROOT') { // c.note(checker.vroot_is_deprecated_message, node.pos) flag = util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } } @@ -2199,13 +2229,13 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { } if flag.contains('@VMODROOT') { flag = util.resolve_vmodroot(flag, c.file.path) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } } if flag.contains('\$env(') { flag = util.resolve_env_value(flag, true) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) return } } @@ -2219,7 +2249,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { } // println('adding flag "$flag"') c.table.parse_cflag(flag, c.mod, c.pref.compile_defines_all) or { - c.error(err.msg, node.pos) + c.error(err.msg(), node.pos) } } else { @@ -3016,14 +3046,6 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type { typ: typ is_optional: is_optional } - if typ == ast.error_type && c.expected_type == ast.string_type - && !c.using_new_err_struct && !c.inside_selector_expr - && !c.inside_println_arg && !c.file.mod.name.contains('v.') - && !c.is_builtin_mod { - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <- TODO: remove; this prevents a failure in the `performance-regressions` CI job - c.warn('string errors are deprecated; use `err.msg` instead', - node.pos) - } // if typ == ast.t_type { // sym := c.table.sym(c.cur_generic_type) // println('IDENT T unresolved $node.name typ=$sym.name') diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 72ffd0448..c2503603b 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -12,7 +12,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { node.left_type = c.expr(node.left) if node.is_env { env_value := util.resolve_env_value("\$env('$node.args_var')", false) or { - c.error(err.msg, node.env_pos) + c.error(err.msg(), node.env_pos) return ast.string_type } node.env_value = env_value @@ -476,7 +476,7 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool { left_type := c.expr(cond.left) right_type := c.expr(cond.right) expr := c.find_definition(cond.left) or { - c.error(err.msg, cond.left.pos) + c.error(err.msg(), cond.left.pos) return false } if !c.check_types(right_type, left_type) { @@ -558,7 +558,7 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool { return false } expr := c.find_obj_definition(cond.obj) or { - c.error(err.msg, cond.pos) + c.error(err.msg(), cond.pos) return false } if !c.check_types(typ, ast.bool_type) { @@ -573,7 +573,7 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool { ast.ComptimeCall { if cond.is_pkgconfig { mut m := pkgconfig.main([cond.args_var]) or { - c.error(err.msg, cond.pos) + c.error(err.msg(), cond.pos) return true } m.run() or { return true } diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index ff24d3bb8..3f71bcf5f 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -22,7 +22,7 @@ pub fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { default_typ := c.check_expr_opt_call(default_expr, c.expr(default_expr)) node.default_type = default_typ c.check_expected(default_typ, node.elem_type) or { - c.error(err.msg, default_expr.pos()) + c.error(err.msg(), default_expr.pos()) } } if node.has_len { @@ -117,7 +117,7 @@ pub fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { } if expr !is ast.TypeNode { c.check_expected(typ, elem_type) or { - c.error('invalid array element: $err.msg', expr.pos()) + c.error('invalid array element: $err.msg()', expr.pos()) } } } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index d882830fe..5b2938bbc 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -894,7 +894,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) continue } } - c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos) + c.error('$err.msg() in argument ${i + 1} to `$fn_name`', call_arg.pos) } // Warn about automatic (de)referencing, which will be removed soon. if func.language != .c && !c.inside_unsafe && typ.nr_muls() != param.typ.nr_muls() @@ -936,7 +936,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) continue } c.check_expected_call_arg(utyp, unwrap_typ, node.language, call_arg) or { - c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos) + c.error('$err.msg() in argument ${i + 1} to `$fn_name`', call_arg.pos) } } } @@ -1143,8 +1143,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { has_method = true mut embed_types := []ast.Type{} method, embed_types = c.table.find_method_from_embeds(left_sym, method_name) or { - if err.msg != '' { - c.error(err.msg, node.pos) + if err.msg() != '' { + c.error(err.msg(), node.pos) } has_method = false ast.Fn{}, []ast.Type{} @@ -1156,7 +1156,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } if left_sym.kind == .aggregate { // the error message contains the problematic type - unknown_method_msg = err.msg + unknown_method_msg = err.msg() } } if has_method { @@ -1285,7 +1285,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { // continue // } if got_arg_typ != ast.void_type { - c.error('$err.msg in argument ${i + 1} to `${left_sym.name}.$method_name`', + c.error('$err.msg() in argument ${i + 1} to `${left_sym.name}.$method_name`', arg.pos) } } @@ -1432,7 +1432,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { c.check_expected_call_arg(targ, c.unwrap_generic(exp_arg_typ), node.language, arg) or { if targ != ast.void_type { - c.error('$err.msg in argument ${i + 1} to `${left_sym.name}.$method_name`', + c.error('$err.msg() in argument ${i + 1} to `${left_sym.name}.$method_name`', arg.pos) } } @@ -1666,7 +1666,7 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast. info := left_sym.info as ast.Map arg_type := c.expr(node.args[0].expr) c.check_expected_call_arg(arg_type, info.key_type, node.language, node.args[0]) or { - c.error('$err.msg in argument 1 to `Map.delete`', node.args[0].pos) + c.error('$err.msg() in argument 1 to `Map.delete`', node.args[0].pos) } } else {} diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index 1249df3a2..9bc3dc5e6 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -203,7 +203,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { } for st in branch.stmts { // must not contain C statements - st.check_c_expr() or { c.error('`if` expression branch has $err.msg', st.pos) } + st.check_c_expr() or { c.error('`if` expression branch has $err.msg()', st.pos) } } } if mut branch.cond is ast.IfGuardExpr { diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index 35a014ba6..df107fdfa 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -41,7 +41,7 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { for st in branch.stmts[0..branch.stmts.len - 1] { // must not contain C statements st.check_c_expr() or { - c.error('`match` expression branch has $err.msg', st.pos) + c.error('`match` expression branch has $err.msg()', st.pos) } } } else if ret_type != ast.void_type { diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 85930d72f..b53014721 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -81,7 +81,7 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) { } } } else { - c.error('incompatible initializer for field `$field.name`: $err.msg', + c.error('incompatible initializer for field `$field.name`: $err.msg()', field.default_expr.pos()) } } @@ -300,7 +300,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { } } else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { c.check_expected(c.unwrap_generic(expr_type), c.unwrap_generic(field_info.typ)) or { - c.error('cannot assign to field `$field_info.name`: $err.msg', + c.error('cannot assign to field `$field_info.name`: $err.msg()', field.pos) } } diff --git a/vlib/v/doc/doc_private_fn_test.v b/vlib/v/doc/doc_private_fn_test.v index 107d76f0c..100a222b7 100644 --- a/vlib/v/doc/doc_private_fn_test.v +++ b/vlib/v/doc/doc_private_fn_test.v @@ -11,7 +11,7 @@ fn test_get_parent_mod_on_root_folder() { // TODO: add an equivalent windows check for c:\ $if !windows { assert '---' == get_parent_mod('/') or { - assert err.msg == 'root folder reached' + assert err.msg() == 'root folder reached' '---' } } @@ -20,7 +20,7 @@ fn test_get_parent_mod_on_root_folder() { fn test_get_parent_mod_current_folder() { // TODO: this should may be return '' reliably on windows too: // assert '' == get_parent_mod('.') or { - // assert err.msg == 'No V files found.' + // assert err.msg() == 'No V files found.' // '---' // } } @@ -34,7 +34,7 @@ fn test_get_parent_mod_on_temp_dir() ? { fn test_get_parent_mod_normal_cases() ? { assert '---' == get_parent_mod(os.join_path(@VMODROOT, 'vlib/v')) or { - assert err.msg == 'No V files found.' + assert err.msg() == 'No V files found.' '---' } // TODO: WTF? diff --git a/vlib/v/fmt/fmt_keep_test.v b/vlib/v/fmt/fmt_keep_test.v index 8ee69a7dc..7a7ca77e3 100644 --- a/vlib/v/fmt/fmt_keep_test.v +++ b/vlib/v/fmt/fmt_keep_test.v @@ -67,7 +67,7 @@ fn test_fmt() { continue } vfmt_result_file := os.join_path(tmpfolder, 'vfmt_run_over_$ifilename') - os.write_file(vfmt_result_file, result_ocontent) or { panic(err.msg) } + os.write_file(vfmt_result_file, result_ocontent) or { panic(err) } eprintln(diff.color_compare_files(diff_cmd, opath, vfmt_result_file)) continue } @@ -75,7 +75,7 @@ fn test_fmt() { eprintln(fmt_bench.step_message_ok(vrelpath)) } restore_bin2v_placeholder() or { - eprintln('failed restoring vbin2v_keep.vv placeholder: $err.msg') + eprintln('failed restoring vbin2v_keep.vv placeholder: $err.msg()') } fmt_bench.stop() eprintln(term.h_divider('-')) @@ -90,7 +90,7 @@ fn prepare_bin2v_file(mut fmt_bench benchmark.Benchmark) { fmt_bench.step() write_bin2v_keep_content() or { fmt_bench.fail() - eprintln(fmt_bench.step_message_fail('Failed preparing bin2v_keep.vv: $err.msg')) + eprintln(fmt_bench.step_message_fail('Failed preparing bin2v_keep.vv: $err.msg()')) return } fmt_bench.ok() diff --git a/vlib/v/fmt/tests/match_keep.vv b/vlib/v/fmt/tests/match_keep.vv index 73326e4c3..3e8ee79b0 100644 --- a/vlib/v/fmt/tests/match_keep.vv +++ b/vlib/v/fmt/tests/match_keep.vv @@ -34,13 +34,13 @@ fn branches_are_struct_inits() { fn branches_are_call_exprs_with_or_blocks() { match 'a' { - 'b' { foo() or { panic(err.msg) } } + 'b' { foo() or { panic(err) } } } match 'a' { 'b' { foo() or { // do stuff - panic(err.msg) + panic(err) } } } @@ -48,7 +48,7 @@ fn branches_are_call_exprs_with_or_blocks() { 'b' { foo() or { another_stmt() - panic(err.msg) + panic(err) } } } diff --git a/vlib/v/gen/c/assert.v b/vlib/v/gen/c/assert.v index 439b6a6ac..24f08d3d4 100644 --- a/vlib/v/gen/c/assert.v +++ b/vlib/v/gen/c/assert.v @@ -51,8 +51,7 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { } struct UnsupportedAssertCtempTransform { - msg string - code int + Error } const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{}) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 102903ba1..33f2ee888 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -6122,11 +6122,12 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty } else if or_block.kind == .propagate { if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) { // In main(), an `opt()?` call is sugar for `opt() or { panic(err) }` + err_msg := 'IError_name_table[${cvar_name}.err._typ]._method_msg(${cvar_name}.err._object)' if g.pref.is_debug { paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) - g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *${cvar_name}.err.msg );') + g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), $err_msg );') } else { - g.writeln('\tpanic_optional_not_set(*${cvar_name}.err.msg);') + g.writeln('\tpanic_optional_not_set( $err_msg );') } } else if !isnil(g.fn_decl) && g.fn_decl.is_test { g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name) @@ -6899,6 +6900,16 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x) methods_struct.writeln('\t\t._method_${c_name(method.name)} = (void*) $method_call,') } } + + // >> Hack to allow old style custom error implementations + // TODO: remove once deprecation period for `IError` methods has ended + // fix MSVC not handling empty struct inits + if methods.len == 0 && interface_name == 'IError' { + methods_struct.writeln('\t\t._method_msg = NULL,') + methods_struct.writeln('\t\t._method_code = NULL,') + } + // << + if g.pref.build_mode != .build_module { methods_struct.writeln('\t},') } diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index 25ece3b36..979d20649 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -166,19 +166,21 @@ pub fn (mut g Gen) write_tests_definitions() { pub fn (mut g Gen) gen_failing_error_propagation_for_test_fn(or_block ast.OrExpr, cvar_name string) { // in test_() functions, an `opt()?` call is sugar for - // `or { cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) }` + // `or { cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg() ) }` // and the test is considered failed paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) - g.writeln('\tmain__TestRunner_name_table[test_runner._typ]._method_fn_error(test_runner._object, $paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *(${cvar_name}.err.msg) );') + err_msg := 'IError_name_table[${cvar_name}.err._typ]._method_msg(${cvar_name}.err._object)' + g.writeln('\tmain__TestRunner_name_table[test_runner._typ]._method_fn_error(test_runner._object, $paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), $err_msg );') g.writeln('\tlongjmp(g_jump_buffer, 1);') } pub fn (mut g Gen) gen_failing_return_error_for_test_fn(return_stmt ast.Return, cvar_name string) { // in test_() functions, a `return error('something')` is sugar for - // `or { err := error('something') cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) return err }` + // `or { err := error('something') cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg() ) return err }` // and the test is considered failed paline, pafile, pamod, pafn := g.panic_debug_info(return_stmt.pos) - g.writeln('\tmain__TestRunner_name_table[test_runner._typ]._method_fn_error(test_runner._object, $paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *(${cvar_name}.err.msg) );') + err_msg := 'IError_name_table[${cvar_name}.err._typ]._method_msg(${cvar_name}.err._object)' + g.writeln('\tmain__TestRunner_name_table[test_runner._typ]._method_fn_error(test_runner._object, $paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), $err_msg );') g.writeln('\tlongjmp(g_jump_buffer, 1);') } diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 893343e0c..3e38406bf 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -320,7 +320,7 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool { } ast.PostfixExpr { ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or { - verror(err.msg) + verror(err.msg()) return false } g.write('defined($ifdef)') diff --git a/vlib/v/gen/js/comptime.v b/vlib/v/gen/js/comptime.v index 8d54fb209..378f1e044 100644 --- a/vlib/v/gen/js/comptime.v +++ b/vlib/v/gen/js/comptime.v @@ -74,7 +74,7 @@ fn (mut g JsGen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool { } ast.PostfixExpr { ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or { - verror(err.msg) + verror(err.msg()) return false } g.write('$ifdef') diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 9c64f8e1b..2adea9543 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -1057,8 +1057,7 @@ fn (mut g JsGen) expr(node ast.Expr) { } struct UnsupportedAssertCtempTransform { - msg string - code int + Error } const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{}) diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index d20f47f48..819d0cbdf 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -218,7 +218,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme if op == .decl_assign { // a, b := a + 1, b for r in right { - p.check_undefined_variables(left, r) or { return p.error_with_pos(err.msg, pos) } + p.check_undefined_variables(left, r) or { return p.error_with_pos(err.msg(), pos) } } } else if left.len > 1 { // a, b = b, a diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 0125dc156..af4b58ca3 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -32,6 +32,7 @@ pub fn (mut p Parser) parse_array_type(expecting token.Kind) ast.Type { fixed_size = const_field.expr.val.int() } else { if mut const_field.expr is ast.InfixExpr { + // QUESTION: this should most likely no be done in the parser, right? mut t := transformer.new_transformer(p.pref) folded_expr := t.infix_expr(mut const_field.expr) diff --git a/vlib/v/parser/sql.v b/vlib/v/parser/sql.v index d813c8ada..307c4076d 100644 --- a/vlib/v/parser/sql.v +++ b/vlib/v/parser/sql.v @@ -41,7 +41,7 @@ fn (mut p Parser) sql_expr() ast.Expr { if e.right is ast.Ident { if !p.scope.known_var(e.right.name) { p.check_undefined_variables([e.left], e.right) or { - return p.error_with_pos(err.msg, e.right.pos) + return p.error_with_pos(err.msg(), e.right.pos) } } } diff --git a/vlib/v/pkgconfig/pkgconfig_test.v b/vlib/v/pkgconfig/pkgconfig_test.v index 663aa9b93..320bf2754 100644 --- a/vlib/v/pkgconfig/pkgconfig_test.v +++ b/vlib/v/pkgconfig/pkgconfig_test.v @@ -21,7 +21,9 @@ fn test_dependency_resolution_fails_correctly() { mut errors := []string{} for pc in pc_files { pcname := os.file_name(pc).replace('.pc', '') - pkgconfig.load(pcname, use_default_paths: false, path: samples_dir) or { errors << err.msg } + pkgconfig.load(pcname, use_default_paths: false, path: samples_dir) or { + errors << err.msg() + } } assert errors.len < pc_files.len assert errors == ['could not resolve dependency xyz-unknown-package'] diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index edf7e99b8..13cb3a8eb 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -110,7 +110,7 @@ pub fn new_scanner_file(file_path string, comments_mode CommentsMode, pref &pref verror('$file_path is not a file') } raw_text := util.read_file(file_path) or { - verror(err.msg) + verror(err.msg()) return voidptr(0) } mut s := &Scanner{ diff --git a/vlib/v/tests/closure_generator_test.v b/vlib/v/tests/closure_generator_test.v index 637f81744..45676a722 100644 --- a/vlib/v/tests/closure_generator_test.v +++ b/vlib/v/tests/closure_generator_test.v @@ -44,7 +44,7 @@ const return_types = [ ReturnType{ name: '?' init: "error('an error')" - assertion: " or { assert err.msg == 'an error' return }\npanic('got no error')" + assertion: " or { assert err.msg() == 'an error' return }\npanic('got no error')" no_assert_kw: true }, ReturnType{ diff --git a/vlib/v/tests/fn_multiple_returns_test.v b/vlib/v/tests/fn_multiple_returns_test.v index bae15dc1c..7c4745e4a 100644 --- a/vlib/v/tests/fn_multiple_returns_test.v +++ b/vlib/v/tests/fn_multiple_returns_test.v @@ -51,7 +51,7 @@ fn test_multiple_ret() { // none case wrapper1 := fn () (string, string) { res2_1, res2_2 := split_to_two('') or { - assert err.msg == '' + assert err.msg() == '' return 'replaced', 'val' } return res2_1, res2_2 @@ -63,7 +63,7 @@ fn test_multiple_ret() { // error case wrapper2 := fn () (string, string) { res3_1, res3_2 := split_to_two('fishhouse') or { - assert err.msg == 'error' + assert err.msg() == 'error' return 'replaced', 'val' } return res3_1, res3_2 diff --git a/vlib/v/tests/if_expr_of_optional_test.v b/vlib/v/tests/if_expr_of_optional_test.v index 724d83c9d..164ac7fcf 100644 --- a/vlib/v/tests/if_expr_of_optional_test.v +++ b/vlib/v/tests/if_expr_of_optional_test.v @@ -26,7 +26,7 @@ fn test_if_expr_of_optional() { if _ := foo3() { assert false } else { - assert err.msg == 'foo3 error' + assert err.msg() == 'foo3 error' } a4 := foo4() or { panic('error') } diff --git a/vlib/v/tests/inout/os.vv b/vlib/v/tests/inout/os.vv index 32b353e10..9d8d42336 100644 --- a/vlib/v/tests/inout/os.vv +++ b/vlib/v/tests/inout/os.vv @@ -3,7 +3,7 @@ import os fn main() { vexe := os.getenv('VEXE') vroot := os.dir(vexe) - mut files := os.ls(vroot) or { panic(err.msg) } + mut files := os.ls(vroot) or { panic(err) } files.sort() for file in files { if file.ends_with('.md') { diff --git a/vlib/v/tests/option_2_test.v b/vlib/v/tests/option_2_test.v index ebb9c7635..8537decd1 100644 --- a/vlib/v/tests/option_2_test.v +++ b/vlib/v/tests/option_2_test.v @@ -12,7 +12,7 @@ fn test_lhs_option() { } fn ret_no_opt(n int) int { - return f(n) or { panic(err.msg) } + return f(n) or { panic(err) } } fn test_opt_return_no_opt() { diff --git a/vlib/v/tests/option_test.v b/vlib/v/tests/option_test.v index c9b857ed5..3d3126793 100644 --- a/vlib/v/tests/option_test.v +++ b/vlib/v/tests/option_test.v @@ -7,11 +7,11 @@ fn test_err_with_code() { assert false _ := w } else { - assert err.msg == 'hi' + assert err.msg() == 'hi' assert err.code == 137 } v := opt_err_with_code() or { - assert err.msg == 'hi' + assert err.msg() == 'hi' assert err.code == 137 return } @@ -25,7 +25,7 @@ fn opt_err() ?string { fn test_err() { v := opt_err() or { - assert err.msg == 'hi' + assert err.msg() == 'hi' return } assert false @@ -74,7 +74,7 @@ fn test_if_else_opt() { if _ := err_call(false) { assert false } else { - assert err.msg.len != 0 + assert err.msg().len != 0 } } @@ -151,12 +151,12 @@ fn test_or_return() { if _ := or_return_error() { assert false } else { - assert err.msg.len != 0 + assert err.msg().len != 0 } if _ := or_return_none() { assert false } else { - assert err.msg.len == 0 + assert err.msg().len == 0 } } @@ -283,7 +283,7 @@ fn test_optional_void_return_types_of_anon_fn() { } f(0) or { - assert err.msg == '0' + assert err.msg() == '0' return } } @@ -304,7 +304,7 @@ fn test_option_void_return_types_of_anon_fn_in_struct() { } foo.f(0) or { - assert err.msg == '0' + assert err.msg() == '0' return } } diff --git a/vlib/v/tests/option_void_test.v b/vlib/v/tests/option_void_test.v index ad59c5c34..2d15d473c 100644 --- a/vlib/v/tests/option_void_test.v +++ b/vlib/v/tests/option_void_test.v @@ -5,7 +5,7 @@ fn foo() ? { fn test_optional_void() { foo() or { println(err) - assert err.msg == 'something' + assert err.msg() == 'something' return } } @@ -17,7 +17,7 @@ fn bar() ? { fn test_optional_void_only_question() { bar() or { println(err) - assert err.msg == 'bar error' + assert err.msg() == 'bar error' return } } @@ -38,12 +38,12 @@ fn option_void(a int) ? { fn test_optional_void_with_return() { option_void(0) or { println(err) - assert err.msg == 'zero error' + assert err.msg() == 'zero error' return } option_void(-1) or { println(err) - assert err.msg == 'zero error' + assert err.msg() == 'zero error' return } assert true diff --git a/vlib/v/tests/prod_test.v b/vlib/v/tests/prod_test.v index 4eac10da0..0b67fa451 100644 --- a/vlib/v/tests/prod_test.v +++ b/vlib/v/tests/prod_test.v @@ -15,7 +15,7 @@ fn test_all_v_prod_files() { bmark.step() fres := runner.run_prod_file(options.wd, options.vexec, file) or { bmark.fail() - eprintln(bmark.step_message_fail(err.msg)) + eprintln(bmark.step_message_fail(err.msg())) assert false continue } diff --git a/vlib/v/tests/repl/repl_test.v b/vlib/v/tests/repl/repl_test.v index c8f134811..3f5034d63 100644 --- a/vlib/v/tests/repl/repl_test.v +++ b/vlib/v/tests/repl/repl_test.v @@ -83,7 +83,7 @@ fn worker_repl(mut p pool.PoolProcessor, idx int, thread_id int) voidptr { session.bmark.fail() tls_bench.fail() os.rmdir_all(tfolder) or { panic(err) } - eprintln(tls_bench.step_message_fail(err.msg)) + eprintln(tls_bench.step_message_fail(err.msg())) assert false return pool.no_result } diff --git a/vlib/v/tests/repl/runner/runner.v b/vlib/v/tests/repl/runner/runner.v index f72d054d0..0c7c08adc 100644 --- a/vlib/v/tests/repl/runner/runner.v +++ b/vlib/v/tests/repl/runner/runner.v @@ -35,7 +35,7 @@ pub fn full_path_to_v(dirs_in int) string { } fn diff_files(file_result string, file_expected string) string { - diffcmd := diff.find_working_diff_command() or { return err.msg } + diffcmd := diff.find_working_diff_command() or { return err.msg() } return diff.color_compare_files(diffcmd, file_result, file_expected) } diff --git a/vlib/v/tests/struct_test.v b/vlib/v/tests/struct_test.v index 1e89c2548..56f61ba06 100644 --- a/vlib/v/tests/struct_test.v +++ b/vlib/v/tests/struct_test.v @@ -374,7 +374,7 @@ fn test_fields_anon_fn_with_optional_void_return_type() { } } - foo.f() or { assert err.msg == 'oops' } + foo.f() or { assert err.msg() == 'oops' } foo.g() or { assert false } } diff --git a/vlib/v/tests/vmod_parser_test.v b/vlib/v/tests/vmod_parser_test.v index 10f48141c..93ad9ef0b 100644 --- a/vlib/v/tests/vmod_parser_test.v +++ b/vlib/v/tests/vmod_parser_test.v @@ -38,7 +38,7 @@ fn test_decode() { assert data.dependencies[0] == 'hello' assert data.unknown['test'][0] == 'foo' vmod.decode('') or { - assert err.msg == 'vmod: no content.' + assert err.msg() == 'vmod: no content.' exit(0) } } diff --git a/vlib/v/transformer/transformer.v b/vlib/v/transformer/transformer.v index 3eb278fd9..d1761b0c6 100644 --- a/vlib/v/transformer/transformer.v +++ b/vlib/v/transformer/transformer.v @@ -8,6 +8,9 @@ pub struct Transformer { pref &pref.Preferences pub mut: index &IndexState + table &ast.Table = 0 +mut: + is_assert bool } pub fn new_transformer(pref &pref.Preferences) &Transformer { @@ -20,6 +23,12 @@ pub fn new_transformer(pref &pref.Preferences) &Transformer { } } +pub fn new_transformer_with_table(table &ast.Table, pref &pref.Preferences) &Transformer { + mut transformer := new_transformer(pref) + transformer.table = table + return transformer +} + pub fn (mut t Transformer) transform_files(ast_files []&ast.File) { for i in 0 .. ast_files.len { mut file := unsafe { ast_files[i] } @@ -104,57 +113,6 @@ pub fn (mut t Transformer) find_mut_self_assign(node ast.AssignStmt) { // even if mutable we can be sure than `a[1] = a[2] is safe } -pub fn (mut t Transformer) find_assert_len(mut node ast.InfixExpr) ast.Expr { - if !t.pref.is_prod { - return node - } - right := t.expr(mut node.right) - match right { - ast.IntegerLiteral { - left := t.expr(mut node.left) - if left is ast.SelectorExpr { - len := right.val.int() - if left.field_name == 'len' { - match node.op { - .eq { // == - t.index.safe_access(left.expr.str(), len - 1) - } - .ge { // >= - t.index.safe_access(left.expr.str(), len - 1) - } - .gt { // > - t.index.safe_access(left.expr.str(), len) - } - else {} - } - } - } - } - ast.SelectorExpr { - left := t.expr(mut node.left) - if left is ast.IntegerLiteral { - len := left.val.int() - if right.field_name == 'len' { - match node.op { - .eq { // == - t.index.safe_access(right.expr.str(), len - 1) - } - .le { // <= - t.index.safe_access(right.expr.str(), len - 1) - } - .lt { // < - t.index.safe_access(right.expr.str(), len) - } - else {} - } - } - } - } - else {} - } - return node -} - pub fn (mut t Transformer) check_safe_array(mut node ast.IndexExpr) { if !t.pref.is_prod { return @@ -212,14 +170,7 @@ pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt { ast.NodeError {} ast.AsmStmt {} ast.AssertStmt { - match mut node.expr { - ast.InfixExpr { - node.expr = t.find_assert_len(mut node.expr) - } - else { - node.expr = t.expr(mut node.expr) - } - } + return t.assert_stmt(mut node) } ast.AssignStmt { t.find_new_array_len(node) @@ -312,9 +263,7 @@ pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt { } ast.Import {} ast.InterfaceDecl { - for mut field in node.fields { - field.default_expr = t.expr(mut field.default_expr) - } + return t.interface_decl(mut node) } ast.Module {} ast.Return { @@ -333,6 +282,63 @@ pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt { return node } +pub fn (mut t Transformer) assert_stmt(mut node ast.AssertStmt) ast.Stmt { + t.is_assert = true + node.expr = t.expr(mut node.expr) + if !t.pref.is_prod { + return node + } + if mut node.expr is ast.InfixExpr { + right := node.expr.right + match right { + ast.IntegerLiteral { + left := node.expr.left + if left is ast.SelectorExpr { + len := right.val.int() + if left.field_name == 'len' { + match node.expr.op { + .eq { // == + t.index.safe_access(left.expr.str(), len - 1) + } + .ge { // >= + t.index.safe_access(left.expr.str(), len - 1) + } + .gt { // > + t.index.safe_access(left.expr.str(), len) + } + else {} + } + } + } + } + ast.SelectorExpr { + left := node.expr.left + if left is ast.IntegerLiteral { + len := left.val.int() + if right.field_name == 'len' { + match node.expr.op { + .eq { // == + t.index.safe_access(right.expr.str(), len - 1) + } + .le { // <= + t.index.safe_access(right.expr.str(), len - 1) + } + .lt { // < + t.index.safe_access(right.expr.str(), len) + } + else {} + } + } + } + } + else {} + } + } + + t.is_assert = false + return node +} + pub fn (mut t Transformer) expr_stmt_if_expr(mut node ast.IfExpr) ast.Expr { mut stop_index, mut unreachable_branches := -1, []int{cap: node.branches.len} if node.is_comptime { @@ -497,6 +503,14 @@ pub fn (mut t Transformer) for_stmt(mut node ast.ForStmt) ast.Stmt { return node } +pub fn (mut t Transformer) interface_decl(mut node ast.InterfaceDecl) ast.Stmt { + for mut field in node.fields { + field.default_expr = t.expr(mut field.default_expr) + } + + return node +} + pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr { match mut node { ast.AnonFn { @@ -629,6 +643,11 @@ pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr { ast.SqlExpr { return t.sql_expr(mut node) } + ast.StringInterLiteral { + for mut expr in node.exprs { + expr = t.expr(mut expr) + } + } ast.StructInit { node.update_expr = t.expr(mut node.update_expr) for mut field in node.fields { @@ -661,7 +680,7 @@ pub fn (mut t Transformer) infix_expr(mut node ast.InfixExpr) ast.Expr { pos.extend(node.pos) pos.extend(node.right.pos()) - if t.pref.is_debug { + if t.pref.is_debug || t.is_assert { // never optimize assert statements return node } else { match mut node.left { diff --git a/vlib/vweb/parse.v b/vlib/vweb/parse.v index 61452b9f0..84d18d461 100644 --- a/vlib/vweb/parse.v +++ b/vlib/vweb/parse.v @@ -34,7 +34,7 @@ fn parse_attrs(name string, attrs []string) ?([]http.Method, string) { } if x.len > 0 { return IError(http.UnexpectedExtraAttributeError{ - msg: 'Encountered unexpected extra attributes: $x' + attributes: x }) } if methods.len == 0 { diff --git a/vlib/vweb/tests/vweb_test.v b/vlib/vweb/tests/vweb_test.v index 435d1ca59..44e0ce1fc 100644 --- a/vlib/vweb/tests/vweb_test.v +++ b/vlib/vweb/tests/vweb_test.v @@ -68,7 +68,7 @@ fn assert_common_headers(received string) { fn test_a_simple_tcp_client_can_connect_to_the_vweb_server() { received := simple_tcp_client(path: '/') or { - assert err.msg == '' + assert err.msg() == '' return } assert_common_headers(received) @@ -79,7 +79,7 @@ fn test_a_simple_tcp_client_can_connect_to_the_vweb_server() { fn test_a_simple_tcp_client_simple_route() { received := simple_tcp_client(path: '/simple') or { - assert err.msg == '' + assert err.msg() == '' return } assert_common_headers(received) @@ -92,7 +92,7 @@ fn test_a_simple_tcp_client_zero_content_length() { // tests that sending a content-length header of 0 doesn't hang on a read timeout watch := time.new_stopwatch(auto_start: true) simple_tcp_client(path: '/', headers: 'Content-Length: 0\r\n\r\n') or { - assert err.msg == '' + assert err.msg() == '' return } assert watch.elapsed() < 1 * time.second @@ -100,7 +100,7 @@ fn test_a_simple_tcp_client_zero_content_length() { fn test_a_simple_tcp_client_html_page() { received := simple_tcp_client(path: '/html_page') or { - assert err.msg == '' + assert err.msg() == '' return } assert_common_headers(received) @@ -230,7 +230,7 @@ $contents\r fn test_http_client_shutdown_does_not_work_without_a_cookie() { x := http.get('http://$localserver/shutdown') or { - assert err.msg == '' + assert err.msg() == '' return } assert x.status() == .not_found @@ -247,7 +247,7 @@ fn testsuite_end() { 'skey': 'superman' } ) or { - assert err.msg == '' + assert err.msg() == '' return } assert x.status() == .ok diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 9327ae066..479c98add 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -255,7 +255,7 @@ pub fn (mut ctx Context) json_pretty(j T) Result { pub fn (mut ctx Context) file(f_path string) Result { ext := os.file_ext(f_path) data := os.read_file(f_path) or { - eprint(err.msg) + eprint(err.msg()) ctx.server_error(500) return Result{} } @@ -417,7 +417,7 @@ pub fn run_at(global_app &T, host string, port int) { request_app.Context = global_app.Context // copy the context ref that contains static files map etc mut conn := l.accept() or { // failures should not panic - eprintln('accept() failed with error: $err.msg') + eprintln('accept() failed with error: $err.msg()') continue } go handle_conn(mut conn, mut request_app, routes) diff --git a/vlib/x/json2/decoder.v b/vlib/x/json2/decoder.v index de88be885..30cbcc159 100644 --- a/vlib/x/json2/decoder.v +++ b/vlib/x/json2/decoder.v @@ -22,13 +22,11 @@ mut: } struct InvalidTokenError { - msg string - code int + MessageError } struct UnknownTokenError { - msg string - code int + MessageError } fn (mut p Parser) next() { diff --git a/vlib/x/json2/decoder_test.v b/vlib/x/json2/decoder_test.v index c14e33a56..805c94fb7 100644 --- a/vlib/x/json2/decoder_test.v +++ b/vlib/x/json2/decoder_test.v @@ -43,7 +43,7 @@ fn test_raw_decode_null() ? { fn test_raw_decode_invalid() ? { raw_decode('1z') or { - assert err.msg == '[x.json2] invalid token `z` (0:17)' + assert err.msg() == '[x.json2] invalid token `z` (0:17)' return } assert false -- 2.30.2