in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/implicits/ImplicitProcessor.scala [207:496]
private[this] def findImplicitObjectsImpl(
`type`: ScType,
includePackagePrefix: Boolean
)(implicit
elementScope: ElementScope,
context: Context
): Seq[ScType] = {
val visited = mutable.HashSet.empty[ScType]
val parts = mutable.Queue.empty[ScType]
val pathTerms = mutable.HashSet.empty[ScType]
def collectPartsIterable(iterable: IterableOnce[ScType]): Unit = {
val iterator = iterable.iterator
while (iterator.hasNext) {
collectParts(iterator.next())
}
}
def collectPartsTypeResult(tr: TypeResult): Unit =
tr.foreach(collectParts(_))
// Java Raw types are converted to F[ScExistentialArgument.Deferred("A", .....), ...]
// In combination with F-Bounds this can lead to different instantiations that are not ==,
// but would not reveal further parts of the type.
//
// Here, we convert such existential arguments to stand-in types that have a useful
// equals/hashCode implementation, and use this as the marker in the `visitedType` set.
def convertRawArgs(tp: ScType): ScType = {
def rawArgToDummy(typeArgType: ScType): ScType = typeArgType match {
case existentialArgument: ScExistentialArgument =>
existentialArgument.typeParamOfRawArg match {
case Some(typeParamRaw) =>
ScAbstractType(typeParamRaw, lower = api.Nothing, upper = api.Any)
case None =>
typeArgType
}
case tp => tp
}
def isRawArg(tp: ScType): Boolean = tp match {
case existentialArgument: ScExistentialArgument =>
existentialArgument.typeParamOfRawArg.isDefined
case _ =>
false
}
tp match {
case ParameterizedType(des, targs) =>
if (targs.exists(isRawArg)) {
val targsNew = targs.map(rawArgToDummy)
val tpNew = ScParameterizedType(des, targsNew)
tpNew
} else
tp
case _ =>
tp
}
}
def collectPartsFromSuperTypes(clazz: PsiClass, subst: ScSubstitutor): Unit =
clazz match {
case td: ScTemplateDefinition =>
collectPartsIterable(td.superTypes.map(subst))
td.selfType.foreach(stpe => collectParts(subst(stpe)))
case clazz: PsiClass =>
collectPartsIterable(clazz.getSuperTypes.map(t => subst(t.toScType())))
}
/**
* In scala 3 references to packages and package objects are anchors only under -source:3.0-migration.
* https://dotty.epfl.ch/3.0.0/docs/reference/changed-features/implicit-resolution.html
*/
def processPackagePrefix(pack: ScPackageLike): Unit =
if (includePackagePrefix) {
for {
packageObject <- pack.findPackageObject(elementScope.scope)
designator = ScDesignatorType(packageObject)
} parts += designator
pack.parentScalaPackage.foreach(processPackagePrefix)
}
/**
* See: [[https://docs.scala-lang.org/scala3/reference/changed-features/implicit-resolution.html#:~:text=of%20a%20type%3A-,Definition,-%3A%20A%20reference]]
*/
def isAnchor(element: PsiNamedElement): Boolean =
if (!element.isInScala3File) false
else element match {
case _: PsiClass => true
case alias: ScTypeAliasDefinition => alias.isEffectivelyOpaque || alias.isMatchTypeAlias
case _: ScTypeAliasDeclaration => true
case _ => false
}
@tailrec
def collectTermsFromPath(path: ScType): Unit = {
def isValueAlias(pat: ScBindingPattern): Boolean = pat.`type`().exists {
case downer: DesignatorOwner => downer.isSingleton
case _ => false
}
path match {
case des @ ScDesignatorType(elem) => elem match {
case pat: ScBindingPattern if isValueAlias(pat) => ()
case _: ScBindingPattern | _: ScFieldId | _: ScParameter => pathTerms.add(des)
case _ => ()
}
case proj @ ScProjectionType(prefix, elem) =>
elem match {
case pat: ScBindingPattern if isValueAlias(pat) => ()
case _: ScBindingPattern | _: ScFieldId | _: ScParameter => pathTerms.add(proj)
case _ => ()
}
collectTermsFromPath(prefix)
case _ => ()
}
}
def collectTypeAliasDefinitionParts(tp: ScType, tdef: ScTypeAliasDefinition): Unit =
if (tdef.isEffectivelyOpaque) parts += tp
else if (tdef.isMatchTypeAlias) {
val matchType = tdef.aliasedType.map(_.asInstanceOf[ScMatchType])
val upperBound = matchType.toOption.flatMap(_.upperBound)
upperBound.foreach(collectParts(_))
}
def collectParts(tp: ScType, dealias: Boolean = true): Unit = {
ProgressManager.checkCanceled()
val tpWithRawTypesConverted = convertRawArgs(tp)
if (!visited.add(tpWithRawTypesConverted))
return
if (dealias) {
tp match {
case AliasType(_, _, Right(t), _) => collectParts(t)
case _ => ()
}
}
tp match {
case ScDesignatorType(v: ScBindingPattern) => collectPartsTypeResult(v.`type`())
case ScDesignatorType(v: ScFieldId) => collectPartsTypeResult(v.`type`())
case ScDesignatorType(p: ScParameter) => collectPartsTypeResult(p.insideParamType)
case ScCompoundType(comps, _, _) => collectPartsIterable(comps)
case ScAndType(lhs, rhs) => collectParts(lhs); collectParts(rhs)
case ScOrType(lhs, rhs) => collectParts(lhs); collectParts(rhs)
case ScDesignatorType(alias: ScTypeAliasDefinition) => collectTypeAliasDefinitionParts(tp, alias)
case ScDesignatorType(alias: ScTypeAliasDeclaration) if alias.isInScala3File => parts += tp
case ParameterizedType(a: ScAbstractType, args) =>
collectParts(a)
collectPartsIterable(args)
case p @ ParameterizedType(des, args) =>
val dealias = des match {
//In scala 3 you can have parameterless type aliases designated to classes with
//type parameters:
//type Foo = ClassWithTypeParams; Foo[Int]
//if tp = Foo[Int] we should not try to further
//expand Foo into [T] ClassWithTypeParams[T]
case DesignatorOwner(_: ScTypeAlias) => false
case _ => true
}
p.extractClassType match {
case Some((clazz, subst)) =>
parts += des
collectParts(des, dealias = dealias)
collectPartsIterable(args)
collectPartsFromSuperTypes(clazz, subst)
case _ =>
collectParts(des, dealias = dealias)
collectPartsIterable(args)
}
case j: JavaArrayType =>
val parameterizedType = j.getParameterizedType
collectParts(
parameterizedType.getOrElse(
return
)
)
case proj @ ScProjectionType(projected, _) =>
collectParts(projected)
val element = proj.actualElement
if (isAnchor(element)) collectTermsFromPath(projected)
element match {
case v: ScBindingPattern => collectPartsTypeResult(v.`type`().map(proj.actualSubst))
case v: ScFieldId => collectPartsTypeResult(v.`type`().map(proj.actualSubst))
case v: ScParameter => collectPartsTypeResult(v.insideParamType.map(proj.actualSubst))
case tdef: ScTypeAliasDefinition => collectTypeAliasDefinitionParts(tp, tdef)
case v: ScTypeAliasDeclaration if v.isInScala3File => parts += tp
case _ => ()
}
tp.extractClassType match {
case Some((clazz, subst)) =>
parts += tp
collectPartsFromSuperTypes(clazz, subst)
case _ =>
}
case ScAbstractType(_, _, upper) => collectParts(upper)
case ScExistentialType(quant, _) => collectParts(quant)
case tpt: TypeParameterType => collectParts(tpt.upperType)
case _ =>
tp.extractClassType match {
case Some((clazz, subst)) =>
parts += tp
val packagePrefix = clazz.parentOfType(classOf[ScPackageLike], strict = false)
packagePrefix.foreach(processPackagePrefix)
collectPartsFromSuperTypes(clazz, subst)
case _ =>
}
}
}
collectParts(`type`)
val res = mutable.HashMap.empty[String, Seq[ScType]]
def addResult(fqn: String, tp: ScType)(implicit context: Context): Unit = {
res.get(fqn) match {
case Some(s) =>
if (s.forall(!_.equiv(tp))) {
res.remove(fqn)
res += ((fqn, s :+ tp))
}
case None => res += ((fqn, Seq(tp)))
}
}
def workWithTypeAlias(alias: ScTypeAlias, subst: ScSubstitutor = ScSubstitutor.empty)(implicit context: Context): Unit = alias match {
case alias: ScTypeAliasDefinition =>
if (alias.isEffectivelyOpaque) {
for (fqn <- alias.qualifiedNameOpt;
companionObject <- elementScope.getCachedObject(fqn)) {
addResult(fqn, ScDesignatorType(companionObject))
}
} else {
collectObjects(subst(alias.aliasedType.getOrAny))
}
case declaration: ScTypeAliasDeclaration if declaration.isInScala3File =>
for (fqn <- alias.qualifiedNameOpt;
companionObject <- elementScope.getCachedObject(fqn)) {
addResult(fqn, ScDesignatorType(companionObject))
}
case _ =>
}
def collectObjects(tp: ScType)(implicit context: Context): Unit =
tp match {
case _ if tp.isAny =>
case tp: StdType if stdTypes.contains(tp.name) =>
elementScope
.getCachedObject("scala." + tp.name)
.foreach(o => addResult(o.qualifiedName, ScDesignatorType(o)))
case ScDesignatorType(ta: ScTypeAlias) => workWithTypeAlias(ta)
case ScProjectionType.withActual(ta: ScTypeAlias, actualSubst) => workWithTypeAlias(ta, actualSubst)
case ParameterizedType(ScDesignatorType(ta: ScTypeAlias), args) =>
val genericSubst = ScSubstitutor.bind(ta.typeParameters, args)
workWithTypeAlias(ta, genericSubst)
case ParameterizedType(ScProjectionType.withActual(ta: ScTypeAliasDefinition, actualSubst), args) =>
val genericSubst = ScSubstitutor.bind(ta.typeParameters, args)
val subst = actualSubst.followed(genericSubst)
workWithTypeAlias(ta, subst)
case _ =>
tp.extractClass match {
case Some(obj: ScObject) => addResult(obj.qualifiedName, tp)
case Some(clazz) =>
getCompanionModule(clazz) match {
case Some(obj: ScObject) =>
tp match {
case ScProjectionType(proj, _) =>
addResult(obj.qualifiedName, ScProjectionType(proj, obj))
case ParameterizedType(ScProjectionType(proj, _), _) =>
addResult(obj.qualifiedName, ScProjectionType(proj, obj))
case _ =>
addResult(obj.qualifiedName, ScDesignatorType(obj))
}
case _ =>
}
case _ =>
}
}
while (parts.nonEmpty) {
collectObjects(parts.dequeue())
}
val objects = res.values.flatten.toSeq
pathTerms.addAll(objects).toSeq
}