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
|
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)
|
// return resolved dep graph (order deps)
|
||||||
pub fn (v &V) resolve_deps() &DepGraph {
|
pub fn (v &V) resolve_deps() &DepGraph {
|
||||||
graph := v.import_graph()
|
graph := v.import_graph()
|
||||||
|
|
|
@ -898,7 +898,11 @@ fn (p mut Parser) get_type() string {
|
||||||
// for q in p.table.types {
|
// for q in p.table.types {
|
||||||
// println(q.name)
|
// 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() {
|
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) {
|
if p.table.known_const(name) {
|
||||||
return p.get_const_type(name, ptr)
|
return p.get_const_type(name, ptr)
|
||||||
}
|
}
|
||||||
|
// TODO: V script? Try os module.
|
||||||
// Function (not method, methods are handled in `.dot()`)
|
// Function (not method, methods are handled in `.dot()`)
|
||||||
mut f := p.table.find_fn_is_script(name, p.v_script) or {
|
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
|
// no () after func, so func is an argument, just gen its name
|
||||||
// TODO verify this and handle errors
|
// TODO verify this and handle errors
|
||||||
|
@ -1838,41 +1849,21 @@ fn (p mut Parser) get_c_func_type(name string) string {
|
||||||
return cfn.typ
|
return cfn.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) get_undefined_fn_type(name string, orig_name string) string {
|
fn (p mut Parser) undefined_error(name string, orig_name 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
|
|
||||||
name_dotted := mod_gen_name_rev(name.replace('__', '.'))
|
name_dotted := mod_gen_name_rev(name.replace('__', '.'))
|
||||||
|
// check for misspelled function / variable / module / type
|
||||||
suggested := p.identify_typo(name)
|
suggested := p.identify_typo(name)
|
||||||
if suggested.len != 0 {
|
if suggested.len != 0 {
|
||||||
p.error('undefined: `$name_dotted`. did you mean:\n$suggested\n')
|
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 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) {
|
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: `$name_dotted` (in module `$orig_name`)')
|
||||||
p.error('undefined function: `$m_name` (in module `$orig_name`)')
|
|
||||||
} else if orig_name in reserved_type_param_names {
|
} else if orig_name in reserved_type_param_names {
|
||||||
p.error('the letter `$orig_name` is reserved for type parameters')
|
p.error('the letter `$orig_name` is reserved for type parameters')
|
||||||
} else {
|
}
|
||||||
p.error('undefined: `$orig_name`')
|
p.error('undefined: `$orig_name`')
|
||||||
}
|
}
|
||||||
return 'void'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p mut Parser) var_expr(v Var) string {
|
fn (p mut Parser) var_expr(v Var) string {
|
||||||
//p.log('\nvar_expr() v.name="$v.name" v.typ="$v.typ"')
|
//p.log('\nvar_expr() v.name="$v.name" v.typ="$v.typ"')
|
||||||
|
|
|
@ -877,7 +877,7 @@ fn (p &Parser) identify_typo(name string) string {
|
||||||
mut output := ''
|
mut output := ''
|
||||||
// check imported modules
|
// check imported modules
|
||||||
mut n := p.table.find_misspelled_imported_mod(name_dotted, p, min_match)
|
mut n := p.table.find_misspelled_imported_mod(name_dotted, p, min_match)
|
||||||
if n != '' {
|
if n.len > 0 {
|
||||||
output += '\n * module: `$n`'
|
output += '\n * module: `$n`'
|
||||||
}
|
}
|
||||||
// check consts
|
// check consts
|
||||||
|
@ -885,41 +885,45 @@ fn (p &Parser) identify_typo(name string) string {
|
||||||
if n != '' {
|
if n != '' {
|
||||||
output += '\n * const: `$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
|
// check functions
|
||||||
n = p.table.find_misspelled_fn(name, p, min_match)
|
n = p.table.find_misspelled_fn(name, p, min_match)
|
||||||
if n != '' {
|
if n.len > 0 {
|
||||||
output += '\n * function: `$n`'
|
output += '\n * function: `$n`'
|
||||||
}
|
}
|
||||||
// check function local variables
|
// check function local variables
|
||||||
n = p.find_misspelled_local_var(name_dotted, min_match)
|
n = p.find_misspelled_local_var(name_dotted, min_match)
|
||||||
if n != '' {
|
if n.len > 0 {
|
||||||
output += '\n * variable: `$n`'
|
output += '\n * variable: `$n`'
|
||||||
}
|
}
|
||||||
return output
|
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`
|
// find function with closest name to `name`
|
||||||
fn (table &Table) find_misspelled_fn(name string, p &Parser, min_match f32) string {
|
fn (table &Table) find_misspelled_fn(name string, p &Parser, min_match f32) string {
|
||||||
mut closest := f32(0)
|
mut closest := f32(0)
|
||||||
mut closest_fn := ''
|
mut closest_fn := ''
|
||||||
n1 := if name.starts_with('main__') { name[6..] } else { name }
|
|
||||||
for _, f in table.fns {
|
for _, f in table.fns {
|
||||||
if n1.len - f.name.len > 2 || f.name.len - n1.len > 2 { continue }
|
if f.name.contains('__') && !p.is_mod_in_scope(f.mod) { continue }
|
||||||
if !(f.mod in ['', 'main', 'builtin']) {
|
c := typo_compare_name_mod(name, f.name, f.mod)
|
||||||
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 c > closest {
|
if c > closest {
|
||||||
closest = c
|
closest = c
|
||||||
closest_fn = f_name_orig
|
closest_fn = mod_gen_name_rev(f.name.replace('__', '.'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if closest >= min_match { closest_fn } else { '' }
|
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 := ''
|
mut closest_mod := ''
|
||||||
n1 := if name.starts_with('main.') { name[5..] } else { name }
|
n1 := if name.starts_with('main.') { name[5..] } else { name }
|
||||||
for alias, mod in p.import_table.imports {
|
for alias, mod in p.import_table.imports {
|
||||||
if n1.len - alias.len > 2 || alias.len - n1.len > 2 { continue }
|
c := typo_compare_name_mod(n1, alias, '')
|
||||||
mod_alias := if alias == mod { alias } else { '$alias ($mod)' }
|
|
||||||
c := strings.dice_coefficient(n1, alias)
|
|
||||||
if c > closest {
|
if c > closest {
|
||||||
closest = c
|
closest = c
|
||||||
closest_mod = '$mod_alias'
|
closest_mod = if alias == mod { alias } else { '$alias ($mod)' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if closest >= min_match { closest_mod } else { '' }
|
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 {
|
fn (table &Table) find_misspelled_const(name string, p &Parser, min_match f32) string {
|
||||||
mut closest := f32(0)
|
mut closest := f32(0)
|
||||||
mut closest_const := ''
|
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 {
|
for cnst in table.consts {
|
||||||
if cnst.mod != p.mod && !(cnst.mod in mods_in_scope) && cnst.mod.contains('__') { continue }
|
if cnst.name.contains('__') && !p.is_mod_in_scope(cnst.mod) { continue }
|
||||||
if name.len - cnst.name.len > 2 || cnst.name.len - name.len > 2 { continue }
|
c := typo_compare_name_mod(name, cnst.name, cnst.mod)
|
||||||
const_name_orig := mod_gen_name_rev(cnst.name.replace('__', '.'))
|
|
||||||
c := strings.dice_coefficient(name, cnst.name.replace('builtin__', 'main__'))
|
|
||||||
if c > closest {
|
if c > closest {
|
||||||
closest = c
|
closest = c
|
||||||
closest_const = const_name_orig
|
closest_const = mod_gen_name_rev(cnst.name.replace('__', '.'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if closest >= min_match { closest_const } else { '' }
|
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