checker: add suggestions for misspelled `mod.func_name()` calls
parent
51c1d666c2
commit
44603f8e59
|
@ -662,6 +662,20 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
continue_check = false
|
continue_check = false
|
||||||
|
if dot_index := fn_name.index('.') {
|
||||||
|
if !fn_name[0].is_capital() {
|
||||||
|
mod_name := fn_name#[..dot_index]
|
||||||
|
mut mod_func_names := []string{}
|
||||||
|
for ctfnk, ctfnv in c.table.fns {
|
||||||
|
if ctfnv.is_pub && ctfnk.starts_with(mod_name) {
|
||||||
|
mod_func_names << ctfnk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suggestion := util.new_suggestion(fn_name, mod_func_names)
|
||||||
|
c.error(suggestion.say('unknown function: $fn_name '), node.pos)
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
}
|
||||||
c.error('unknown function: $fn_name', node.pos)
|
c.error('unknown function: $fn_name', node.pos)
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
vlib/v/checker/tests/misspelled_mod_fn_name_should_have_suggestion.vv:3:9: error: unknown function: os.read_fil .
|
||||||
|
Did you mean `os.read_file`?
|
||||||
|
1 | import os
|
||||||
|
2 |
|
||||||
|
3 | dump(os.read_fil('abc'))
|
||||||
|
| ~~~~~~~~~~~~~~~
|
|
@ -0,0 +1,3 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
dump(os.read_fil('abc'))
|
|
@ -1,4 +1,5 @@
|
||||||
vlib/v/checker/tests/unknown_function.vv:4:15: error: unknown function: math.max_i64
|
vlib/v/checker/tests/unknown_function.vv:4:15: error: unknown function: math.max_i64 .
|
||||||
|
Did you mean `math.max`?
|
||||||
2 |
|
2 |
|
||||||
3 | fn main() {
|
3 | fn main() {
|
||||||
4 | println(math.max_i64())
|
4 | println(math.max_i64())
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
module util
|
module util
|
||||||
|
|
||||||
|
import term
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
|
// Possibility is a simple pair of a string, with a similarity coefficient
|
||||||
|
// determined by the editing distance to a wanted value.
|
||||||
struct Possibility {
|
struct Possibility {
|
||||||
value string
|
value string
|
||||||
svalue string
|
svalue string
|
||||||
mut:
|
mut:
|
||||||
similarity f32
|
similarity f32 // Note: 0.0 for *equal* strings.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Suggestion is set of known possibilities and a wanted string.
|
||||||
|
// It has helper methods for making educated guesses based on the possibilities,
|
||||||
|
// on which of them match best the wanted string.
|
||||||
struct Suggestion {
|
struct Suggestion {
|
||||||
mut:
|
mut:
|
||||||
known []Possibility
|
known []Possibility
|
||||||
|
@ -16,6 +22,7 @@ mut:
|
||||||
swanted string
|
swanted string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new_suggestion creates a new Suggestion, given a wanted value and a list of possibilities.
|
||||||
pub fn new_suggestion(wanted string, possibilities []string) Suggestion {
|
pub fn new_suggestion(wanted string, possibilities []string) Suggestion {
|
||||||
mut s := Suggestion{
|
mut s := Suggestion{
|
||||||
wanted: wanted
|
wanted: wanted
|
||||||
|
@ -26,6 +33,8 @@ pub fn new_suggestion(wanted string, possibilities []string) Suggestion {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add adds the `val` to the list of known possibilities of the suggestion.
|
||||||
|
// It calculates the similarity metric towards the wanted value.
|
||||||
pub fn (mut s Suggestion) add(val string) {
|
pub fn (mut s Suggestion) add(val string) {
|
||||||
if val in [s.wanted, s.swanted] {
|
if val in [s.wanted, s.swanted] {
|
||||||
return
|
return
|
||||||
|
@ -43,16 +52,21 @@ pub fn (mut s Suggestion) add(val string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add adds all of the `many` to the list of known possibilities of the suggestion
|
||||||
pub fn (mut s Suggestion) add_many(many []string) {
|
pub fn (mut s Suggestion) add_many(many []string) {
|
||||||
for x in many {
|
for x in many {
|
||||||
s.add(x)
|
s.add(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sort sorts the list of known possibilities, based on their similarity metric.
|
||||||
|
// Equal strings will be first, followed by less similar ones, very distinct ones will be last.
|
||||||
pub fn (mut s Suggestion) sort() {
|
pub fn (mut s Suggestion) sort() {
|
||||||
s.known.sort(a.similarity < b.similarity)
|
s.known.sort(a.similarity < b.similarity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// say produces a final suggestion message, based on the preset `wanted` and
|
||||||
|
// `possibilities` fields, accumulated in the Suggestion.
|
||||||
pub fn (s Suggestion) say(msg string) string {
|
pub fn (s Suggestion) say(msg string) string {
|
||||||
mut res := msg
|
mut res := msg
|
||||||
mut found := false
|
mut found := false
|
||||||
|
@ -61,14 +75,14 @@ pub fn (s Suggestion) say(msg string) string {
|
||||||
if top_posibility.similarity > 0.5 {
|
if top_posibility.similarity > 0.5 {
|
||||||
val := top_posibility.value
|
val := top_posibility.value
|
||||||
if !val.starts_with('[]') {
|
if !val.starts_with('[]') {
|
||||||
res += '.\nDid you mean `$val`?'
|
res += '.\nDid you mean `${highlight_suggestion(val)}`?'
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
if s.known.len > 0 {
|
if s.known.len > 0 {
|
||||||
mut values := s.known.map('`$it.svalue`')
|
mut values := s.known.map('`${highlight_suggestion(it.svalue)}`')
|
||||||
values.sort()
|
values.sort()
|
||||||
if values.len == 1 {
|
if values.len == 1 {
|
||||||
res += '.\n1 possibility: ${values[0]}.'
|
res += '.\n1 possibility: ${values[0]}.'
|
||||||
|
@ -81,6 +95,8 @@ pub fn (s Suggestion) say(msg string) string {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// short_module_name returns a shortened version of the fully qualified `name`,
|
||||||
|
// i.e. `xyz.def.abc.symname` -> `abc.symname`
|
||||||
pub fn short_module_name(name string) string {
|
pub fn short_module_name(name string) string {
|
||||||
if !name.contains('.') {
|
if !name.contains('.') {
|
||||||
return name
|
return name
|
||||||
|
@ -93,3 +109,10 @@ pub fn short_module_name(name string) string {
|
||||||
symname := vals[vals.len - 1]
|
symname := vals[vals.len - 1]
|
||||||
return '${mname}.$symname'
|
return '${mname}.$symname'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// highlight_suggestion returns a colorfull/highlighted version of `message`,
|
||||||
|
// but only if the standart error output allows for color messages, otherwise
|
||||||
|
// the plain message will be returned.
|
||||||
|
pub fn highlight_suggestion(message string) string {
|
||||||
|
return term.ecolorize(term.bright_blue, message)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue