server/failtracer: skip self-referential undefined-ref hints

The compile-time fail hints used a fuzzy match against the ref's
top-level segment to suggest typo fixes (e.g. input.frut -> input.fruit).
levenshtein.ClosestStrings returns the exact match itself when the
top-level segment already matches a declared unknown, so any failure
caused by a missing/undefined sub-field (rather than a misspelled
top-level name) produced a hint suggesting the exact same ref back,
e.g. "input.resource.heading undefined, did you mean
input.resource.heading?".

Skip the hint entirely in that case, since the fuzzy matcher has no
visibility into sub-fields and suggesting the ref unchanged is a no-op.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
This commit is contained in:
Stephan Renatus
2026-07-21 11:07:16 +02:00
committed by Stephan Renatus
parent 31065f123e
commit bdf1d301e0
2 changed files with 15 additions and 0 deletions
+7
View File
@@ -85,6 +85,13 @@ func (b *failTracer) Hints(unknowns []ast.Ref) []Hint {
continue
}
miss := string(tblPart)
if slices.Contains(candidates, miss) {
// The top-level segment is already a declared unknown, so the
// failure comes from something deeper in the ref that this
// fuzzy match can't see. Suggesting the ref itself as a "fix"
// would be a no-op, so skip it.
continue
}
rs := ref[1:].String()
if _, ok := seenRefs[rs]; ok {
continue
+8
View File
@@ -84,6 +84,14 @@ func TestHints(t *testing.T) {
{Message: "input.fruit.colour undefined, did you mean input.fruits.colour?"},
},
},
{
note: "top-level ref already correct, no self-referential hint",
evts: []topdown.Event{
evtFromExpr(`__local1__ = input.resource.heading`),
},
unknowns: []string{"input.resource"},
exp: nil,
},
{
note: "same typo, multiple fail events",
evts: []topdown.Event{