[Analysis API FIR] fix call resolution on error elements

This commit is contained in:
Ilya Kirillov
2022-10-01 10:53:59 +02:00
parent 2d7218b542
commit 81c52e6886
7 changed files with 95 additions and 17 deletions
@@ -124,6 +124,10 @@ internal class KtFe10CallResolver(
Errors.UNRESOLVED_REFERENCE,
)
private val syntaxErrors = setOf(
Errors.ASSIGNMENT_IN_EXPRESSION_CONTEXT,
)
val diagnosticWithResolvedCallsAtPosition1 = setOf(
Errors.OVERLOAD_RESOLUTION_AMBIGUITY,
Errors.NONE_APPLICABLE,
@@ -640,6 +644,7 @@ internal class KtFe10CallResolver(
diagnostics: Diagnostics = context.diagnostics
) = diagnostics.firstOrNull { diagnostic ->
if (diagnostic.severity != Severity.ERROR) return@firstOrNull false
if (diagnostic.factory in syntaxErrors) return@firstOrNull true
val isResolutionError = diagnostic.factory in resolutionFailureErrors
val isCallArgError = diagnostic.factory in callArgErrors
val reportedPsi = diagnostic.psiElement
@@ -854,6 +854,22 @@ public class Fe10IdeNormalAnalysisSourceModuleResolveCallTestGenerated extends A
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode")
@TestDataPath("$PROJECT_ROOT")
public class InvalidCode {
@Test
public void testAllFilesPresentInInvalidCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("getterAssignment.kt")
public void testGetterAssignment() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode/getterAssignment.kt");
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls")
@TestDataPath("$PROJECT_ROOT")
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.calls.*
import org.jetbrains.kotlin.analysis.api.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.analysis.api.diagnostics.KtNonBoundToPsiErrorDiagnostic
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.getCandidateSymbols
@@ -71,6 +72,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.analysis.utils.errors.buildErrorWithAttachment
import org.jetbrains.kotlin.analysis.utils.errors.shouldIjPlatformExceptionBeRethrown
import org.jetbrains.kotlin.analysis.utils.errors.withPsiEntry
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
internal class KtFirCallResolver(
override val analysisSession: KtFirAnalysisSession,
@@ -88,15 +90,21 @@ internal class KtFirCallResolver(
override fun resolveCall(psi: KtElement): KtCallInfo? {
return wrapError(psi) {
val ktCallInfos = getCallInfo(psi) { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall ->
listOfNotNull(
toKtCallInfo(
psiToResolve,
resolveCalleeExpressionOfFunctionCall,
resolveFragmentOfCall
val ktCallInfos = getCallInfo(
psi,
getErrorCallInfo = { psiToResolve ->
listOf(KtErrorCallInfo(emptyList(), createKtDiagnostic(psiToResolve), token))
},
getCallInfo = { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall ->
listOfNotNull(
toKtCallInfo(
psiToResolve,
resolveCalleeExpressionOfFunctionCall,
resolveFragmentOfCall
)
)
)
}
}
)
check(ktCallInfos.size <= 1) { "Should only return 1 KtCallInfo" }
ktCallInfos.singleOrNull()
}
@@ -104,6 +112,7 @@ internal class KtFirCallResolver(
private inline fun <T> getCallInfo(
psi: KtElement,
getErrorCallInfo: FirDiagnosticHolder.(psiToResolve: KtElement) -> List<T>,
getCallInfo: FirElement.(
psiToResolve: KtElement,
resolveCalleeExpressionOfFunctionCall: Boolean,
@@ -121,6 +130,9 @@ internal class KtFirCallResolver(
?: containingUnaryExpressionForIncOrDec
?: psi
val fir = psiToResolve.getOrBuildFir(analysisSession.firResolveSession) ?: return emptyList()
if (fir is FirDiagnosticHolder) {
return fir.getErrorCallInfo(psiToResolve)
}
return fir.getCallInfo(
psiToResolve,
psiToResolve == containingCallExpressionForCalleeExpression,
@@ -163,8 +175,7 @@ internal class KtFirCallResolver(
}
is FirErrorNamedReference -> {
val diagnostic = calleeReference.diagnostic
val ktDiagnostic = (source?.let { diagnostic.asKtDiagnostic(it, psi.toKtPsiSourceElement()) }
?: KtNonBoundToPsiErrorDiagnostic(factoryName = null, diagnostic.reason, token))
val ktDiagnostic = calleeReference.createKtDiagnostic(psi)
if (diagnostic is ConeHiddenCandidateError)
return KtErrorCallInfo(emptyList(), ktDiagnostic, token)
@@ -793,13 +804,17 @@ internal class KtFirCallResolver(
}
override fun collectCallCandidates(psi: KtElement): List<KtCallCandidateInfo> = wrapError(psi) {
getCallInfo(psi) { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall ->
collectCallCandidates(
psiToResolve,
resolveCalleeExpressionOfFunctionCall,
resolveFragmentOfCall
)
}
getCallInfo(
psi,
getErrorCallInfo = { emptyList() },
getCallInfo = { psiToResolve, resolveCalleeExpressionOfFunctionCall, resolveFragmentOfCall ->
collectCallCandidates(
psiToResolve,
resolveCalleeExpressionOfFunctionCall,
resolveFragmentOfCall
)
}
)
}
// TODO: Refactor common code with FirElement.toKtCallInfo() when other FirResolvables are handled
@@ -1185,4 +1200,9 @@ internal class KtFirCallResolver(
}
}
private fun FirDiagnosticHolder.createKtDiagnostic(psi: KtElement?): KtDiagnostic {
return (source?.let { diagnostic.asKtDiagnostic(it, psi?.toKtPsiSourceElement()) }
?: KtNonBoundToPsiErrorDiagnostic(factoryName = null, diagnostic.reason, token))
}
}
@@ -854,6 +854,22 @@ public class FirIdeNormalAnalysisSourceModuleResolveCallTestGenerated extends Ab
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode")
@TestDataPath("$PROJECT_ROOT")
public class InvalidCode {
@Test
public void testAllFilesPresentInInvalidCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("getterAssignment.kt")
public void testGetterAssignment() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode/getterAssignment.kt");
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls")
@TestDataPath("$PROJECT_ROOT")
@@ -854,6 +854,22 @@ public class FirStandaloneNormalAnalysisSourceModuleResolveCallTestGenerated ext
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode")
@TestDataPath("$PROJECT_ROOT")
public class InvalidCode {
@Test
public void testAllFilesPresentInInvalidCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode"), Pattern.compile("^(.+)\\.kt$"), null, true, "withTestCompilerPluginEnabled");
}
@Test
@TestMetadata("getterAssignment.kt")
public void testGetterAssignment() throws Exception {
runTest("analysis/analysis-api/testData/components/callResolver/resolveCall/invalidCode/getterAssignment.kt");
}
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/callResolver/resolveCall/nonCalls")
@TestDataPath("$PROJECT_ROOT")
@@ -0,0 +1,2 @@
val commonSettings: Int =
<expr>get()</expr> = 1
@@ -0,0 +1,3 @@
KtErrorCallInfo:
candidateCalls = []
diagnostic = ERROR<ASSIGNMENT_IN_EXPRESSION_CONTEXT: Assignments are not expressions, and only expressions are allowed in this context>