diff --git a/vlib/compiler/cc.v b/vlib/compiler/cc.v index cd29930485..2c9a8755b3 100644 --- a/vlib/compiler/cc.v +++ b/vlib/compiler/cc.v @@ -174,9 +174,9 @@ fn (v mut V) cc() { println('$path not found... building module $imp') if path.ends_with('vlib/ui.o') { println('copying ui...') - os.cp('$vdir/thirdparty/ui/ui.o', path) - os.cp('$vdir/thirdparty/ui/ui.vh', v_modules_path + - '/vlib/ui.vh') + _ = os.cp('$vdir/thirdparty/ui/ui.o', path) or { panic('error copying ui files') } + _ = os.cp('$vdir/thirdparty/ui/ui.vh', v_modules_path + + '/vlib/ui.vh') or { panic('error copying ui files') } } else { os.system('$vexe build module vlib${os.path_separator}$imp_path') diff --git a/vlib/compiler/gen_c.v b/vlib/compiler/gen_c.v index 13f71c1ef0..317dd2aada 100644 --- a/vlib/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -11,12 +11,15 @@ fn (p mut Parser) gen_or_else(pos int) string { } */ + // returns the type of the new variable fn (p mut Parser) gen_var_decl(name string, is_static bool) string { // Generate expression to tmp because we need its type first // `[typ] [name] = bool_expression();` pos := p.cgen.add_placeholder() + p.is_var_decl = true mut typ := p.bool_expression() + p.is_var_decl = false if typ.starts_with('...') { typ = typ[3..] } //p.gen('/*after expr*/') // Option check ? or { @@ -68,10 +71,13 @@ fn (p mut Parser) gen_blank_identifier_assign() { is_indexer := p.peek() == .lsbr is_fn_call, next_expr := p.is_next_expr_fn_call() pos := p.cgen.add_placeholder() + p.is_var_decl = true typ := p.bool_expression() + p.is_var_decl = false if !is_indexer && !is_fn_call { p.error_with_token_index('assigning `$next_expr` to `_` is redundant', assign_error_tok_idx) } + // handle or if p.tok == .key_orelse { p.gen_handle_option_or_else(typ, '', pos) } else { @@ -93,6 +99,7 @@ fn (p mut Parser) gen_handle_option_or_else(_typ, name string, fn_call_ph int) s p.cgen.set_placeholder(fn_call_ph, '$typ $tmp = ') typ = typ[7..] p.genln(';') + or_tok_idx := p.token_idx p.check(.key_orelse) p.check(.lcbr) p.register_var(Var { @@ -107,15 +114,26 @@ fn (p mut Parser) gen_handle_option_or_else(_typ, name string, fn_call_ph int) s is_mut: false is_used: true }) + if is_assign { + p.genln('$typ $name;') + } p.genln('if (!$tmp .ok) {') p.genln('string err = $tmp . error;') p.genln('int errcode = $tmp . ecode;') - p.statements() - if is_assign { - p.genln('$typ $name = *($typ*) $tmp . data;') + last_ph := p.cgen.add_placeholder() + last_typ := p.statements() + if is_assign && last_typ == typ { + expr_line := p.cgen.lines[p.cgen.lines.len-3] + last_expr := expr_line[last_ph..] + p.cgen.lines[p.cgen.lines.len-3] = '' + p.genln('if (!$tmp .ok) {') + p.genln('$name = $last_expr;') + p.genln('}') + } else if is_assign { + p.genln('$name = *($typ*) $tmp . data;') } - if !p.returns && p.prev_tok2 != .key_continue && p.prev_tok2 != .key_break { - p.error('`or` block must return/exit/continue/break/panic') + if !p.returns && last_typ != typ && is_assign && p.prev_tok2 != .key_continue && p.prev_tok2 != .key_break { + p.error_with_token_index('`or` block must provide a default value or return/exit/continue/break/panic', or_tok_idx) } p.returns = false return typ diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index b38b523371..fc1ef7d2b7 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -45,6 +45,7 @@ mut: inside_return_expr bool inside_unsafe bool is_struct_init bool + is_var_decl bool if_expr_cnt int for_expr_cnt int // to detect whether `continue` can be used ptr_cast bool @@ -1700,6 +1701,7 @@ fn (p mut Parser) name_expr() string { // p.error('`$f.name` used as value') } + fn_call_ph := p.cgen.add_placeholder() // println('call to fn $f.name of type $f.typ') // TODO replace the following dirty hacks (needs ptr access to fn table) new_f := f @@ -1713,6 +1715,17 @@ fn (p mut Parser) name_expr() string { } f = new_f + // optional function call `function() or {}`, no return assignment + is_or_else := p.tok == .key_orelse + if !p.is_var_decl && is_or_else { + f.typ = p.gen_handle_option_or_else(f.typ, '', fn_call_ph) + } + else if !p.is_var_decl && !is_or_else && !p.inside_return_expr && + f.typ.starts_with('Option_') { + opt_type := f.typ[7..] + p.error('unhandled option type: `?$opt_type`') + } + // dot after a function call: `get_user().age` if p.tok == .dot { mut typ := '' @@ -2049,11 +2062,21 @@ struct $typ.name { return field.typ } // method - method := p.table.find_method(typ, field_name) or { + mut method := p.table.find_method(typ, field_name) or { p.error_with_token_index('could not find method `$field_name`', fname_tidx) // should never happen exit(1) } p.fn_call(mut method, method_ph, '', str_typ) + // optional method call `a.method() or {}`, no return assignment + is_or_else := p.tok == .key_orelse + if !p.is_var_decl && is_or_else { + method.typ = p.gen_handle_option_or_else(method.typ, '', method_ph) + } + else if !p.is_var_decl && !is_or_else && !p.inside_return_expr && + method.typ.starts_with('Option_') { + opt_type := method.typ[7..] + p.error('unhandled option type: `?$opt_type`') + } // Methods returning `array` should return `array_string` etc if method.typ == 'array' && typ.name.starts_with('array_') { return typ.name @@ -3098,7 +3121,9 @@ fn (p mut Parser) if_st(is_expr bool, elif_depth int) string { var_name := p.lit p.next() p.check(.decl_assign) + p.is_var_decl = true option_type, expr := p.tmp_expr()// := p.bool_expression() + p.is_var_decl = false typ := option_type[7..] // Option_User tmp = get_user(1); // if (tmp.ok) { diff --git a/vlib/compiler/tests/defer_test.v b/vlib/compiler/tests/defer_test.v index 88957fa5cd..c2e71900d3 100644 --- a/vlib/compiler/tests/defer_test.v +++ b/vlib/compiler/tests/defer_test.v @@ -46,6 +46,8 @@ fn test_defer_early_exit() { fn test_defer_option() { mut ok := Num{0} - set_num_opt(mut ok) + set_num_opt(mut ok) or { + assert false + } assert ok.val == 1 } diff --git a/vlib/compiler/tests/option_test.v b/vlib/compiler/tests/option_test.v index a661fbe369..f9250ad38a 100644 --- a/vlib/compiler/tests/option_test.v +++ b/vlib/compiler/tests/option_test.v @@ -58,3 +58,15 @@ fn test_if_opt() { assert 1 == 1 println('nice') } + +fn for_opt_default() ?string { + return error('awww') +} + +fn test_opt_default() { + a := for_opt_default() or { + // panic(err) + 'default' + } + assert a == 'default' +} diff --git a/vlib/crypto/md5/md5.v b/vlib/crypto/md5/md5.v index 140d33def3..ce4c533e25 100644 --- a/vlib/crypto/md5/md5.v +++ b/vlib/crypto/md5/md5.v @@ -55,7 +55,7 @@ pub fn new() &Digest { return d } -pub fn (d mut Digest) write(p_ []byte) ?int { +pub fn (d mut Digest) write(p_ []byte) int { mut p := p_ nn := p.len d.len += u64(nn) diff --git a/vlib/crypto/sha1/sha1.v b/vlib/crypto/sha1/sha1.v index 0ee34864a5..ca96b4891f 100644 --- a/vlib/crypto/sha1/sha1.v +++ b/vlib/crypto/sha1/sha1.v @@ -58,7 +58,7 @@ pub fn new() &Digest { return d } -pub fn (d mut Digest) write(p_ []byte) ?int { +pub fn (d mut Digest) write(p_ []byte) int { mut p := p_ nn := p.len d.len += u64(nn) diff --git a/vlib/crypto/sha256/sha256.v b/vlib/crypto/sha256/sha256.v index 9753555e87..0126973682 100644 --- a/vlib/crypto/sha256/sha256.v +++ b/vlib/crypto/sha256/sha256.v @@ -92,7 +92,7 @@ pub fn new224() &Digest { return d } -fn (d mut Digest) write(p_ []byte) ?int { +fn (d mut Digest) write(p_ []byte) int { mut p := p_ nn := p.len d.len += u64(nn) diff --git a/vlib/crypto/sha512/sha512.v b/vlib/crypto/sha512/sha512.v index d5fcd11d2b..b804286d99 100644 --- a/vlib/crypto/sha512/sha512.v +++ b/vlib/crypto/sha512/sha512.v @@ -151,7 +151,7 @@ fn new384() &Digest { return new_digest(.sha384) } -fn (d mut Digest) write(p_ []byte) ?int { +fn (d mut Digest) write(p_ []byte) int { mut p := p_ nn := p.len d.len += u64(nn) diff --git a/vlib/encoding/csv/csv_test.v b/vlib/encoding/csv/csv_test.v index 08e7c80539..b859d7b4e9 100644 --- a/vlib/encoding/csv/csv_test.v +++ b/vlib/encoding/csv/csv_test.v @@ -32,9 +32,9 @@ fn test_encoding_csv_reader() { fn test_encoding_csv_writer() { mut csv_writer := csv.new_writer() - csv_writer.write(['name', 'email', 'phone', 'other']) - csv_writer.write(['joe', 'joe@blow.com', '0400000000', 'test']) - csv_writer.write(['sam', 'sam@likesham.com', '0433000000', 'needs, quoting']) + csv_writer.write(['name', 'email', 'phone', 'other']) or {} + csv_writer.write(['joe', 'joe@blow.com', '0400000000', 'test']) or {} + csv_writer.write(['sam', 'sam@likesham.com', '0433000000', 'needs, quoting']) or {} assert csv_writer.str() == 'name,email,phone,other\njoe,joe@blow.com,0400000000,test\nsam,sam@likesham.com,0433000000,"needs, quoting"\n' } diff --git a/vlib/http/http_client.v b/vlib/http/http_client.v index 1d04a7013f..0fd701c77a 100644 --- a/vlib/http/http_client.v +++ b/vlib/http/http_client.v @@ -10,13 +10,13 @@ fn (req &Request) http_do(port int, method, host_name, path string) ?Response { s := req.build_request_headers(method, host_name, path) client := net.dial( host_name, port) or { return error(err) } - client.send( s.str, s.len ) + client.send( s.str, s.len ) or {} for { readbytes := client.crecv( rbuffer, bufsize ) if readbytes < 0 { return error('http_do error reading response. readbytes: $readbytes') } if readbytes == 0 { break } sb.write( tos(rbuffer, readbytes) ) } - client.close() + client.close() or {} return parse_response(sb.str()) } diff --git a/vlib/net/socket_test.v b/vlib/net/socket_test.v index e72b8d28f6..a98b31ad3d 100644 --- a/vlib/net/socket_test.v +++ b/vlib/net/socket_test.v @@ -13,7 +13,9 @@ fn test_socket() { } message := 'Hello World' - socket.send(message.str, message.len) + socket.send(message.str, message.len) or { + assert false + } $if debug { println('message send: $message') } $if debug { println('send socket: $socket.sockfd') } @@ -24,7 +26,7 @@ fn test_socket() { assert message == received - server.close() - client.close() - socket.close() + server.close() or {} + client.close() or {} + socket.close() or {} } diff --git a/vlib/net/urllib/urllib.v b/vlib/net/urllib/urllib.v index 1909a40653..55de2d4f62 100644 --- a/vlib/net/urllib/urllib.v +++ b/vlib/net/urllib/urllib.v @@ -537,7 +537,7 @@ fn parse_url(rawurl string, via_request bool) ?URL { // raw_path is a hint of the encoding of path. We don't want to set it if // the default escaping of path is equivalent, to help make sure that people // don't rely on it in general. - _ = url.set_path(rest) or { + url.set_path(rest) or { return error(err) } return url @@ -974,7 +974,7 @@ pub fn (u &URL) resolve_reference(ref &URL) ?URL { // The 'absoluteURI' or 'net_path' cases. // We can ignore the error from set_path since we know we provided a // validly-escaped path. - url.set_path(resolve_path(ref.escaped_path(), '')) + url.set_path(resolve_path(ref.escaped_path(), '')) or {return error(err)} return url } if ref.opaque != '' { @@ -992,7 +992,7 @@ pub fn (u &URL) resolve_reference(ref &URL) ?URL { // The 'abs_path' or 'rel_path' cases. url.host = u.host url.user = u.user - url.set_path(resolve_path(u.escaped_path(), ref.escaped_path())) + url.set_path(resolve_path(u.escaped_path(), ref.escaped_path())) or { return error(err) } return url } diff --git a/vlib/sqlite/sqlite_test.v b/vlib/sqlite/sqlite_test.v index e3918f76d3..0ee541e99e 100644 --- a/vlib/sqlite/sqlite_test.v +++ b/vlib/sqlite/sqlite_test.v @@ -2,6 +2,7 @@ import sqlite fn test_sqlite() { db := sqlite.connect('users.db') + db.exec("drop table if exists users") db.exec("create table users (id integer primary key, name text default '');") db.exec("insert into users (name) values ('Sam')")