checker: add suggestions for method mispellings and unknown types

pull/6027/head
Delyan Angelov 2020-07-30 17:34:05 +03:00
parent 2c45e601ef
commit f300f787f3
5 changed files with 153 additions and 3 deletions

View File

@ -315,13 +315,15 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) {
} }
sym := c.table.get_type_symbol(field.typ) sym := c.table.get_type_symbol(field.typ)
if sym.kind == .placeholder && decl.language != .c && !sym.name.starts_with('C.') { if sym.kind == .placeholder && decl.language != .c && !sym.name.starts_with('C.') {
c.error('unknown type `$sym.name`', field.pos) c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'),
field.pos)
} }
if sym.kind == .array { if sym.kind == .array {
array_info := sym.array_info() array_info := sym.array_info()
elem_sym := c.table.get_type_symbol(array_info.elem_type) elem_sym := c.table.get_type_symbol(array_info.elem_type)
if elem_sym.kind == .placeholder { if elem_sym.kind == .placeholder {
c.error('unknown type `$elem_sym.name`', field.pos) c.error(util.new_suggestion(elem_sym.name, c.table.known_type_names()).say('unknown type `$elem_sym.name`'),
field.pos)
} }
} }
if sym.kind == .struct_ { if sym.kind == .struct_ {
@ -1024,7 +1026,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
} }
} }
if left_type != table.void_type { if left_type != table.void_type {
c.error('unknown method: `${left_type_sym.name}.$method_name`', call_expr.pos) suggestion := util.new_suggestion(method_name, left_type_sym.methods.map(it.name))
c.error(suggestion.say('unknown method: `${left_type_sym.name}.$method_name`'),
call_expr.pos)
} }
return table.void_type return table.void_type
} }

View File

@ -0,0 +1,14 @@
vlib/v/checker/tests/unknown_method_suggest_name.v:7:2: error: unknown type `hash.crc32.Crc33`. Did you mean `crc32.Crc33` ?
5 | y int
6 | z int
7 | ccc crc32.Crc33
| ~~~~~~~~~~~~~~~
8 | }
9 |
vlib/v/checker/tests/unknown_method_suggest_name.v:27:9: error: unknown method: `Point.tranzlate`. Did you mean `translate` ?
25 | p := Point{1, 2, 3}
26 | v := Vector{5, 5, 10}
27 | z := p.tranzlate(v)
| ~~~~~~~~~~~~
28 | println('p: $p')
29 | println('v: $v')

View File

@ -0,0 +1,31 @@
import hash.crc32
struct Point {
x int
y int
z int
ccc crc32.Crc33
}
struct Vector {
x int
y int
z int
}
fn (p Point) translate(v Vector) Point {
return Point{p.x + v.x, p.y + v.y, p.z + v.z}
}
fn (p Point) identity() Point {
return Point{1, 1, 1}
}
fn main() {
p := Point{1, 2, 3}
v := Vector{5, 5, 10}
z := p.tranzlate(v)
println('p: $p')
println('v: $v')
println('z: $z')
}

View File

@ -529,3 +529,14 @@ pub fn (table &Table) sumtype_has_variant(parent, variant Type) bool {
return false return false
} }
pub fn (table &Table) known_type_names() []string {
mut res := []string{}
for _, idx in table.type_idxs {
if idx == 0 {
continue
}
res << table.type_to_str(idx)
}
return res
}

View File

@ -0,0 +1,90 @@
module util
import strings
struct Possibility {
value string
svalue string
mut:
similarity f32
}
fn compare_by_similarity(a, b &Possibility) int {
if a.similarity < b.similarity {
return -1
}
if a.similarity > b.similarity {
return 1
}
return 0
}
//
struct Suggestion {
mut:
known []Possibility
wanted string
swanted string
}
pub fn new_suggestion(wanted string, possibilities []string) Suggestion {
mut s := Suggestion{
wanted: wanted
swanted: short_module_name(wanted)
}
s.add_many(possibilities)
s.sort()
return s
}
pub fn (mut s Suggestion) add(val string) {
if val == s.wanted {
return
}
sval := short_module_name(val)
if sval == s.wanted {
return
}
s.known << Possibility{
value: val
svalue: sval
similarity: strings.dice_coefficient(s.swanted, sval)
}
}
pub fn (mut s Suggestion) add_many(many []string) {
for x in many {
s.add(x)
}
}
pub fn (mut s Suggestion) sort() {
s.known.sort_with_compare(compare_by_similarity)
}
pub fn (s Suggestion) say(msg string) string {
mut res := msg
if s.known.len > 0 {
top_posibility := s.known.last()
if top_posibility.similarity > 0.10 {
val := top_posibility.value
if !val.starts_with('[]') {
res += '. Did you mean `$val` ?'
}
}
}
return res
}
pub fn short_module_name(name string) string {
if !name.contains('.') {
return name
}
vals := name.split('.')
if vals.len < 2 {
return name
}
mname := vals[vals.len - 2]
symname := vals[vals.len - 1]
return '${mname}.$symname'
}