[Analysis API FIR] fix call resolution of non-resolvable elements

This commit is contained in:
Ilya Kirillov
2022-10-01 10:36:02 +02:00
parent ccdb6fc599
commit ab3f916f3f
13 changed files with 124 additions and 24 deletions
@@ -145,7 +145,7 @@ internal class KtFe10CallResolver(
get() = analysisSession.token get() = analysisSession.token
override fun resolveCall(psi: KtElement): KtCallInfo? = with(analysisContext.analyze(psi, AnalysisMode.PARTIAL_WITH_DIAGNOSTICS)) { override fun resolveCall(psi: KtElement): KtCallInfo? = with(analysisContext.analyze(psi, AnalysisMode.PARTIAL_WITH_DIAGNOSTICS)) {
if (psi.isNotResolvable()) return null if (!canBeResolvedAsCall(psi)) return null
val parentBinaryExpression = psi.parentOfType<KtBinaryExpression>() val parentBinaryExpression = psi.parentOfType<KtBinaryExpression>()
val lhs = KtPsiUtil.deparenthesize(parentBinaryExpression?.left) val lhs = KtPsiUtil.deparenthesize(parentBinaryExpression?.left)
@@ -177,7 +177,7 @@ internal class KtFe10CallResolver(
override fun collectCallCandidates(psi: KtElement): List<KtCallCandidateInfo> = override fun collectCallCandidates(psi: KtElement): List<KtCallCandidateInfo> =
with(analysisContext.analyze(psi, AnalysisMode.PARTIAL_WITH_DIAGNOSTICS)) { with(analysisContext.analyze(psi, AnalysisMode.PARTIAL_WITH_DIAGNOSTICS)) {
if (psi.isNotResolvable()) return emptyList() if (!canBeResolvedAsCall(psi)) return emptyList()
val resolvedKtCallInfo = resolveCall(psi) val resolvedKtCallInfo = resolveCall(psi)
val bestCandidateDescriptors = val bestCandidateDescriptors =
@@ -854,6 +854,34 @@ public class Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated extends A
} }
} }
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls")
@TestDataPath("$PROJECT_ROOT")
public class NonCalls {
@Test
public void testAllFilesPresentInNonCalls() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("ifExpression.kt")
public void testIfExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/ifExpression.kt");
}
@Test
@TestMetadata("literalExpression.kt")
public void testLiteralExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/literalExpression.kt");
}
@Test
@TestMetadata("whenExpression.kt")
public void testWhenExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/whenExpression.kt");
}
}
@Nested @Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/withTestCompilerPluginEnabled") @TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/withTestCompilerPluginEnabled")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -86,18 +86,20 @@ internal class KtFirCallResolver(
result result
} }
override fun resolveCall(psi: KtElement): KtCallInfo? = wrapError(psi) { override fun resolveCall(psi: KtElement): KtCallInfo? {
val ktCallInfos = getCallInfo(psi) { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall -> return wrapError(psi) {
listOfNotNull( val ktCallInfos = getCallInfo(psi) { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall ->
toKtCallInfo( listOfNotNull(
psiToResolve, toKtCallInfo(
resolveCalleeExpressionOfFunctionCall, psiToResolve,
resolveFragmentOfCall resolveCalleeExpressionOfFunctionCall,
resolveFragmentOfCall
)
) )
) }
check(ktCallInfos.size <= 1) { "Should only return 1 KtCallInfo" }
ktCallInfos.singleOrNull()
} }
check(ktCallInfos.size <= 1) { "Should only return 1 KtCallInfo" }
ktCallInfos.singleOrNull()
} }
private inline fun <T> getCallInfo( private inline fun <T> getCallInfo(
@@ -108,7 +110,7 @@ internal class KtFirCallResolver(
resolveFragmentOfCall: Boolean resolveFragmentOfCall: Boolean
) -> List<T> ) -> List<T>
): List<T> { ): List<T> {
if (psi.isNotResolvable()) return emptyList() if (!canBeResolvedAsCall(psi)) return emptyList()
val containingCallExpressionForCalleeExpression = psi.getContainingCallExpressionForCalleeExpression() val containingCallExpressionForCalleeExpression = psi.getContainingCallExpressionForCalleeExpression()
val containingBinaryExpressionForLhs = psi.getContainingBinaryExpressionForIncompleteLhs() val containingBinaryExpressionForLhs = psi.getContainingBinaryExpressionForIncompleteLhs()
@@ -854,6 +854,34 @@ public class FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated extends Ab
} }
} }
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls")
@TestDataPath("$PROJECT_ROOT")
public class NonCalls {
@Test
public void testAllFilesPresentInNonCalls() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("ifExpression.kt")
public void testIfExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/ifExpression.kt");
}
@Test
@TestMetadata("literalExpression.kt")
public void testLiteralExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/literalExpression.kt");
}
@Test
@TestMetadata("whenExpression.kt")
public void testWhenExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/whenExpression.kt");
}
}
@Nested @Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/withTestCompilerPluginEnabled") @TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/withTestCompilerPluginEnabled")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -45,17 +45,19 @@ abstract class AbstractKtCallResolver : KtCallResolver() {
return KtPsiUtil.deparenthesize(safeQualifiedExpression.receiverExpression) == KtPsiUtil.deparenthesize(this) return KtPsiUtil.deparenthesize(safeQualifiedExpression.receiverExpression) == KtPsiUtil.deparenthesize(this)
} }
protected fun canBeResolvedAsCall(ktElement: KtElement): Boolean = when (ktElement) {
is KtBinaryExpression -> ktElement.operationToken !in nonCallBinaryOperator
is KtOperationReferenceExpression -> ktElement.operationSignTokenType !in nonCallBinaryOperator
is KtCallElement -> true
is KtConstructorCalleeExpression -> true
is KtDotQualifiedExpression -> true
is KtNameReferenceExpression -> true
is KtOperationExpression -> true
is KtArrayAccessExpression -> true
else -> false
}
protected companion object { protected companion object {
private val nonCallBinaryOperator: Set<KtSingleValueToken> = setOf(KtTokens.ELVIS, KtTokens.EQEQEQ, KtTokens.EXCLEQEQEQ) private val nonCallBinaryOperator: Set<KtSingleValueToken> = setOf(KtTokens.ELVIS, KtTokens.EQEQEQ, KtTokens.EXCLEQEQEQ)
/**
* We don't want to resolve the operators from the [AbstractKtCallResolver.nonCallBinaryOperator] list, because it's either
* not possible or not desirable.
*/
fun KtElement.isNotResolvable(): Boolean {
return this is KtBinaryExpression && operationToken in nonCallBinaryOperator ||
this is KtOperationReferenceExpression && operationSignTokenType in nonCallBinaryOperator
}
} }
} }
@@ -853,4 +853,32 @@ public class FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated ext
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/assignments/insidePlusAssignTarget.kt"); runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/assignments/insidePlusAssignTarget.kt");
} }
} }
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls")
@TestDataPath("$PROJECT_ROOT")
public class NonCalls {
@Test
public void testAllFilesPresentInNonCalls() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls"), Pattern.compile("^(.+)\\.kt$"), null, true, "withTestCompilerPluginEnabled");
}
@Test
@TestMetadata("ifExpression.kt")
public void testIfExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/ifExpression.kt");
}
@Test
@TestMetadata("literalExpression.kt")
public void testLiteralExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/literalExpression.kt");
}
@Test
@TestMetadata("whenExpression.kt")
public void testWhenExpression() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls/whenExpression.kt");
}
}
} }
@@ -0,0 +1,3 @@
fun main() {
val x = <expr>if (true) 1 else 2</expr>
}
@@ -0,0 +1,3 @@
fun main() {
val x = <expr>1</expr>
}
@@ -0,0 +1,3 @@
fun main() {
val x = <expr>when (true) { else -> 2 }</expr>
}
@@ -1,3 +1,3 @@
KtErrorCallInfo: KtErrorCallInfo:
candidateCalls = [] candidateCalls = []
diagnostic = ERROR<OTHER_ERROR: Unknown (other) error> diagnostic = ERROR<null: Erroneous delegated constructor call>