[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
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 lhs = KtPsiUtil.deparenthesize(parentBinaryExpression?.left)
@@ -177,7 +177,7 @@ internal class KtFe10CallResolver(
override fun collectCallCandidates(psi: KtElement): List<KtCallCandidateInfo> =
with(analysisContext.analyze(psi, AnalysisMode.PARTIAL_WITH_DIAGNOSTICS)) {
if (psi.isNotResolvable()) return emptyList()
if (!canBeResolvedAsCall(psi)) return emptyList()
val resolvedKtCallInfo = resolveCall(psi)
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
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/withTestCompilerPluginEnabled")
@TestDataPath("$PROJECT_ROOT")
@@ -86,18 +86,20 @@ internal class KtFirCallResolver(
result
}
override fun resolveCall(psi: KtElement): KtCallInfo? = wrapError(psi) {
val ktCallInfos = getCallInfo(psi) { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall ->
listOfNotNull(
toKtCallInfo(
psiToResolve,
resolveCalleeExpressionOfFunctionCall,
resolveFragmentOfCall
override fun resolveCall(psi: KtElement): KtCallInfo? {
return wrapError(psi) {
val ktCallInfos = getCallInfo(psi) { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall ->
listOfNotNull(
toKtCallInfo(
psiToResolve,
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(
@@ -108,7 +110,7 @@ internal class KtFirCallResolver(
resolveFragmentOfCall: Boolean
) -> List<T>
): List<T> {
if (psi.isNotResolvable()) return emptyList()
if (!canBeResolvedAsCall(psi)) return emptyList()
val containingCallExpressionForCalleeExpression = psi.getContainingCallExpressionForCalleeExpression()
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
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/withTestCompilerPluginEnabled")
@TestDataPath("$PROJECT_ROOT")
@@ -45,17 +45,19 @@ abstract class AbstractKtCallResolver : KtCallResolver() {
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 {
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");
}
}
@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:
candidateCalls = []
diagnostic = ERROR<OTHER_ERROR: Unknown (other) error>
diagnostic = ERROR<null: Erroneous delegated constructor call>