checker: check opt call in more places (#9538)

pull/9555/head
zakuro 2021-04-01 18:49:13 +09:00 committed by GitHub
parent 1a76cb1c36
commit 0d1714cb0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 202 additions and 156 deletions

View File

@ -414,7 +414,10 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
} }
if field.has_default_expr { if field.has_default_expr {
c.expected_type = field.typ c.expected_type = field.typ
field_expr_type := c.expr(field.default_expr) mut field_expr_type := c.expr(field.default_expr)
if !field.typ.has_flag(.optional) {
c.check_expr_opt_call(field.default_expr, field_expr_type)
}
struct_sym.info.fields[i].default_expr_typ = field_expr_type struct_sym.info.fields[i].default_expr_typ = field_expr_type
c.check_expected(field_expr_type, field.typ) or { c.check_expected(field_expr_type, field.typ) or {
if !(sym.kind == .interface_ if !(sym.kind == .interface_
@ -611,7 +614,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
inited_fields << field_name inited_fields << field_name
field_type_sym := c.table.get_type_symbol(info_field.typ) field_type_sym := c.table.get_type_symbol(info_field.typ)
c.expected_type = info_field.typ c.expected_type = info_field.typ
expr_type := c.expr(field.expr) mut expr_type := c.expr(field.expr)
if !info_field.typ.has_flag(.optional) {
expr_type = c.check_expr_opt_call(field.expr, expr_type)
}
expr_type_sym := c.table.get_type_symbol(expr_type) expr_type_sym := c.table.get_type_symbol(expr_type)
if field_type_sym.kind == .interface_ { if field_type_sym.kind == .interface_ {
c.type_implements(expr_type, info_field.typ, field.pos) c.type_implements(expr_type, info_field.typ, field.pos)
@ -948,6 +954,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
c.error('array append cannot be used in an expression', infix_expr.pos) c.error('array append cannot be used in an expression', infix_expr.pos)
} }
// `array << elm` // `array << elm`
c.check_expr_opt_call(infix_expr.right, right_type)
infix_expr.auto_locked, _ = c.fail_if_immutable(infix_expr.left) infix_expr.auto_locked, _ = c.fail_if_immutable(infix_expr.left)
left_value_type := c.table.value_type(left_type) left_value_type := c.table.value_type(left_type)
left_value_sym := c.table.get_type_symbol(left_value_type) left_value_sym := c.table.get_type_symbol(left_value_type)
@ -2340,7 +2347,8 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) t
pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type, expr_return_type table.Type) { pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type, expr_return_type table.Type) {
if or_expr.kind == .propagate { if or_expr.kind == .propagate {
if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' { if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main'
&& !c.inside_const {
c.error('to propagate the optional call, `$c.cur_fn.name` must return an optional', c.error('to propagate the optional call, `$c.cur_fn.name` must return an optional',
or_expr.pos) or_expr.pos)
} }
@ -2697,12 +2705,7 @@ pub fn (mut c Checker) const_decl(mut node ast.ConstDecl) {
for i, field in node.fields { for i, field in node.fields {
c.const_decl = field.name c.const_decl = field.name
c.const_deps << field.name c.const_deps << field.name
mut typ := c.expr(field.expr) typ := c.check_expr_opt_call(field.expr, c.expr(field.expr))
if field.expr is ast.CallExpr {
if field.expr.or_block.kind != .absent {
typ = typ.clear_flag(.optional)
}
}
node.fields[i].typ = c.table.mktyp(typ) node.fields[i].typ = c.table.mktyp(typ)
for cd in c.const_deps { for cd in c.const_deps {
for j, f in node.fields { for j, f in node.fields {
@ -3207,9 +3210,10 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type {
} }
} }
if array_init.has_default { if array_init.has_default {
default_typ := c.expr(array_init.default_expr) default_expr := array_init.default_expr
default_typ := c.check_expr_opt_call(default_expr, c.expr(default_expr))
c.check_expected(default_typ, array_init.elem_type) or { c.check_expected(default_typ, array_init.elem_type) or {
c.error(err.msg, array_init.default_expr.position()) c.error(err.msg, default_expr.position())
} }
} }
if array_init.has_len { if array_init.has_len {
@ -3513,7 +3517,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
fn (mut c Checker) assert_stmt(node ast.AssertStmt) { fn (mut c Checker) assert_stmt(node ast.AssertStmt) {
cur_exp_typ := c.expected_type cur_exp_typ := c.expected_type
assert_type := c.expr(node.expr) assert_type := c.check_expr_opt_call(node.expr, c.expr(node.expr))
if assert_type != table.bool_type_idx { if assert_type != table.bool_type_idx {
atype_name := c.table.get_type_symbol(assert_type).name atype_name := c.table.get_type_symbol(assert_type).name
c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead', c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead',

View File

@ -1,104 +1,160 @@
vlib/v/checker/tests/optional_fn_err.vv:25:2: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:13:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
23 | fn main() { 11 |
24 | // Calling foo() without ? or an or block, should be an error. 12 | const (
25 | foo() 13 | const_value = bar(0)
| ~~~~~
26 |
27 | _ := bar(0)
vlib/v/checker/tests/optional_fn_err.vv:27:7: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
25 | foo()
26 |
27 | _ := bar(0)
| ~~~~~~
28 | _ := [bar(0)]
29 |
vlib/v/checker/tests/optional_fn_err.vv:28:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
26 |
27 | _ := bar(0)
28 | _ := [bar(0)]
| ~~~~~~
29 |
30 | // Calling fn with optional argument
vlib/v/checker/tests/optional_fn_err.vv:31:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
29 |
30 | // Calling fn with optional argument
31 | println(twice(bar(0)))
| ~~~~~~ | ~~~~~~
32 | // method and fn field 14 | )
33 | mut v := Data{fn (_ int) {}, 1} 15 |
vlib/v/checker/tests/optional_fn_err.vv:34:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:19:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
32 | // method and fn field 17 | f fn (int)
33 | mut v := Data{fn (_ int) {}, 1} 18 | mut:
34 | v.add(bar(0)) 19 | value int = bar(0)
| ~~~~~~
35 | v.f(bar(0))
36 | // anon fn
vlib/v/checker/tests/optional_fn_err.vv:35:6: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
33 | mut v := Data{fn (_ int) {}, 1}
34 | v.add(bar(0))
35 | v.f(bar(0))
| ~~~~~~
36 | // anon fn
37 | fn (_ int) {}(bar(0))
vlib/v/checker/tests/optional_fn_err.vv:37:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
35 | v.f(bar(0))
36 | // anon fn
37 | fn (_ int) {}(bar(0))
| ~~~~~~
38 | // array builtin methods
39 | mut arr := [1, 2]
vlib/v/checker/tests/optional_fn_err.vv:40:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
38 | // array builtin methods
39 | mut arr := [1, 2]
40 | arr.insert(0, bar(0))
| ~~~~~~
41 | arr.prepend(bar(0))
42 | arr.contains(bar(0))
vlib/v/checker/tests/optional_fn_err.vv:41:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
39 | mut arr := [1, 2]
40 | arr.insert(0, bar(0))
41 | arr.prepend(bar(0))
| ~~~~~~ | ~~~~~~
42 | arr.contains(bar(0)) 20 | opt ?int = bar(0)
43 | arr.index(bar(0)) 21 | }
vlib/v/checker/tests/optional_fn_err.vv:42:15: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:33:2: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end
40 | arr.insert(0, bar(0)) 31 | fn main() {
41 | arr.prepend(bar(0)) 32 | // call fn
42 | arr.contains(bar(0)) 33 | foo()
| ~~~~~
34 | _ := bar(0)
35 | println(twice(bar(0)))
vlib/v/checker/tests/optional_fn_err.vv:34:7: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
32 | // call fn
33 | foo()
34 | _ := bar(0)
| ~~~~~~
35 | println(twice(bar(0)))
36 |
vlib/v/checker/tests/optional_fn_err.vv:35:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
33 | foo()
34 | _ := bar(0)
35 | println(twice(bar(0)))
| ~~~~~~
36 |
37 | // anon fn
vlib/v/checker/tests/optional_fn_err.vv:38:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
36 |
37 | // anon fn
38 | fn (_ int) {}(bar(0))
| ~~~~~~
39 |
40 | // assert
vlib/v/checker/tests/optional_fn_err.vv:41:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
39 |
40 | // assert
41 | assert bar(true)
| ~~~~~~~~~
42 |
43 | // struct
vlib/v/checker/tests/optional_fn_err.vv:46:10: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
44 | mut v := Data{
45 | f: fn (_ int) {},
46 | value: bar(0),
| ~~~~~~
47 | opt: bar(0),
48 | }
vlib/v/checker/tests/optional_fn_err.vv:49:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
47 | opt: bar(0),
48 | }
49 | v.add(bar(0)) // call method
| ~~~~~~
50 | v.f(bar(0)) // call fn field
51 |
vlib/v/checker/tests/optional_fn_err.vv:50:6: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
48 | }
49 | v.add(bar(0)) // call method
50 | v.f(bar(0)) // call fn field
| ~~~~~~
51 |
52 | // array
vlib/v/checker/tests/optional_fn_err.vv:54:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
52 | // array
53 | mut arr := [1, 2]
54 | arr << bar(0)
| ~~~~~~
55 | // init
56 | _ := [bar(0)]
vlib/v/checker/tests/optional_fn_err.vv:56:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
54 | arr << bar(0)
55 | // init
56 | _ := [bar(0)]
| ~~~~~~
57 | _ := []int{init: bar(0)}
58 | _ := [bar(0)]!
vlib/v/checker/tests/optional_fn_err.vv:57:19: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
55 | // init
56 | _ := [bar(0)]
57 | _ := []int{init: bar(0)}
| ~~~~~~
58 | _ := [bar(0)]!
59 | _ := [1]int{init: bar(0)}
vlib/v/checker/tests/optional_fn_err.vv:58:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
56 | _ := [bar(0)]
57 | _ := []int{init: bar(0)}
58 | _ := [bar(0)]!
| ~~~~~~
59 | _ := [1]int{init: bar(0)}
60 | // index
vlib/v/checker/tests/optional_fn_err.vv:61:13: error: cannot use optional as index (array type `[]int`)
59 | _ := [1]int{init: bar(0)}
60 | // index
61 | println(arr[bar(0)])
| ~~~~~~~~
62 | // array builtin methods
63 | arr.insert(0, bar(0))
vlib/v/checker/tests/optional_fn_err.vv:63:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
61 | println(arr[bar(0)])
62 | // array builtin methods
63 | arr.insert(0, bar(0))
| ~~~~~~
64 | arr.prepend(bar(0))
65 | arr.contains(bar(0))
vlib/v/checker/tests/optional_fn_err.vv:64:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
62 | // array builtin methods
63 | arr.insert(0, bar(0))
64 | arr.prepend(bar(0))
| ~~~~~~
65 | arr.contains(bar(0))
66 | arr.index(bar(0))
vlib/v/checker/tests/optional_fn_err.vv:65:15: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
63 | arr.insert(0, bar(0))
64 | arr.prepend(bar(0))
65 | arr.contains(bar(0))
| ~~~~~~ | ~~~~~~
43 | arr.index(bar(0)) 66 | arr.index(bar(0))
44 | println(arr.map(bar(0))) 67 | println(arr.map(bar(0)))
vlib/v/checker/tests/optional_fn_err.vv:43:12: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:66:12: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
41 | arr.prepend(bar(0)) 64 | arr.prepend(bar(0))
42 | arr.contains(bar(0)) 65 | arr.contains(bar(0))
43 | arr.index(bar(0)) 66 | arr.index(bar(0))
| ~~~~~~ | ~~~~~~
44 | println(arr.map(bar(0))) 67 | println(arr.map(bar(0)))
45 | println(arr.filter(bar(true))) 68 | println(arr.filter(bar(true)))
vlib/v/checker/tests/optional_fn_err.vv:44:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:67:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
42 | arr.contains(bar(0)) 65 | arr.contains(bar(0))
43 | arr.index(bar(0)) 66 | arr.index(bar(0))
44 | println(arr.map(bar(0))) 67 | println(arr.map(bar(0)))
| ~~~~~~ | ~~~~~~
45 | println(arr.filter(bar(true))) 68 | println(arr.filter(bar(true)))
46 | println(arr.any(bar(true))) 69 | println(arr.any(bar(true)))
vlib/v/checker/tests/optional_fn_err.vv:45:21: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:68:21: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
43 | arr.index(bar(0)) 66 | arr.index(bar(0))
44 | println(arr.map(bar(0))) 67 | println(arr.map(bar(0)))
45 | println(arr.filter(bar(true))) 68 | println(arr.filter(bar(true)))
| ~~~~~~~~~ | ~~~~~~~~~
46 | println(arr.any(bar(true))) 69 | println(arr.any(bar(true)))
47 | println(arr.all(bar(true))) 70 | println(arr.all(bar(true)))
vlib/v/checker/tests/optional_fn_err.vv:46:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:69:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
44 | println(arr.map(bar(0))) 67 | println(arr.map(bar(0)))
45 | println(arr.filter(bar(true))) 68 | println(arr.filter(bar(true)))
46 | println(arr.any(bar(true))) 69 | println(arr.any(bar(true)))
| ~~~~~~~~~ | ~~~~~~~~~
47 | println(arr.all(bar(true))) 70 | println(arr.all(bar(true)))
48 | } 71 | }
vlib/v/checker/tests/optional_fn_err.vv:47:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end vlib/v/checker/tests/optional_fn_err.vv:70:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
45 | println(arr.filter(bar(true))) 68 | println(arr.filter(bar(true)))
46 | println(arr.any(bar(true))) 69 | println(arr.any(bar(true)))
47 | println(arr.all(bar(true))) 70 | println(arr.all(bar(true)))
| ~~~~~~~~~ | ~~~~~~~~~
48 | } 71 | }

View File

@ -1,3 +1,6 @@
// use optional without ? or an or block in places where it is not allowed
fn foo() ? { fn foo() ? {
println('foo is called') println('foo is called')
} }
@ -6,10 +9,15 @@ fn bar<T>(v T) ?T {
return none return none
} }
const (
const_value = bar(0)
)
struct Data { struct Data {
f fn (int) f fn (int)
mut: mut:
value int value int = bar(0)
opt ?int = bar(0)
} }
fn (mut v Data) add(n int) { fn (mut v Data) add(n int) {
@ -21,22 +29,37 @@ fn twice(n int) int {
} }
fn main() { fn main() {
// Calling foo() without ? or an or block, should be an error. // call fn
foo() foo()
_ := bar(0) _ := bar(0)
_ := [bar(0)]
// Calling fn with optional argument
println(twice(bar(0))) println(twice(bar(0)))
// method and fn field
mut v := Data{fn (_ int) {}, 1}
v.add(bar(0))
v.f(bar(0))
// anon fn // anon fn
fn (_ int) {}(bar(0)) fn (_ int) {}(bar(0))
// array builtin methods
// assert
assert bar(true)
// struct
mut v := Data{
f: fn (_ int) {},
value: bar(0),
opt: bar(0),
}
v.add(bar(0)) // call method
v.f(bar(0)) // call fn field
// array
mut arr := [1, 2] mut arr := [1, 2]
arr << bar(0)
// init
_ := [bar(0)]
_ := []int{init: bar(0)}
_ := [bar(0)]!
_ := [1]int{init: bar(0)}
// index
println(arr[bar(0)])
// array builtin methods
arr.insert(0, bar(0)) arr.insert(0, bar(0))
arr.prepend(bar(0)) arr.prepend(bar(0))
arr.contains(bar(0)) arr.contains(bar(0))

View File

@ -1,6 +0,0 @@
vlib/v/checker/tests/optional_index_err.vv:3:14: error: cannot use optional as index (type `string`)
1 | fn main() {
2 | v := 'hello'
3 | println(v[v.last_index('l')])
| ~~~~~~~~~~~~~~~~~~~
4 | }

View File

@ -1,4 +0,0 @@
fn main() {
v := 'hello'
println(v[v.last_index('l')])
}

View File

@ -1,7 +0,0 @@
vlib/v/checker/tests/optional_method_err.vv:17:10: error: inc_to_limit() returns an option, so it should have either an `or {}` block, or `?` at the end
15 | mut a := Abc{}
16 | for _ in 0 .. 4 {
17 | _ := a.inc_to_limit(2)
| ~~~~~~~~~~~~~~~
18 | eprintln('a: $a')
19 | }

View File

@ -1,20 +0,0 @@
struct Abc {
mut:
x int
}
fn (mut a Abc) inc_to_limit(max int) ?int {
if a.x >= max {
return error('x is already $max')
}
a.x++
return a.x
}
fn main() {
mut a := Abc{}
for _ in 0 .. 4 {
_ := a.inc_to_limit(2)
eprintln('a: $a')
}
}