checker: add suggestions for method mispellings and unknown types
							parent
							
								
									2c45e601ef
								
							
						
					
					
						commit
						f300f787f3
					
				| 
						 | 
				
			
			@ -315,13 +315,15 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) {
 | 
			
		|||
		}
 | 
			
		||||
		sym := c.table.get_type_symbol(field.typ)
 | 
			
		||||
		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 {
 | 
			
		||||
			array_info := sym.array_info()
 | 
			
		||||
			elem_sym := c.table.get_type_symbol(array_info.elem_type)
 | 
			
		||||
			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_ {
 | 
			
		||||
| 
						 | 
				
			
			@ -1024,7 +1026,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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')
 | 
			
		||||
| 
						 | 
				
			
			@ -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')
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -529,3 +529,14 @@ pub fn (table &Table) sumtype_has_variant(parent, variant Type) bool {
 | 
			
		|||
	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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue