Skip to content

Commit

Permalink
Suggest most similar function id when we can't find a function type
Browse files Browse the repository at this point in the history
Find identifier with smallest optimal string alignment (OSA) distance
(within reason) and suggest it. Only do this if the OSA distance is 4
or less, and the strings only differ in length be two or less to avoid
suggesting unrelated identifiers. These numbers just felt ok with some
basic testing, but maybe a better heuristic exists.
  • Loading branch information
Alasdair committed Nov 30, 2023
1 parent cea7087 commit 464751b
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 4 deletions.
18 changes: 16 additions & 2 deletions src/lib/type_env.ml
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ let add_typ_synonym id typq arg env =

let get_val_spec_orig id env =
try get_item (id_loc id) env (Bindings.find id env.global.val_specs)
with Not_found -> typ_error (id_loc id) ("No type signature found for " ^ string_of_id id)
with Not_found -> typ_error (id_loc id) ("No function type found for " ^ string_of_id id)

let get_val_spec_opt id env =
match Bindings.find_opt id env.global.val_specs with
Expand All @@ -922,7 +922,21 @@ let get_val_spec_opt id env =
let get_val_spec id env =
match get_val_spec_opt id env with
| Some (bind, _) -> bind
| None -> typ_error (id_loc id) ("No type declaration found for " ^ string_of_id id)
| None ->
(* Try to find the most similar function name, within reason, to include in the error *)
let closest = ref (Int.max_int, None) in
Bindings.iter
(fun other_id item ->
let id_str = string_of_id id in
let other_str = string_of_id other_id in
if abs (String.length id_str - String.length other_str) <= 2 then (
let distance = Util.levenshtein_distance ~osa:true id_str other_str in
if distance <= 4 && distance < fst !closest then closest := (distance, Some other_str)
)
)
env.global.val_specs;
let hint_msg = match snd !closest with Some other_str -> "\n\nDid you mean " ^ other_str ^ "?" | None -> "" in
typ_error (id_loc id) ("No function type found for " ^ string_of_id id ^ hint_msg)

let get_val_specs env = filter_items env env.global.val_specs

Expand Down
4 changes: 2 additions & 2 deletions src/lib/util.ml
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,8 @@ let levenshtein_distance ?(osa = false) str1 str2 =
for i = 1 to String.length str1 do
for j = 1 to String.length str2 do
let subst_cost = if str1.[i - 1] = str2.[j - 1] then 0 else 1 in
dist.(i).(j) <- min (min (dist.(i - 1).(j) + 1) (dist.(j).(i - 1) + 1)) (dist.(i - 1).(j - 1) + subst_cost);
if osa && i > 1 && j > 1 && str1.[i] = str2.[j - 1] && str1.[i - 1] = str2.[j] then
dist.(i).(j) <- min (min (dist.(i - 1).(j) + 1) (dist.(i).(j - 1) + 1)) (dist.(i - 1).(j - 1) + subst_cost);
if osa && i > 1 && j > 1 && str1.[i - 1] = str2.[j - 2] && str1.[i - 2] = str2.[j - 1] then
dist.(i).(j) <- min dist.(i).(j) (dist.(i - 2).(j - 2) + 1)
done
done;
Expand Down
7 changes: 7 additions & 0 deletions test/typecheck/fail/no_function.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Type error:
fail/no_function.sail:10.10-17:
10 | let _ = foo_baz()
 | ^-----^
 | No function type found for foo_baz
 |
 | Did you mean foo_bar?
11 changes: 11 additions & 0 deletions test/typecheck/fail/no_function.sail
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
default Order dec

$include <prelude.sail>

val foo_quux : unit -> unit

val foo_bar : unit -> unit

function test() -> unit = {
let _ = foo_baz()
}
7 changes: 7 additions & 0 deletions test/typecheck/fail/no_function2.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Type error:
fail/no_function2.sail:8.10-17:
8 | let _ = foo_baz()
 | ^-----^
 | No function type found for foo_baz
 |
 | Did you mean foo_quux?
9 changes: 9 additions & 0 deletions test/typecheck/fail/no_function2.sail
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
default Order dec

$include <prelude.sail>

val foo_quux : unit -> unit

function test() -> unit = {
let _ = foo_baz()
}
5 changes: 5 additions & 0 deletions test/typecheck/fail/no_function3.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Type error:
fail/no_function3.sail:6.10-17:
6 | let _ = foo_baz()
 | ^-----^
 | No function type found for foo_baz
7 changes: 7 additions & 0 deletions test/typecheck/fail/no_function3.sail
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
default Order dec

$include <prelude.sail>

function test() -> unit = {
let _ = foo_baz()
}

0 comments on commit 464751b

Please sign in to comment.