@@ -13,6 +13,7 @@ import dotty.tools.dotc.core.Flags._
13
13
import dotty .tools .dotc .core .Names .{Name , TermName }
14
14
import dotty .tools .dotc .core .NameKinds .SimpleNameKind
15
15
import dotty .tools .dotc .core .NameOps ._
16
+ import dotty .tools .dotc .core .Signature
16
17
import dotty .tools .dotc .core .Symbols .{NoSymbol , Symbol , TermSymbol , defn , newSymbol }
17
18
import dotty .tools .dotc .core .Scopes .Scope
18
19
import dotty .tools .dotc .core .StdNames .{nme , tpnme }
@@ -21,6 +22,8 @@ import dotty.tools.dotc.core.TypeComparer
21
22
import dotty .tools .dotc .core .TypeError
22
23
import dotty .tools .dotc .core .Types .{ExprType , MethodOrPoly , NameFilter , NamedType , NoType , PolyType , TermRef , Type }
23
24
import dotty .tools .dotc .printing .Texts ._
25
+ import dotty .tools .dotc .typer .Implicits
26
+ import dotty .tools .dotc .typer .Implicits .SearchSuccess
24
27
import dotty .tools .dotc .util .{NameTransformer , NoSourcePosition , SourcePosition }
25
28
26
29
import scala .collection .mutable
@@ -115,7 +118,7 @@ object Completion {
115
118
// Ignore synthetic select from `This` because in code it was `Ident`
116
119
// See example in dotty.tools.languageserver.CompletionTest.syntheticThis
117
120
case Select (qual @ This (_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions
118
- case Select (qual, _) :: _ if qual.tpe.hasSimpleKind => completer.selectionCompletions(qual )
121
+ case (sel @ Select (qual, _)) :: _ if qual.tpe.hasSimpleKind => completer.selectionCompletions(sel )
119
122
case Select (qual, _) :: _ => Map .empty
120
123
case Import (expr, _) :: _ => completer.directMemberCompletions(expr)
121
124
case (_ : untpd.ImportSelector ) :: Import (expr, _) :: _ => completer.directMemberCompletions(expr)
@@ -230,10 +233,10 @@ object Completion {
230
233
* Direct members take priority over members from extensions
231
234
* and so do members from extensions over members from implicit conversions
232
235
*/
233
- def selectionCompletions (qual : Tree )(using Context ): CompletionMap =
234
- implicitConversionMemberCompletions(qual ) ++
235
- extensionCompletions(qual ) ++
236
- directMemberCompletions(qual )
236
+ def selectionCompletions (sel : Select )(using Context ): CompletionMap =
237
+ implicitConversionMemberCompletions(sel ) ++
238
+ extensionCompletions(sel.qualifier ) ++
239
+ directMemberCompletions(sel.qualifier )
237
240
238
241
/** Completions for members of `qual`'s type.
239
242
* These include inherited definitions but not members added by extensions or implicit conversions
@@ -285,13 +288,36 @@ object Completion {
285
288
}
286
289
287
290
/** Completions from implicit conversions including old style extensions using implicit classes */
288
- private def implicitConversionMemberCompletions (qual : Tree )(using Context ): CompletionMap =
291
+ private def implicitConversionMemberCompletions (sel : Select )(using Context ): CompletionMap =
292
+ val qual = sel.qualifier
289
293
if qual.tpe.widenDealias.isExactlyNothing || qual.tpe.isNullType then
290
294
Map .empty
291
295
else
296
+ // Take all possible conversions for `qual`
297
+ val typer = ctx.typer
298
+ val searchContext = ctx.fresh.setExploreTyperState()
299
+ val search = new typer.ImplicitSearch (defn.AnyType , qual, pos.span)(using searchContext)
292
300
val membersFromConversion =
293
- implicitConversionTargets(qual)(using ctx.fresh.setExploreTyperState()).flatMap(accessibleMembers)
294
- membersFromConversion.toSeq.groupByName
301
+ search.allImplicits.flatMap(r => accessibleMembers(r.ref.widen.finalResultType).map(_ -> r))
302
+
303
+ // There might be more that one conversions that have members with the same name and signature
304
+ // In order to avoid duplicate completions a kind of ranking (per Name + ParameterSig) is performed
305
+ type Key = (Name , List [Signature .ParamSig ])
306
+ val init = Map .empty[Key , (SingleDenotation , SearchSuccess )]
307
+ membersFromConversion.foldLeft(init) { case (acc, (rawDenot, found)) =>
308
+ val denot = rawDenot.asSeenFrom(found.tree.tpe)
309
+ val sig = denot.info.signature
310
+ val key = (denot.name, sig.paramsSig)
311
+ acc.get(key) match {
312
+ case None => acc.updated(key, (denot, found))
313
+ case Some ((_, owner)) if search.compareAlternatives(found, owner) > 0 =>
314
+ acc.updated(key, (denot, found))
315
+ case Some (_) =>
316
+ acc
317
+ }
318
+ }.map {case (_, (denot, _)) => denot}
319
+ .toSeq
320
+ .groupByName
295
321
296
322
/** Completions from extension methods */
297
323
private def extensionCompletions (qual : Tree )(using Context ): CompletionMap =
@@ -394,21 +420,6 @@ object Completion {
394
420
}
395
421
}
396
422
397
- /**
398
- * Given `qual` of type T, finds all the types S such that there exists an implicit conversion
399
- * from T to S.
400
- *
401
- * @param qual The argument to which the implicit conversion should be applied.
402
- * @return The set of types that `qual` can be converted to.
403
- */
404
- private def implicitConversionTargets (qual : Tree )(using Context ): Set [Type ] = {
405
- val typer = ctx.typer
406
- val conversions = new typer.ImplicitSearch (defn.AnyType , qual, pos.span).allImplicits
407
- val targets = conversions.map(_.widen.finalResultType)
408
- interactiv.println(i " implicit conversion targets considered: ${targets.toList}%, % " )
409
- targets
410
- }
411
-
412
423
/** Filter for names that should appear when looking for completions. */
413
424
private object completionsFilter extends NameFilter {
414
425
def apply (pre : Type , name : Name )(using Context ): Boolean =
0 commit comments