builtin: change IError `msg` and `code` to methods + fix vlib, add a deprecation notice for the old usages (#13041)

pull/13440/head
Tim Basel 2022-02-11 14:52:33 +01:00 committed by GitHub
parent 61024d4b75
commit 9d0a5942ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 493 additions and 324 deletions

View File

@ -14,41 +14,85 @@ pub fn panic(s string) {
// IError holds information about an error instance // IError holds information about an error instance
pub interface IError { pub interface IError {
// >> Hack to allow old style custom error implementations
// TODO: remove once deprecation period for `IError` methods has ended
msg string 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 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: pub:
msg string msg string
code int 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__ { struct None__ {
msg string Error
code int
} }
fn (_ None__) str() string { fn (_ None__) str() string {
return 'none' return 'none'
} }
pub const none__ = IError(None__{'', 0})
pub struct Option { pub struct Option {
state byte state byte
err IError = none__ 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 { pub fn (o Option) str() string {
if o.state == 0 { if o.state == 0 {
return 'Option{ ok }' return 'Option{ ok }'
@ -69,7 +113,7 @@ fn trace_error(x string) {
[inline] [inline]
pub fn error(message string) IError { pub fn error(message string) IError {
// trace_error(message) // trace_error(message)
return &Error{ return &MessageError{
msg: message msg: message
} }
} }
@ -79,7 +123,7 @@ pub fn error(message string) IError {
[inline] [inline]
pub fn error_with_code(message string, code int) IError { pub fn error_with_code(message string, code int) IError {
// trace_error('$message | code: $code') // trace_error('$message | code: $code')
return &Error{ return &MessageError{
msg: message msg: message
code: code code: code
} }

View File

@ -8,7 +8,6 @@ struct C.IError {
[unsafe] [unsafe]
pub fn (ie &IError) free() { pub fn (ie &IError) free() {
unsafe { unsafe {
ie.msg.free()
cie := &C.IError(ie) cie := &C.IError(ie)
free(cie._object) free(cie._object)
} }

View File

@ -5,30 +5,79 @@ module builtin
// IError holds information about an error instance // IError holds information about an error instance
pub interface IError { pub interface IError {
// >> Hack to allow old style custom error implementations
// TODO: remove once deprecation period for `IError` methods has ended
msg string 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 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: pub:
msg string msg string
code int code int
} }
pub fn (err IError) str() string { pub fn (err MessageError) msg() string {
return match err { return err.msg
None__ { 'none' } }
Error { err.msg }
else { '$err.type_name(): $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__{}) const none__ = IError(&None__{})
struct None__ { struct None__ {
msg string Error
code int
} }
fn (_ None__) str() string { fn (_ None__) str() string {
@ -45,7 +94,7 @@ fn trace_error(x string) {
[inline] [inline]
pub fn error(message string) IError { pub fn error(message string) IError {
trace_error(message) trace_error(message)
return &Error{ return &MessageError{
msg: message msg: message
} }
} }
@ -55,7 +104,7 @@ pub fn error(message string) IError {
[inline] [inline]
pub fn error_with_code(message string, code int) IError { pub fn error_with_code(message string, code int) IError {
trace_error('$message | code: $code') trace_error('$message | code: $code')
return &Error{ return &MessageError{
msg: message msg: message
code: code 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 { pub fn (_ none) str() string {
return 'none' return 'none'
} }

View File

@ -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('password'.bytes(), hash.bytes()) or { panic(err) }
bcrypt.compare_hash_and_password('password2'.bytes(), hash.bytes()) or { bcrypt.compare_hash_and_password('password2'.bytes(), hash.bytes()) or {
assert err == error('mismatched hash and password') assert err.msg() == 'mismatched hash and password'
} }
} }

View File

@ -5,6 +5,9 @@
module rand module rand
struct ReadError { struct ReadError {
msg string = 'crypto.rand.read() error reading random bytes' Error
code int }
pub fn (err ReadError) msg() string {
return 'crypto.rand.read() error reading random bytes'
} }

View File

@ -11,23 +11,28 @@ pub:
} }
struct UnkownFlagError { struct UnkownFlagError {
msg string Error
code int flag string
} }
struct MinimumArgsCountError { fn (err UnkownFlagError) msg() string {
msg string return 'Unknown flag `$err.flag`'
code int
} }
struct MaximumArgsCountError { struct ArgsCountError {
msg string Error
code int got int
want int
} }
struct NoArgsExpectedError { fn (err ArgsCountError) msg() string {
msg string if err.want == 0 {
code int 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 // free frees the resources associated with a given Flag
@ -616,24 +621,27 @@ pub fn (mut fs FlagParser) finalize() ?[]string {
for a in remaining { for a in remaining {
if (a.len >= 2 && a[..2] == '--') || (a.len == 2 && a[0] == `-`) { if (a.len >= 2 && a[..2] == '--') || (a.len == 2 && a[0] == `-`) {
return IError(&UnkownFlagError{ return IError(&UnkownFlagError{
msg: 'Unknown flag `$a`' flag: a
}) })
} }
} }
} }
if remaining.len < fs.min_free_args && fs.min_free_args > 0 { if remaining.len < fs.min_free_args && fs.min_free_args > 0 {
return IError(&MinimumArgsCountError{ return IError(&ArgsCountError{
msg: 'Expected at least $fs.min_free_args arguments, but given $remaining.len' want: fs.min_free_args
got: remaining.len
}) })
} }
if remaining.len > fs.max_free_args && fs.max_free_args > 0 { if remaining.len > fs.max_free_args && fs.max_free_args > 0 {
return IError(&MaximumArgsCountError{ return IError(&ArgsCountError{
msg: 'Expected at most $fs.max_free_args arguments, but given $remaining.len' want: fs.max_free_args
got: remaining.len
}) })
} }
if remaining.len > 0 && fs.max_free_args == 0 && fs.min_free_args == 0 { if remaining.len > 0 && fs.max_free_args == 0 && fs.min_free_args == 0 {
return IError(&NoArgsExpectedError{ return IError(&ArgsCountError{
msg: 'Expected no arguments, but given $remaining.len' want: 0
got: remaining.len
}) })
} }
remaining << fs.all_after_dashdash remaining << fs.all_after_dashdash
@ -647,7 +655,7 @@ pub fn (mut fs FlagParser) finalize() ?[]string {
// you want more control over the error handling. // you want more control over the error handling.
pub fn (mut fs FlagParser) remaining_parameters() []string { pub fn (mut fs FlagParser) remaining_parameters() []string {
return fs.finalize() or { return fs.finalize() or {
eprintln(err.msg) eprintln(err.msg())
println(fs.usage()) println(fs.usage())
exit(1) exit(1)
} }

View File

@ -137,7 +137,7 @@ fn test_finalize_returns_error_for_unknown_flags_long() {
mut fp := flag.new_flag_parser(['--known', '--unknown']) mut fp := flag.new_flag_parser(['--known', '--unknown'])
fp.bool('known', 0, false, '') fp.bool('known', 0, false, '')
finalized := fp.finalize() or { finalized := fp.finalize() or {
assert err.msg == 'Unknown flag `--unknown`' assert err.msg() == 'Unknown flag `--unknown`'
return return
} }
assert finalized.len < 0 // expect error to be returned 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']) mut fp := flag.new_flag_parser(['--known', '-x'])
fp.bool('known', 0, false, '') fp.bool('known', 0, false, '')
finalized := fp.finalize() or { finalized := fp.finalize() or {
assert err.msg == 'Unknown flag `-x`' assert err.msg() == 'Unknown flag `-x`'
return return
} }
assert finalized.len < 0 // expect error to be returned 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']) mut fp1 := flag.new_flag_parser(['a', 'b', 'c'])
fp1.limit_free_args(5, 6) ? fp1.limit_free_args(5, 6) ?
args := fp1.finalize() or { 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 return
} }
assert args.len < 0 // expect an error and need to use args 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']) mut fp1 := flag.new_flag_parser(['a', 'b', 'c'])
fp1.limit_free_args(1, 2) ? fp1.limit_free_args(1, 2) ?
args := fp1.finalize() or { 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 return
} }
assert args.len < 0 // expect an error and need to use args 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']) mut fp1 := flag.new_flag_parser(['a'])
fp1.limit_free_args(0, 0) ? fp1.limit_free_args(0, 0) ?
args := fp1.finalize() or { args := fp1.finalize() or {
assert err.msg.starts_with('Expected no arguments') assert err.msg().starts_with('Expected no arguments')
return return
} }
assert args.len < 0 // expect an error and need to use args assert args.len < 0 // expect an error and need to use args

View File

@ -24,7 +24,7 @@ pub fn temp_file(tfo TempFileOptions) ?(os.File, string) {
' could not create temporary file in "$d". Please ensure write permissions.') ' could not create temporary file in "$d". Please ensure write permissions.')
} }
d = d.trim_right(os.path_separator) 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++ { for retry := 0; retry < util.retries; retry++ {
path := os.join_path(d, prefix + random_number() + suffix) path := os.join_path(d, prefix + random_number() + suffix)
mut mode := 'rw+' mut mode := 'rw+'
@ -57,7 +57,7 @@ pub fn temp_dir(tdo TempFileOptions) ?string {
' could not create temporary directory "$d". Please ensure write permissions.') ' could not create temporary directory "$d". Please ensure write permissions.')
} }
d = d.trim_right(os.path_separator) 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++ { for retry := 0; retry < util.retries; retry++ {
path := os.join_path(d, prefix + random_number() + suffix) path := os.join_path(d, prefix + random_number() + suffix)
os.mkdir_all(path) or { continue } os.mkdir_all(path) or { continue }

View File

@ -14,7 +14,7 @@ mut:
fn test_json_decode_fails_to_decode_unrecognised_array_of_dicts() { 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"}]}]' data := '[{"twins":[{"id":123,"seed":"abcde","pubkey":"xyzasd"},{"id":456,"seed":"dfgdfgdfgd","pubkey":"skjldskljh45sdf"}]}]'
json.decode(TestTwins, data) or { json.decode(TestTwins, data) or {
assert err.msg == "expected field 'twins' is missing" assert err.msg() == "expected field 'twins' is missing"
return return
} }
assert false assert false

View File

@ -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}}}' 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 { json.decode(Data, data) or {
println(err) 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 return
} }
assert false 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}}}' 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 { json.decode(Data, data) or {
println(err) 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 return
} }
assert false assert false

View File

@ -20,8 +20,7 @@ pub enum ConnectionFlag {
} }
struct SQLError { struct SQLError {
msg string MessageError
code int
} }
// TODO: Documentation // TODO: Documentation

View File

@ -21,7 +21,7 @@ pub const (
) )
pub fn socket_error_message(potential_code int, s string) ?int { 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 { pub fn socket_error(potential_code int) ?int {

View File

@ -627,18 +627,25 @@ fn (mut h Header) add_key(key string) {
// Custom error struct for invalid header tokens // Custom error struct for invalid header tokens
struct HeaderKeyError { struct HeaderKeyError {
msg string Error
code int code int
header string header string
invalid_char byte 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 // is_valid checks if the header token contains all valid bytes
fn is_valid(header string) ? { fn is_valid(header string) ? {
for _, c in header { for _, c in header {
if int(c) >= 128 || !is_token(c) { if int(c) >= 128 || !is_token(c) {
return IError(HeaderKeyError{ return IError(HeaderKeyError{
msg: "Invalid header key: '$header'"
code: 1 code: 1
header: header header: header
invalid_char: c invalid_char: c
@ -647,7 +654,6 @@ fn is_valid(header string) ? {
} }
if header.len == 0 { if header.len == 0 {
return IError(HeaderKeyError{ return IError(HeaderKeyError{
msg: "Invalid header key: '$header'"
code: 2 code: 2
header: header header: header
invalid_char: 0 invalid_char: 0

View File

@ -261,15 +261,20 @@ pub:
} }
pub struct UnexpectedExtraAttributeError { pub struct UnexpectedExtraAttributeError {
pub: Error
msg string attributes []string
code int }
pub fn (err UnexpectedExtraAttributeError) msg() string {
return 'Encountered unexpected extra attributes: $err.attributes'
} }
pub struct MultiplePathAttributesError { pub struct MultiplePathAttributesError {
pub: Error
msg string = 'Expected at most one path attribute' }
code int
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 // multipart_form_body converts form and file data into a multipart/form

View File

@ -48,7 +48,7 @@ pub fn (mut s Server) listen_and_serve() ? {
break break
} }
mut conn := s.listener.accept() or { 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') eprintln('accept() failed: $err; skipping')
} }
continue continue

View File

@ -21,13 +21,13 @@ mut:
pub fn dial_tcp(address string) ?&TcpConn { pub fn dial_tcp(address string) ?&TcpConn {
addrs := resolve_addrs_fuzzy(address, .tcp) or { 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 // Very simple dialer
for addr in addrs { for addr in addrs {
mut s := new_tcp_socket(addr.family()) or { 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 { s.connect(addr) or {
// Connection failed // Connection failed
@ -215,10 +215,10 @@ mut:
} }
pub fn listen_tcp(family AddrFamily, saddr string) ?&TcpListener { 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 { 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) // TODO(logic to pick here)

View File

@ -410,7 +410,7 @@ fn split_by_scheme(rawurl string) ?[]string {
} }
fn get_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] return split[0]
} }
@ -593,9 +593,9 @@ fn parse_host(host string) ?string {
// We do impose some restrictions on the zone, to avoid stupidity // We do impose some restrictions on the zone, to avoid stupidity
// like newlines. // like newlines.
if zone := host[..i].index('%25') { if zone := host[..i].index('%25') {
host1 := unescape(host[..zone], .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 } host2 := unescape(host[zone..i], .encode_zone) or { return err.msg() }
host3 := unescape(host[i..], .encode_host) or { return err.msg } host3 := unescape(host[i..], .encode_host) or { return err.msg() }
return host1 + host2 + host3 return host1 + host2 + host3
} }
if idx := host.last_index(':') { 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 return h
// host = h // host = h
// return host // return host

View File

@ -122,7 +122,7 @@ fn (mut s Server) serve_client(mut c Client) ? {
} }
s.setup_callbacks(mut server_client) s.setup_callbacks(mut server_client)
c.listen() or { c.listen() or {
s.logger.error(err.msg) s.logger.error(err.msg())
return err return err
} }
} }

View File

@ -478,22 +478,28 @@ pub fn (mut f File) flush() {
C.fflush(f.cfile) C.fflush(f.cfile)
} }
pub struct ErrFileNotOpened { pub struct FileNotOpenedError {
msg string = 'os: file not opened' Error
code int
} }
pub struct ErrSizeOfTypeIs0 { pub fn (err FileNotOpenedError) msg() string {
msg string = 'os: size of type is 0' return 'os: file not opened'
code int }
pub struct SizeOfTypeIs0Error {
Error
}
pub fn (err SizeOfTypeIs0Error) msg() string {
return 'os: size of type is 0'
} }
fn error_file_not_opened() IError { fn error_file_not_opened() IError {
return IError(&ErrFileNotOpened{}) return IError(&FileNotOpenedError{})
} }
fn error_size_of_type_0() IError { fn error_size_of_type_0() IError {
return IError(&ErrSizeOfTypeIs0{}) return IError(&SizeOfTypeIs0Error{})
} }
// read_struct reads a single struct of type `T` // read_struct reads a single struct of type `T`

View File

@ -273,7 +273,7 @@ fn test_write_raw_at_negative_pos() ? {
if _ := f.write_raw_at(another_point, -1) { if _ := f.write_raw_at(another_point, -1) {
assert false 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() f.close()
} }
@ -328,7 +328,7 @@ fn test_read_raw_at_negative_pos() ? {
if _ := f.read_raw_at<Point>(-1) { if _ := f.read_raw_at<Point>(-1) {
assert false assert false
} }
f.read_raw_at<Point>(-234) or { assert err.msg == 'Invalid argument' } f.read_raw_at<Point>(-234) or { assert err.msg() == 'Invalid argument' }
f.close() f.close()
} }

View File

@ -150,12 +150,12 @@ pub fn rmdir_all(path string) ? {
for item in items { for item in items {
fullpath := join_path_single(path, item) fullpath := join_path_single(path, item)
if is_dir(fullpath) && !is_link(fullpath) { if is_dir(fullpath) && !is_link(fullpath) {
rmdir_all(fullpath) or { ret_err = err.msg } rmdir_all(fullpath) or { ret_err = err.msg() }
} else { } 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 { if ret_err.len > 0 {
return error(ret_err) return error(ret_err)
} }
@ -404,13 +404,16 @@ fn executable_fallback() string {
return exepath return exepath
} }
pub struct ErrExecutableNotFound { pub struct ExecutableNotFoundError {
msg string = 'os: failed to find executable' Error
code int }
pub fn (err ExecutableNotFoundError) msg() string {
return 'os: failed to find executable'
} }
fn error_failed_to_find_executable() IError { 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 // find_exe_path walks the environment PATH, just like most shell do, it returns

View File

@ -35,7 +35,7 @@ fn test_open_file() {
filename := './test1.txt' filename := './test1.txt'
hello := 'hello world!' hello := 'hello world!'
os.open_file(filename, 'r+', 0o666) or { 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{} os.File{}
} }
mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) } mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) }
@ -51,7 +51,7 @@ fn test_open_file_binary() {
filename := './test1.dat' filename := './test1.dat'
hello := 'hello \n world!' hello := 'hello \n world!'
os.open_file(filename, 'r+', 0o666) or { 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{} os.File{}
} }
mut file := os.open_file(filename, 'wb+', 0o666) or { panic(err) } mut file := os.open_file(filename, 'wb+', 0o666) or { panic(err) }

View File

@ -20,7 +20,7 @@ fn test_signal_opt_invalid_argument() {
assert false assert false
} }
os.signal_opt(.kill, default_handler) or { os.signal_opt(.kill, default_handler) or {
assert err.msg == 'Invalid argument' assert err.msg() == 'Invalid argument'
assert err.code == 22 assert err.code == 22
} }
} }

View File

@ -29,14 +29,8 @@ struct Range {
comparator_sets []ComparatorSet comparator_sets []ComparatorSet
} }
struct InvalidComparatorCountError {
msg string
code int
}
struct InvalidComparatorFormatError { struct InvalidComparatorFormatError {
msg string MessageError
code int
} }
fn (r Range) satisfies(ver Version) bool { fn (r Range) satisfies(ver Version) bool {

View File

@ -20,13 +20,20 @@ pub enum Increment {
} }
struct EmptyInputError { struct EmptyInputError {
msg string = 'Empty input' Error
code int }
pub fn (err EmptyInputError) msg() string {
return 'Empty input'
} }
struct InvalidVersionFormatError { struct InvalidVersionFormatError {
msg string Error
code int input string
}
pub fn (err InvalidVersionFormatError) msg() string {
return 'Invalid version format for input "$err.input"'
} }
// * Constructor. // * Constructor.
@ -38,7 +45,7 @@ pub fn from(input string) ?Version {
raw_version := parse(input) raw_version := parse(input)
version := raw_version.validate() or { version := raw_version.validate() or {
return IError(&InvalidVersionFormatError{ return IError(&InvalidVersionFormatError{
msg: 'Invalid version format for input "$input"' input: input
}) })
} }
return version return version

View File

@ -33,8 +33,7 @@ struct Stmt {
} }
struct SQLError { struct SQLError {
msg string MessageError
code int
} }
// //

View File

@ -14,7 +14,7 @@ fn do_rec_calc_send(chs []chan i64, mut sem sync.Semaphore) {
mut msg := '' mut msg := ''
for { for {
mut s := get_val_from_chan(chs[0]) or { mut s := get_val_from_chan(chs[0]) or {
msg = err.msg msg = err.msg()
break break
} }
s++ s++

View File

@ -4,13 +4,16 @@
module time module time
pub struct TimeParseError { pub struct TimeParseError {
msg string Error
code int code int
} }
pub fn (err TimeParseError) msg() string {
return 'Invalid time format code: $err.code'
}
fn error_invalid_time(code int) IError { fn error_invalid_time(code int) IError {
return TimeParseError{ return TimeParseError{
msg: 'Invalid time format code: $code'
code: code code: code
} }
} }

View File

@ -477,14 +477,14 @@ fn (c Checker) check_quoted_escapes(q ast.Quoted) ? {
c.check_unicode_escape(s.text[pos..pos + 11]) or { c.check_unicode_escape(s.text[pos..pos + 11]) or {
st := s.state() st := s.state()
return error(@MOD + '.' + @STRUCT + '.' + @FN + 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 { } else {
pos := s.state().pos pos := s.state().pos
c.check_unicode_escape(s.text[pos..]) or { c.check_unicode_escape(s.text[pos..]) or {
st := s.state() st := s.state()
return error(@MOD + '.' + @STRUCT + '.' + @FN + 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)}...')
} }
} }
} }

View File

@ -47,7 +47,7 @@ pub fn (c Config) read_input() ?string {
if os.is_file(c.file_path) { if os.is_file(c.file_path) {
text = os.read_file(c.file_path) or { text = os.read_file(c.file_path) or {
return error(@MOD + '.' + @STRUCT + '.' + @FN + return error(@MOD + '.' + @STRUCT + '.' + @FN +
' Could not read "$c.file_path": "$err.msg"') ' Could not read "$c.file_path": "$err.msg()"')
} }
} }
return text return text

View File

@ -55,7 +55,7 @@ pub fn new_scanner(config Config) ?&Scanner {
if os.is_file(file_path) { if os.is_file(file_path) {
text = os.read_file(file_path) or { text = os.read_file(file_path) or {
return error(@MOD + '.' + @STRUCT + '.' + @FN + return error(@MOD + '.' + @STRUCT + '.' + @FN +
' Could not read "$file_path": "$err.msg"') ' Could not read "$file_path": "$err.msg()"')
} }
} }
mut s := &Scanner{ mut s := &Scanner{

View File

@ -155,12 +155,12 @@ fn test_alexcrichton_toml_rs() ? {
v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or { v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or {
contents := os.read_file(v_toml_json_path) ? 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_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"',
alexcrichton_toml_json_path]) or { alexcrichton_toml_json_path]) or {
contents := os.read_file(v_toml_json_path) ? 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 assert alexcrichton_normalized_json == v_normalized_json
@ -199,7 +199,7 @@ fn test_alexcrichton_toml_rs() ? {
assert false assert false
} else { } else {
if !hide_oks { if !hide_oks {
println(' $err.msg') println(' $err.msg()')
} }
assert true assert true
} }

View File

@ -141,11 +141,11 @@ fn test_burnt_sushi_tomltest() ? {
v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or { v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or {
contents := os.read_file(v_toml_json_path) ? 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 { bs_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', bs_toml_json_path]) or {
contents := os.read_file(v_toml_json_path) ? 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 assert bs_normalized_json == v_normalized_json
@ -182,7 +182,7 @@ fn test_burnt_sushi_tomltest() ? {
assert false assert false
} else { } else {
if !hide_oks { if !hide_oks {
println(' $err.msg') println(' $err.msg()')
} }
assert true assert true
} }

View File

@ -181,7 +181,7 @@ fn test_iarna_toml_spec_tests() ? {
// NOTE there's known errors with the python convertion method. // 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. // For now we just ignore them as it's a broken tool - not a wrong test-case.
// Uncomment this print to see/check them. // Uncomment this print to see/check them.
// eprintln(err.msg + '\n$contents') // eprintln(err.msg() + '\n$contents')
e++ e++
println('ERR [${i + 1}/$valid_test_files.len] "$valid_test_file" EXCEPTION [$e/$valid_value_exceptions.len]...') println('ERR [${i + 1}/$valid_test_files.len] "$valid_test_file" EXCEPTION [$e/$valid_value_exceptions.len]...')
continue 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 { v_normalized_json := run([jq, '-S', '-f "$jq_normalize_path"', v_toml_json_path]) or {
contents := os.read_file(v_toml_json_path) ? 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] cmd := [jq, '-S', '-f "$jq_normalize_path"', iarna_toml_json_path]
iarna_normalized_json := run(cmd) or { iarna_normalized_json := run(cmd) or {
contents := os.read_file(v_toml_json_path) ? 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 assert iarna_normalized_json == v_normalized_json
@ -250,7 +250,7 @@ fn test_iarna_toml_spec_tests() ? {
assert false assert false
} else { } else {
if !hide_oks { if !hide_oks {
println(' $err.msg') println(' $err.msg()')
} }
assert true assert true
} }

View File

@ -37,13 +37,13 @@ fn test_toml_with_bom() {
// Re-cycle bad_toml_doc // Re-cycle bad_toml_doc
mut bad_toml_doc := empty_toml_document mut bad_toml_doc := empty_toml_document
bad_toml_doc = toml.parse(toml_text_with_utf16_bom) or { bad_toml_doc = toml.parse(toml_text_with_utf16_bom) or {
println(' $err.msg') println(' $err.msg()')
assert true assert true
empty_toml_document empty_toml_document
} }
bad_toml_doc = toml.parse(toml_text_with_utf32_bom) or { bad_toml_doc = toml.parse(toml_text_with_utf32_bom) or {
println(' $err.msg') println(' $err.msg()')
assert true assert true
empty_toml_document empty_toml_document
} }

View File

@ -183,7 +183,6 @@ pub:
pub struct StringInterLiteral { pub struct StringInterLiteral {
pub: pub:
vals []string vals []string
exprs []Expr
fwidths []int fwidths []int
precisions []int precisions []int
pluss []bool pluss []bool
@ -191,6 +190,7 @@ pub:
fmt_poss []token.Pos fmt_poss []token.Pos
pos token.Pos pos token.Pos
pub mut: pub mut:
exprs []Expr
expr_types []Type expr_types []Type
fmts []byte fmts []byte
need_fmts []bool // an explicit non-default fmt required, e.g. `x` need_fmts []bool // an explicit non-default fmt required, e.g. `x`

View File

@ -69,7 +69,7 @@ pub fn new_builder(pref &pref.Preferences) Builder {
pref: pref pref: pref
table: table table: table
checker: checker.new_checker(table, pref) checker: checker.new_checker(table, pref)
transformer: transformer.new_transformer(pref) transformer: transformer.new_transformer_with_table(table, pref)
compiled_dir: compiled_dir compiled_dir: compiled_dir
cached_msvc: msvc cached_msvc: msvc
} }

View File

@ -36,11 +36,12 @@ pub fn compile_c(mut b builder.Builder) {
pub fn gen_c(mut b builder.Builder, v_files []string) string { pub fn gen_c(mut b builder.Builder, v_files []string) string {
b.front_and_middle_stages(v_files) or { b.front_and_middle_stages(v_files) or {
if err.code != 9999 { if err.code() != 9999 {
builder.verror(err.msg) builder.verror(err.msg())
} }
return '' return ''
} }
util.timing_start('C GEN') util.timing_start('C GEN')
res := c.gen(b.parsed_files, b.table, b.pref) res := c.gen(b.parsed_files, b.table, b.pref)
util.timing_measure('C GEN') util.timing_measure('C GEN')

View File

@ -22,7 +22,7 @@ pub fn compile(command string, pref &pref.Preferences, backend_cb FnBackend) {
} }
os.is_writable_folder(output_folder) or { os.is_writable_folder(output_folder) or {
// An early error here, is better than an unclear C error later: // 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 // Construct the V object from command line arguments
mut b := new_builder(pref) mut b := new_builder(pref)

View File

@ -181,7 +181,7 @@ fn (mut b Builder) rebuild_cached_module(vexe string, imp_path string) string {
} }
b.v_build_module(vexe, imp_path) b.v_build_module(vexe, imp_path)
rebuilded_o := b.pref.cache_manager.exists('.o', imp_path) or { 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 return rebuilded_o
} }

View File

@ -535,7 +535,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
node.pos) node.pos)
} }
} else { } else {
c.error('cannot assign to `$left`: $err.msg', right.pos()) c.error('cannot assign to `$left`: $err.msg()', right.pos())
} }
} }
} }

View File

@ -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_] { if left_sym.kind !in [.sum_type, .interface_] {
elem_type := right_final.array_info().elem_type elem_type := right_final.array_info().elem_type
c.check_expected(left_type, elem_type) or { 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) left_right_pos)
} }
} }
@ -620,7 +620,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
.map { .map {
map_info := right_final.map_info() map_info := right_final.map_info()
c.check_expected(left_type, map_info.key_type) or { 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) left_right_pos)
} }
node.left_type = map_info.key_type 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 // Verify methods
for imethod in imethods { for imethod in imethods {
method := c.table.find_method_with_embeds(typ_sym, imethod.name) or { 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 { typ_sym.find_method_with_generic_parent(imethod.name) or {
c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`", c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
pos) pos)
@ -1343,10 +1352,17 @@ 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 // voidptr is an escape hatch, it should be allowed to be passed
if utyp != ast.voidptr_type { if utyp != ast.voidptr_type {
// >> 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`", c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`",
pos) pos)
} }
} }
}
inter_sym.info.types << utyp inter_sym.info.types << utyp
} }
return true return true
@ -1571,15 +1587,15 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
has_field = true has_field = true
mut embed_types := []ast.Type{} mut embed_types := []ast.Type{}
field, embed_types = c.table.find_field_from_embeds(sym, field_name) or { field, embed_types = c.table.find_field_from_embeds(sym, field_name) or {
if err.msg != '' { if err.msg() != '' {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
} }
has_field = false has_field = false
ast.StructField{}, []ast.Type{} ast.StructField{}, []ast.Type{}
} }
node.from_embed_types = embed_types node.from_embed_types = embed_types
if sym.kind in [.aggregate, .sum_type] { if sym.kind in [.aggregate, .sum_type] {
unknown_field_msg = err.msg unknown_field_msg = err.msg()
} }
} }
if !c.inside_unsafe { if !c.inside_unsafe {
@ -1600,8 +1616,8 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
has_field = true has_field = true
mut embed_types := []ast.Type{} mut embed_types := []ast.Type{}
field, embed_types = c.table.find_field_from_embeds(gs, field_name) or { field, embed_types = c.table.find_field_from_embeds(gs, field_name) or {
if err.msg != '' { if err.msg() != '' {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
} }
has_field = false has_field = false
ast.StructField{}, []ast.Type{} 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)) suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name))
c.error(suggestion.say(unknown_field_msg), node.pos) 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) c.error(unknown_field_msg, node.pos)
} }
return ast.void_type return ast.void_type
@ -2128,7 +2158,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
if flag.contains('@VROOT') { if flag.contains('@VROOT') {
// c.note(checker.vroot_is_deprecated_message, node.pos) // c.note(checker.vroot_is_deprecated_message, node.pos)
vroot := util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or { 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 return
} }
node.val = 'include $vroot' node.val = 'include $vroot'
@ -2143,7 +2173,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
} }
if flag.contains('@VMODROOT') { if flag.contains('@VMODROOT') {
vroot := util.resolve_vmodroot(flag, c.file.path) or { vroot := util.resolve_vmodroot(flag, c.file.path) or {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
return return
} }
node.val = 'include $vroot' node.val = 'include $vroot'
@ -2152,7 +2182,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
} }
if flag.contains('\$env(') { if flag.contains('\$env(') {
env := util.resolve_env_value(flag, true) or { env := util.resolve_env_value(flag, true) or {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
return return
} }
node.main = env node.main = env
@ -2171,15 +2201,15 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
'--cflags --libs $node.main'.split(' ') '--cflags --libs $node.main'.split(' ')
} }
mut m := pkgconfig.main(args) or { mut m := pkgconfig.main(args) or {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
return return
} }
cflags := m.run() or { cflags := m.run() or {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
return return
} }
c.table.parse_cflag(cflags, c.mod, c.pref.compile_defines_all) or { 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 return
} }
} }
@ -2189,7 +2219,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
if flag.contains('@VROOT') { if flag.contains('@VROOT') {
// c.note(checker.vroot_is_deprecated_message, node.pos) // c.note(checker.vroot_is_deprecated_message, node.pos)
flag = util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or { 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 return
} }
} }
@ -2199,13 +2229,13 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
} }
if flag.contains('@VMODROOT') { if flag.contains('@VMODROOT') {
flag = util.resolve_vmodroot(flag, c.file.path) or { flag = util.resolve_vmodroot(flag, c.file.path) or {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
return return
} }
} }
if flag.contains('\$env(') { if flag.contains('\$env(') {
flag = util.resolve_env_value(flag, true) or { flag = util.resolve_env_value(flag, true) or {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
return return
} }
} }
@ -2219,7 +2249,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
} }
// println('adding flag "$flag"') // println('adding flag "$flag"')
c.table.parse_cflag(flag, c.mod, c.pref.compile_defines_all) or { 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 { else {
@ -3016,14 +3046,6 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
typ: typ typ: typ
is_optional: is_optional 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 { // if typ == ast.t_type {
// sym := c.table.sym(c.cur_generic_type) // sym := c.table.sym(c.cur_generic_type)
// println('IDENT T unresolved $node.name typ=$sym.name') // println('IDENT T unresolved $node.name typ=$sym.name')

View File

@ -12,7 +12,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
node.left_type = c.expr(node.left) node.left_type = c.expr(node.left)
if node.is_env { if node.is_env {
env_value := util.resolve_env_value("\$env('$node.args_var')", false) or { 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 return ast.string_type
} }
node.env_value = env_value 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) left_type := c.expr(cond.left)
right_type := c.expr(cond.right) right_type := c.expr(cond.right)
expr := c.find_definition(cond.left) or { expr := c.find_definition(cond.left) or {
c.error(err.msg, cond.left.pos) c.error(err.msg(), cond.left.pos)
return false return false
} }
if !c.check_types(right_type, left_type) { 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 return false
} }
expr := c.find_obj_definition(cond.obj) or { expr := c.find_obj_definition(cond.obj) or {
c.error(err.msg, cond.pos) c.error(err.msg(), cond.pos)
return false return false
} }
if !c.check_types(typ, ast.bool_type) { 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 { ast.ComptimeCall {
if cond.is_pkgconfig { if cond.is_pkgconfig {
mut m := pkgconfig.main([cond.args_var]) or { mut m := pkgconfig.main([cond.args_var]) or {
c.error(err.msg, cond.pos) c.error(err.msg(), cond.pos)
return true return true
} }
m.run() or { return true } m.run() or { return true }

View File

@ -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)) default_typ := c.check_expr_opt_call(default_expr, c.expr(default_expr))
node.default_type = default_typ node.default_type = default_typ
c.check_expected(default_typ, node.elem_type) or { 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 { 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 { if expr !is ast.TypeNode {
c.check_expected(typ, elem_type) or { 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())
} }
} }
} }

View File

@ -894,7 +894,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
continue 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. // Warn about automatic (de)referencing, which will be removed soon.
if func.language != .c && !c.inside_unsafe && typ.nr_muls() != param.typ.nr_muls() 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 continue
} }
c.check_expected_call_arg(utyp, unwrap_typ, node.language, call_arg) or { 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 has_method = true
mut embed_types := []ast.Type{} mut embed_types := []ast.Type{}
method, embed_types = c.table.find_method_from_embeds(left_sym, method_name) or { method, embed_types = c.table.find_method_from_embeds(left_sym, method_name) or {
if err.msg != '' { if err.msg() != '' {
c.error(err.msg, node.pos) c.error(err.msg(), node.pos)
} }
has_method = false has_method = false
ast.Fn{}, []ast.Type{} 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 { if left_sym.kind == .aggregate {
// the error message contains the problematic type // the error message contains the problematic type
unknown_method_msg = err.msg unknown_method_msg = err.msg()
} }
} }
if has_method { if has_method {
@ -1285,7 +1285,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
// continue // continue
// } // }
if got_arg_typ != ast.void_type { 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) 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, c.check_expected_call_arg(targ, c.unwrap_generic(exp_arg_typ), node.language,
arg) or { arg) or {
if targ != ast.void_type { 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) 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 info := left_sym.info as ast.Map
arg_type := c.expr(node.args[0].expr) 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.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 {} else {}

View File

@ -203,7 +203,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
} }
for st in branch.stmts { for st in branch.stmts {
// must not contain C statements // 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 { if mut branch.cond is ast.IfGuardExpr {

View File

@ -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] { for st in branch.stmts[0..branch.stmts.len - 1] {
// must not contain C statements // must not contain C statements
st.check_c_expr() or { 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 { } else if ret_type != ast.void_type {

View File

@ -81,7 +81,7 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
} }
} }
} else { } 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()) 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 { } 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.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) field.pos)
} }
} }

View File

@ -11,7 +11,7 @@ fn test_get_parent_mod_on_root_folder() {
// TODO: add an equivalent windows check for c:\ // TODO: add an equivalent windows check for c:\
$if !windows { $if !windows {
assert '---' == get_parent_mod('/') or { 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() { fn test_get_parent_mod_current_folder() {
// TODO: this should may be return '' reliably on windows too: // TODO: this should may be return '' reliably on windows too:
// assert '' == get_parent_mod('.') or { // 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() ? { fn test_get_parent_mod_normal_cases() ? {
assert '---' == get_parent_mod(os.join_path(@VMODROOT, 'vlib/v')) or { 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? // TODO: WTF?

View File

@ -67,7 +67,7 @@ fn test_fmt() {
continue continue
} }
vfmt_result_file := os.join_path(tmpfolder, 'vfmt_run_over_$ifilename') 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)) eprintln(diff.color_compare_files(diff_cmd, opath, vfmt_result_file))
continue continue
} }
@ -75,7 +75,7 @@ fn test_fmt() {
eprintln(fmt_bench.step_message_ok(vrelpath)) eprintln(fmt_bench.step_message_ok(vrelpath))
} }
restore_bin2v_placeholder() or { 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() fmt_bench.stop()
eprintln(term.h_divider('-')) eprintln(term.h_divider('-'))
@ -90,7 +90,7 @@ fn prepare_bin2v_file(mut fmt_bench benchmark.Benchmark) {
fmt_bench.step() fmt_bench.step()
write_bin2v_keep_content() or { write_bin2v_keep_content() or {
fmt_bench.fail() 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 return
} }
fmt_bench.ok() fmt_bench.ok()

View File

@ -34,13 +34,13 @@ fn branches_are_struct_inits() {
fn branches_are_call_exprs_with_or_blocks() { fn branches_are_call_exprs_with_or_blocks() {
match 'a' { match 'a' {
'b' { foo() or { panic(err.msg) } } 'b' { foo() or { panic(err) } }
} }
match 'a' { match 'a' {
'b' { 'b' {
foo() or { foo() or {
// do stuff // do stuff
panic(err.msg) panic(err)
} }
} }
} }
@ -48,7 +48,7 @@ fn branches_are_call_exprs_with_or_blocks() {
'b' { 'b' {
foo() or { foo() or {
another_stmt() another_stmt()
panic(err.msg) panic(err)
} }
} }
} }

View File

@ -51,8 +51,7 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) {
} }
struct UnsupportedAssertCtempTransform { struct UnsupportedAssertCtempTransform {
msg string Error
code int
} }
const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{}) const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{})

View File

@ -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 { } else if or_block.kind == .propagate {
if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) { 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) }` // 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 { if g.pref.is_debug {
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) 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 { } 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 { } else if !isnil(g.fn_decl) && g.fn_decl.is_test {
g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name) 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,') 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 { if g.pref.build_mode != .build_module {
methods_struct.writeln('\t},') methods_struct.writeln('\t},')
} }

View File

@ -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) { 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 // 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 // and the test is considered failed
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) 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);') 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) { 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 // 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 // and the test is considered failed
paline, pafile, pamod, pafn := g.panic_debug_info(return_stmt.pos) 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);') g.writeln('\tlongjmp(g_jump_buffer, 1);')
} }

View File

@ -320,7 +320,7 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool {
} }
ast.PostfixExpr { ast.PostfixExpr {
ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or { ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or {
verror(err.msg) verror(err.msg())
return false return false
} }
g.write('defined($ifdef)') g.write('defined($ifdef)')

View File

@ -74,7 +74,7 @@ fn (mut g JsGen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool {
} }
ast.PostfixExpr { ast.PostfixExpr {
ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or { ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or {
verror(err.msg) verror(err.msg())
return false return false
} }
g.write('$ifdef') g.write('$ifdef')

View File

@ -1057,8 +1057,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
} }
struct UnsupportedAssertCtempTransform { struct UnsupportedAssertCtempTransform {
msg string Error
code int
} }
const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{}) const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{})

View File

@ -218,7 +218,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
if op == .decl_assign { if op == .decl_assign {
// a, b := a + 1, b // a, b := a + 1, b
for r in right { 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 { } else if left.len > 1 {
// a, b = b, a // a, b = b, a

View File

@ -32,6 +32,7 @@ pub fn (mut p Parser) parse_array_type(expecting token.Kind) ast.Type {
fixed_size = const_field.expr.val.int() fixed_size = const_field.expr.val.int()
} else { } else {
if mut const_field.expr is ast.InfixExpr { 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) mut t := transformer.new_transformer(p.pref)
folded_expr := t.infix_expr(mut const_field.expr) folded_expr := t.infix_expr(mut const_field.expr)

View File

@ -41,7 +41,7 @@ fn (mut p Parser) sql_expr() ast.Expr {
if e.right is ast.Ident { if e.right is ast.Ident {
if !p.scope.known_var(e.right.name) { if !p.scope.known_var(e.right.name) {
p.check_undefined_variables([e.left], e.right) or { 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)
} }
} }
} }

View File

@ -21,7 +21,9 @@ fn test_dependency_resolution_fails_correctly() {
mut errors := []string{} mut errors := []string{}
for pc in pc_files { for pc in pc_files {
pcname := os.file_name(pc).replace('.pc', '') 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.len < pc_files.len
assert errors == ['could not resolve dependency xyz-unknown-package'] assert errors == ['could not resolve dependency xyz-unknown-package']

View File

@ -110,7 +110,7 @@ pub fn new_scanner_file(file_path string, comments_mode CommentsMode, pref &pref
verror('$file_path is not a file') verror('$file_path is not a file')
} }
raw_text := util.read_file(file_path) or { raw_text := util.read_file(file_path) or {
verror(err.msg) verror(err.msg())
return voidptr(0) return voidptr(0)
} }
mut s := &Scanner{ mut s := &Scanner{

View File

@ -44,7 +44,7 @@ const return_types = [
ReturnType{ ReturnType{
name: '?' name: '?'
init: "error('an error')" 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 no_assert_kw: true
}, },
ReturnType{ ReturnType{

View File

@ -51,7 +51,7 @@ fn test_multiple_ret() {
// none case // none case
wrapper1 := fn () (string, string) { wrapper1 := fn () (string, string) {
res2_1, res2_2 := split_to_two('') or { res2_1, res2_2 := split_to_two('') or {
assert err.msg == '' assert err.msg() == ''
return 'replaced', 'val' return 'replaced', 'val'
} }
return res2_1, res2_2 return res2_1, res2_2
@ -63,7 +63,7 @@ fn test_multiple_ret() {
// error case // error case
wrapper2 := fn () (string, string) { wrapper2 := fn () (string, string) {
res3_1, res3_2 := split_to_two('fishhouse') or { res3_1, res3_2 := split_to_two('fishhouse') or {
assert err.msg == 'error' assert err.msg() == 'error'
return 'replaced', 'val' return 'replaced', 'val'
} }
return res3_1, res3_2 return res3_1, res3_2

View File

@ -26,7 +26,7 @@ fn test_if_expr_of_optional() {
if _ := foo3() { if _ := foo3() {
assert false assert false
} else { } else {
assert err.msg == 'foo3 error' assert err.msg() == 'foo3 error'
} }
a4 := foo4() or { panic('error') } a4 := foo4() or { panic('error') }

View File

@ -3,7 +3,7 @@ import os
fn main() { fn main() {
vexe := os.getenv('VEXE') vexe := os.getenv('VEXE')
vroot := os.dir(vexe) vroot := os.dir(vexe)
mut files := os.ls(vroot) or { panic(err.msg) } mut files := os.ls(vroot) or { panic(err) }
files.sort() files.sort()
for file in files { for file in files {
if file.ends_with('.md') { if file.ends_with('.md') {

View File

@ -12,7 +12,7 @@ fn test_lhs_option() {
} }
fn ret_no_opt(n int) int { 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() { fn test_opt_return_no_opt() {

View File

@ -7,11 +7,11 @@ fn test_err_with_code() {
assert false assert false
_ := w _ := w
} else { } else {
assert err.msg == 'hi' assert err.msg() == 'hi'
assert err.code == 137 assert err.code == 137
} }
v := opt_err_with_code() or { v := opt_err_with_code() or {
assert err.msg == 'hi' assert err.msg() == 'hi'
assert err.code == 137 assert err.code == 137
return return
} }
@ -25,7 +25,7 @@ fn opt_err() ?string {
fn test_err() { fn test_err() {
v := opt_err() or { v := opt_err() or {
assert err.msg == 'hi' assert err.msg() == 'hi'
return return
} }
assert false assert false
@ -74,7 +74,7 @@ fn test_if_else_opt() {
if _ := err_call(false) { if _ := err_call(false) {
assert false assert false
} else { } else {
assert err.msg.len != 0 assert err.msg().len != 0
} }
} }
@ -151,12 +151,12 @@ fn test_or_return() {
if _ := or_return_error() { if _ := or_return_error() {
assert false assert false
} else { } else {
assert err.msg.len != 0 assert err.msg().len != 0
} }
if _ := or_return_none() { if _ := or_return_none() {
assert false assert false
} else { } 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 { f(0) or {
assert err.msg == '0' assert err.msg() == '0'
return return
} }
} }
@ -304,7 +304,7 @@ fn test_option_void_return_types_of_anon_fn_in_struct() {
} }
foo.f(0) or { foo.f(0) or {
assert err.msg == '0' assert err.msg() == '0'
return return
} }
} }

View File

@ -5,7 +5,7 @@ fn foo() ? {
fn test_optional_void() { fn test_optional_void() {
foo() or { foo() or {
println(err) println(err)
assert err.msg == 'something' assert err.msg() == 'something'
return return
} }
} }
@ -17,7 +17,7 @@ fn bar() ? {
fn test_optional_void_only_question() { fn test_optional_void_only_question() {
bar() or { bar() or {
println(err) println(err)
assert err.msg == 'bar error' assert err.msg() == 'bar error'
return return
} }
} }
@ -38,12 +38,12 @@ fn option_void(a int) ? {
fn test_optional_void_with_return() { fn test_optional_void_with_return() {
option_void(0) or { option_void(0) or {
println(err) println(err)
assert err.msg == 'zero error' assert err.msg() == 'zero error'
return return
} }
option_void(-1) or { option_void(-1) or {
println(err) println(err)
assert err.msg == 'zero error' assert err.msg() == 'zero error'
return return
} }
assert true assert true

View File

@ -15,7 +15,7 @@ fn test_all_v_prod_files() {
bmark.step() bmark.step()
fres := runner.run_prod_file(options.wd, options.vexec, file) or { fres := runner.run_prod_file(options.wd, options.vexec, file) or {
bmark.fail() bmark.fail()
eprintln(bmark.step_message_fail(err.msg)) eprintln(bmark.step_message_fail(err.msg()))
assert false assert false
continue continue
} }

View File

@ -83,7 +83,7 @@ fn worker_repl(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
session.bmark.fail() session.bmark.fail()
tls_bench.fail() tls_bench.fail()
os.rmdir_all(tfolder) or { panic(err) } 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 assert false
return pool.no_result return pool.no_result
} }

View File

@ -35,7 +35,7 @@ pub fn full_path_to_v(dirs_in int) string {
} }
fn diff_files(file_result string, file_expected string) 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) return diff.color_compare_files(diffcmd, file_result, file_expected)
} }

View File

@ -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 } foo.g() or { assert false }
} }

View File

@ -38,7 +38,7 @@ fn test_decode() {
assert data.dependencies[0] == 'hello' assert data.dependencies[0] == 'hello'
assert data.unknown['test'][0] == 'foo' assert data.unknown['test'][0] == 'foo'
vmod.decode('') or { vmod.decode('') or {
assert err.msg == 'vmod: no content.' assert err.msg() == 'vmod: no content.'
exit(0) exit(0)
} }
} }

View File

@ -8,6 +8,9 @@ pub struct Transformer {
pref &pref.Preferences pref &pref.Preferences
pub mut: pub mut:
index &IndexState index &IndexState
table &ast.Table = 0
mut:
is_assert bool
} }
pub fn new_transformer(pref &pref.Preferences) &Transformer { 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) { pub fn (mut t Transformer) transform_files(ast_files []&ast.File) {
for i in 0 .. ast_files.len { for i in 0 .. ast_files.len {
mut file := unsafe { ast_files[i] } 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 // 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) { pub fn (mut t Transformer) check_safe_array(mut node ast.IndexExpr) {
if !t.pref.is_prod { if !t.pref.is_prod {
return return
@ -212,14 +170,7 @@ pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt {
ast.NodeError {} ast.NodeError {}
ast.AsmStmt {} ast.AsmStmt {}
ast.AssertStmt { ast.AssertStmt {
match mut node.expr { return t.assert_stmt(mut node)
ast.InfixExpr {
node.expr = t.find_assert_len(mut node.expr)
}
else {
node.expr = t.expr(mut node.expr)
}
}
} }
ast.AssignStmt { ast.AssignStmt {
t.find_new_array_len(node) 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.Import {}
ast.InterfaceDecl { ast.InterfaceDecl {
for mut field in node.fields { return t.interface_decl(mut node)
field.default_expr = t.expr(mut field.default_expr)
}
} }
ast.Module {} ast.Module {}
ast.Return { ast.Return {
@ -333,6 +282,63 @@ pub fn (mut t Transformer) stmt(mut node ast.Stmt) ast.Stmt {
return node 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 { 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} mut stop_index, mut unreachable_branches := -1, []int{cap: node.branches.len}
if node.is_comptime { if node.is_comptime {
@ -497,6 +503,14 @@ pub fn (mut t Transformer) for_stmt(mut node ast.ForStmt) ast.Stmt {
return node 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 { pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr {
match mut node { match mut node {
ast.AnonFn { ast.AnonFn {
@ -629,6 +643,11 @@ pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr {
ast.SqlExpr { ast.SqlExpr {
return t.sql_expr(mut node) return t.sql_expr(mut node)
} }
ast.StringInterLiteral {
for mut expr in node.exprs {
expr = t.expr(mut expr)
}
}
ast.StructInit { ast.StructInit {
node.update_expr = t.expr(mut node.update_expr) node.update_expr = t.expr(mut node.update_expr)
for mut field in node.fields { 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.pos)
pos.extend(node.right.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 return node
} else { } else {
match mut node.left { match mut node.left {

View File

@ -34,7 +34,7 @@ fn parse_attrs(name string, attrs []string) ?([]http.Method, string) {
} }
if x.len > 0 { if x.len > 0 {
return IError(http.UnexpectedExtraAttributeError{ return IError(http.UnexpectedExtraAttributeError{
msg: 'Encountered unexpected extra attributes: $x' attributes: x
}) })
} }
if methods.len == 0 { if methods.len == 0 {

View File

@ -68,7 +68,7 @@ fn assert_common_headers(received string) {
fn test_a_simple_tcp_client_can_connect_to_the_vweb_server() { fn test_a_simple_tcp_client_can_connect_to_the_vweb_server() {
received := simple_tcp_client(path: '/') or { received := simple_tcp_client(path: '/') or {
assert err.msg == '' assert err.msg() == ''
return return
} }
assert_common_headers(received) 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() { fn test_a_simple_tcp_client_simple_route() {
received := simple_tcp_client(path: '/simple') or { received := simple_tcp_client(path: '/simple') or {
assert err.msg == '' assert err.msg() == ''
return return
} }
assert_common_headers(received) 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 // tests that sending a content-length header of 0 doesn't hang on a read timeout
watch := time.new_stopwatch(auto_start: true) watch := time.new_stopwatch(auto_start: true)
simple_tcp_client(path: '/', headers: 'Content-Length: 0\r\n\r\n') or { simple_tcp_client(path: '/', headers: 'Content-Length: 0\r\n\r\n') or {
assert err.msg == '' assert err.msg() == ''
return return
} }
assert watch.elapsed() < 1 * time.second 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() { fn test_a_simple_tcp_client_html_page() {
received := simple_tcp_client(path: '/html_page') or { received := simple_tcp_client(path: '/html_page') or {
assert err.msg == '' assert err.msg() == ''
return return
} }
assert_common_headers(received) assert_common_headers(received)
@ -230,7 +230,7 @@ $contents\r
fn test_http_client_shutdown_does_not_work_without_a_cookie() { fn test_http_client_shutdown_does_not_work_without_a_cookie() {
x := http.get('http://$localserver/shutdown') or { x := http.get('http://$localserver/shutdown') or {
assert err.msg == '' assert err.msg() == ''
return return
} }
assert x.status() == .not_found assert x.status() == .not_found
@ -247,7 +247,7 @@ fn testsuite_end() {
'skey': 'superman' 'skey': 'superman'
} }
) or { ) or {
assert err.msg == '' assert err.msg() == ''
return return
} }
assert x.status() == .ok assert x.status() == .ok

View File

@ -255,7 +255,7 @@ pub fn (mut ctx Context) json_pretty<T>(j T) Result {
pub fn (mut ctx Context) file(f_path string) Result { pub fn (mut ctx Context) file(f_path string) Result {
ext := os.file_ext(f_path) ext := os.file_ext(f_path)
data := os.read_file(f_path) or { data := os.read_file(f_path) or {
eprint(err.msg) eprint(err.msg())
ctx.server_error(500) ctx.server_error(500)
return Result{} return Result{}
} }
@ -417,7 +417,7 @@ pub fn run_at<T>(global_app &T, host string, port int) {
request_app.Context = global_app.Context // copy the context ref that contains static files map etc request_app.Context = global_app.Context // copy the context ref that contains static files map etc
mut conn := l.accept() or { mut conn := l.accept() or {
// failures should not panic // failures should not panic
eprintln('accept() failed with error: $err.msg') eprintln('accept() failed with error: $err.msg()')
continue continue
} }
go handle_conn<T>(mut conn, mut request_app, routes) go handle_conn<T>(mut conn, mut request_app, routes)

View File

@ -22,13 +22,11 @@ mut:
} }
struct InvalidTokenError { struct InvalidTokenError {
msg string MessageError
code int
} }
struct UnknownTokenError { struct UnknownTokenError {
msg string MessageError
code int
} }
fn (mut p Parser) next() { fn (mut p Parser) next() {

View File

@ -43,7 +43,7 @@ fn test_raw_decode_null() ? {
fn test_raw_decode_invalid() ? { fn test_raw_decode_invalid() ? {
raw_decode('1z') or { 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 return
} }
assert false assert false