compiler: improve typo detection, support all types and fn definitions

pull/2643/head
joe-conigliaro 2019-11-04 12:59:28 +11:00 committed by Alexander Medvednikov
parent a6a233df6b
commit 5f1e634d82
3 changed files with 100 additions and 67 deletions

View File

@ -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()

View File

@ -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"')

View File

@ -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
}