Skip to content

Commit c4d6ab2

Browse files
rochalaWojciechMazur
authored andcommitted
Make PC more resiliant to crashes
[Cherry-picked 38d252e]
1 parent 1168afb commit c4d6ab2

14 files changed

+87
-49
lines changed

compiler/src/dotty/tools/dotc/interactive/Completion.scala

+26-22
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,12 @@ object Completion:
184184
val completions = adjustedPath match
185185
// Ignore synthetic select from `This` because in code it was `Ident`
186186
// See example in dotty.tools.languageserver.CompletionTest.syntheticThis
187-
case Select(qual @ This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions
188-
case Select(qual, _) :: _ if qual.tpe.hasSimpleKind => completer.selectionCompletions(qual)
189-
case Select(qual, _) :: _ => Map.empty
190-
case (tree: ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr)
191-
case (_: untpd.ImportSelector) :: Import(expr, _) :: _ => completer.directMemberCompletions(expr)
192-
case _ => completer.scopeCompletions
187+
case Select(qual @ This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions
188+
case Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => completer.selectionCompletions(qual)
189+
case Select(qual, _) :: _ => Map.empty
190+
case (tree: ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr)
191+
case (_: untpd.ImportSelector) :: Import(expr, _) :: _ => completer.directMemberCompletions(expr)
192+
case _ => completer.scopeCompletions
193193

194194
val describedCompletions = describeCompletions(completions)
195195
val backtickedCompletions =
@@ -348,7 +348,7 @@ object Completion:
348348
/** Widen only those types which are applied or are exactly nothing
349349
*/
350350
def widenQualifier(qual: Tree)(using Context): Tree =
351-
qual.tpe.widenDealias match
351+
qual.typeOpt.widenDealias match
352352
case widenedType if widenedType.isExactlyNothing => qual.withType(widenedType)
353353
case appliedType: AppliedType => qual.withType(appliedType)
354354
case _ => qual
@@ -368,10 +368,10 @@ object Completion:
368368
* These include inherited definitions but not members added by extensions or implicit conversions
369369
*/
370370
def directMemberCompletions(qual: Tree)(using Context): CompletionMap =
371-
if qual.tpe.isExactlyNothing then
371+
if qual.typeOpt.isExactlyNothing then
372372
Map.empty
373373
else
374-
accessibleMembers(qual.tpe).groupByName
374+
accessibleMembers(qual.typeOpt).groupByName
375375

376376
/** Completions introduced by imports directly in this context.
377377
* Completions from outer contexts are not included.
@@ -415,7 +415,7 @@ object Completion:
415415

416416
/** Completions from implicit conversions including old style extensions using implicit classes */
417417
private def implicitConversionMemberCompletions(qual: Tree)(using Context): CompletionMap =
418-
if qual.tpe.isExactlyNothing || qual.tpe.isNullType then
418+
if qual.typeOpt.isExactlyNothing || qual.typeOpt.isNullType then
419419
Map.empty
420420
else
421421
implicitConversionTargets(qual)(using ctx.fresh.setExploreTyperState())
@@ -432,7 +432,7 @@ object Completion:
432432
def tryApplyingReceiverToExtension(termRef: TermRef): Option[SingleDenotation] =
433433
ctx.typer.tryApplyingExtensionMethod(termRef, qual)
434434
.map { tree =>
435-
val tpe = asDefLikeType(tree.tpe.dealias)
435+
val tpe = asDefLikeType(tree.typeOpt.dealias)
436436
termRef.denot.asSingleDenotation.mapInfo(_ => tpe)
437437
}
438438

@@ -453,16 +453,16 @@ object Completion:
453453

454454
// 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference.
455455
val termCompleter = new Completer(Mode.Term, prefix, pos)
456-
val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap {
457-
case (name, denots) => denots.collect { case d: SymDenotation if d.isTerm => (d.termRef, name.asTermName) }
458-
}
456+
val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap:
457+
case (name, denots) => denots.collect:
458+
case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName)
459459

460460
// 2. The extension method is a member of some given instance that is visible at the point of the reference.
461461
val givensInScope = ctx.implicits.eligible(defn.AnyType).map(_.implicitRef.underlyingRef)
462462
val extMethodsFromGivensInScope = extractMemberExtensionMethods(givensInScope)
463463

464464
// 3. The reference is of the form r.m and the extension method is defined in the implicit scope of the type of r.
465-
val implicitScopeCompanions = ctx.run.nn.implicitScope(qual.tpe).companionRefs.showAsList
465+
val implicitScopeCompanions = ctx.run.nn.implicitScope(qual.typeOpt).companionRefs.showAsList
466466
val extMethodsFromImplicitScope = extractMemberExtensionMethods(implicitScopeCompanions)
467467

468468
// 4. The reference is of the form r.m and the extension method is defined in some given instance in the implicit scope of the type of r.
@@ -472,7 +472,7 @@ object Completion:
472472
val availableExtMethods = extMethodsFromGivensInImplicitScope ++ extMethodsFromImplicitScope ++ extMethodsFromGivensInScope ++ extMethodsInScope
473473
val extMethodsWithAppliedReceiver = availableExtMethods.flatMap {
474474
case (termRef, termName) =>
475-
if termRef.symbol.is(ExtensionMethod) && !qual.tpe.isBottomType then
475+
if termRef.symbol.is(ExtensionMethod) && !qual.typeOpt.isBottomType then
476476
tryApplyingReceiverToExtension(termRef)
477477
.map(denot => termName -> denot)
478478
else None
@@ -551,21 +551,25 @@ object Completion:
551551
* @param qual The argument to which the implicit conversion should be applied.
552552
* @return The set of types after `qual` implicit conversion.
553553
*/
554-
private def implicitConversionTargets(qual: Tree)(using Context): Set[Type] = {
554+
private def implicitConversionTargets(qual: Tree)(using Context): Set[Type] =
555555
val typer = ctx.typer
556-
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits
557-
val targets = conversions.map(_.tree.tpe)
556+
val targets = try {
557+
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits
558+
conversions.map(_.tree.typeOpt)
559+
} catch {
560+
case _ =>
561+
interactiv.println(i"implicit conversion targets failed: ${qual.show}")
562+
Set.empty
563+
}
558564

559565
interactiv.println(i"implicit conversion targets considered: ${targets.toList}%, %")
560566
targets
561-
}
562567

563568
/** Filter for names that should appear when looking for completions. */
564-
private object completionsFilter extends NameFilter {
569+
private object completionsFilter extends NameFilter:
565570
def apply(pre: Type, name: Name)(using Context): Boolean =
566571
!name.isConstructorName && name.toTermName.info.kind == SimpleNameKind
567572
def isStable = true
568-
}
569573

570574
extension (denotations: Seq[SingleDenotation])
571575
def groupByName(using Context): CompletionMap = denotations.groupBy(_.name)

presentation-compiler/src/main/dotty/tools/pc/ConvertToNamedArgumentsProvider.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ final class ConvertToNamedArgumentsProvider(
3535
val tree = Interactive.pathTo(trees, pos)(using newctx).headOption
3636

3737
def paramss(fun: tpd.Tree)(using Context): List[String] =
38-
fun.tpe match
38+
fun.typeOpt match
3939
case m: MethodType => m.paramNamess.flatten.map(_.toString)
4040
case _ =>
4141
fun.symbol.rawParamss.flatten

presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ final class ExtractMethodProvider(
128128
yield
129129
val defnPos = stat.sourcePos
130130
val extractedPos = head.sourcePos.withEnd(expr.sourcePos.end)
131-
val exprType = prettyPrint(expr.tpe.widen)
131+
val exprType = prettyPrint(expr.typeOpt.widen)
132132
val name =
133133
genName(indexedCtx.scopeSymbols.map(_.decodedName).toSet, "newMethod")
134134
val (methodParams, typeParams) =

presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ object HoverProvider:
4848
val indexedContext = IndexedContext(ctx)
4949

5050
def typeFromPath(path: List[Tree]) =
51-
if path.isEmpty then NoType else path.head.tpe
51+
if path.isEmpty then NoType else path.head.typeOpt
5252

5353
val tp = typeFromPath(path)
5454
val tpw = tp.widenTermRefExpr
@@ -185,7 +185,7 @@ object HoverProvider:
185185
findRefinement(parent)
186186
case _ => None
187187

188-
val refTpe = sel.tpe.widen.metalsDealias match
188+
val refTpe = sel.typeOpt.widen.metalsDealias match
189189
case r: RefinedType => Some(r)
190190
case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.metalsDealias)
191191
case _ => None

presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ final class InferredTypeProvider(
138138
adjustOpt.foreach(adjust => endPos.setEnd(adjust.adjustedEndPos))
139139
new TextEdit(
140140
endPos,
141-
": " + printType(optDealias(tpt.tpe)) + {
141+
": " + printType(optDealias(tpt.typeOpt)) + {
142142
if withParens then ")" else ""
143143
}
144144
)
@@ -211,7 +211,7 @@ final class InferredTypeProvider(
211211
adjustOpt.foreach(adjust => end.setEnd(adjust.adjustedEndPos))
212212
new TextEdit(
213213
end,
214-
": " + printType(optDealias(tpt.tpe))
214+
": " + printType(optDealias(tpt.typeOpt))
215215
)
216216
end typeNameEdit
217217

@@ -241,7 +241,7 @@ final class InferredTypeProvider(
241241
def baseEdit(withParens: Boolean) =
242242
new TextEdit(
243243
bind.endPos.toLsp,
244-
": " + printType(optDealias(body.tpe)) + {
244+
": " + printType(optDealias(body.typeOpt)) + {
245245
if withParens then ")" else ""
246246
}
247247
)
@@ -274,7 +274,7 @@ final class InferredTypeProvider(
274274
case Some(i @ Ident(name)) =>
275275
val typeNameEdit = new TextEdit(
276276
i.endPos.toLsp,
277-
": " + printType(optDealias(i.tpe.widen))
277+
": " + printType(optDealias(i.typeOpt.widen))
278278
)
279279
typeNameEdit :: imports
280280

presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ object MetalsInteractive:
190190
*/
191191
case (tpt: TypeTree) :: parent :: _
192192
if tpt.span != parent.span && !tpt.symbol.is(Synthetic) =>
193-
List((tpt.symbol, tpt.tpe))
193+
List((tpt.symbol, tpt.typeOpt))
194194

195195
/* TypeTest class https://dotty.epfl.ch/docs/reference/other-new-features/type-test.html
196196
* compiler automatically adds unapply if possible, we need to find the type symbol

presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class PcDefinitionProvider(
108108
case Nil =>
109109
path.headOption match
110110
case Some(value: Literal) =>
111-
definitionsForSymbol(List(value.tpe.widen.typeSymbol), pos)
111+
definitionsForSymbol(List(value.typeOpt.widen.typeSymbol), pos)
112112
case _ => DefinitionResultImpl.empty
113113
case _ =>
114114
definitionsForSymbol(typeSymbols, pos)

presentation-compiler/src/main/dotty/tools/pc/PcSyntheticDecorationProvider.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ final class PcSyntheticDecorationsProvider(
114114
): String =
115115
val tpdPath =
116116
Interactive.pathTo(unit.tpdTree, pos.span)
117-
117+
118118
val indexedCtx = IndexedContext(Interactive.contextOfPath(tpdPath))
119119
val printer = ShortenedTypePrinter(
120120
symbolSearch
@@ -210,7 +210,7 @@ object TypeParameters:
210210
case sel: Select if sel.isInfix =>
211211
sel.sourcePos.withEnd(sel.nameSpan.end)
212212
case _ => fun.sourcePos
213-
val tpes = args.map(_.tpe.stripTypeVar.widen.finalResultType)
213+
val tpes = args.map(_.typeOpt.stripTypeVar.widen.finalResultType)
214214
Some((tpes, pos.endPos, fun))
215215
case _ => None
216216
private def inferredTypeArgs(args: List[Tree]): Boolean =
@@ -232,15 +232,15 @@ object InferredType:
232232
!vd.symbol.is(Flags.Enum) &&
233233
!isValDefBind(text, vd) =>
234234
if vd.symbol == vd.symbol.sourceSymbol then
235-
Some(tpe.tpe, tpe.sourcePos.withSpan(vd.nameSpan), vd)
235+
Some(tpe.typeOpt, tpe.sourcePos.withSpan(vd.nameSpan), vd)
236236
else None
237237
case vd @ DefDef(_, _, tpe, _)
238238
if isValidSpan(tpe.span, vd.nameSpan) &&
239239
tpe.span.start >= vd.nameSpan.end &&
240240
!vd.symbol.isConstructor &&
241241
!vd.symbol.is(Flags.Mutable) =>
242242
if vd.symbol == vd.symbol.sourceSymbol then
243-
Some(tpe.tpe, tpe.sourcePos, vd)
243+
Some(tpe.typeOpt, tpe.sourcePos, vd)
244244
else None
245245
case bd @ Bind(
246246
name,
@@ -290,4 +290,4 @@ case class Synthetics(
290290
end Synthetics
291291

292292
object Synthetics:
293-
def empty: Synthetics = Synthetics(Nil, Set.empty)
293+
def empty: Synthetics = Synthetics(Nil, Set.empty)

presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class Completions(
119119
// should not show completions for toplevel
120120
case Nil if pos.source.file.extension != "sc" =>
121121
(allAdvanced, SymbolSearch.Result.COMPLETE)
122-
case Select(qual, _) :: _ if qual.tpe.isErroneous =>
122+
case Select(qual, _) :: _ if qual.typeOpt.isErroneous =>
123123
(allAdvanced, SymbolSearch.Result.COMPLETE)
124124
case Select(qual, _) :: _ =>
125125
val (_, compilerCompletions) = Completion.completions(pos)
@@ -749,7 +749,7 @@ class Completions(
749749
items
750750

751751
def forSelect(sel: Select): CompletionApplication =
752-
val tpe = sel.qualifier.tpe
752+
val tpe = sel.qualifier.typeOpt
753753
val members = tpe.allMembers.map(_.symbol).toSet
754754

755755
new CompletionApplication:

presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ object CaseKeywordCompletion:
7474
val parents: Parents = selector match
7575
case EmptyTree =>
7676
val seenFromType = parent match
77-
case TreeApply(fun, _) if !fun.tpe.isErroneous => fun.tpe
78-
case _ => parent.tpe
77+
case TreeApply(fun, _) if !fun.typeOpt.isErroneous => fun.typeOpt
78+
case _ => parent.typeOpt
7979
seenFromType.paramInfoss match
8080
case (head :: Nil) :: _
8181
if definitions.isFunctionType(head) || head.isRef(
@@ -84,7 +84,7 @@ object CaseKeywordCompletion:
8484
val argTypes = head.argTypes.init
8585
new Parents(argTypes, definitions)
8686
case _ => new Parents(NoType, definitions)
87-
case sel => new Parents(sel.tpe, definitions)
87+
case sel => new Parents(sel.typeOpt, definitions)
8888

8989
val selectorSym = parents.selector.widen.metalsDealias.typeSymbol
9090

@@ -240,7 +240,7 @@ object CaseKeywordCompletion:
240240
completionPos,
241241
clientSupportsSnippets
242242
)
243-
val tpe = selector.tpe.widen.metalsDealias.bounds.hi match
243+
val tpe = selector.typeOpt.widen.metalsDealias.bounds.hi match
244244
case tr @ TypeRef(_, _) => tr.underlying
245245
case t => t
246246

presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ object OverrideCompletions:
9595
// not using `td.tpe.abstractTermMembers` because those members includes
9696
// the abstract members in `td.tpe`. For example, when we type `def foo@@`,
9797
// `td.tpe.abstractTermMembers` contains `method foo: <error>` and it overrides the parent `foo` method.
98-
val overridables = td.tpe.parents
98+
val overridables = td.typeOpt.parents
9999
.flatMap { parent =>
100100
parent.membersBasedOnFlags(
101101
flags,
@@ -279,7 +279,7 @@ object OverrideCompletions:
279279
else ""
280280
(indent, indent, lastIndent)
281281
end calcIndent
282-
val abstractMembers = defn.tpe.abstractTermMembers.map(_.symbol)
282+
val abstractMembers = defn.typeOpt.abstractTermMembers.map(_.symbol)
283283

284284
val caseClassOwners = Set("Product", "Equals")
285285
val overridables =
@@ -307,7 +307,7 @@ object OverrideCompletions:
307307
if edits.isEmpty then Nil
308308
else
309309
// A list of declarations in the class/object to implement
310-
val decls = defn.tpe.decls.toList
310+
val decls = defn.typeOpt.decls.toList
311311
.filter(sym =>
312312
!sym.isPrimaryConstructor &&
313313
!sym.isTypeParam &&
@@ -418,7 +418,7 @@ object OverrideCompletions:
418418
// `iterator` method in `new Iterable[Int] { def iterato@@ }`
419419
// should be completed as `def iterator: Iterator[Int]` instead of `Iterator[A]`.
420420
val seenFrom =
421-
val memInfo = defn.tpe.memberInfo(sym.symbol)
421+
val memInfo = defn.typeOpt.memberInfo(sym.symbol)
422422
if memInfo.isErroneous || memInfo.finalResultType.isAny then
423423
sym.info.widenTermRefExpr
424424
else memInfo

presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ object MtagsEnrichments extends CommonMtagsEnrichments:
300300
def seenFrom(sym: Symbol)(using Context): (Type, Symbol) =
301301
try
302302
val pre = tree.qual
303-
val denot = sym.denot.asSeenFrom(pre.tpe.widenTermRefExpr)
303+
val denot = sym.denot.asSeenFrom(pre.typeOpt.widenTermRefExpr)
304304
(denot.info, sym.withUpdatedTpe(denot.info))
305305
catch case NonFatal(e) => (sym.info, sym)
306306

@@ -357,7 +357,7 @@ object MtagsEnrichments extends CommonMtagsEnrichments:
357357
case t: GenericApply
358358
if t.fun.srcPos.span.contains(
359359
pos.span
360-
) && !t.tpe.isErroneous =>
360+
) && !t.typeOpt.isErroneous =>
361361
tryTail(tail).orElse(Some(enclosing))
362362
case in: Inlined =>
363363
tryTail(tail).orElse(Some(enclosing))

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala

+8
Original file line numberDiff line numberDiff line change
@@ -1679,3 +1679,11 @@ class CompletionSuite extends BaseCompletionSuite:
16791679
|""".stripMargin,
16801680
""
16811681
)
1682+
1683+
@Test def `dont-crash-implicit-search` =
1684+
check(
1685+
"""object M:
1686+
| Array[Int].fi@@
1687+
|""".stripMargin,
1688+
""
1689+
)

presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala

+26
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,29 @@ class HoverTypeSuite extends BaseHoverSuite:
368368
"""|extension (i: MyIntOut) def uneven: Boolean
369369
|""".stripMargin.hover,
370370
)
371+
372+
@Test def `recursive-enum-without-type` =
373+
check(
374+
"""class Wrapper(n: Int):
375+
| extension (x: Int)
376+
| def + (y: Int) = new Wrap@@per(x) + y
377+
|""".stripMargin,
378+
"""```scala
379+
|def this(n: Int): Wrapper
380+
|```
381+
|""".stripMargin
382+
)
383+
384+
@Test def `recursive-enum-without-type-1` =
385+
check(
386+
"""class Wrapper(n: Int):
387+
| def add(x: Int): Wrapper = ???
388+
| extension (x: Int)
389+
| def + (y: Int) = Wrap@@per(x).add(5)
390+
|""".stripMargin,
391+
"""```scala
392+
|def this(n: Int): Wrapper
393+
|```
394+
|""".stripMargin
395+
)
396+

0 commit comments

Comments
 (0)