compiler: improve typo detection, support all types and fn definitions
parent
a6a233df6b
commit
5f1e634d82
|
@ -100,6 +100,15 @@ fn (it &ImportTable) is_used_import(alias string) bool {
|
|||
return alias in it.used_imports
|
||||
}
|
||||
|
||||
// should module be accessable
|
||||
pub fn (p &Parser) is_mod_in_scope(mod string) bool {
|
||||
mut mods_in_scope := ['', 'builtin', 'main', p.mod]
|
||||
for _, m in p.import_table.imports {
|
||||
mods_in_scope << m
|
||||
}
|
||||
return mod in mods_in_scope
|
||||
}
|
||||
|
||||
// return resolved dep graph (order deps)
|
||||
pub fn (v &V) resolve_deps() &DepGraph {
|
||||
graph := v.import_graph()
|
||||
|
|
|
@ -898,7 +898,11 @@ fn (p mut Parser) get_type() string {
|
|||
// for q in p.table.types {
|
||||
// println(q.name)
|
||||
// }
|
||||
p.error('unknown type `$typ`')
|
||||
mut t_suggest, tc_suggest := p.table.find_misspelled_type(typ, p, 0.50)
|
||||
if t_suggest.len > 0 {
|
||||
t_suggest = '. did you mean: ($tc_suggest) `$t_suggest`'
|
||||
}
|
||||
p.error('unknown type `$typ`$t_suggest')
|
||||
}
|
||||
}
|
||||
else if !t.is_public && t.mod != p.mod && !p.is_vgen && t.name != '' && !p.first_pass() {
|
||||
|
@ -1676,10 +1680,17 @@ fn (p mut Parser) name_expr() string {
|
|||
if p.table.known_const(name) {
|
||||
return p.get_const_type(name, ptr)
|
||||
}
|
||||
|
||||
// TODO: V script? Try os module.
|
||||
// Function (not method, methods are handled in `.dot()`)
|
||||
mut f := p.table.find_fn_is_script(name, p.v_script) or {
|
||||
return p.get_undefined_fn_type(name, orig_name)
|
||||
// First pass, the function can be defined later.
|
||||
if p.first_pass() {
|
||||
p.next()
|
||||
return 'void'
|
||||
}
|
||||
// exhaused all options type,enum,const,mod,var,fn etc
|
||||
// so show undefined error (also checks typos)
|
||||
p.undefined_error(name, orig_name) return '' // panics
|
||||
}
|
||||
// no () after func, so func is an argument, just gen its name
|
||||
// TODO verify this and handle errors
|
||||
|
@ -1838,40 +1849,20 @@ fn (p mut Parser) get_c_func_type(name string) string {
|
|||
return cfn.typ
|
||||
}
|
||||
|
||||
fn (p mut Parser) get_undefined_fn_type(name string, orig_name string) string {
|
||||
if p.first_pass() {
|
||||
p.next()
|
||||
// First pass, the function can be defined later.
|
||||
return 'void'
|
||||
} else {
|
||||
// We are in the second pass, that means this function was not defined, throw an error.
|
||||
|
||||
// V script? Try os module.
|
||||
// TODO
|
||||
if p.v_script {
|
||||
//name = name.replace('main__', 'os__')
|
||||
//f = p.table.find_fn(name)
|
||||
}
|
||||
|
||||
// check for misspelled function / variable / module
|
||||
fn (p mut Parser) undefined_error(name string, orig_name string) {
|
||||
name_dotted := mod_gen_name_rev(name.replace('__', '.'))
|
||||
// check for misspelled function / variable / module / type
|
||||
suggested := p.identify_typo(name)
|
||||
if suggested.len != 0 {
|
||||
p.error('undefined: `$name_dotted`. did you mean:\n$suggested\n')
|
||||
}
|
||||
|
||||
// If orig_name is a mod, then printing undefined: `mod` tells us nothing
|
||||
// if p.table.known_mod(orig_name) {
|
||||
if p.table.known_mod(orig_name) || p.import_table.known_alias(orig_name) {
|
||||
m_name := mod_gen_name_rev(name.replace('__', '.'))
|
||||
p.error('undefined function: `$m_name` (in module `$orig_name`)')
|
||||
p.error('undefined: `$name_dotted` (in module `$orig_name`)')
|
||||
} else if orig_name in reserved_type_param_names {
|
||||
p.error('the letter `$orig_name` is reserved for type parameters')
|
||||
} else {
|
||||
}
|
||||
p.error('undefined: `$orig_name`')
|
||||
}
|
||||
return 'void'
|
||||
}
|
||||
}
|
||||
|
||||
fn (p mut Parser) var_expr(v Var) string {
|
||||
|
|
|
@ -877,7 +877,7 @@ fn (p &Parser) identify_typo(name string) string {
|
|||
mut output := ''
|
||||
// check imported modules
|
||||
mut n := p.table.find_misspelled_imported_mod(name_dotted, p, min_match)
|
||||
if n != '' {
|
||||
if n.len > 0 {
|
||||
output += '\n * module: `$n`'
|
||||
}
|
||||
// check consts
|
||||
|
@ -885,41 +885,45 @@ fn (p &Parser) identify_typo(name string) string {
|
|||
if n != '' {
|
||||
output += '\n * const: `$n`'
|
||||
}
|
||||
// check types
|
||||
typ, type_cat := p.table.find_misspelled_type(name, p, min_match)
|
||||
if typ.len > 0 {
|
||||
output += '\n * $type_cat: `$typ`'
|
||||
}
|
||||
// check functions
|
||||
n = p.table.find_misspelled_fn(name, p, min_match)
|
||||
if n != '' {
|
||||
if n.len > 0 {
|
||||
output += '\n * function: `$n`'
|
||||
}
|
||||
// check function local variables
|
||||
n = p.find_misspelled_local_var(name_dotted, min_match)
|
||||
if n != '' {
|
||||
if n.len > 0 {
|
||||
output += '\n * variable: `$n`'
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// compare just name part, some items are mod prefied
|
||||
fn typo_compare_name_mod(a, b, b_mod string) f32 {
|
||||
if a.len - b.len > 2 || b.len - a.len > 2 { return 0 }
|
||||
auidx := a.index('__')
|
||||
a_mod := if auidx != -1 { mod_gen_name_rev(a[..auidx]) } else { '' }
|
||||
a_name := if auidx != -1 { a[auidx..] } else { a }
|
||||
b_name := if b.contains('__') { b.all_after('__') } else { b }
|
||||
if a_mod.len > 0 && b_mod.len > 0 && a_mod != b_mod { return 0 }
|
||||
return strings.dice_coefficient(a_name, b_name)
|
||||
}
|
||||
|
||||
// find function with closest name to `name`
|
||||
fn (table &Table) find_misspelled_fn(name string, p &Parser, min_match f32) string {
|
||||
mut closest := f32(0)
|
||||
mut closest_fn := ''
|
||||
n1 := if name.starts_with('main__') { name[6..] } else { name }
|
||||
for _, f in table.fns {
|
||||
if n1.len - f.name.len > 2 || f.name.len - n1.len > 2 { continue }
|
||||
if !(f.mod in ['', 'main', 'builtin']) {
|
||||
mut mod_imported := false
|
||||
for _, m in p.import_table.imports {
|
||||
if f.mod == m {
|
||||
mod_imported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !mod_imported { continue }
|
||||
}
|
||||
c := strings.dice_coefficient(n1, f.name)
|
||||
f_name_orig := mod_gen_name_rev(f.name.replace('__', '.'))
|
||||
if f.name.contains('__') && !p.is_mod_in_scope(f.mod) { continue }
|
||||
c := typo_compare_name_mod(name, f.name, f.mod)
|
||||
if c > closest {
|
||||
closest = c
|
||||
closest_fn = f_name_orig
|
||||
closest_fn = mod_gen_name_rev(f.name.replace('__', '.'))
|
||||
}
|
||||
}
|
||||
return if closest >= min_match { closest_fn } else { '' }
|
||||
|
@ -931,12 +935,10 @@ fn (table &Table) find_misspelled_imported_mod(name string, p &Parser, min_match
|
|||
mut closest_mod := ''
|
||||
n1 := if name.starts_with('main.') { name[5..] } else { name }
|
||||
for alias, mod in p.import_table.imports {
|
||||
if n1.len - alias.len > 2 || alias.len - n1.len > 2 { continue }
|
||||
mod_alias := if alias == mod { alias } else { '$alias ($mod)' }
|
||||
c := strings.dice_coefficient(n1, alias)
|
||||
c := typo_compare_name_mod(n1, alias, '')
|
||||
if c > closest {
|
||||
closest = c
|
||||
closest_mod = '$mod_alias'
|
||||
closest_mod = if alias == mod { alias } else { '$alias ($mod)' }
|
||||
}
|
||||
}
|
||||
return if closest >= min_match { closest_mod } else { '' }
|
||||
|
@ -946,20 +948,51 @@ fn (table &Table) find_misspelled_imported_mod(name string, p &Parser, min_match
|
|||
fn (table &Table) find_misspelled_const(name string, p &Parser, min_match f32) string {
|
||||
mut closest := f32(0)
|
||||
mut closest_const := ''
|
||||
mut mods_in_scope := ['builtin', 'main']
|
||||
for _, mod in p.import_table.imports {
|
||||
mods_in_scope << mod
|
||||
}
|
||||
for cnst in table.consts {
|
||||
if cnst.mod != p.mod && !(cnst.mod in mods_in_scope) && cnst.mod.contains('__') { continue }
|
||||
if name.len - cnst.name.len > 2 || cnst.name.len - name.len > 2 { continue }
|
||||
const_name_orig := mod_gen_name_rev(cnst.name.replace('__', '.'))
|
||||
c := strings.dice_coefficient(name, cnst.name.replace('builtin__', 'main__'))
|
||||
if cnst.name.contains('__') && !p.is_mod_in_scope(cnst.mod) { continue }
|
||||
c := typo_compare_name_mod(name, cnst.name, cnst.mod)
|
||||
if c > closest {
|
||||
closest = c
|
||||
closest_const = const_name_orig
|
||||
closest_const = mod_gen_name_rev(cnst.name.replace('__', '.'))
|
||||
}
|
||||
}
|
||||
return if closest >= min_match { closest_const } else { '' }
|
||||
}
|
||||
|
||||
// find type with closest name to `name`
|
||||
fn (table &Table) find_misspelled_type(name string, p &Parser, min_match f32) (string, string) {
|
||||
mut closest := f32(0)
|
||||
mut closest_type := ''
|
||||
mut type_cat := ''
|
||||
for _, typ in table.typesmap {
|
||||
if typ.name.contains('__') && !p.is_mod_in_scope(typ.mod) { continue }
|
||||
c := typo_compare_name_mod(name, typ.name, typ.mod)
|
||||
if c > closest {
|
||||
closest = c
|
||||
closest_type = mod_gen_name_rev(typ.name.replace('__', '.'))
|
||||
type_cat = type_cat_str(typ.cat)
|
||||
}
|
||||
}
|
||||
if closest >= min_match {
|
||||
return closest_type, type_cat
|
||||
}
|
||||
return '', ''
|
||||
}
|
||||
|
||||
fn type_cat_str(tc TypeCategory) string {
|
||||
tc_str := match tc {
|
||||
.builtin { 'builtin' }
|
||||
.struct_ { 'struct' }
|
||||
.func { 'function' }
|
||||
.interface_ { 'interface' }
|
||||
.enum_ { 'enum' }
|
||||
.union_ { 'union' }
|
||||
.c_struct { 'C struct' }
|
||||
.c_typedef { 'C typedef' }
|
||||
.objc_interface { 'obj C interface' }
|
||||
.array { 'array' }
|
||||
.alias { 'type alias' }
|
||||
else { 'unknown' }
|
||||
}
|
||||
return tc_str
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue