in scala/scala-impl/src/org/jetbrains/plugins/scala/annotator/element/ScReferenceAnnotator.scala [170:339]
private def checkNotQualifiedReferenceElement(refElement: ScReference, typeAware: Boolean)
(implicit holder: ScalaAnnotationHolder): Unit = {
refElement match {
case _: ScInterpolatedExpressionPrefix => return // do not inspect interpolated literal, it will be highlighted in other place
case _ if refElement.isSoft => return
case _ =>
}
val resolve = refElement.multiResolveScala(false)
refElement match {
case scalaDocRef: ScDocResolvableCodeReference =>
annotateScalaDocReference(scalaDocRef, resolve)
return
case _ =>
}
if (resolve.length != 1) {
def addUnknownSymbolProblem(): Unit = {
if (resolve.isEmpty) {
createUnknownSymbolProblem(refElement)
}
}
val parent = refElement.getParent
refElement match {
case refElement: ScReferenceExpression =>
// Let's try to hide dynamic named parameter usage
refElement.getContext match {
case assignment@ScAssignment(`refElement`, _) if resolve.isEmpty && assignment.isDynamicNamedAssignment => return
case _ => addUnknownSymbolProblem()
}
case _ =>
parent match {
case ScInfixPattern(_, `refElement`, _) if refElement.isInstanceOf[ScStableCodeReference] =>
// todo: this is hide A op B in patterns
() //skip
case _: ScImportSelector if resolve.length > 0 =>
() //skip
case tag: ScDocTag =>
if (MyScaladocParsing.TagNames.ParamOrTParamSet.contains(tag.name)) {
//skip, references to parameters in ScalaDoc tags are handled in [[org.jetbrains.plugins.scala.codeInspection.scaladoc.ScalaDocReferenceInspection]]
}
else {
holder.createWeakWarningAnnotation(refElement, ScalaBundle.message("cannot.resolve", refElement.refName))
}
case _ =>
addUnknownSymbolProblem()
}
}
} else {
def showError(): Unit = {
val error = ScalaBundle.message("forward.reference.detected")
holder.createErrorAnnotation(refElement.nameId, error)
}
refElement.getContainingFile match {
case file: ScalaFile if !file.allowsForwardReferences =>
resolve(0) match {
case r if r.isForwardReference =>
r.getActualElement.nameContext match {
case v: ScValue if !v.hasModifierProperty("lazy") => showError()
case _: ScVariable => showError()
case nameContext if nameContext.isValid =>
//if it has not lazy val or var between reference and statement then it's forward reference
val context = findCommonContext(refElement, nameContext)
if (context != null) {
val neighbour = (findFirstContext(nameContext, false, elem => elem.getContext.eq(context)) match {
case s: ScalaPsiElement => s.getDeepSameElementInContext
case elem => elem
}).getPrevSibling
@scala.annotation.tailrec
def check(neighbour: PsiElement): Boolean = {
if (neighbour == null ||
neighbour.getTextRange.getStartOffset <= refElement.getTextRange.getStartOffset) return false
neighbour match {
case v: ScValue if !v.hasModifierProperty("lazy") => true
case _: ScVariable => true
case _ => check(neighbour.getPrevSibling)
}
}
if (check(neighbour)) showError()
}
}
case _ =>
}
case _ =>
}
}
runCommonChecksForReference(resolve, refElement)
if (resolve.length == 1) {
val resolveResult = resolve(0)
refElement match {
case e: ScReferenceExpression if e.getParent.isInstanceOf[ScPrefixExpr] &&
e.getParent.asInstanceOf[ScPrefixExpr].operation == e =>
resolveResult.implicitFunction match {
case Some(_) =>
highlightImplicitMethod(refElement)
case _ =>
}
case e: ScReferenceExpression if e.getParent.isInstanceOf[ScInfixExpr] &&
e.getParent.asInstanceOf[ScInfixExpr].operation == e =>
resolveResult.implicitFunction match {
case Some(_) =>
highlightImplicitMethod(refElement)
case _ =>
}
case _ =>
}
}
def isOnTopLevel(element: PsiElement) = element match {
case scalaPsi: ScalaPsiElement => !scalaPsi.parents.exists(_.isInstanceOf[ScTypeDefinition])
case _ => false
}
//don't highlight ambiguous definitions, if they are resolved to multiple top-level declarations
//needed for worksheet and scala notebook files (e.g. zeppelin)
def isTopLevelResolve =
resolve.length > 1 && resolve.headOption.map(_.element).filter(isOnTopLevel).exists {
firstElement =>
val fFile = firstElement.getContainingFile
resolve.tail.map(_.element).forall(nextEl => nextEl.getContainingFile == fFile && isOnTopLevel(nextEl))
}
if (typeAware && resolve.length != 1 && !(refElement.containingScalaFile.exists(_.isMultipleDeclarationsAllowed) && isTopLevelResolve)) {
val parent = refElement.getParent
def addCreateApplyOrUnapplyFix(errorWithRefName: String => String, fix: ScTypeDefinition => IntentionAction): Boolean = {
val refWithoutArgs = ScalaPsiElementFactory.createReferenceFromText(refElement.getText, parent.getContext, parent)
if (refWithoutArgs != null && refWithoutArgs.multiResolveScala(false).exists(!_.getElement.isInstanceOf[PsiPackage])) {
// We can't resolve the method call A(arg1, arg2), but we can resolve A. Highlight this differently.
val message = errorWithRefName(refElement.refName)
val typeDefFix = refWithoutArgs match {
case ResolvesTo(obj: ScObject) => fix(obj) :: Nil
case InstanceOfClass(td: ScTypeDefinition) => fix(td) :: Nil
case _ => Nil
}
holder.createErrorAnnotation(
refElement.nameId,
message,
ReportHighlightingErrorQuickFix :: typeDefFix
)
true
} else false
}
parent match {
case _: ScImportSelector if resolve.length > 0 => return
case _: MethodInvocation if resolve.length > 1 =>
val error = ScalaBundle.message("cannot.resolve.overloaded", refElement.refName)
holder.createErrorAnnotation(refElement.nameId, error)
case mc: ScMethodCall if addCreateApplyOrUnapplyFix(
ScalaBundle.message("cannot.resolve.apply.method", _),
td => new CreateApplyQuickFix(td, mc)
) =>
return
case (p: ScPattern) & (_: ScExtractorPattern) =>
val errorWithRefName: String => String = ScalaBundle.message("cannot.resolve.unapply.method", _)
if (addCreateApplyOrUnapplyFix(errorWithRefName, td => new CreateUnapplyQuickFix(td, p))) return
case scalaDocTag: ScDocTag if scalaDocTag.name == MyScaladocParsing.TagNames.Throws =>
return //see SCL-9490
case _ =>
}
}
}