in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/ReferenceExpressionResolver.scala [311:877]
def doResolve(
ref: ScReferenceExpression,
proc: BaseProcessor,
accessibilityCheck: Boolean = true,
tryThisQualifier: Boolean = false,
): Array[ScalaResolveResult] =
doResolve(ref, proc, accessibilityCheck, tryThisQualifier, None)
private def doResolve(
ref: ScReferenceExpression,
proc: BaseProcessor,
accessibilityCheck: Boolean,
tryThisQualifier: Boolean,
contextInfo: Option[ContextInfo]
): Array[ScalaResolveResult] = {
implicit val context: Context = Context(ref)
val info = contextInfo.getOrElse(getContextInfo(ref, ref))
val isShape = proc match {
case m: MethodResolveProcessor => m.isShapeResolve
case _ => false
}
def resolveUnqualified(): Array[ScalaResolveResult] =
ref.getContext match {
case ScSugarCallExpr(operand, operation, _) if ref == operation =>
processQualifier(operand)
case (gc: ScGenericCall) childOf ScInfixExpr(lhs, ref, _) if ref == gc.referencedExpr =>
processQualifier(lhs)
case _ =>
resolveUnqualifiedExpression()
proc.candidates
}
def resolveUnqualifiedExpression(): Unit = {
@tailrec
def treeWalkUp(@Nullable place: PsiElement, @Nullable lastParent: PsiElement, state: ResolveState): Unit = {
if (place == null) return
val newState = place match {
/** An extension method `f` can be referenced by a simple identifier (and rewritten by the compiler to the qualified form)
* if it is called from inside the body of an extension method `g`, which is defined in the same collective extension.
* To support resolve of such cases we store information about enclosing extension in the resolve state.
*/
case fdef: ScFunction => fdef.extensionMethodOwner.fold(state)(state.withExtensionContext)
case (cc: ScCaseClause) & Parent(Parent(m: ScMatch)) =>
if (cc.pattern.exists(pat => isContextAncestor(pat, ref, true))) {
//Don't trigger pattern type inference, when resolving references inside patterns.
//Avoids recursion related problems.
state
} else {
//@TODO: partial functions as well???
val subst = PatternTypeInference.doForMatchClause(m, cc)
val oldSubst = state.matchClauseSubstitutor
state.withMatchClauseSubstitutor(oldSubst.followed(subst))
}
case _ => state
}
if (!place.processDeclarations(proc, newState, lastParent, ref)) return
place match {
case _: ScTemplateBody | _: ScExtendsBlock => //template body and inherited members are at the same level
case enum: ScEnum =>
if (!enum.fakeCompanionModule.forall(_.processDeclarations(proc, state, lastParent, place))) return
case _ => if (!proc.changedLevel) return
}
treeWalkUp(place.getContext, place, newState)
}
val context = ref.getContext
val contextElement = (context, proc) match {
case (x: ScAssignment, _) if x.leftExpression == ref => Some(context)
case (_, _: DependencyProcessor) => None
case (_, _: CompletionProcessor) => Some(ref)
case _ => None
}
contextElement.foreach(processAssignment)
treeWalkUp(ref, null, ScalaResolveState.empty)
}
def processAssignment(assign: PsiElement): Unit = assign.getContext match {
//trying to resolve naming parameter
case args: ScArgumentExprList =>
args.getContext match {
case invocation: MethodInvocation =>
processMethodAssignment(args, invocation)
case invocation: ConstructorInvocationLike =>
processConstructorReference(args, invocation, assign, args.invocationCount - 1)
case _ =>
}
case tuple: ScTuple => tuple.getContext match {
case infix @ ScInfixExpr.withAssoc(_, operation, `tuple`) =>
processAnyAssignment(tuple.exprs, infix, operation)
case _ =>
}
case p: ScParenthesisedExpr => p.getContext match {
case infix@ScInfixExpr.withAssoc(_, operation, `p`) =>
processAnyAssignment(p.innerElement.toSeq, infix, operation)
case _ =>
}
case _ =>
}
def processMethodAssignment(
args: ScArgumentExprList,
call: MethodInvocation
): Unit =
args.callReference.foreach { reference =>
val isNamedParametersEnabled = call match {
case call: ScMethodCall => call.isNamedParametersEnabledEverywhere
case _ => false
}
processAnyAssignment(
args.exprs,
call,
reference,
args.invocationCount - 1,
isNamedParametersEnabled
)
}
def processAnyAssignment(
exprs: Seq[ScExpression],
call: MethodInvocation,
callReference: ScReferenceExpression,
index: Int = 0,
isNamedParametersEnabled: Boolean = false
): Unit = {
val refName = ref.refName
def addParamForApplyDynamicNamed(): Unit = proc match {
case _: CompletionProcessor =>
case _ =>
proc.execute(
createParameterFromText(refName + ": Any", ref),
ScalaResolveState.withNamedParam
)
}
def processNamedParameterOf(result: ScalaResolveResult, index: Int): Unit = result.element match {
case _: ScFunction if isApplyDynamicNamed(result) => addParamForApplyDynamicNamed()
case _ if call.applyOrUpdateElement.exists(isApplyDynamicNamed) => addParamForApplyDynamicNamed()
case fun: ScMethodLike =>
val substitutor = result.substitutor
proc match {
case completionProcessor: CompletionProcessor =>
collectNamedCompletions(fun.parameterList, completionProcessor, substitutor, exprs, index)
case _ =>
getParamByName(fun, refName, index).foreach { param =>
val rename =
if (!equivalent(param.name, refName)) param.deprecatedName
else None
val state = ScalaResolveState
.withSubstitutor(substitutor)
.withNamedParam
.withRename(rename)
proc.execute(param, state)
}
}
case _: FakePsiMethod => //todo: ?
case method: PsiMethod if isNamedParametersEnabled =>
val state = ScalaResolveState
.withSubstitutor(result.substitutor)
.withNamedParam
method.parameters.foreach {
proc.execute(_, state)
}
case _ =>
}
def tryProcessApplyMethodArgs(): Unit = {
@tailrec
def traverseInvokedExprs(call: ScExpression, dropped: Int): Unit = call match {
case mc: MethodInvocation =>
val tp = mc.`type`().getOrAny
val applyResolves = mc.resolveApplyOrUpdateMethod(
mc,
tp,
shapesOnly = false,
stripTypeArgs = false,
withImplicits = true
)
applyResolves.foreach(processNamedParameterOf(_, dropped))
if (proc.candidates.isEmpty) traverseInvokedExprs(mc.getEffectiveInvokedExpr, dropped + 1)
case _ => ()
}
traverseInvokedExprs(call.getEffectiveInvokedExpr, 0)
}
for (variant <- callReference.multiResolveScala(false)) {
processNamedParameterOf(variant, index)
// Consider named parameters of apply method; see SCL-2407
variant.innerResolveResult.foreach(processNamedParameterOf(_, index))
}
// Check if argument clause is actually an apply method invocation SCL-17892
if (proc.candidates.isEmpty) tryProcessApplyMethodArgs()
}
def processConstructorReference(
args: ScArgumentExprList,
invocation: ConstructorInvocationLike,
assign: PsiElement,
index: Int
): Unit = {
def processConstructor(
typeable: Typeable
)(
isTargetClass: ScConstructorOwner => Boolean
)(
isAcceptableConstructor: ScFunction => Boolean
): Unit = for {
scType <- typeable.`type`().toOption
(clazz, subst) <- scType.extractClassType
} {
if (!clazz.is[ScTemplateDefinition] && clazz.annotationType) {
proc match {
case completionProcessor: CompletionProcessor =>
if (index == 0) {
val methods = clazz.getMethods.collect {
case annotationMethod: PsiAnnotationMethod => annotationMethod
}.toBuffer
val exprs = args.exprs
var i = 0
def tail(): Unit = {
if (methods.nonEmpty) methods.remove(0)
}
while (exprs(i) != assign) {
exprs(i) match {
case assignStmt: ScAssignment =>
assignStmt.leftExpression match {
case ref: ScReferenceExpression =>
val ind = methods.indexWhere(p => equivalent(p.name, ref.refName))
if (ind != -1) methods.remove(ind)
else tail()
case _ => tail()
}
case _ => tail()
}
i = i + 1
}
val state = ScalaResolveState.withSubstitutor(subst).withNamedParam
for (method <- methods) {
completionProcessor.execute(method, state)
}
}
case _ =>
for {
method <- clazz.getMethods.toSeq.filterByType[PsiAnnotationMethod]
if equivalent(method.name, ref.refName)
} proc.execute(method, ScalaResolveState.empty)
}
} else {
val arguments = invocation.arguments.toList
val processor = new MethodResolveProcessor(
invocation,
"this",
arguments.map(_.exprs),
invocation.typeArgList.fold(Seq.empty[ScTypeElement])(_.typeArgs),
Seq.empty /* todo: ? */ ,
constructorResolve = true,
enableTupling = true
)
val state = ScalaResolveState.withSubstitutor(subst)
clazz match {
case clazz: ScConstructorOwner =>
if (isTargetClass(clazz)) {
for {
constructor <- clazz.secondaryConstructors
if isAcceptableConstructor(constructor)
} processor.execute(constructor, state)
}
for {
constructor <- clazz.constructor
} processor.execute(constructor, state)
case _ =>
for (constr <- clazz.getConstructors) {
processor.execute(constr, state)
}
}
val refName = ref.refName
for (candidate <- processor.candidatesS) {
candidate.element match {
case method: ScMethodLike =>
val isFunction = method.is[ScFunction]
proc match {
case baseProcessor: CompletionProcessor =>
collectNamedCompletions(
method.parameterList,
baseProcessor,
if (isFunction) candidate.substitutor else subst,
args.exprs,
index
)
case _ =>
for {
parameter <- getParamByName(method, refName, arguments.indexOf(args))
name = if (isFunction && !equivalent(parameter.name, refName))
parameter.deprecatedName.map(clean)
else
None
} proc.execute(
parameter,
ScalaResolveState
.withSubstitutor(subst)
.withNamedParam
.withRename(name)
)
}
case _ =>
}
}
}
}
invocation match {
case invocation: ScSelfInvocation =>
getContextOfType(invocation, true, classOf[ScClass]) match {
case null =>
case clazz =>
processConstructor(clazz)(_ == clazz) { constructor =>
constructor.getTextRange.getStartOffset < invocation.getTextRange.getStartOffset &&
!isContextAncestor(constructor, invocation, true)
}
}
case invocation: ScConstructorInvocation =>
processConstructor(invocation.typeElement)(Function.const(true))(Function.const(true))
}
}
def processQualifier(qualifier: ScExpression): Array[ScalaResolveResult] = {
ProgressManager.checkCanceled()
qualifier.getNonValueType() match {
case Right(tpt @ ScTypePolymorphicType(internal, tp)) if tp.nonEmpty &&
!internal.is[ScMethodType, UndefinedType] /* optimization */ =>
val substed = tpt.abstractOrLowerTypeSubstitutor(context)(internal)
processType(substed, qualifier)
if (proc.candidates.nonEmpty) return proc.candidates
case _ =>
}
//if it's ordinary case
qualifier.`type`().toOption match {
case Some(tp) => processType(tp, qualifier)
case _ => proc.candidates
}
}
def explicitApplyReferenceResolve(found: Array[ScalaResolveResult]): Array[ScalaResolveResult] = {
val maybeExplicitApplyRefAndContextInfo =
if (ref.refName != CommonNames.Apply
&& inMethodCallContext(ref)
&& found.length == 1
&& !ref.startsWithToken(ScalaTokenTypes.tUNDER)
) {
val srr = found.head
val hasParams = srr.elementHasParameters
val hasTypeParams = srr.elementHasTypeParameters
val hasArgs = info.arguments.nonEmpty
val hasMismatchedTypeArgs = !hasTypeParams && info.typeArgs.nonEmpty
if (!hasParams || srr.name == CommonNames.Apply) {
if (hasMismatchedTypeArgs) {
// the case when type arguments belong to apply method
// foo[A](10) -> foo.apply[A](1)
val res = createRef(ref.getContext, s"${ref.getText}.apply") -> info
Option(res)
} else if (hasArgs) {
// the case when potential type arguments belong to the initial method invocation
// foo[A](10) -> foo[A].apply(10)
val invokedExpr = info.invokedExpr.getOrElse(ref)
val res =
createRef(invokedExpr.getContext, s"(${invokedExpr.getText}).apply") ->
info.copy(typeArgs = Seq.empty)
Option(res)
}
else None
} else None
} else None
maybeExplicitApplyRefAndContextInfo match {
case Some((applyRef, info)) =>
val resolvedWithApplyRef =
doResolve(
applyRef,
updateResolveProcessor(proc)(_.copy(refName = CommonNames.Apply)),
accessibilityCheck,
tryThisQualifier,
contextInfo = Option(info)
)
if (resolvedWithApplyRef.isEmpty) found
else {
val prevSrr = found.head
val innerSrr = prevSrr.innerResolveResult.getOrElse(prevSrr)
val res = resolvedWithApplyRef.map { actualSrr =>
actualSrr.copy(
innerResolveResult = innerSrr.toOption,
parentElement = innerSrr.element.toOption
)
}
res
}
case None => found
}
}
def shouldTryImplicitConversions(cands: Array[ScalaResolveResult]) =
cands.isEmpty || (!isShape && cands.forall(!_.isApplicable()))
def processType(aType: ScType, qualifier: ScExpression): Array[ScalaResolveResult] = {
val (fromType, matchSubst) = qualifier match {
case ref: ScReferenceExpression =>
val srr = ref.bind()
val subst = srr.fold(ScSubstitutor.empty)(_.matchClauseSubstitutor)
val tpe = srr match {
case Some(ScalaResolveResult(_: ScSelfTypeElement, _)) => aType
case Some(r @ ScalaResolveResult(b: ScTypedDefinition, _)) if b.isStable =>
r.fromType match {
case Some(fT) => ScProjectionType(fT, b)
case None => ScalaType.designator(b)
}
case _ => aType
}
(tpe, subst)
case _ => (aType, ScSubstitutor.empty)
}
val state = fromType match {
case ScDesignatorType(_: PsiPackage) => ScalaResolveState.empty
case _ => ScalaResolveState.withFromType(fromType)
}
proc.processType(aType, qualifier, state.withSubstitutor(matchSubst))
val candidates = proc.candidates
aType match {
case d: ScDesignatorType if d.isStatic => return candidates
case ScDesignatorType(_: PsiPackage) => return candidates
case _ =>
}
val withImplicitConversion = proc match {
case cp: CompletionProcessor => cp.withImplicitConversions
case _ => false
}
val withExplicitApply =
if (candidates.forall(!_.isApplicable())) explicitApplyReferenceResolve(candidates)
else candidates
if (shouldTryImplicitConversions(withExplicitApply) || withImplicitConversion) {
val procForConversions = updateResolveProcessor(proc)(_.copy(noImplicitsForArgs = candidates.nonEmpty))
ImplicitConversionResolveResult.processImplicitConversionsAndExtensions(
targetNameForImplicitProcessor(proc),
ref,
procForConversions,
noImplicitsForArgs = candidates.nonEmpty,
forCompletion = procForConversions.is[CompletionProcessor]
)(_.withImports.withImplicitType.withType)(qualifier)
val fromImplicits = (procForConversions, procForConversions.candidates) match {
case (methodProcessor: MethodResolveProcessor, Array()) if conformsToDynamic(fromType, ref.resolveScope) =>
val dynamicProcessor = dynamicResolveProcessor(ref, qualifier, methodProcessor)
dynamicProcessor.processType(fromType, qualifier, state)
dynamicProcessor.candidates
case (_, cands) => cands
}
//If none of the candidates from implicit conversions/extensions are applicable,
//and simple resolve produced some (inapplicable) candidates, return them.
if (fromImplicits.forall(!_.isApplicable()) && withExplicitApply.nonEmpty) withExplicitApply
else fromImplicits
} else withExplicitApply
}
def updateResolveProcessor(
processor: BaseProcessor
)(
update: MethodResolveProcessor => BaseProcessor
): BaseProcessor = processor match {
case mrp: MethodResolveProcessor => update(mrp)
case other => other
}
def targetNameForImplicitProcessor(processor: BaseProcessor): Option[String] =
processor match {
case _: CompletionProcessor => None
case processor: ResolveProcessor => Option(processor.name) // See SCL-2934.
case _ => Option(ref.refName)
}
if (!accessibilityCheck) proc.doNotCheckAccessibility()
var res = ref.qualifier match {
case None =>
val unqualified = resolveUnqualified()
if (unqualified.forall(!_.isApplicable()))
explicitApplyReferenceResolve(unqualified)
else unqualified
case Some(superQ: ScSuperReference) =>
ResolveUtils.processSuperReference(superQ, proc, ref)
case Some(q) => processQualifier(q)
}
if (accessibilityCheck && res.isEmpty) {
res = doResolve(
ref,
proc,
accessibilityCheck = false,
tryThisQualifier = false,
contextInfo = Option(info)
)
}
val isInfixOp = ref.getContext match {
case inf: ScInfixExpr => inf.operation == ref
case _ => false
}
if (
res.nonEmpty &&
res.forall(!_.isValidResult) &&
ref.qualifier.isEmpty &&
tryThisQualifier &&
!isInfixOp
) {
val thisExpr = createRef(ref.getContext, s"this.${ref.getText}")
res = doResolve(
thisExpr,
proc,
accessibilityCheck,
tryThisQualifier = false,
contextInfo = Option(info)
)
}
res
}