Skip to content

Commit ad162cd

Browse files
authored
checker: stricter unknown type checks, show better suggestions (#8816)
1 parent 6a75251 commit ad162cd

14 files changed

+120
-142
lines changed

vlib/fontstash/fontstash_structs.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ pub struct C.FONStextIter {
6767
codepoint u32
6868
isize i16
6969
iblur i16
70-
font &FONSfont
70+
font &C.FONSfont
7171
prevGlyphIndex int
7272
str byteptr
7373
next byteptr

vlib/sqlite/sqlite.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ fn C.sqlite3_column_text(&C.sqlite3_stmt, int) byteptr
5454

5555
fn C.sqlite3_column_int(&C.sqlite3_stmt, int) int
5656

57-
fn C.sqlite3_column_int64(&C.sqlite3_stmt, int) int64
57+
fn C.sqlite3_column_int64(&C.sqlite3_stmt, int) i64
5858

5959
fn C.sqlite3_column_double(&C.sqlite3_stmt, int) f64
6060

vlib/time/time_windows.c.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct C.timespec {
5050
tv_nsec i64
5151
}
5252

53-
fn C._mkgmtime(&C.tm) time_t
53+
fn C._mkgmtime(&C.tm) C.time_t
5454

5555
fn C.QueryPerformanceCounter(&u64) C.BOOL
5656

vlib/v/checker/checker.v

Lines changed: 57 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -338,49 +338,21 @@ pub fn (mut c Checker) interface_decl(decl ast.InterfaceDecl) {
338338
c.check_valid_pascal_case(decl.name, 'interface name', decl.pos)
339339
for method in decl.methods {
340340
c.check_valid_snake_case(method.name, 'method name', method.pos)
341+
if method.return_type != table.Type(0) {
342+
c.ensure_type_exists(method.return_type, method.pos) or { return }
343+
}
344+
for param in method.params {
345+
c.ensure_type_exists(param.typ, param.pos) or { return }
346+
}
341347
}
342-
// TODO: copy pasta from StructDecl
343348
for i, field in decl.fields {
344349
c.check_valid_snake_case(field.name, 'field name', field.pos)
345-
sym := c.table.get_type_symbol(field.typ)
350+
c.ensure_type_exists(field.typ, field.pos) or { return }
346351
for j in 0 .. i {
347352
if field.name == decl.fields[j].name {
348353
c.error('field name `$field.name` duplicate', field.pos)
349354
}
350355
}
351-
if sym.kind == .placeholder && !sym.name.starts_with('C.') {
352-
c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'),
353-
field.type_pos)
354-
}
355-
// Separate error condition for `int_literal` and `float_literal` because `util.suggestion` may give different
356-
// suggestions due to f32 comparision issue.
357-
if sym.kind in [.int_literal, .float_literal] {
358-
msg := if sym.kind == .int_literal {
359-
'unknown type `$sym.name`.\nDid you mean `int`?'
360-
} else {
361-
'unknown type `$sym.name`.\nDid you mean `f64`?'
362-
}
363-
c.error(msg, field.type_pos)
364-
}
365-
if sym.kind == .array {
366-
array_info := sym.array_info()
367-
elem_sym := c.table.get_type_symbol(array_info.elem_type)
368-
if elem_sym.kind == .placeholder {
369-
c.error(util.new_suggestion(elem_sym.name, c.table.known_type_names()).say('unknown type `$elem_sym.name`'),
370-
field.type_pos)
371-
}
372-
}
373-
if sym.kind == .map {
374-
info := sym.map_info()
375-
key_sym := c.table.get_type_symbol(info.key_type)
376-
value_sym := c.table.get_type_symbol(info.value_type)
377-
if key_sym.kind == .placeholder {
378-
c.error('unknown type `$key_sym.name`', field.type_pos)
379-
}
380-
if value_sym.kind == .placeholder {
381-
c.error('unknown type `$value_sym.name`', field.type_pos)
382-
}
383-
}
384356
}
385357
}
386358

@@ -407,6 +379,7 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
407379
}
408380
}
409381
for i, field in decl.fields {
382+
c.ensure_type_exists(field.typ, field.type_pos) or { return }
410383
if decl.language == .v {
411384
c.check_valid_snake_case(field.name, 'field name', field.pos)
412385
}
@@ -416,45 +389,12 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
416389
c.error('field name `$field.name` duplicate', field.pos)
417390
}
418391
}
419-
if sym.kind == .placeholder && decl.language != .c && !sym.name.starts_with('C.') {
420-
c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'),
421-
field.type_pos)
422-
}
423-
// Separate error condition for `int_literal` and `float_literal` because `util.suggestion` may give different
424-
// suggestions due to f32 comparision issue.
425-
if sym.kind in [.int_literal, .float_literal] {
426-
msg := if sym.kind == .int_literal {
427-
'unknown type `$sym.name`.\nDid you mean `int`?'
428-
} else {
429-
'unknown type `$sym.name`.\nDid you mean `f64`?'
430-
}
431-
c.error(msg, field.type_pos)
432-
}
433-
if sym.kind == .array {
434-
array_info := sym.array_info()
435-
elem_sym := c.table.get_type_symbol(array_info.elem_type)
436-
if elem_sym.kind == .placeholder {
437-
c.error(util.new_suggestion(elem_sym.name, c.table.known_type_names()).say('unknown type `$elem_sym.name`'),
438-
field.type_pos)
439-
}
440-
}
441392
if sym.kind == .struct_ {
442393
info := sym.info as table.Struct
443394
if info.is_heap && !field.typ.is_ptr() {
444395
struct_sym.info.is_heap = true
445396
}
446397
}
447-
if sym.kind == .map {
448-
info := sym.map_info()
449-
key_sym := c.table.get_type_symbol(info.key_type)
450-
value_sym := c.table.get_type_symbol(info.value_type)
451-
if key_sym.kind == .placeholder {
452-
c.error('unknown type `$key_sym.name`', field.type_pos)
453-
}
454-
if value_sym.kind == .placeholder {
455-
c.error('unknown type `$value_sym.name`', field.type_pos)
456-
}
457-
}
458398
if field.has_default_expr {
459399
c.expected_type = field.typ
460400
field_expr_type := c.expr(field.default_expr)
@@ -509,10 +449,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
509449
struct_init.typ = c.expected_type
510450
}
511451
}
512-
if struct_init.typ == 0 {
513-
c.error('unknown type', struct_init.pos)
514-
}
515452
utyp := c.unwrap_generic(struct_init.typ)
453+
c.ensure_type_exists(utyp, struct_init.pos) or { }
516454
type_sym := c.table.get_type_symbol(utyp)
517455
if type_sym.kind == .sum_type && struct_init.fields.len == 1 {
518456
sexpr := struct_init.fields[0].expr.str()
@@ -1107,14 +1045,8 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
11071045
}
11081046
ast.SelectorExpr {
11091047
// retrieve table.Field
1110-
if expr.expr_type == 0 {
1111-
c.error('0 type in SelectorExpr', expr.pos)
1112-
return '', pos
1113-
}
1114-
mut typ_sym := c.table.get_type_symbol(c.unwrap_generic(expr.expr_type))
1115-
if mut typ_sym.info is table.Alias {
1116-
typ_sym = c.table.get_type_symbol(typ_sym.info.parent_type)
1117-
}
1048+
c.ensure_type_exists(expr.expr_type, expr.pos) or { return '', pos }
1049+
mut typ_sym := c.table.get_final_type_symbol(c.unwrap_generic(expr.expr_type))
11181050
match typ_sym.kind {
11191051
.struct_ {
11201052
struct_info := typ_sym.info as table.Struct
@@ -1874,9 +1806,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
18741806
gts := c.table.get_type_symbol(call_expr.generic_types[0])
18751807
nrt := '$rts.name<$gts.name>'
18761808
idx := c.table.type_idxs[nrt]
1877-
if idx == 0 {
1878-
c.error('unknown type: $nrt', call_expr.pos)
1879-
}
1809+
c.ensure_type_exists(idx, call_expr.pos) or { }
18801810
call_expr.return_type = table.new_type(idx).derive(f.return_type)
18811811
}
18821812
}
@@ -4278,10 +4208,8 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
42784208
cond_type := c.expr(node.cond)
42794209
// we setting this here rather than at the end of the method
42804210
// since it is used in c.match_exprs() it saves checking twice
4281-
node.cond_type = cond_type
4282-
if cond_type == 0 {
4283-
c.error('compiler bug: match 0 cond type', node.pos)
4284-
}
4211+
node.cond_type = c.table.mktyp(cond_type)
4212+
c.ensure_type_exists(node.cond_type, node.pos) or { return table.void_type }
42854213
cond_type_sym := c.table.get_type_symbol(cond_type)
42864214
if cond_type_sym.kind !in [.interface_, .sum_type] {
42874215
node.is_sum_type = false
@@ -5661,17 +5589,10 @@ fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) table.Type {
56615589
defer {
56625590
c.inside_sql = false
56635591
}
5664-
sym := c.table.get_type_symbol(node.table_expr.typ)
5665-
if node.table_expr.typ == 0 {
5666-
c.error('orm: unknown type `$sym.name`', node.pos)
5667-
}
5668-
if sym.kind == .placeholder {
5669-
c.error('orm: unknown type `$sym.name`', node.pos)
5670-
return table.void_type
5671-
}
5672-
c.cur_orm_ts = sym
5673-
info := sym.info as table.Struct
5592+
c.ensure_type_exists(node.table_expr.typ, node.pos) or { return table.void_type }
56745593
table_sym := c.table.get_type_symbol(node.table_expr.typ)
5594+
c.cur_orm_ts = table_sym
5595+
info := table_sym.info as table.Struct
56755596
fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
56765597
mut sub_structs := map[int]ast.SqlStmt{}
56775598
for f in fields.filter(c.table.types[int(it.typ)].kind == .struct_) {
@@ -5788,19 +5709,11 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
57885709
if node.language == .v {
57895710
// Make sure all types are valid
57905711
for arg in node.params {
5791-
sym := c.table.get_type_symbol(arg.typ)
5792-
if sym.kind == .placeholder
5793-
|| (sym.kind in [table.Kind.int_literal, .float_literal] && !c.is_builtin_mod) {
5794-
c.error('unknown type `$sym.name`', node.pos)
5795-
}
5712+
c.ensure_type_exists(arg.typ, node.pos) or { return }
57965713
}
57975714
}
57985715
if node.return_type != table.Type(0) {
5799-
return_sym := c.table.get_type_symbol(node.return_type)
5800-
if node.language == .v && return_sym.kind in [.placeholder, .int_literal, .float_literal]
5801-
&& return_sym.language == .v {
5802-
c.error('unknown type `$return_sym.name`', node.pos)
5803-
}
5716+
c.ensure_type_exists(node.return_type, node.pos) or { return }
58045717
if node.language == .v && node.is_method && node.name == 'str' {
58055718
if node.return_type != table.string_type {
58065719
c.error('.str() methods should return `string`', node.pos)
@@ -5949,3 +5862,41 @@ fn (mut c Checker) trace(fbase string, message string) {
59495862
println('> c.trace | ${fbase:-10s} | $message')
59505863
}
59515864
}
5865+
5866+
fn (mut c Checker) ensure_type_exists(typ table.Type, pos token.Position) ? {
5867+
if typ == 0 {
5868+
c.error('unknown type', pos)
5869+
}
5870+
sym := c.table.get_type_symbol(typ)
5871+
match sym.kind {
5872+
.placeholder {
5873+
if sym.language == .v && !sym.name.starts_with('C.') {
5874+
c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'),
5875+
pos)
5876+
return none
5877+
}
5878+
}
5879+
.int_literal, .float_literal {
5880+
// Separate error condition for `int_literal` and `float_literal` because `util.suggestion` may give different
5881+
// suggestions due to f32 comparision issue.
5882+
if !c.is_builtin_mod {
5883+
msg := if sym.kind == .int_literal {
5884+
'unknown type `$sym.name`.\nDid you mean `int`?'
5885+
} else {
5886+
'unknown type `$sym.name`.\nDid you mean `f64`?'
5887+
}
5888+
c.error(msg, pos)
5889+
return none
5890+
}
5891+
}
5892+
.array {
5893+
c.ensure_type_exists((sym.info as table.Array).elem_type, pos) ?
5894+
}
5895+
.map {
5896+
info := sym.info as table.Map
5897+
c.ensure_type_exists(info.key_type, pos) ?
5898+
c.ensure_type_exists(info.value_type, pos) ?
5899+
}
5900+
else {}
5901+
}
5902+
}

vlib/v/checker/tests/any_int_float_ban_err.out

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@ vlib/v/checker/tests/any_int_float_ban_err.vv:2:1: error: type `int_literal` doe
99
| ~~~~~~~~
1010
3 |
1111
4 | struct Int {
12-
vlib/v/checker/tests/any_int_float_ban_err.vv:5:7: error: unknown type `int_literal`.
13-
Did you mean `float_literal`?
12+
vlib/v/checker/tests/any_int_float_ban_err.vv:5:7: error: unknown type `int_literal`
1413
3 |
1514
4 | struct Int {
1615
5 | i int_literal
1716
| ~~~~~~~~~~~
1817
6 | f float_literal
1918
7 | }
20-
vlib/v/checker/tests/any_int_float_ban_err.vv:6:7: error: unknown type `float_literal`.
21-
Did you mean `int_literal`?
19+
vlib/v/checker/tests/any_int_float_ban_err.vv:6:7: error: unknown type `float_literal`
2220
4 | struct Int {
2321
5 | i int_literal
2422
6 | f float_literal
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
vlib/v/checker/tests/filter_on_non_arr_err.vv:2:14: error: unknown method: `string.filter`.
2-
Did you mean `after`?
1+
vlib/v/checker/tests/filter_on_non_arr_err.vv:2:14: error: unknown method: `string.filter`
32
1 | fn main() {
43
2 | _ := 'test'.filter(it == `t`)
54
| ~~~~~~~~~~~~~~~~~
6-
3 | }
5+
3 | }

vlib/v/checker/tests/import_symbol_type_err.out

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ vlib/v/checker/tests/import_symbol_type_err.vv:1:17: error: module `crypto` has
33
| ~~~~
44
2 | fn main() {
55
3 | println(Coin{})
6-
vlib/v/checker/tests/import_symbol_type_err.vv:3:11: error: unknown struct: crypto.Coin
6+
vlib/v/checker/tests/import_symbol_type_err.vv:3:11: error: unknown type `crypto.Coin`.
7+
Did you mean `crypto.Hash`?
78
1 | import crypto { Coin }
89
2 | fn main() {
910
3 | println(Coin{})
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
vlib/v/checker/tests/interface_return_parameter_err.vv:2:5: error: unknown type `Baz`.
2+
Did you mean `Foo`?
3+
1 | interface Foo {
4+
2 | bar(string) []Baz
5+
| ~~~~~~~~~~~
6+
3 | bar2(Bax) string
7+
4 | }
8+
vlib/v/checker/tests/interface_return_parameter_err.vv:3:10: error: unknown type `Bax`.
9+
Did you mean `Foo`?
10+
1 | interface Foo {
11+
2 | bar(string) []Baz
12+
3 | bar2(Bax) string
13+
| ~~~
14+
4 | }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface Foo {
2+
bar(string) []Baz
3+
bar2(Bax) string
4+
}

vlib/v/checker/tests/map_unknown_value.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ vlib/v/checker/tests/map_unknown_value.vv:2:23: error: unknown type `DoesNotExis
22
1 | struct App {
33
2 | my_map map[string]DoesNotExist
44
| ~~~~~~~~~~~~
5-
3 | }
5+
3 | }

0 commit comments

Comments
 (0)