1
1
package dotty .tools .dotc .interactive
2
2
3
3
import dotty .tools .dotc .ast .untpd
4
+ import dotty .tools .dotc .ast .tpd
4
5
import dotty .tools .dotc .ast .NavigateAST
5
6
import dotty .tools .dotc .config .Printers .interactiv
6
7
import dotty .tools .dotc .core .Contexts .*
@@ -45,8 +46,6 @@ case class Completion(label: String, description: String, symbols: List[Symbol])
45
46
46
47
object Completion :
47
48
48
- import dotty .tools .dotc .ast .tpd .*
49
-
50
49
/** Get possible completions from tree at `pos`
51
50
*
52
51
* @return offset and list of symbols for possible completions
@@ -62,7 +61,6 @@ object Completion:
62
61
63
62
postProcessCompletions(untpdPath, completions, rawPrefix)
64
63
65
-
66
64
/** Get possible completions from tree at `pos`
67
65
* This method requires manually computing the mode, prefix and paths.
68
66
*
@@ -72,7 +70,7 @@ object Completion:
72
70
pos : SourcePosition ,
73
71
mode : Mode ,
74
72
rawPrefix : String ,
75
- tpdPath : List [Tree ],
73
+ tpdPath : List [tpd. Tree ],
76
74
untpdPath : List [untpd.Tree ]
77
75
)(using Context ): CompletionMap =
78
76
val adjustedPath = typeCheckExtensionConstructPath(untpdPath, tpdPath, pos)
@@ -94,7 +92,7 @@ object Completion:
94
92
path match
95
93
case untpd.Ident (_) :: untpd.Import (_, _) :: _ => Mode .ImportOrExport
96
94
case untpd.Ident (_) :: (_ : untpd.ImportSelector ) :: _ => Mode .ImportOrExport
97
- case Literal (Constants .Constant (_ : String )) :: _ => Mode .Term // literal completions
95
+ case untpd. Literal (Constants .Constant (_ : String )) :: _ => Mode .Term // literal completions
98
96
case (ref : untpd.RefTree ) :: _ =>
99
97
if (ref.name.isTermName) Mode .Term
100
98
else if (ref.name.isTypeName) Mode .Type
@@ -109,9 +107,9 @@ object Completion:
109
107
110
108
val completionKind : Mode =
111
109
path match
112
- case Nil | (_ : PackageDef ) :: _ => Mode .None
110
+ case Nil | (_ : untpd. PackageDef ) :: _ => Mode .None
113
111
case untpd.Ident (_) :: (_ : untpd.ImportSelector ) :: _ => Mode .Member
114
- case (_ : Select ) :: _ => Mode .Member
112
+ case (_ : untpd. Select ) :: _ => Mode .Member
115
113
case _ => Mode .Scope
116
114
117
115
completionSymbolKind | completionKind
@@ -188,8 +186,8 @@ object Completion:
188
186
* @return Typed path to the parameter of the extension construct if found or tpdPath
189
187
*/
190
188
private def typeCheckExtensionConstructPath (
191
- untpdPath : List [untpd.Tree ], tpdPath : List [Tree ], pos : SourcePosition
192
- )(using Context ): List [Tree ] =
189
+ untpdPath : List [untpd.Tree ], tpdPath : List [tpd. Tree ], pos : SourcePosition
190
+ )(using Context ): List [tpd. Tree ] =
193
191
untpdPath.collectFirst:
194
192
case untpd.ExtMethods (paramss, _) =>
195
193
val enclosingParam = paramss.flatten.find(_.span.contains(pos.span))
@@ -199,27 +197,29 @@ object Completion:
199
197
Interactive .pathTo(typedEnclosingParam, pos.span)
200
198
.flatten.getOrElse(tpdPath)
201
199
202
- private def computeCompletions (pos : SourcePosition , mode : Mode , rawPrefix : String , adjustedPath : List [Tree ])(using Context ): CompletionMap =
200
+ private def computeCompletions (
201
+ pos : SourcePosition , mode : Mode , rawPrefix : String , adjustedPath : List [tpd.Tree ]
202
+ )(using Context ): CompletionMap =
203
203
val hasBackTick = rawPrefix.headOption.contains('`' )
204
204
val prefix = if hasBackTick then rawPrefix.drop(1 ) else rawPrefix
205
205
val completer = new Completer (mode, prefix, pos)
206
206
207
207
val result = adjustedPath match
208
208
// Ignore synthetic select from `This` because in code it was `Ident`
209
209
// See example in dotty.tools.languageserver.CompletionTest.syntheticThis
210
- case Select (qual @ This (_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions
211
- case Select (qual, _) :: _ if qual.tpe.hasSimpleKind => completer.selectionCompletions(qual)
212
- case Select (qual, _) :: _ => Map .empty
213
- case (tree : ImportOrExport ) :: _ => completer.directMemberCompletions(tree.expr)
214
- case (_ : untpd.ImportSelector ) :: Import (expr, _) :: _ => completer.directMemberCompletions(expr)
215
- case _ => completer.scopeCompletions
210
+ case tpd. Select (qual @ tpd. This (_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions
211
+ case tpd. Select (qual, _) :: _ if qual.tpe.hasSimpleKind => completer.selectionCompletions(qual)
212
+ case tpd. Select (qual, _) :: _ => Map .empty
213
+ case (tree : tpd. ImportOrExport ) :: _ => completer.directMemberCompletions(tree.expr)
214
+ case (_ : untpd.ImportSelector ) :: tpd. Import (expr, _) :: _ => completer.directMemberCompletions(expr)
215
+ case _ => completer.scopeCompletions
216
216
217
217
interactiv.println(i """ completion info with pos = $pos,
218
- | prefix = ${completer.prefix},
219
- | term = ${completer.mode.is(Mode .Term )},
220
- | type = ${completer.mode.is(Mode .Type )},
221
- | scope = ${completer.mode.is(Mode .Scope )},
222
- | member = ${completer.mode.is(Mode .Member )}""" )
218
+ | prefix = ${completer.prefix},
219
+ | term = ${completer.mode.is(Mode .Term )},
220
+ | type = ${completer.mode.is(Mode .Type )},
221
+ | scope = ${completer.mode.is(Mode .Scope )},
222
+ | member = ${completer.mode.is(Mode .Member )}""" )
223
223
224
224
result
225
225
@@ -279,6 +279,16 @@ object Completion:
279
279
if denot.isType then denot.symbol.showFullName
280
280
else denot.info.widenTermRefExpr.show
281
281
282
+ given ScopeOrdering (using Context ): Ordering [Seq [SingleDenotation ]] with
283
+ val order =
284
+ List (defn.ScalaPredefModuleClass , defn.ScalaPackageClass , defn.JavaLangPackageClass )
285
+
286
+ override def compare (x : Seq [SingleDenotation ], y : Seq [SingleDenotation ]): Int =
287
+ val owner0 = x.headOption.map(_.symbol.effectiveOwner).getOrElse(NoSymbol )
288
+ val owner1 = y.headOption.map(_.symbol.effectiveOwner).getOrElse(NoSymbol )
289
+
290
+ order.indexOf(owner0) - order.indexOf(owner1)
291
+
282
292
/** Computes code completions depending on the context in which completion is requested
283
293
* @param mode Should complete names of terms, types or both
284
294
* @param prefix The prefix that all suggested completions should start with
@@ -295,32 +305,41 @@ object Completion:
295
305
* This mimics the logic for deciding what is ambiguous used by the compiler.
296
306
* In general in case of a name clash symbols introduced in more deeply nested scopes
297
307
* have higher priority and shadow previous definitions with the same name although:
298
- * - imports with the same level of nesting cause an ambiguity
308
+ * - imports with the same level of nesting cause an ambiguity if they are in the same name space
299
309
* - members and local definitions with the same level of nesting are allowed for overloading
300
310
* - an import is ignored if there is a local definition or a member introduced in the same scope
301
311
* (even if the import follows it syntactically)
302
312
* - a more deeply nested import shadowing a member or a local definition causes an ambiguity
303
313
*/
304
314
def scopeCompletions (using context : Context ): CompletionMap =
315
+
316
+ /** Temporary data structure representing denotations with the same name introduced in a given scope
317
+ * as a member of a type, by a local definition or by an import clause
318
+ */
319
+ case class ScopedDenotations private (denots : Seq [SingleDenotation ], ctx : Context )
320
+ object ScopedDenotations :
321
+ def apply (denots : Seq [SingleDenotation ], ctx : Context , includeFn : SingleDenotation => Boolean ): ScopedDenotations =
322
+ ScopedDenotations (denots.filter(includeFn), ctx)
323
+
305
324
val mappings = collection.mutable.Map .empty[Name , List [ScopedDenotations ]].withDefaultValue(List .empty)
306
325
def addMapping (name : Name , denots : ScopedDenotations ) =
307
326
mappings(name) = mappings(name) :+ denots
308
327
309
328
ctx.outersIterator.foreach { case ctx @ given Context =>
310
329
if ctx.isImportContext then
311
330
importedCompletions.foreach { (name, denots) =>
312
- addMapping(name, ScopedDenotations (denots, ctx))
331
+ addMapping(name, ScopedDenotations (denots, ctx, include(_, name) ))
313
332
}
314
333
else if ctx.owner.isClass then
315
334
accessibleMembers(ctx.owner.thisType)
316
335
.groupByName.foreach { (name, denots) =>
317
- addMapping(name, ScopedDenotations (denots, ctx))
336
+ addMapping(name, ScopedDenotations (denots, ctx, include(_, name) ))
318
337
}
319
338
else if ctx.scope ne EmptyScope then
320
339
ctx.scope.toList.filter(symbol => include(symbol, symbol.name))
321
340
.flatMap(_.alternatives)
322
341
.groupByName.foreach { (name, denots) =>
323
- addMapping(name, ScopedDenotations (denots, ctx))
342
+ addMapping(name, ScopedDenotations (denots, ctx, include(_, name) ))
324
343
}
325
344
}
326
345
@@ -333,26 +352,22 @@ object Completion:
333
352
def isSingleImport = denotss.length < 2
334
353
// import a.C
335
354
// locally { import b.C }
336
- def isImportedInDifferentScope = first.ctx.scope ne denotss(1 ).ctx.scope
355
+ def isImportedInDifferentScope = first.ctx.scope ne denotss(1 ).ctx.scope
337
356
// import a.C
338
357
// import a.C
339
- def isSameSymbolImportedDouble = denotss.forall(_.denots == first.denots)
340
-
341
- def isScalaPackage (scopedDenots : ScopedDenotations ) =
342
- scopedDenots.denots.exists(_.info.typeSymbol.owner == defn.ScalaPackageClass )
343
-
344
- def isJavaLangPackage (scopedDenots : ScopedDenotations ) =
345
- scopedDenots.denots.exists(_.info.typeSymbol.owner == defn.JavaLangPackageClass )
346
-
347
- // For example
348
- // import java.lang.annotation
349
- // is shadowed by
350
- // import scala.annotation
351
- def isJavaLangAndScala =
352
- try
353
- denotss.forall(denots => isScalaPackage(denots) || isJavaLangPackage(denots))
354
- catch
355
- case NonFatal (_) => false
358
+ def isSameSymbolImportedDouble = denotss.forall(_.denots == first.denots)
359
+
360
+ // https://scala-lang.org/files/archive/spec/3.4/02-identifiers-names-and-scopes.html
361
+ // import java.lang.*
362
+ // {
363
+ // import scala.*
364
+ // {
365
+ // import Predef.*
366
+ // { /* source */ }
367
+ // }
368
+ // }
369
+ def notConflictingWithDefaults = // is imported symbol
370
+ denotss.filterNot(_.denots.exists(denot => Interactive .isImportedByDefault(denot.symbol))).size <= 1
356
371
357
372
denotss.find(! _.ctx.isImportContext) match {
358
373
// most deeply nested member or local definition if not shadowed by an import
@@ -361,13 +376,9 @@ object Completion:
361
376
362
377
case None if isSingleImport || isImportedInDifferentScope || isSameSymbolImportedDouble =>
363
378
resultMappings += name -> first.denots
364
- case None if isJavaLangAndScala =>
365
- denotss.foreach{
366
- denots =>
367
- if isScalaPackage(denots) then
368
- resultMappings += name -> denots.denots
369
- }
370
-
379
+ case None if notConflictingWithDefaults =>
380
+ val ordered = denotss.map(_.denots).sorted
381
+ resultMappings += name -> ordered.head
371
382
case _ =>
372
383
}
373
384
}
@@ -377,7 +388,7 @@ object Completion:
377
388
378
389
/** Widen only those types which are applied or are exactly nothing
379
390
*/
380
- def widenQualifier (qual : Tree )(using Context ): Tree =
391
+ def widenQualifier (qual : tpd. Tree )(using Context ): tpd. Tree =
381
392
qual.tpe.widenDealias match
382
393
case widenedType if widenedType.isExactlyNothing => qual.withType(widenedType)
383
394
case appliedType : AppliedType => qual.withType(appliedType)
@@ -387,7 +398,7 @@ object Completion:
387
398
* Direct members take priority over members from extensions
388
399
* and so do members from extensions over members from implicit conversions
389
400
*/
390
- def selectionCompletions (qual : Tree )(using Context ): CompletionMap =
401
+ def selectionCompletions (qual : tpd. Tree )(using Context ): CompletionMap =
391
402
val adjustedQual = widenQualifier(qual)
392
403
393
404
implicitConversionMemberCompletions(adjustedQual) ++
@@ -397,7 +408,7 @@ object Completion:
397
408
/** Completions for members of `qual`'s type.
398
409
* These include inherited definitions but not members added by extensions or implicit conversions
399
410
*/
400
- def directMemberCompletions (qual : Tree )(using Context ): CompletionMap =
411
+ def directMemberCompletions (qual : tpd. Tree )(using Context ): CompletionMap =
401
412
if qual.tpe.isExactlyNothing then
402
413
Map .empty
403
414
else
@@ -444,13 +455,13 @@ object Completion:
444
455
end importedCompletions
445
456
446
457
/** Completions from implicit conversions including old style extensions using implicit classes */
447
- private def implicitConversionMemberCompletions (qual : Tree )(using Context ): CompletionMap =
458
+ private def implicitConversionMemberCompletions (qual : tpd. Tree )(using Context ): CompletionMap =
448
459
449
460
def tryToInstantiateTypeVars (conversionTarget : SearchSuccess ): Type =
450
461
try
451
462
val typingCtx = ctx.fresh
452
463
inContext(typingCtx):
453
- val methodRefTree = ref(conversionTarget.ref, needLoad = false )
464
+ val methodRefTree = tpd. ref(conversionTarget.ref, needLoad = false )
454
465
val convertedTree = ctx.typer.typedAheadExpr(untpd.Apply (untpd.TypedSplice (methodRefTree), untpd.TypedSplice (qual) :: Nil ))
455
466
Inferencing .fullyDefinedType(convertedTree.tpe, " " , pos)
456
467
catch
@@ -465,7 +476,7 @@ object Completion:
465
476
.groupByName
466
477
467
478
/** Completions from extension methods */
468
- private def extensionCompletions (qual : Tree )(using Context ): CompletionMap =
479
+ private def extensionCompletions (qual : tpd. Tree )(using Context ): CompletionMap =
469
480
def asDefLikeType (tpe : Type ): Type = tpe match
470
481
case _ : MethodOrPoly => tpe
471
482
case _ => ExprType (tpe)
@@ -592,7 +603,7 @@ object Completion:
592
603
* @param qual The argument to which the implicit conversion should be applied.
593
604
* @return The set of types after `qual` implicit conversion.
594
605
*/
595
- private def implicitConversionTargets (qual : Tree )(using Context ): Set [SearchSuccess ] = {
606
+ private def implicitConversionTargets (qual : tpd. Tree )(using Context ): Set [SearchSuccess ] = {
596
607
val typer = ctx.typer
597
608
val conversions = new typer.ImplicitSearch (defn.AnyType , qual, pos.span).allImplicits
598
609
val targets = conversions.map(_.tree.tpe)
@@ -617,11 +628,6 @@ object Completion:
617
628
618
629
private type CompletionMap = Map [Name , Seq [SingleDenotation ]]
619
630
620
- /** Temporary data structure representing denotations with the same name introduced in a given scope
621
- * as a member of a type, by a local definition or by an import clause
622
- */
623
- private case class ScopedDenotations (denots : Seq [SingleDenotation ], ctx : Context )
624
-
625
631
/**
626
632
* The completion mode: defines what kinds of symbols should be included in the completion
627
633
* results.
0 commit comments