Skip to content

Commit 1752609

Browse files
committed
Refactor "did you mean" hinting
- Handle all members instead of just decls. - Simplify the logic and implementation. To tell the truth, the previous implementation was too convoluted for me to be able to say what it does exactly. But the new one seems to fit the intuitive spec.
1 parent 255a538 commit 1752609

File tree

1 file changed

+21
-34
lines changed

1 file changed

+21
-34
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -314,13 +314,19 @@ object messages {
314314

315315
val msg: String = {
316316
import core.Flags._
317-
val maxDist = 3
318-
val decls = site.decls.toList
319-
.filter(_.isType == name.isTypeName)
320-
.flatMap { sym =>
321-
if (sym.flagsUNSAFE.isOneOf(Synthetic | PrivateLocal) || sym.isConstructor) Nil
322-
else List((sym.name.show, sym))
323-
}
317+
val maxDist = 3 // maximal number of differences to be considered for a hint
318+
val missing = name.show
319+
320+
// The names of all non-synthetic, non-private members of `site`
321+
// that are of the same type/term kind as the missing member.
322+
def candidates: Set[String] =
323+
for
324+
bc <- site.baseClasses.toSet
325+
sym <- bc.info.decls.filter(sym =>
326+
sym.isType == name.isTypeName
327+
&& !sym.isConstructor
328+
&& !sym.flagsUNSAFE.isOneOf(Synthetic | Private))
329+
yield sym.name.show
324330

325331
// Calculate Levenshtein distance
326332
def distance(n1: Iterable[?], n2: Iterable[?]) =
@@ -333,37 +339,18 @@ object messages {
333339
}
334340
}.last
335341

336-
// Count number of wrong characters
337-
def incorrectChars(x: (String, Int, Symbol)): (String, Symbol, Int) = {
338-
val (currName, _, sym) = x
339-
val matching = name.show.zip(currName).foldLeft(0) {
340-
case (acc, (x,y)) => if (x != y) acc + 1 else acc
341-
}
342-
(currName, sym, matching)
343-
}
344-
345-
// Get closest match in `site`
346-
def closest: List[String] =
347-
decls
348-
.map { (n, sym) => (n, distance(n, name.show), sym) }
349-
.collect {
350-
case (n, dist, sym)
351-
if dist <= maxDist && dist < (name.toString.length min n.length) =>
352-
(n, dist, sym)
353-
}
354-
.groupBy(_._2).toList
355-
.sortBy(_._1)
356-
.headOption.map(_._2).getOrElse(Nil)
357-
.map(incorrectChars).toList
358-
.sortBy(_._3)
359-
.map(_._1)
360-
// [Martin] Note: I have no idea what this does. This shows the
361-
// pitfalls of not naming things, functional or not.
342+
// A list of possible candidate strings with their Levenstein distances
343+
// to the name of the missing member
344+
def closest: List[(Int, String)] = candidates
345+
.toList
346+
.map(n => (distance(n.show, missing), n))
347+
.filter((d, n) => d <= maxDist && d < missing.length & d < n.length)
348+
.sorted // sort by distance first, alphabetically second
362349

363350
val finalAddendum =
364351
if addendum.nonEmpty then addendum
365352
else closest match {
366-
case n :: _ =>
353+
case (d, n) :: _ =>
367354
val siteName = site match
368355
case site: NamedType => site.name.show
369356
case site => i"$site"

0 commit comments

Comments
 (0)