Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport "Make PC more resilient to crashes" to LTS #20846

Merged
merged 1 commit into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 26 additions & 22 deletions compiler/src/dotty/tools/dotc/interactive/Completion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,12 @@ object Completion:
val completions = adjustedPath match
// Ignore synthetic select from `This` because in code it was `Ident`
// See example in dotty.tools.languageserver.CompletionTest.syntheticThis
case Select(qual @ This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions
case Select(qual, _) :: _ if qual.tpe.hasSimpleKind => completer.selectionCompletions(qual)
case Select(qual, _) :: _ => Map.empty
case (tree: ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr)
case (_: untpd.ImportSelector) :: Import(expr, _) :: _ => completer.directMemberCompletions(expr)
case _ => completer.scopeCompletions
case Select(qual @ This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions
case Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => completer.selectionCompletions(qual)
case Select(qual, _) :: _ => Map.empty
case (tree: ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr)
case (_: untpd.ImportSelector) :: Import(expr, _) :: _ => completer.directMemberCompletions(expr)
case _ => completer.scopeCompletions

val describedCompletions = describeCompletions(completions)
val backtickedCompletions =
Expand Down Expand Up @@ -348,7 +348,7 @@ object Completion:
/** Widen only those types which are applied or are exactly nothing
*/
def widenQualifier(qual: Tree)(using Context): Tree =
qual.tpe.widenDealias match
qual.typeOpt.widenDealias match
case widenedType if widenedType.isExactlyNothing => qual.withType(widenedType)
case appliedType: AppliedType => qual.withType(appliedType)
case _ => qual
Expand All @@ -368,10 +368,10 @@ object Completion:
* These include inherited definitions but not members added by extensions or implicit conversions
*/
def directMemberCompletions(qual: Tree)(using Context): CompletionMap =
if qual.tpe.isExactlyNothing then
if qual.typeOpt.isExactlyNothing then
Map.empty
else
accessibleMembers(qual.tpe).groupByName
accessibleMembers(qual.typeOpt).groupByName

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

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

Expand All @@ -453,16 +453,16 @@ object Completion:

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

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

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

// 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.
Expand All @@ -472,7 +472,7 @@ object Completion:
val availableExtMethods = extMethodsFromGivensInImplicitScope ++ extMethodsFromImplicitScope ++ extMethodsFromGivensInScope ++ extMethodsInScope
val extMethodsWithAppliedReceiver = availableExtMethods.flatMap {
case (termRef, termName) =>
if termRef.symbol.is(ExtensionMethod) && !qual.tpe.isBottomType then
if termRef.symbol.is(ExtensionMethod) && !qual.typeOpt.isBottomType then
tryApplyingReceiverToExtension(termRef)
.map(denot => termName -> denot)
else None
Expand Down Expand Up @@ -551,21 +551,25 @@ object Completion:
* @param qual The argument to which the implicit conversion should be applied.
* @return The set of types after `qual` implicit conversion.
*/
private def implicitConversionTargets(qual: Tree)(using Context): Set[Type] = {
private def implicitConversionTargets(qual: Tree)(using Context): Set[Type] =
val typer = ctx.typer
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits
val targets = conversions.map(_.tree.tpe)
val targets = try {
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits
conversions.map(_.tree.typeOpt)
} catch {
case _ =>
interactiv.println(i"implicit conversion targets failed: ${qual.show}")
Set.empty
}

interactiv.println(i"implicit conversion targets considered: ${targets.toList}%, %")
targets
}

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

extension (denotations: Seq[SingleDenotation])
def groupByName(using Context): CompletionMap = denotations.groupBy(_.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ final class ConvertToNamedArgumentsProvider(
val tree = Interactive.pathTo(trees, pos)(using newctx).headOption

def paramss(fun: tpd.Tree)(using Context): List[String] =
fun.tpe match
fun.typeOpt match
case m: MethodType => m.paramNamess.flatten.map(_.toString)
case _ =>
fun.symbol.rawParamss.flatten
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ final class ExtractMethodProvider(
yield
val defnPos = stat.sourcePos
val extractedPos = head.sourcePos.withEnd(expr.sourcePos.end)
val exprType = prettyPrint(expr.tpe.widen)
val exprType = prettyPrint(expr.typeOpt.widen)
val name =
genName(indexedCtx.scopeSymbols.map(_.decodedName).toSet, "newMethod")
val (methodParams, typeParams) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ object HoverProvider:
val indexedContext = IndexedContext(ctx)

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

val tp = typeFromPath(path)
val tpw = tp.widenTermRefExpr
Expand Down Expand Up @@ -185,7 +185,7 @@ object HoverProvider:
findRefinement(parent)
case _ => None

val refTpe = sel.tpe.widen.metalsDealias match
val refTpe = sel.typeOpt.widen.metalsDealias match
case r: RefinedType => Some(r)
case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.metalsDealias)
case _ => None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ final class InferredTypeProvider(
adjustOpt.foreach(adjust => endPos.setEnd(adjust.adjustedEndPos))
new TextEdit(
endPos,
": " + printType(optDealias(tpt.tpe)) + {
": " + printType(optDealias(tpt.typeOpt)) + {
if withParens then ")" else ""
}
)
Expand Down Expand Up @@ -211,7 +211,7 @@ final class InferredTypeProvider(
adjustOpt.foreach(adjust => end.setEnd(adjust.adjustedEndPos))
new TextEdit(
end,
": " + printType(optDealias(tpt.tpe))
": " + printType(optDealias(tpt.typeOpt))
)
end typeNameEdit

Expand Down Expand Up @@ -241,7 +241,7 @@ final class InferredTypeProvider(
def baseEdit(withParens: Boolean) =
new TextEdit(
bind.endPos.toLsp,
": " + printType(optDealias(body.tpe)) + {
": " + printType(optDealias(body.typeOpt)) + {
if withParens then ")" else ""
}
)
Expand Down Expand Up @@ -274,7 +274,7 @@ final class InferredTypeProvider(
case Some(i @ Ident(name)) =>
val typeNameEdit = new TextEdit(
i.endPos.toLsp,
": " + printType(optDealias(i.tpe.widen))
": " + printType(optDealias(i.typeOpt.widen))
)
typeNameEdit :: imports

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ object MetalsInteractive:
*/
case (tpt: TypeTree) :: parent :: _
if tpt.span != parent.span && !tpt.symbol.is(Synthetic) =>
List((tpt.symbol, tpt.tpe))
List((tpt.symbol, tpt.typeOpt))

/* TypeTest class https://dotty.epfl.ch/docs/reference/other-new-features/type-test.html
* compiler automatically adds unapply if possible, we need to find the type symbol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class PcDefinitionProvider(
case Nil =>
path.headOption match
case Some(value: Literal) =>
definitionsForSymbol(List(value.tpe.widen.typeSymbol), pos)
definitionsForSymbol(List(value.typeOpt.widen.typeSymbol), pos)
case _ => DefinitionResultImpl.empty
case _ =>
definitionsForSymbol(typeSymbols, pos)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ final class PcSyntheticDecorationsProvider(
): String =
val tpdPath =
Interactive.pathTo(unit.tpdTree, pos.span)

val indexedCtx = IndexedContext(Interactive.contextOfPath(tpdPath))
val printer = ShortenedTypePrinter(
symbolSearch
Expand Down Expand Up @@ -210,7 +210,7 @@ object TypeParameters:
case sel: Select if sel.isInfix =>
sel.sourcePos.withEnd(sel.nameSpan.end)
case _ => fun.sourcePos
val tpes = args.map(_.tpe.stripTypeVar.widen.finalResultType)
val tpes = args.map(_.typeOpt.stripTypeVar.widen.finalResultType)
Some((tpes, pos.endPos, fun))
case _ => None
private def inferredTypeArgs(args: List[Tree]): Boolean =
Expand All @@ -232,15 +232,15 @@ object InferredType:
!vd.symbol.is(Flags.Enum) &&
!isValDefBind(text, vd) =>
if vd.symbol == vd.symbol.sourceSymbol then
Some(tpe.tpe, tpe.sourcePos.withSpan(vd.nameSpan), vd)
Some(tpe.typeOpt, tpe.sourcePos.withSpan(vd.nameSpan), vd)
else None
case vd @ DefDef(_, _, tpe, _)
if isValidSpan(tpe.span, vd.nameSpan) &&
tpe.span.start >= vd.nameSpan.end &&
!vd.symbol.isConstructor &&
!vd.symbol.is(Flags.Mutable) =>
if vd.symbol == vd.symbol.sourceSymbol then
Some(tpe.tpe, tpe.sourcePos, vd)
Some(tpe.typeOpt, tpe.sourcePos, vd)
else None
case bd @ Bind(
name,
Expand Down Expand Up @@ -290,4 +290,4 @@ case class Synthetics(
end Synthetics

object Synthetics:
def empty: Synthetics = Synthetics(Nil, Set.empty)
def empty: Synthetics = Synthetics(Nil, Set.empty)
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class Completions(
// should not show completions for toplevel
case Nil if pos.source.file.extension != "sc" =>
(allAdvanced, SymbolSearch.Result.COMPLETE)
case Select(qual, _) :: _ if qual.tpe.isErroneous =>
case Select(qual, _) :: _ if qual.typeOpt.isErroneous =>
(allAdvanced, SymbolSearch.Result.COMPLETE)
case Select(qual, _) :: _ =>
val (_, compilerCompletions) = Completion.completions(pos)
Expand Down Expand Up @@ -749,7 +749,7 @@ class Completions(
items

def forSelect(sel: Select): CompletionApplication =
val tpe = sel.qualifier.tpe
val tpe = sel.qualifier.typeOpt
val members = tpe.allMembers.map(_.symbol).toSet

new CompletionApplication:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ object CaseKeywordCompletion:
val parents: Parents = selector match
case EmptyTree =>
val seenFromType = parent match
case TreeApply(fun, _) if !fun.tpe.isErroneous => fun.tpe
case _ => parent.tpe
case TreeApply(fun, _) if !fun.typeOpt.isErroneous => fun.typeOpt
case _ => parent.typeOpt
seenFromType.paramInfoss match
case (head :: Nil) :: _
if definitions.isFunctionType(head) || head.isRef(
Expand All @@ -84,7 +84,7 @@ object CaseKeywordCompletion:
val argTypes = head.argTypes.init
new Parents(argTypes, definitions)
case _ => new Parents(NoType, definitions)
case sel => new Parents(sel.tpe, definitions)
case sel => new Parents(sel.typeOpt, definitions)

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

Expand Down Expand Up @@ -240,7 +240,7 @@ object CaseKeywordCompletion:
completionPos,
clientSupportsSnippets
)
val tpe = selector.tpe.widen.metalsDealias.bounds.hi match
val tpe = selector.typeOpt.widen.metalsDealias.bounds.hi match
case tr @ TypeRef(_, _) => tr.underlying
case t => t

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ object OverrideCompletions:
// not using `td.tpe.abstractTermMembers` because those members includes
// the abstract members in `td.tpe`. For example, when we type `def foo@@`,
// `td.tpe.abstractTermMembers` contains `method foo: <error>` and it overrides the parent `foo` method.
val overridables = td.tpe.parents
val overridables = td.typeOpt.parents
.flatMap { parent =>
parent.membersBasedOnFlags(
flags,
Expand Down Expand Up @@ -279,7 +279,7 @@ object OverrideCompletions:
else ""
(indent, indent, lastIndent)
end calcIndent
val abstractMembers = defn.tpe.abstractTermMembers.map(_.symbol)
val abstractMembers = defn.typeOpt.abstractTermMembers.map(_.symbol)

val caseClassOwners = Set("Product", "Equals")
val overridables =
Expand Down Expand Up @@ -307,7 +307,7 @@ object OverrideCompletions:
if edits.isEmpty then Nil
else
// A list of declarations in the class/object to implement
val decls = defn.tpe.decls.toList
val decls = defn.typeOpt.decls.toList
.filter(sym =>
!sym.isPrimaryConstructor &&
!sym.isTypeParam &&
Expand Down Expand Up @@ -418,7 +418,7 @@ object OverrideCompletions:
// `iterator` method in `new Iterable[Int] { def iterato@@ }`
// should be completed as `def iterator: Iterator[Int]` instead of `Iterator[A]`.
val seenFrom =
val memInfo = defn.tpe.memberInfo(sym.symbol)
val memInfo = defn.typeOpt.memberInfo(sym.symbol)
if memInfo.isErroneous || memInfo.finalResultType.isAny then
sym.info.widenTermRefExpr
else memInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ object MtagsEnrichments extends CommonMtagsEnrichments:
def seenFrom(sym: Symbol)(using Context): (Type, Symbol) =
try
val pre = tree.qual
val denot = sym.denot.asSeenFrom(pre.tpe.widenTermRefExpr)
val denot = sym.denot.asSeenFrom(pre.typeOpt.widenTermRefExpr)
(denot.info, sym.withUpdatedTpe(denot.info))
catch case NonFatal(e) => (sym.info, sym)

Expand Down Expand Up @@ -357,7 +357,7 @@ object MtagsEnrichments extends CommonMtagsEnrichments:
case t: GenericApply
if t.fun.srcPos.span.contains(
pos.span
) && !t.tpe.isErroneous =>
) && !t.typeOpt.isErroneous =>
tryTail(tail).orElse(Some(enclosing))
case in: Inlined =>
tryTail(tail).orElse(Some(enclosing))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1679,3 +1679,11 @@ class CompletionSuite extends BaseCompletionSuite:
|""".stripMargin,
""
)

@Test def `dont-crash-implicit-search` =
check(
"""object M:
| Array[Int].fi@@
|""".stripMargin,
""
)
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,29 @@ class HoverTypeSuite extends BaseHoverSuite:
"""|extension (i: MyIntOut) def uneven: Boolean
|""".stripMargin.hover,
)

@Test def `recursive-enum-without-type` =
check(
"""class Wrapper(n: Int):
| extension (x: Int)
| def + (y: Int) = new Wrap@@per(x) + y
|""".stripMargin,
"""```scala
|def this(n: Int): Wrapper
|```
|""".stripMargin
)

@Test def `recursive-enum-without-type-1` =
check(
"""class Wrapper(n: Int):
| def add(x: Int): Wrapper = ???
| extension (x: Int)
| def + (y: Int) = Wrap@@per(x).add(5)
|""".stripMargin,
"""```scala
|def this(n: Int): Wrapper
|```
|""".stripMargin
)