FIR: pass the qualified access source when reporting ErrorNamedReference

Currently if there is an error in a function call, FIR would report the
entire expression if this call is qualified, but *only* the name if it's
not qualified. For example, assume the following two calls are all
contains some errors.

```
a.foo(1,2,3)
^^^^^^^^^^^^
bar(1,2,3)
^^^
```

The entire call of `foo` is reported since it's qualified. But only the
reference `bar` is reported since it's not qualified. This limits the
usage of position strategies because the IDE does not allow position
strategies to go outside of the initially reported PSI element
(org.jetbrains.kotlin.idea.fir.highlighter.KotlinHighLevelDiagnosticHighlightingPass#addDiagnostic).

This change passes both the original error named reference and the
surrounding qualified access expression and defer the decision of which
to use to the reporting logic.

For unresolved reference and checks on `super` keyword, the position
strategy should not highlight the surrounding parentheses. Hence a new
position strategy `REFERENCED_NAME_BY_QUALIFIED` is added.

In addition, this change also has the following side effect

* some diagnostics are no longer reported when there is a syntax error
  since the higher level structure does not exist when there is a syntax
  error
This commit is contained in:
Tianyu Geng
2021-03-07 16:10:29 -08:00
committed by Dmitriy Novozhilov
parent 8805675539
commit d6907222cd
53 changed files with 247 additions and 155 deletions
@@ -28,9 +28,9 @@ fun test_2(a: A<C>) {
} }
fun test_3(a: A<D>) { fun test_3(a: A<D>) {
<!AMBIGUITY!>a[0] += D()<!> // ambiguity a[0] <!AMBIGUITY!>+=<!> D() // ambiguity
} }
fun test_4(b: B) { fun test_4(b: B) {
<!UNRESOLVED_REFERENCE!>b<!NO_GET_METHOD!>[0]<!> += B()<!> // unresolved b<!NO_GET_METHOD!>[0]<!> <!UNRESOLVED_REFERENCE!>+=<!> B() // unresolved
} }
@@ -21,7 +21,7 @@ class Bar {
} }
// NB! abc() here is resolved to member Foo.abc(), and not to extension member of Bar // NB! abc() here is resolved to member Foo.abc(), and not to extension member of Bar
fun Foo.check() = <!NONE_APPLICABLE{LT}!>abc() <!NONE_APPLICABLE{PSI}!>+<!> bar()<!> fun Foo.check() = abc() <!NONE_APPLICABLE!>+<!> bar()
// NB! + here is resolved to member String.plus (not to extension member above) // NB! + here is resolved to member String.plus (not to extension member above)
fun Foo.check2() = "" + bar() fun Foo.check2() = "" + bar()
@@ -7,7 +7,7 @@ class Foo {
fun test_1() { fun test_1() {
val f = Foo() val f = Foo()
<!UNRESOLVED_REFERENCE{LT}!>f <!UNRESOLVED_REFERENCE{PSI}!>+<!> f<!> f <!UNRESOLVED_REFERENCE!>+<!> f
} }
fun test_2() { fun test_2() {
@@ -19,5 +19,5 @@ abstract class A {
// FILE: main.kt // FILE: main.kt
fun test(b: B): String { fun test(b: B): String {
return <!HIDDEN{LT}!>b <!HIDDEN{PSI}!>foo<!> "hello"<!> // should be an error return b <!HIDDEN!>foo<!> "hello" // should be an error
} }
@@ -46,6 +46,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
INNER_MODIFIER, INNER_MODIFIER,
SELECTOR_BY_QUALIFIED, SELECTOR_BY_QUALIFIED,
REFERENCE_BY_QUALIFIED, REFERENCE_BY_QUALIFIED,
REFERENCED_NAME_BY_QUALIFIED,
PRIVATE_MODIFIER, PRIVATE_MODIFIER,
COMPANION_OBJECT, COMPANION_OBJECT,
CONST_MODIFIER, CONST_MODIFIER,
@@ -67,7 +67,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val HIDDEN by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) { val HIDDEN by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<AbstractFirBasedSymbol<*>>("hidden") parameter<AbstractFirBasedSymbol<*>>("hidden")
} }
val UNRESOLVED_REFERENCE by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) { val UNRESOLVED_REFERENCE by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<String>("reference") parameter<String>("reference")
} }
val UNRESOLVED_LABEL by error<FirSourceElement, PsiElement>() val UNRESOLVED_LABEL by error<FirSourceElement, PsiElement>()
@@ -79,9 +79,9 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
} }
val SUPER by object : DiagnosticGroup("Super") { val SUPER by object : DiagnosticGroup("Super") {
val SUPER_IS_NOT_AN_EXPRESSION by error<FirSourceElement, PsiElement>() val SUPER_IS_NOT_AN_EXPRESSION by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
val SUPER_NOT_AVAILABLE by error<FirSourceElement, PsiElement>() val SUPER_NOT_AVAILABLE by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
val ABSTRACT_SUPER_CALL by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) val ABSTRACT_SUPER_CALL by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
val INSTANCE_ACCESS_BEFORE_SUPER_CALL by error<FirSourceElement, PsiElement> { val INSTANCE_ACCESS_BEFORE_SUPER_CALL by error<FirSourceElement, PsiElement> {
parameter<String>("target") parameter<String>("target")
} }
@@ -497,12 +497,12 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val UNSAFE_IMPLICIT_INVOKE_CALL by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) { val UNSAFE_IMPLICIT_INVOKE_CALL by error<FirSourceElement, PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<ConeKotlinType>("receiverType") parameter<ConeKotlinType>("receiverType")
} }
val UNSAFE_INFIX_CALL by error<FirSourceElement, KtExpression> { val UNSAFE_INFIX_CALL by error<FirSourceElement, KtExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<FirExpression>("lhs") parameter<FirExpression>("lhs")
parameter<String>("operator") parameter<String>("operator")
parameter<FirExpression>("rhs") parameter<FirExpression>("rhs")
} }
val UNSAFE_OPERATOR_CALL by error<FirSourceElement, KtExpression> { val UNSAFE_OPERATOR_CALL by error<FirSourceElement, KtExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<FirExpression>("lhs") parameter<FirExpression>("lhs")
parameter<String>("operator") parameter<String>("operator")
parameter<FirExpression>("rhs") parameter<FirExpression>("rhs")
@@ -77,7 +77,7 @@ object FirErrors {
// Unresolved // Unresolved
val HIDDEN by error1<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val HIDDEN by error1<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val UNRESOLVED_REFERENCE by error1<FirSourceElement, PsiElement, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val UNRESOLVED_REFERENCE by error1<FirSourceElement, PsiElement, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val UNRESOLVED_LABEL by error0<FirSourceElement, PsiElement>() val UNRESOLVED_LABEL by error0<FirSourceElement, PsiElement>()
val DESERIALIZATION_ERROR by error0<FirSourceElement, PsiElement>() val DESERIALIZATION_ERROR by error0<FirSourceElement, PsiElement>()
val ERROR_FROM_JAVA_RESOLUTION by error0<FirSourceElement, PsiElement>() val ERROR_FROM_JAVA_RESOLUTION by error0<FirSourceElement, PsiElement>()
@@ -86,9 +86,9 @@ object FirErrors {
val NO_THIS by error0<FirSourceElement, PsiElement>() val NO_THIS by error0<FirSourceElement, PsiElement>()
// Super // Super
val SUPER_IS_NOT_AN_EXPRESSION by error0<FirSourceElement, PsiElement>() val SUPER_IS_NOT_AN_EXPRESSION by error0<FirSourceElement, PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val SUPER_NOT_AVAILABLE by error0<FirSourceElement, PsiElement>() val SUPER_NOT_AVAILABLE by error0<FirSourceElement, PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val ABSTRACT_SUPER_CALL by error0<FirSourceElement, PsiElement>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val ABSTRACT_SUPER_CALL by error0<FirSourceElement, PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val INSTANCE_ACCESS_BEFORE_SUPER_CALL by error1<FirSourceElement, PsiElement, String>() val INSTANCE_ACCESS_BEFORE_SUPER_CALL by error1<FirSourceElement, PsiElement, String>()
// Supertypes // Supertypes
@@ -303,8 +303,8 @@ object FirErrors {
// Nullability // Nullability
val UNSAFE_CALL by error1<FirSourceElement, PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.DOT_BY_QUALIFIED) val UNSAFE_CALL by error1<FirSourceElement, PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.DOT_BY_QUALIFIED)
val UNSAFE_IMPLICIT_INVOKE_CALL by error1<FirSourceElement, PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val UNSAFE_IMPLICIT_INVOKE_CALL by error1<FirSourceElement, PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val UNSAFE_INFIX_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>() val UNSAFE_INFIX_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val UNSAFE_OPERATOR_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>() val UNSAFE_OPERATOR_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
// When expressions // When expressions
val NO_ELSE_IN_WHEN by error1<FirSourceElement, KtWhenExpression, List<WhenMissingCase>>(SourceElementPositioningStrategies.WHEN_EXPRESSION) val NO_ELSE_IN_WHEN by error1<FirSourceElement, KtWhenExpression, List<WhenMissingCase>>(SourceElementPositioningStrategies.WHEN_EXPRESSION)
@@ -14,9 +14,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.toFirDiagnostics import org.jetbrains.kotlin.fir.analysis.diagnostics.toFirDiagnostics
import org.jetbrains.kotlin.fir.declarations.FirErrorFunction import org.jetbrains.kotlin.fir.declarations.FirErrorFunction
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirErrorExpression import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirErrorLoop
import org.jetbrains.kotlin.fir.expressions.FirErrorResolvedQualifier
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
@@ -35,15 +33,18 @@ class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollect
} }
override fun visitErrorNamedReference(errorNamedReference: FirErrorNamedReference, data: CheckerContext) { override fun visitErrorNamedReference(errorNamedReference: FirErrorNamedReference, data: CheckerContext) {
val source = data.qualifiedAccesses.lastOrNull()?.source?.takeIf { it.elementType == KtNodeTypes.DOT_QUALIFIED_EXPRESSION } val source = errorNamedReference.source ?: return
?: errorNamedReference.source ?: return val qualifiedAccessSource = data.qualifiedAccesses.lastOrNull()?.takeIf {
// Use the source of the enclosing FirQualifiedAccessExpression if it is exactly the call to the erroneous callee.
it is FirQualifiedAccessExpression && it.calleeReference == errorNamedReference
}?.source
// Don't report duplicated unresolved reference on annotation entry (already reported on its type) // Don't report duplicated unresolved reference on annotation entry (already reported on its type)
if (source.elementType == KtNodeTypes.ANNOTATION_ENTRY && errorNamedReference.diagnostic is ConeUnresolvedNameError) return if (source.elementType == KtNodeTypes.ANNOTATION_ENTRY && errorNamedReference.diagnostic is ConeUnresolvedNameError) return
// Already reported in FirConventionFunctionCallChecker // Already reported in FirConventionFunctionCallChecker
if (errorNamedReference.source?.kind == FirFakeSourceElementKind.ArrayAccessNameReference && if (source.kind == FirFakeSourceElementKind.ArrayAccessNameReference &&
errorNamedReference.diagnostic is ConeUnresolvedNameError errorNamedReference.diagnostic is ConeUnresolvedNameError
) return ) return
reportFirDiagnostic(errorNamedReference.diagnostic, source, reporter, data) reportFirDiagnostic(errorNamedReference.diagnostic, source, reporter, data, qualifiedAccessSource)
} }
override fun visitErrorExpression(errorExpression: FirErrorExpression, data: CheckerContext) { override fun visitErrorExpression(errorExpression: FirErrorExpression, data: CheckerContext) {
@@ -65,7 +66,8 @@ class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollect
diagnostic: ConeDiagnostic, diagnostic: ConeDiagnostic,
source: FirSourceElement, source: FirSourceElement,
reporter: DiagnosticReporter, reporter: DiagnosticReporter,
context: CheckerContext context: CheckerContext,
qualifiedAccessSource: FirSourceElement? = null
) { ) {
// Will be handled by [FirDestructuringDeclarationChecker] // Will be handled by [FirDestructuringDeclarationChecker]
if (source.elementType == KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY) { if (source.elementType == KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY) {
@@ -81,7 +83,7 @@ class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollect
if (source.kind == FirFakeSourceElementKind.ImplicitConstructor) { if (source.kind == FirFakeSourceElementKind.ImplicitConstructor) {
return return
} }
for (coneDiagnostic in diagnostic.toFirDiagnostics(source)) { for (coneDiagnostic in diagnostic.toFirDiagnostics(source, qualifiedAccessSource)) {
reporter.report(coneDiagnostic, context) reporter.report(coneDiagnostic, context)
} }
} }
@@ -343,10 +343,11 @@ object LightTreePositioningStrategies {
endOffset: Int, endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode> tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> { ): List<TextRange> {
if (node.tokenType != KtNodeTypes.DOT_QUALIFIED_EXPRESSION) { if (node.tokenType == KtNodeTypes.DOT_QUALIFIED_EXPRESSION) {
return super.mark(node, startOffset, endOffset, tree) return markElement(tree.dotOperator(node) ?: node, startOffset, endOffset, tree, node)
} }
return markElement(tree.dotOperator(node) ?: node, startOffset, endOffset, tree, node) // Fallback to mark the callee reference.
return REFERENCE_BY_QUALIFIED.mark(node, startOffset, endOffset, tree)
} }
} }
@@ -368,7 +369,13 @@ object LightTreePositioningStrategies {
} }
} }
val REFERENCE_BY_QUALIFIED: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { val REFERENCE_BY_QUALIFIED: LightTreePositioningStrategy = FindReferencePositioningStrategy(false)
val REFERENCED_NAME_BY_QUALIFIED: LightTreePositioningStrategy = FindReferencePositioningStrategy(true)
/**
* @param locateReferencedName see doc on [referenceExpression]
*/
class FindReferencePositioningStrategy(val locateReferencedName: Boolean) : LightTreePositioningStrategy() {
override fun mark( override fun mark(
node: LighterASTNode, node: LighterASTNode,
startOffset: Int, startOffset: Int,
@@ -376,7 +383,10 @@ object LightTreePositioningStrategies {
tree: FlyweightCapableTreeStructure<LighterASTNode> tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> { ): List<TextRange> {
if (node.tokenType == KtNodeTypes.CALL_EXPRESSION || node.tokenType == KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL) { if (node.tokenType == KtNodeTypes.CALL_EXPRESSION || node.tokenType == KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL) {
return markElement(tree.referenceExpression(node) ?: node, startOffset, endOffset, tree, node) return markElement(tree.referenceExpression(node, locateReferencedName) ?: node, startOffset, endOffset, tree, node)
}
if (node.tokenType in nodeTypesWithOperation) {
return markElement(tree.operationReference(node) ?: node, startOffset, endOffset, tree, node)
} }
if (node.tokenType != KtNodeTypes.DOT_QUALIFIED_EXPRESSION && if (node.tokenType != KtNodeTypes.DOT_QUALIFIED_EXPRESSION &&
node.tokenType != KtNodeTypes.SAFE_ACCESS_EXPRESSION && node.tokenType != KtNodeTypes.SAFE_ACCESS_EXPRESSION &&
@@ -389,14 +399,30 @@ object LightTreePositioningStrategies {
when (selector.tokenType) { when (selector.tokenType) {
KtNodeTypes.REFERENCE_EXPRESSION -> KtNodeTypes.REFERENCE_EXPRESSION ->
return markElement(selector, startOffset, endOffset, tree, node) return markElement(selector, startOffset, endOffset, tree, node)
KtNodeTypes.CALL_EXPRESSION, KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL, KtNodeTypes.SUPER_TYPE_CALL_ENTRY -> KtNodeTypes.CALL_EXPRESSION, KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL,KtNodeTypes.SUPER_TYPE_CALL_ENTRY ->
return markElement(tree.referenceExpression(selector) ?: selector, startOffset, endOffset, tree, node) return markElement(
tree.referenceExpression(selector, locateReferencedName) ?: selector,
startOffset,
endOffset,
tree,
node
)
} }
} }
return super.mark(node, startOffset, endOffset, tree) return super.mark(node, startOffset, endOffset, tree)
} }
} }
private val nodeTypesWithOperation = setOf(
KtNodeTypes.IS_EXPRESSION,
KtNodeTypes.BINARY_WITH_TYPE,
KtNodeTypes.BINARY_EXPRESSION,
KtNodeTypes.POSTFIX_EXPRESSION,
KtNodeTypes.PREFIX_EXPRESSION,
KtNodeTypes.BINARY_EXPRESSION,
KtNodeTypes.WHEN_CONDITION_IN_RANGE
)
val WHEN_EXPRESSION = object : LightTreePositioningStrategy() { val WHEN_EXPRESSION = object : LightTreePositioningStrategy() {
override fun mark( override fun mark(
node: LighterASTNode, node: LighterASTNode,
@@ -464,12 +490,33 @@ private fun FlyweightCapableTreeStructure<LighterASTNode>.nameIdentifier(node: L
private fun FlyweightCapableTreeStructure<LighterASTNode>.operationReference(node: LighterASTNode): LighterASTNode? = private fun FlyweightCapableTreeStructure<LighterASTNode>.operationReference(node: LighterASTNode): LighterASTNode? =
findChildByType(node, KtNodeTypes.OPERATION_REFERENCE) findChildByType(node, KtNodeTypes.OPERATION_REFERENCE)
private fun FlyweightCapableTreeStructure<LighterASTNode>.referenceExpression(node: LighterASTNode): LighterASTNode? { /**
* @param locateReferencedName whether to remove any nested parentheses while locating the reference element. This is useful for diagnostics
* on super and unresolved references. For example, with the following, only the part inside the parentheses should be highlighted.
*
* ```
* fun foo() {
* (super)()
* ^^^^^
* (random123)()
* ^^^^^^^^^
* }
* ```
*/
private fun FlyweightCapableTreeStructure<LighterASTNode>.referenceExpression(
node: LighterASTNode,
locateReferencedName: Boolean
): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode?>>() val childrenRef = Ref<Array<LighterASTNode?>>()
getChildren(node, childrenRef) getChildren(node, childrenRef)
return childrenRef.get()?.firstOrNull { var result = childrenRef.get()?.firstOrNull {
it?.tokenType == KtNodeTypes.REFERENCE_EXPRESSION || it?.tokenType == KtNodeTypes.CONSTRUCTOR_DELEGATION_REFERENCE it?.tokenType == KtNodeTypes.REFERENCE_EXPRESSION || it?.tokenType == KtNodeTypes.CONSTRUCTOR_DELEGATION_REFERENCE ||
it?.tokenType == KtNodeTypes.SUPER_EXPRESSION || it?.tokenType == KtNodeTypes.PARENTHESIZED
} }
while (locateReferencedName && result != null && result.tokenType == KtNodeTypes.PARENTHESIZED) {
result = referenceExpression(result, locateReferencedName = true)
}
return result
} }
private fun FlyweightCapableTreeStructure<LighterASTNode>.rightParenthesis(node: LighterASTNode): LighterASTNode? = private fun FlyweightCapableTreeStructure<LighterASTNode>.rightParenthesis(node: LighterASTNode): LighterASTNode? =
@@ -133,6 +133,11 @@ object SourceElementPositioningStrategies {
PositioningStrategies.REFERENCE_BY_QUALIFIED PositioningStrategies.REFERENCE_BY_QUALIFIED
) )
val REFERENCED_NAME_BY_QUALIFIED = SourceElementPositioningStrategy(
LightTreePositioningStrategies.REFERENCED_NAME_BY_QUALIFIED,
PositioningStrategies.REFERENCED_NAME_BY_QUALIFIED
)
val WHEN_EXPRESSION = SourceElementPositioningStrategy( val WHEN_EXPRESSION = SourceElementPositioningStrategy(
LightTreePositioningStrategies.WHEN_EXPRESSION, LightTreePositioningStrategies.WHEN_EXPRESSION,
PositioningStrategies.WHEN_EXPRESSION PositioningStrategies.WHEN_EXPRESSION
@@ -23,7 +23,10 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private fun ConeDiagnostic.toFirDiagnostic(source: FirSourceElement): FirDiagnostic<FirSourceElement>? = when (this) { private fun ConeDiagnostic.toFirDiagnostic(
source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?
): FirDiagnostic<FirSourceElement>? = when (this) {
is ConeUnresolvedReferenceError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.name?.asString() ?: "<No name>") is ConeUnresolvedReferenceError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.name?.asString() ?: "<No name>")
is ConeUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.classId.asString()) is ConeUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.classId.asString())
is ConeUnresolvedNameError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.name.asString()) is ConeUnresolvedNameError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.name.asString())
@@ -36,14 +39,14 @@ private fun ConeDiagnostic.toFirDiagnostic(source: FirSourceElement): FirDiagnos
} }
is ConeOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.on(source, this.candidates) is ConeOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.on(source, this.candidates)
is ConeVariableExpectedError -> FirErrors.VARIABLE_EXPECTED.on(source) is ConeVariableExpectedError -> FirErrors.VARIABLE_EXPECTED.on(source)
is ConeTypeMismatchError -> FirErrors.TYPE_MISMATCH.on(source, this.expectedType, this.actualType) is ConeTypeMismatchError -> FirErrors.TYPE_MISMATCH.on(qualifiedAccessSource ?: source, this.expectedType, this.actualType)
is ConeUnexpectedTypeArgumentsError -> FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.on(this.source.safeAs() ?: source) is ConeUnexpectedTypeArgumentsError -> FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.on(this.source.safeAs() ?: source)
is ConeIllegalAnnotationError -> FirErrors.NOT_AN_ANNOTATION_CLASS.on(source, this.name.asString()) is ConeIllegalAnnotationError -> FirErrors.NOT_AN_ANNOTATION_CLASS.on(source, this.name.asString())
is ConeWrongNumberOfTypeArgumentsError -> is ConeWrongNumberOfTypeArgumentsError ->
FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(source, this.desiredCount, this.type) FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(qualifiedAccessSource ?: source, this.desiredCount, this.type)
is ConeSimpleDiagnostic -> when { is ConeSimpleDiagnostic -> when {
source.kind is FirFakeSourceElementKind -> null source.kind is FirFakeSourceElementKind -> null
else -> this.getFactory().on(source) else -> this.getFactory().on(qualifiedAccessSource ?: source)
} }
is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(source, this.target) is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(source, this.target)
is ConeStubDiagnostic -> null is ConeStubDiagnostic -> null
@@ -54,11 +57,14 @@ private fun ConeDiagnostic.toFirDiagnostic(source: FirSourceElement): FirDiagnos
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
} }
fun ConeDiagnostic.toFirDiagnostics(source: FirSourceElement): List<FirDiagnostic<FirSourceElement>> { fun ConeDiagnostic.toFirDiagnostics(
source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?
): List<FirDiagnostic<FirSourceElement>> {
if (this is ConeInapplicableCandidateError) { if (this is ConeInapplicableCandidateError) {
return mapInapplicableCandidateError(this, source) return mapInapplicableCandidateError(this, source, qualifiedAccessSource)
} }
return listOfNotNull(toFirDiagnostic(source)) return listOfNotNull(toFirDiagnostic(source, qualifiedAccessSource))
} }
private fun ConeKotlinType.isEffectivelyNotNull(): Boolean { private fun ConeKotlinType.isEffectivelyNotNull(): Boolean {
@@ -75,13 +81,14 @@ private fun mapUnsafeCallError(
diagnostic: ConeInapplicableCandidateError, diagnostic: ConeInapplicableCandidateError,
source: FirSourceElement, source: FirSourceElement,
rootCause: ResolutionDiagnostic?, rootCause: ResolutionDiagnostic?,
qualifiedAccessSource: FirSourceElement?,
): FirDiagnostic<*>? { ): FirDiagnostic<*>? {
if (rootCause is InapplicableWrongReceiver && if (rootCause !is InapplicableWrongReceiver) return null
rootCause.actualType?.isNullable == true && val actualType = rootCause.actualType ?: return null
(rootCause.expectedType == null || rootCause.expectedType!!.isEffectivelyNotNull()) val expectedType = rootCause.expectedType
) { if (actualType.isNullable && (expectedType == null || expectedType.isEffectivelyNotNull())) {
if (diagnostic.candidate.callInfo.isImplicitInvoke) { if (diagnostic.candidate.callInfo.isImplicitInvoke) {
return FirErrors.UNSAFE_IMPLICIT_INVOKE_CALL.on(source, rootCause.actualType!!) return FirErrors.UNSAFE_IMPLICIT_INVOKE_CALL.on(source, actualType)
} }
val candidateFunction = diagnostic.candidate.symbol.fir as? FirSimpleFunction val candidateFunction = diagnostic.candidate.symbol.fir as? FirSimpleFunction
@@ -100,8 +107,11 @@ private fun mapUnsafeCallError(
return FirErrors.UNSAFE_OPERATOR_CALL.on(source, left, candidateFunctionName!!.asString(), right) return FirErrors.UNSAFE_OPERATOR_CALL.on(source, left, candidateFunctionName!!.asString(), right)
} }
} }
return if (source.kind == FirFakeSourceElementKind.ArrayAccessNameReference) {
return FirErrors.UNSAFE_CALL.on(source, rootCause.actualType!!) FirErrors.UNSAFE_CALL.on(source, actualType)
} else {
FirErrors.UNSAFE_CALL.on(qualifiedAccessSource ?: source, actualType)
}
} }
return null return null
} }
@@ -109,15 +119,18 @@ private fun mapUnsafeCallError(
private fun mapInapplicableCandidateError( private fun mapInapplicableCandidateError(
diagnostic: ConeInapplicableCandidateError, diagnostic: ConeInapplicableCandidateError,
source: FirSourceElement, source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?,
): List<FirDiagnostic<FirSourceElement>> { ): List<FirDiagnostic<FirSourceElement>> {
// TODO: Need to distinguish SMARTCAST_IMPOSSIBLE // TODO: Need to distinguish SMARTCAST_IMPOSSIBLE
return diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause -> return diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
mapUnsafeCallError(diagnostic, source, rootCause)?.let { return@mapNotNull it } mapUnsafeCallError(diagnostic, source, rootCause, qualifiedAccessSource)?.let { return@mapNotNull it }
when (rootCause) { when (rootCause) {
is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(rootCause.argument.source ?: source) is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(
rootCause.argument.source ?: qualifiedAccessSource
)
is NamedArgumentNotAllowed -> FirErrors.NAMED_ARGUMENTS_NOT_ALLOWED.on( is NamedArgumentNotAllowed -> FirErrors.NAMED_ARGUMENTS_NOT_ALLOWED.on(
rootCause.argument.source ?: source, rootCause.argument.source ?: qualifiedAccessSource,
rootCause.forbiddenNamedArgumentsTarget rootCause.forbiddenNamedArgumentsTarget
) )
else -> null else -> null
@@ -45,7 +45,7 @@ internal fun KtWhenCondition.toFirWhenCondition(
firRange.generateContainsOperation( firRange.generateContainsOperation(
firSubjectExpression, firSubjectExpression,
isNegated, isNegated,
rangeExpression?.toFirPsiSourceElement(FirFakeSourceElementKind.WhenCondition), this@toFirWhenCondition.toFirPsiSourceElement(FirFakeSourceElementKind.WhenCondition),
operationReference.toFirPsiSourceElement() operationReference.toFirPsiSourceElement()
) )
} }
@@ -729,7 +729,8 @@ object PositioningStrategies {
return mark(element.operationTokenNode.psi) return mark(element.operationTokenNode.psi)
} }
} }
return super.mark(element) // Fallback to mark the callee reference.
return REFERENCE_BY_QUALIFIED.mark(element)
} }
} }
@@ -744,21 +745,45 @@ object PositioningStrategies {
} }
} }
val REFERENCE_BY_QUALIFIED: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() { val REFERENCE_BY_QUALIFIED: PositioningStrategy<PsiElement> = FindReferencePositioningStrategy(false)
val REFERENCED_NAME_BY_QUALIFIED: PositioningStrategy<PsiElement> = FindReferencePositioningStrategy(true)
/**
* @param locateReferencedName whether to remove any nested parentheses while locating the reference element. This is useful for
* diagnostics on super and unresolved references. For example, with the following, only the part inside the parentheses should be
* highlighted.
*
* ```
* fun foo() {
* (super)()
* ^^^^^
* (random123)()
* ^^^^^^^^^
* }
* ```
*/
class FindReferencePositioningStrategy(val locateReferencedName: Boolean) : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> { override fun mark(element: PsiElement): List<TextRange> {
when (element) { var result: PsiElement = when (element) {
is KtQualifiedExpression -> { is KtQualifiedExpression -> {
when (val selectorExpression = element.selectorExpression) { when (val selectorExpression = element.selectorExpression) {
is KtCallExpression -> return mark(selectorExpression.calleeExpression ?: selectorExpression) is KtCallExpression -> selectorExpression.calleeExpression ?: selectorExpression
is KtReferenceExpression -> return mark(selectorExpression) is KtReferenceExpression -> selectorExpression
else -> element
} }
} }
is KtCallableReferenceExpression -> return mark(element.callableReference) is KtCallableReferenceExpression -> element.callableReference
is KtCallExpression -> return mark(element.calleeExpression ?: element) is KtCallExpression -> element.calleeExpression ?: element
is KtConstructorDelegationCall -> return mark(element.calleeExpression ?: element) is KtConstructorDelegationCall -> element.calleeExpression ?: element
is KtSuperTypeCallEntry -> return mark(element.calleeExpression) is KtSuperTypeCallEntry -> element.calleeExpression
is KtOperationExpression -> element.operationReference
is KtWhenConditionInRange -> element.operationReference
else -> element
} }
return super.mark(element) while (locateReferencedName && result is KtParenthesizedExpression) {
result = result.expression ?: break
}
return super.mark(result)
} }
} }
} }
@@ -9,7 +9,7 @@ fun f(): Unit {
x <!NONE_APPLICABLE!>+<!> 1 x <!NONE_APPLICABLE!>+<!> 1
x <!NONE_APPLICABLE!>plus<!> 1 x <!NONE_APPLICABLE!>plus<!> 1
x <!NONE_APPLICABLE!><<!> 1 x <!NONE_APPLICABLE!><<!> 1
<!UNRESOLVED_REFERENCE!>x += 1<!> x <!UNRESOLVED_REFERENCE!>+=<!> 1
x == 1 x == 1
x != 1 x != 1
@@ -28,20 +28,20 @@ fun <T> fooT2() : (t : T) -> T {
fun main(args : Array<String>) { fun main(args : Array<String>) {
args.foo()() args.foo()()
args.<!INAPPLICABLE_CANDIDATE!>foo1<!>()() <!INAPPLICABLE_CANDIDATE!>args.foo1()<!>()
<!UNRESOLVED_REFERENCE!>a<!>.<!INAPPLICABLE_CANDIDATE!>foo1<!>()() <!INAPPLICABLE_CANDIDATE!><!UNRESOLVED_REFERENCE!>a<!>.foo1()<!>()
<!UNRESOLVED_REFERENCE!>a<!>.foo1()(<!UNRESOLVED_REFERENCE!>a<!>) <!UNRESOLVED_REFERENCE!>a<!>.foo1()(<!UNRESOLVED_REFERENCE!>a<!>)
args.foo1()(1) args.foo1()(1)
args.<!INAPPLICABLE_CANDIDATE!>foo1<!>()("1") <!INAPPLICABLE_CANDIDATE!>args.foo1()<!>("1")
<!UNRESOLVED_REFERENCE!>a<!>.<!INAPPLICABLE_CANDIDATE!>foo1<!>()("1") <!INAPPLICABLE_CANDIDATE!><!UNRESOLVED_REFERENCE!>a<!>.foo1()<!>("1")
<!UNRESOLVED_REFERENCE!>a<!>.foo1()(<!UNRESOLVED_REFERENCE!>a<!>) <!UNRESOLVED_REFERENCE!>a<!>.foo1()(<!UNRESOLVED_REFERENCE!>a<!>)
foo2()({}) foo2()({})
<!INAPPLICABLE_CANDIDATE!>foo2<!>(){} <!INAPPLICABLE_CANDIDATE!>foo2<!>(){}
(foo2()){} (foo2()){}
<!INAPPLICABLE_CANDIDATE!>(foo2())<!>{x -> } <!INAPPLICABLE_CANDIDATE!>(foo2())<!>{x -> }
<!INAPPLICABLE_CANDIDATE!>foo2<!>()({x -> }) <!INAPPLICABLE_CANDIDATE!>foo2()<!>({x -> })
val a = fooT1(1)() val a = fooT1(1)()
checkSubtype<Int>(a) checkSubtype<Int>(a)
@@ -58,13 +58,13 @@ fun main(args : Array<String>) {
fun f() : Int.() -> Unit = {} fun f() : Int.() -> Unit = {}
fun main1() { fun main1() {
1.<!UNRESOLVED_REFERENCE!>(fun Int.() = 1)<!>(); 1.(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)();
{1}(); {1}();
(fun (x : Int) = x)(1) (fun (x : Int) = x)(1)
1.<!UNRESOLVED_REFERENCE!>(fun Int.(x : Int) = x)<!>(1); 1.(<!UNRESOLVED_REFERENCE!>fun Int.(x : Int) = x<!>)(1);
l@{1}() l@{1}()
1.<!UNRESOLVED_REFERENCE!>((fun Int.() = 1))<!>() 1.((<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>))()
1.<!UNRESOLVED_REFERENCE!>(f())<!>() 1.(<!UNRESOLVED_REFERENCE!>f()<!>)()
1.<!UNRESOLVED_REFERENCE!>if(true){f()}else{f()}<!>() 1.<!UNRESOLVED_REFERENCE!>if(true){f()}else{f()}<!>()
1.<!UNRESOLVED_REFERENCE!>if(true)(fun Int.() {})else{f()}<!>() 1.<!UNRESOLVED_REFERENCE!>if(true)(fun Int.() {})else{f()}<!>()
@@ -78,10 +78,10 @@ fun main1() {
fun test() { fun test() {
<!INAPPLICABLE_CANDIDATE!>{x : Int -> 1}<!>(); <!INAPPLICABLE_CANDIDATE!>{x : Int -> 1}<!>();
<!INAPPLICABLE_CANDIDATE!>(fun Int.() = 1)<!>() <!INAPPLICABLE_CANDIDATE!>(fun Int.() = 1)<!>()
"sd".<!UNRESOLVED_REFERENCE!>(fun Int.() = 1)<!>() "sd".(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)()
val i : Int? = null val i : Int? = null
i.<!UNRESOLVED_REFERENCE!>(fun Int.() = 1)<!>(); i.(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)();
<!INAPPLICABLE_CANDIDATE!>{}<!><Int>() <!INAPPLICABLE_CANDIDATE!>{}<!><Int>()
1?.<!UNRESOLVED_REFERENCE!>(fun Int.() = 1)<!>() 1?.(<!UNRESOLVED_REFERENCE!>fun Int.() = 1<!>)()
1.<!UNRESOLVED_REFERENCE!>{}<!>() 1.<!UNRESOLVED_REFERENCE!>{}<!>()
} }
@@ -14,5 +14,5 @@ operator fun A.set(i: IFoo, newValue: Int) {}
fun withVararg(vararg xs: Int) = 42 fun withVararg(vararg xs: Int) = 42
fun test1() { fun test1() {
<!UNRESOLVED_REFERENCE!>A[::withVararg] += 1<!> A[::withVararg] <!UNRESOLVED_REFERENCE!>+=<!> 1
} }
@@ -45,7 +45,7 @@ fun test() {
x4 checkType { _<Function1<Int, Unit>>() } x4 checkType { _<Function1<Int, Unit>>() }
{ y: Int -> fun named14(): Int {return 1} } { y: Int -> fun named14(): Int {return 1} }
val b = <!UNRESOLVED_REFERENCE!>(<!EXPRESSION_REQUIRED!>fun named15(): Boolean { return true }<!>)<!>() val b = (<!EXPRESSION_REQUIRED, UNRESOLVED_REFERENCE!>fun named15(): Boolean { return true }<!>)()
baz(<!EXPRESSION_REQUIRED!>fun named16(){}<!>) baz(<!EXPRESSION_REQUIRED!>fun named16(){}<!>)
} }
@@ -18,4 +18,4 @@ enum class TestMultipleConstructors(val x: String = "", val y: Int = 0) {
enum class TestVarargs(val x: Int) { enum class TestVarargs(val x: Int) {
TEST; TEST;
constructor(vararg xs: Any) : this(xs.size) constructor(vararg xs: Any) : this(xs.size)
} }
@@ -35,7 +35,7 @@ fun test4() {
// should be an error on receiver, shouldn't be thrown away // should be an error on receiver, shouldn't be thrown away
fun test5() { fun test5() {
1.<!UNRESOLVED_REFERENCE!>(fun String.()=1)<!>() 1.(<!UNRESOLVED_REFERENCE!>fun String.()=1<!>)()
} }
fun <R: Any> R?.sure() : R = this!! fun <R: Any> R?.sure() : R = this!!
@@ -1,9 +1,9 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
val receiver = { Int.(<!SYNTAX!><!>) <!SYNTAX!>-><!> } val receiver = { Int.(<!SYNTAX!><!>) <!SYNTAX!>-><!> }
val receiverWithParameter = { <!UNRESOLVED_REFERENCE!>Int.(a)<!> <!SYNTAX!>-><!> } val receiverWithParameter = { Int.(<!UNRESOLVED_REFERENCE!>a<!>) <!SYNTAX!>-><!> }
val receiverAndReturnType = { Int.(<!SYNTAX!><!>)<!SYNTAX!>: Int -> 5<!> } val receiverAndReturnType = { Int.(<!SYNTAX!><!>)<!SYNTAX!>: Int -> 5<!> }
val receiverAndReturnTypeWithParameter = { Int.(a<!SYNTAX!><!SYNTAX!><!>: Int): Int -> 5<!> } val receiverAndReturnTypeWithParameter = { Int.(<!UNRESOLVED_REFERENCE!>a<!><!SYNTAX!><!SYNTAX!><!>: Int): Int -> 5<!> }
val returnType = { (<!SYNTAX!><!>): Int -> 5 } val returnType = { (<!SYNTAX!><!>): Int -> 5 }
val returnTypeWithParameter = { <!COMPONENT_FUNCTION_MISSING!>(b: Int): Int<!> -> 5 } val returnTypeWithParameter = { <!COMPONENT_FUNCTION_MISSING!>(b: Int): Int<!> -> 5 }
@@ -8,4 +8,3 @@ fun <F, G> getMap() : Map<F, G> = throw Exception()
fun bar123() { fun bar123() {
foo(<!INAPPLICABLE_CANDIDATE!>getMap<!>( foo(<!INAPPLICABLE_CANDIDATE!>getMap<!>(
<!SYNTAX!><!>} <!SYNTAX!><!>}
@@ -2,4 +2,4 @@ package l
fun test(a: Int) { fun test(a: Int) {
if (a <!UNRESOLVED_REFERENCE!>in<!><!SYNTAX!><!> ) {} //a is not unresolved if (a <!UNRESOLVED_REFERENCE!>in<!><!SYNTAX!><!> ) {} //a is not unresolved
} }
@@ -2,4 +2,4 @@ package l
fun test(a: Int) { fun test(a: Int) {
if (a in<!SYNTAX!><!> ) {} //a is not unresolved if (a in<!SYNTAX!><!> ) {} //a is not unresolved
} }
@@ -8,4 +8,4 @@ class Test {
open class StaticClass open class StaticClass
open inner class InnerClass open inner class InnerClass
} }
+1 -1
View File
@@ -14,4 +14,4 @@ class Outer {
inner class Inner2 : FinalNested() inner class Inner2 : FinalNested()
inner class Inner3 : OpenInner() inner class Inner3 : OpenInner()
inner class Inner4 : FinalInner() inner class Inner4 : FinalInner()
} }
+2 -2
View File
@@ -3,5 +3,5 @@ fun Any.foo1() : (i : Int) -> Unit {
} }
fun test(a : Any) { fun test(a : Any) {
a.<!INAPPLICABLE_CANDIDATE!>foo1<!>()() <!INAPPLICABLE_CANDIDATE!>a.foo1()<!>()
} }
@@ -30,7 +30,7 @@ fun test() {
oldAndNew %= 1 oldAndNew %= 1
val onlyOld = OnlyOld() val onlyOld = OnlyOld()
<!INAPPLICABLE_CANDIDATE!>onlyOld %= 1<!> onlyOld <!INAPPLICABLE_CANDIDATE!>%=<!> 1
val onlyNew = OnlyNew() val onlyNew = OnlyNew()
onlyNew %= 1 onlyNew %= 1
@@ -40,4 +40,4 @@ fun test() {
var modAndRemAssign = ModAndRemAssign() var modAndRemAssign = ModAndRemAssign()
modAndRemAssign %= 1 modAndRemAssign %= 1
} }
@@ -27,6 +27,6 @@ fun foo() {
r.remAssign(1) r.remAssign(1)
val m = JustMod val m = JustMod
<!UNRESOLVED_REFERENCE!>m %= 1<!> m <!UNRESOLVED_REFERENCE!>%=<!> 1
m.modAssign(1) m.modAssign(1)
} }
@@ -25,7 +25,7 @@ fun test() {
<!INAPPLICABLE_CANDIDATE!>takeInt<!>(ModAndRem % 1) <!INAPPLICABLE_CANDIDATE!>takeInt<!>(ModAndRem % 1)
val c = ModAssignAndRemAssign val c = ModAssignAndRemAssign
<!INAPPLICABLE_CANDIDATE!>c %= ""<!> c <!INAPPLICABLE_CANDIDATE!>%=<!> ""
var c1 = RemAndModAssign var c1 = RemAndModAssign
c1 %= 1 c1 %= 1
@@ -36,4 +36,4 @@ fun test() {
c2 %= 1 c2 %= 1
} }
fun takeInt(x: Int) {} fun takeInt(x: Int) {}
@@ -20,7 +20,7 @@ object MismatchingTypes {
fun testMismatchingTypes() { fun testMismatchingTypes() {
++<!INAPPLICABLE_CANDIDATE!>MismatchingTypes[0]<!> ++<!INAPPLICABLE_CANDIDATE!>MismatchingTypes[0]<!>
<!INAPPLICABLE_CANDIDATE!>MismatchingTypes[0]<!>++ <!INAPPLICABLE_CANDIDATE!>MismatchingTypes[0]<!>++
<!UNRESOLVED_REFERENCE!>MismatchingTypes[0] += 1<!> MismatchingTypes[0] <!UNRESOLVED_REFERENCE!>+=<!> 1
} }
object MismatchingArities1 { object MismatchingArities1 {
@@ -36,10 +36,9 @@ object MismatchingArities2 {
fun testMismatchingArities() { fun testMismatchingArities() {
++<!INAPPLICABLE_CANDIDATE!>MismatchingArities1[0]<!> ++<!INAPPLICABLE_CANDIDATE!>MismatchingArities1[0]<!>
<!INAPPLICABLE_CANDIDATE!>MismatchingArities1[0]<!>++ <!INAPPLICABLE_CANDIDATE!>MismatchingArities1[0]<!>++
<!UNRESOLVED_REFERENCE!>MismatchingArities1[0] += 1<!> MismatchingArities1[0] <!UNRESOLVED_REFERENCE!>+=<!> 1
++<!INAPPLICABLE_CANDIDATE!>MismatchingArities2[0]<!> ++<!INAPPLICABLE_CANDIDATE!>MismatchingArities2[0]<!>
<!INAPPLICABLE_CANDIDATE!>MismatchingArities2[0]<!>++ <!INAPPLICABLE_CANDIDATE!>MismatchingArities2[0]<!>++
<!UNRESOLVED_REFERENCE!><!INAPPLICABLE_CANDIDATE!>MismatchingArities2[0]<!> += 1<!> <!INAPPLICABLE_CANDIDATE!>MismatchingArities2[0]<!> <!UNRESOLVED_REFERENCE!>+=<!> 1
} }
@@ -1,12 +1,12 @@
fun intBinEq() { fun intBinEq() {
var x = 0 var x = 0
<!UNRESOLVED_REFERENCE!>x += 'a'<!> x <!UNRESOLVED_REFERENCE!>+=<!> 'a'
x += 1.toByte() x += 1.toByte()
x += 1.toShort() x += 1.toShort()
x += 1L x += 1L
x += 1f x += 1f
x += 1.0 x += 1.0
<!UNRESOLVED_REFERENCE!>x *= 'a'<!> x <!UNRESOLVED_REFERENCE!>*=<!> 'a'
x *= 1.toByte() x *= 1.toByte()
x *= 1.toShort() x *= 1.toShort()
x *= 1L x *= 1L
@@ -16,14 +16,14 @@ fun intBinEq() {
fun shortBinEq() { fun shortBinEq() {
var x = 0.toShort() var x = 0.toShort()
<!UNRESOLVED_REFERENCE!>x += 'a'<!> x <!UNRESOLVED_REFERENCE!>+=<!> 'a'
x += 1.toByte() x += 1.toByte()
x += 1.toShort() x += 1.toShort()
x += 1L x += 1L
x += 1f x += 1f
x += 1.0 x += 1.0
<!UNRESOLVED_REFERENCE!>x *= 'a'<!> x <!UNRESOLVED_REFERENCE!>*=<!> 'a'
x *= 1.toByte() x *= 1.toByte()
x *= 1.toShort() x *= 1.toShort()
x *= 1L x *= 1L
@@ -16,5 +16,5 @@ fun test() {
val c = C() val c = C()
c[0] += "" c[0] += ""
var c1 = C1() var c1 = C1()
<!AMBIGUITY!>c1[0] += ""<!> c1[0] <!AMBIGUITY!>+=<!> ""
} }
@@ -19,5 +19,5 @@ public class J {
// FILE: k.kt // FILE: k.kt
var A by J.staticNN var A by J.staticNN
var B by J<!UNSAFE_CALL, UNSAFE_CALL!>.<!>staticN var B by J<!UNSAFE_CALL!>.<!>staticN
var C by J.staticJ var C by J.staticJ
@@ -1,2 +1,2 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
val unwrapped = <!UNRESOLVED_REFERENCE!>some<!><!UNRESOLVED_REFERENCE!><<!><!UNRESOLVED_REFERENCE, UNRESOLVED_REFERENCE, UNRESOLVED_REFERENCE!>sdf<!>()()<out Any>::<!UNRESOLVED_REFERENCE!>unwrap<!> val unwrapped = <!UNRESOLVED_REFERENCE!>some<!><!UNRESOLVED_REFERENCE!><<!><!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!>sdf<!>()<!>()<!><out Any>::<!UNRESOLVED_REFERENCE!>unwrap<!>
@@ -7,5 +7,5 @@ fun test1(f: String.() -> Unit) {
fun test2(f: (Int) -> Int) { fun test2(f: (Int) -> Int) {
1.<!UNRESOLVED_REFERENCE!>f<!>(2) 1.<!UNRESOLVED_REFERENCE!>f<!>(2)
2.<!UNRESOLVED_REFERENCE!>(f)<!>(2) 2.(<!UNRESOLVED_REFERENCE!>f<!>)(2)
} }
@@ -1,17 +1,17 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
fun test1() { fun test1() {
1. <!UNRESOLVED_REFERENCE!>(fun String.(i: Int) = i )<!>(1) 1. (<!UNRESOLVED_REFERENCE!>fun String.(i: Int) = i<!> )(1)
1.<!UNRESOLVED_REFERENCE!>(label@ fun String.(i: Int) = i )<!>(1) 1.(<!UNRESOLVED_REFERENCE!>label@ fun String.(i: Int) = i<!> )(1)
} }
fun test2(f: String.(Int) -> Unit) { fun test2(f: String.(Int) -> Unit) {
11.<!INAPPLICABLE_CANDIDATE!>(f)<!>(1) 11.(<!INAPPLICABLE_CANDIDATE!>f<!>)(1)
11.<!INAPPLICABLE_CANDIDATE!>(f)<!>() 11.(<!INAPPLICABLE_CANDIDATE!>f<!>)()
} }
fun test3() { fun test3() {
fun foo(): String.(Int) -> Unit = {} fun foo(): String.(Int) -> Unit = {}
1.<!UNRESOLVED_REFERENCE!>(foo())<!>(1) 1.(<!UNRESOLVED_REFERENCE!>foo()<!>)(1)
} }
@@ -2,11 +2,11 @@ class A(val x: (String.() -> Unit)?)
fun test(a: A) { fun test(a: A) {
if (a.x != null) { if (a.x != null) {
"".<!UNRESOLVED_REFERENCE!>(a.x)<!>() "".(<!UNRESOLVED_REFERENCE!>a.x<!>)()
a.x("") // todo a.x("") // todo
(a.x)("") (a.x)("")
} }
"".<!UNRESOLVED_REFERENCE!>(a.x)<!>() "".(<!UNRESOLVED_REFERENCE!>a.x<!>)()
a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>("") a.<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>("")
<!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>("") <!UNSAFE_IMPLICIT_INVOKE_CALL!>(a.x)<!>("")
@@ -8,7 +8,7 @@ class B
val A.foo: B.() -> Unit get() = {} val A.foo: B.() -> Unit get() = {}
fun test(a: A, b: B) { fun test(a: A, b: B) {
b.<!UNRESOLVED_REFERENCE!>(a.foo)<!>() b.(<!UNRESOLVED_REFERENCE!>a.foo<!>)()
(a.foo)(b) (a.foo)(b)
a.foo(b) a.foo(b)
@@ -25,7 +25,7 @@ fun test(a: A, b: B) {
with(b) { with(b) {
a.<!INAPPLICABLE_CANDIDATE!>foo<!>() a.<!INAPPLICABLE_CANDIDATE!>foo<!>()
a.<!INAPPLICABLE_CANDIDATE!>(foo)<!>() a.(<!INAPPLICABLE_CANDIDATE!>foo<!>)()
(a.foo)() (a.foo)()
@@ -50,7 +50,7 @@ class A {
class B class B
fun test(a: A, b: B) { fun test(a: A, b: B) {
b.<!UNRESOLVED_REFERENCE!>(a.foo)<!>() b.(<!UNRESOLVED_REFERENCE!>a.foo<!>)()
(a.foo)(b) (a.foo)(b)
a.foo(b) a.foo(b)
@@ -59,7 +59,7 @@ fun test(a: A, b: B) {
b.(foo)() b.(foo)()
<!UNRESOLVED_REFERENCE!>(b.<!UNRESOLVED_REFERENCE!>foo<!>)<!>() (<!UNRESOLVED_REFERENCE!>b.<!UNRESOLVED_REFERENCE!>foo<!><!>)()
foo(b) foo(b)
(foo)(b) (foo)(b)
@@ -67,7 +67,7 @@ fun test(a: A, b: B) {
with(b) { with(b) {
a.<!INAPPLICABLE_CANDIDATE!>foo<!>() a.<!INAPPLICABLE_CANDIDATE!>foo<!>()
a.<!INAPPLICABLE_CANDIDATE!>(foo)<!>() a.(<!INAPPLICABLE_CANDIDATE!>foo<!>)()
(a.foo)() (a.foo)()
@@ -8,7 +8,7 @@ fun foo(): String {
var t: String? = "y" var t: String? = "y"
if (t == null) t = "x" if (t == null) t = "x"
var x: Int? = null var x: Int? = null
if (x == null) <!UNRESOLVED_REFERENCE!>x += null<!> if (x == null) x <!UNRESOLVED_REFERENCE!>+=<!> null
return t + s return t + s
} }
@@ -7,4 +7,4 @@ interface Foo2 : <!INTERFACE_WITH_SUPERCLASS!>bar<!>, Foo {
} }
open class Foo1() : bar(), bar, Foo, <!UNRESOLVED_REFERENCE!>Foo<!>() {} open class Foo1() : bar(), bar, Foo, <!UNRESOLVED_REFERENCE!>Foo<!>() {}
open class Foo12 : bar(), bar {} open class Foo12 : bar(), bar {}
@@ -26,7 +26,7 @@ fun <T> id(x: T) = x
fun foo2() = { fun foo2() = {
var x = A() var x = A()
<!INAPPLICABLE_CANDIDATE!>x += { "" }<!> x <!INAPPLICABLE_CANDIDATE!>+=<!> { "" }
var y = A() var y = A()
y += 1 y += 1
} }
@@ -63,7 +63,7 @@ fun case3() {
*/ */
fun case4(marker : Marker?) { fun case4(marker : Marker?) {
marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.foo; typeCall: function")!>foo(y=1)<!> marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.foo; typeCall: function")!>foo(y=1)<!>
marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.invoke; typeCall: variable&invoke")!>foo(x=1)<!><!UNSAFE_CALL!>><!><!SYNTAX!><!> marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.invoke; typeCall: variable&invoke")!>foo(x=1)<!>><!SYNTAX!><!>
} }
@@ -63,7 +63,7 @@ fun case3() {
*/ */
fun case4(marker: Marker?) { fun case4(marker: Marker?) {
marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.foo; typeCall: function")!>foo(y = { 1 })<!> marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.foo; typeCall: function")!>foo(y = { 1 })<!>
marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.invoke; typeCall: variable&invoke")!>foo(x = { 1 })<!><!UNSAFE_CALL!>><!><!SYNTAX!><!> marker?.<!DEBUG_INFO_CALL("fqName: libPackage.Marker.invoke; typeCall: variable&invoke")!>foo(x = { 1 })<!>><!SYNTAX!><!>
} }
@@ -26,9 +26,9 @@ import LibPackCase1.b.*
fun case1 (){ fun case1 (){
var b: B? = B() var b: B? = B()
<!AMBIGUITY, DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b += { C() }<!> <!DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b <!AMBIGUITY!>+=<!> { C() }<!>
<!AMBIGUITY, DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b += {1}<!> <!DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b <!AMBIGUITY!>+=<!> {1}<!>
} }
class B { class B {
@@ -63,9 +63,9 @@ import LibPackCase2.b.*
fun case2 (){ fun case2 (){
var b: B = B() var b: B = B()
<!AMBIGUITY, DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b += { C() }<!> <!DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b <!AMBIGUITY!>+=<!> { C() }<!>
<!AMBIGUITY, DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b += {1}<!> <!DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b <!AMBIGUITY!>+=<!> {1}<!>
} }
class B { class B {
@@ -10,9 +10,9 @@ import LibPackCase1.a.*
import LibPackCase1.b.* import LibPackCase1.b.*
fun case1 (){ fun case1 (){
var b: B? = null var b: B? = null
<!AMBIGUITY, DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b += { C() }<!> <!DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b <!AMBIGUITY!>+=<!> { C() }<!>
<!AMBIGUITY, DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b += {1}<!> <!DEBUG_INFO_CALL("fqName: fqName is unknown; typeCall: unresolved")!>b <!AMBIGUITY!>+=<!> {1}<!>
} }
class B { class B {
@@ -15,9 +15,9 @@ import LibPackCase1.b.*
fun case1 (){ fun case1 (){
var b: B? = B() var b: B? = B()
<!AMBIGUITY!>b += { C() }<!> b <!AMBIGUITY!>+=<!> { C() }
<!AMBIGUITY!>b += {1}<!> b <!AMBIGUITY!>+=<!> {1}
} }
class B { class B {
@@ -15,9 +15,9 @@ import LibPackCase1.b.plusAssign
fun case1 (){ fun case1 (){
var b: B? = B() var b: B? = B()
<!AMBIGUITY!>b += { C() }<!> b <!AMBIGUITY!>+=<!> { C() }
<!AMBIGUITY!>b += {1}<!> b <!AMBIGUITY!>+=<!> {1}
} }
class B { class B {
@@ -15,8 +15,8 @@ import LibPackCase1.b.plusAssign
fun case1 (){ fun case1 (){
var b: B = B() var b: B = B()
<!AMBIGUITY!>b +={ C() }<!> b <!AMBIGUITY!>+=<!>{ C() }
<!AMBIGUITY!>b +={ 1 }<!> b <!AMBIGUITY!>+=<!>{ 1 }
} }
class B class B
@@ -48,8 +48,8 @@ import LibPackCase2.b.plusAssign
fun case2 (){ fun case2 (){
var b: B = B() var b: B = B()
<!AMBIGUITY!>b +={ C() }<!> b <!AMBIGUITY!>+=<!>{ C() }
<!AMBIGUITY!>b +={ 1 }<!> b <!AMBIGUITY!>+=<!>{ 1 }
b.<!AMBIGUITY!>plusAssign<!>{ C() } b.<!AMBIGUITY!>plusAssign<!>{ C() }
} }
@@ -17,7 +17,7 @@ operator fun I2.invoke(): String = TODO()
fun case1(a: A) { fun case1(a: A) {
a.<!AMBIGUITY!>invoke<!>() a.<!AMBIGUITY!>invoke<!>()
<!AMBIGUITY!>a<!>() <!AMBIGUITY!>a<!>()
<!AMBIGUITY!>A<!>()() <!AMBIGUITY!>A()<!>()
} }
// FILE: TestCase2.kt // FILE: TestCase2.kt
@@ -17,7 +17,7 @@ operator fun <R>I2.invoke(): String = TODO()
fun case1(a: A) { fun case1(a: A) {
a.<!AMBIGUITY!>invoke<!>() a.<!AMBIGUITY!>invoke<!>()
<!AMBIGUITY!>a<!>() <!AMBIGUITY!>a<!>()
<!AMBIGUITY!>A<!>()() <!AMBIGUITY!>A()<!>()
} }
// FILE: TestCase2.kt // FILE: TestCase2.kt
@@ -6,7 +6,7 @@
fun case_1() { fun case_1() {
var x: Int? = 11 var x: Int? = 11
x!! x!!
try {x = null;} finally { <!UNRESOLVED_REFERENCE!><!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!> += 10<!>; } try {x = null;} finally { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!> <!UNRESOLVED_REFERENCE!>+=<!> 10; }
} }
// TESTCASE NUMBER: 2 // TESTCASE NUMBER: 2
@@ -30,8 +30,8 @@ internal interface KtFirAnalysisSessionComponent {
fun FirPsiDiagnostic<*>.asKtDiagnostic(): KtDiagnosticWithPsi<*> = fun FirPsiDiagnostic<*>.asKtDiagnostic(): KtDiagnosticWithPsi<*> =
KT_DIAGNOSTIC_CONVERTER.convert(analysisSession, this as FirDiagnostic<*>) KT_DIAGNOSTIC_CONVERTER.convert(analysisSession, this as FirDiagnostic<*>)
fun ConeDiagnostic.asKtDiagnostic(source: FirSourceElement): KtDiagnosticWithPsi<*>? { fun ConeDiagnostic.asKtDiagnostic(source: FirSourceElement, qualifiedAccessSource: FirSourceElement?): KtDiagnosticWithPsi<*>? {
val firDiagnostic = toFirDiagnostics(source).firstOrNull() ?: return null val firDiagnostic = toFirDiagnostics(source, qualifiedAccessSource).firstOrNull() ?: return null
check(firDiagnostic is FirPsiDiagnostic<*>) check(firDiagnostic is FirPsiDiagnostic<*>)
return firDiagnostic.asKtDiagnostic() return firDiagnostic.asKtDiagnostic()
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.components package org.jetbrains.kotlin.idea.frontend.api.fir.components
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression
@@ -85,7 +86,7 @@ internal class KtFirCallResolver(
} }
is FirErrorNamedReference -> KtVariableWithInvokeFunctionCall( is FirErrorNamedReference -> KtVariableWithInvokeFunctionCall(
variableLikeSymbol, variableLikeSymbol,
callReference.createErrorCallTarget() callReference.createErrorCallTarget(source)
) )
else -> error("Unexpected call reference ${callReference::class.simpleName}") else -> error("Unexpected call reference ${callReference::class.simpleName}")
} }
@@ -93,8 +94,8 @@ internal class KtFirCallResolver(
private fun FirFunctionCall.asSimpleFunctionCall(): KtFunctionCall? { private fun FirFunctionCall.asSimpleFunctionCall(): KtFunctionCall? {
val target = when (val calleeReference = calleeReference) { val target = when (val calleeReference = calleeReference) {
is FirResolvedNamedReference -> calleeReference.getKtFunctionOrConstructorSymbol()?.let { KtSuccessCallTarget(it) } is FirResolvedNamedReference -> calleeReference.getKtFunctionOrConstructorSymbol()?.let { KtSuccessCallTarget(it) }
is FirErrorNamedReference -> calleeReference.createErrorCallTarget() is FirErrorNamedReference -> calleeReference.createErrorCallTarget(source)
is FirErrorReferenceWithCandidate -> calleeReference.createErrorCallTarget() is FirErrorReferenceWithCandidate -> calleeReference.createErrorCallTarget(source)
is FirSimpleNamedReference -> is FirSimpleNamedReference ->
error( error(
""" """
@@ -109,16 +110,16 @@ internal class KtFirCallResolver(
return KtFunctionCall(target) return KtFunctionCall(target)
} }
private fun FirErrorNamedReference.createErrorCallTarget(): KtErrorCallTarget = private fun FirErrorNamedReference.createErrorCallTarget(qualifiedAccessSource: FirSourceElement?): KtErrorCallTarget =
KtErrorCallTarget( KtErrorCallTarget(
getCandidateSymbols().mapNotNull { it.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol }, getCandidateSymbols().mapNotNull { it.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol },
source?.let { diagnostic.asKtDiagnostic(it) } ?: KtNonBoundToPsiErrorDiagnostic(factoryName = null, diagnostic.reason, token) source?.let { diagnostic.asKtDiagnostic(it, qualifiedAccessSource) } ?: KtNonBoundToPsiErrorDiagnostic(factoryName = null, diagnostic.reason, token)
) )
private fun FirErrorReferenceWithCandidate.createErrorCallTarget(): KtErrorCallTarget = private fun FirErrorReferenceWithCandidate.createErrorCallTarget(qualifiedAccessSource: FirSourceElement?): KtErrorCallTarget =
KtErrorCallTarget( KtErrorCallTarget(
getCandidateSymbols().mapNotNull { it.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol }, getCandidateSymbols().mapNotNull { it.fir.buildSymbol(firSymbolBuilder) as? KtFunctionLikeSymbol },
source?.let { diagnostic.asKtDiagnostic(it) } ?: KtNonBoundToPsiErrorDiagnostic(factoryName = null, diagnostic.reason, token) source?.let { diagnostic.asKtDiagnostic(it,qualifiedAccessSource) } ?: KtNonBoundToPsiErrorDiagnostic(factoryName = null, diagnostic.reason, token)
) )
private fun FirResolvedNamedReference.getKtFunctionOrConstructorSymbol(): KtFunctionLikeSymbol? = private fun FirResolvedNamedReference.getKtFunctionOrConstructorSymbol(): KtFunctionLikeSymbol? =