Skip to content

Commit d94e1ec

Browse files
authored
Merge pull request #8773 from dotty-staging/fix-#8763
Fix #8763: Limit number of import suggestions
2 parents 5772538 + 24856df commit d94e1ec

File tree

1 file changed

+48
-9
lines changed

1 file changed

+48
-9
lines changed

compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import collection.mutable
1919
trait ImportSuggestions:
2020
this: Typer =>
2121

22+
/** The maximal number of suggested imports to make */
23+
inline val MaxSuggestions = 10
24+
2225
import tpd._
2326

2427
/** Timeout to test a single implicit value as a suggestion, in ms */
@@ -249,6 +252,46 @@ trait ImportSuggestions:
249252
finally timer.cancel()
250253
end importSuggestions
251254

255+
/** The `ref` parts of this list of pairs, discarding subsequent elements that
256+
* have the same String part. Elements are sorted by their String parts.
257+
*/
258+
def (refs: List[(TermRef, String)]).distinctRefs(using Context): List[TermRef] = refs match
259+
case (ref, str) :: refs1 =>
260+
ref :: refs1.dropWhile(_._2 == str).distinctRefs
261+
case Nil =>
262+
Nil
263+
264+
/** The best `n` references in `refs`, according to `compare`
265+
* `compare` is a partial order. If there's a tie, we take elements
266+
* in the order thy appear in the list.
267+
*/
268+
def (refs: List[TermRef]).best(n: Int)(using Context): List[TermRef] =
269+
val top = new Array[TermRef](n)
270+
var filled = 0
271+
val rest = new mutable.ListBuffer[TermRef]
272+
val noImplicitsCtx = ctx.retractMode(Mode.ImplicitsEnabled)
273+
for ref <- refs do
274+
var i = 0
275+
var diff = 0
276+
while i < filled && diff == 0 do
277+
diff = compare(ref, top(i))(using noImplicitsCtx)
278+
if diff > 0 then
279+
rest += top(i)
280+
top(i) = ref
281+
i += 1
282+
end while
283+
if diff == 0 && filled < n then
284+
top(filled) = ref
285+
filled += 1
286+
else if diff <= 0 then
287+
rest += ref
288+
end for
289+
val remaining =
290+
if filled < n && rest.nonEmpty then rest.toList.best(n - filled)
291+
else Nil
292+
top.take(filled).toList ++ remaining
293+
end best
294+
252295
/** An addendum to an error message where the error might be fixed
253296
* by some implicit value of type `pt` that is however not found.
254297
* The addendum suggests given imports that might fix the problem.
@@ -265,15 +308,11 @@ trait ImportSuggestions:
265308
s" import ${ctx.printer.toTextRef(ref).show}"
266309
val suggestions = suggestedRefs
267310
.zip(suggestedRefs.map(importString))
268-
.filter((ref, str) => str.contains('.'))
269-
.sortWith { (x, y) =>
270-
// sort by specificity first, alphabetically second
271-
val ((ref1, str1), (ref2, str2)) = (x, y)
272-
val diff = compare(ref1, ref2)
273-
diff > 0 || diff == 0 && str1 < str2
274-
}
275-
.map((ref, str) => str)
276-
.distinct // TermRefs might be different but generate the same strings
311+
.filter((ref, str) => str.contains('.')) // must be a real import with `.`
312+
.sortBy(_._2) // sort first alphabetically for stability
313+
.distinctRefs // TermRefs might be different but generate the same strings
314+
.best(MaxSuggestions) // take MaxSuggestions best references according to specificity
315+
.map(importString)
277316
if suggestions.isEmpty then ""
278317
else
279318
val fix =

0 commit comments

Comments
 (0)