Change FIR analysis API resolution for super type calls.
The IDE expects the super type calls to resolve to the constructor invoked and not the class. The frontend itself resolves to the class, so an extra layer is needed to adjust that to the constructor.
This commit is contained in:
+57
-5
@@ -12,9 +12,6 @@ import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirPackageSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirSafe
|
||||
import org.jetbrains.kotlin.fir.FirPackageDirective
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildImport
|
||||
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
||||
@@ -23,7 +20,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.references.*
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnmatchedTypeArgumentsError
|
||||
@@ -34,6 +30,13 @@ import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.analysis.api.fir.getCandidateSymbols
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
|
||||
import org.jetbrains.kotlin.analysis.api.fir.buildSymbol
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -172,6 +175,25 @@ internal object FirReferenceResolveHelper {
|
||||
return false
|
||||
}
|
||||
|
||||
internal fun adjustResolutionExpression(expression: KtElement): KtElement {
|
||||
// If we are at a super-type constructor call, adjust the resolution expression so that we
|
||||
// get the constructor instead of the class.
|
||||
//
|
||||
// For the example:
|
||||
//
|
||||
// class A {
|
||||
// constructor()
|
||||
// }
|
||||
// class B: <caret>A()
|
||||
//
|
||||
// We want to resolve to the secondary constructor in A. Therefore, we check that the caret is at a supertype
|
||||
// call entry and if so we resolve the constructor callee expression.
|
||||
val userType = expression.parent as? KtUserType ?: return expression
|
||||
val typeReference = userType.parent as? KtTypeReference ?: return expression
|
||||
val constructorCalleeExpression = typeReference.parent as? KtConstructorCalleeExpression ?: return expression
|
||||
return if (constructorCalleeExpression.parent is KtSuperTypeCallEntry) constructorCalleeExpression else expression
|
||||
}
|
||||
|
||||
|
||||
internal fun resolveSimpleNameReference(
|
||||
ref: KtFirSimpleNameReference,
|
||||
@@ -180,7 +202,8 @@ internal object FirReferenceResolveHelper {
|
||||
val expression = ref.expression
|
||||
if (expression.isSyntheticOperatorReference()) return emptyList()
|
||||
val symbolBuilder = analysisSession.firSymbolBuilder
|
||||
val fir = expression.getOrBuildFir(analysisSession.firResolveState)
|
||||
val adjustedResolutionExpression = adjustResolutionExpression(expression)
|
||||
val fir = adjustedResolutionExpression.getOrBuildFir(analysisSession.firResolveState)
|
||||
val session = analysisSession.firResolveState.rootModuleSession
|
||||
return when (fir) {
|
||||
is FirResolvedTypeRef -> getSymbolsForResolvedTypeRef(fir, expression, session, symbolBuilder)
|
||||
@@ -198,12 +221,41 @@ internal object FirReferenceResolveHelper {
|
||||
is FirErrorNamedReference -> getSymbolsByErrorNamedReference(fir, symbolBuilder)
|
||||
is FirVariableAssignment -> getSymbolsByVariableAssignment(fir, session, symbolBuilder)
|
||||
is FirResolvedNamedReference -> getSymbolByResolvedNameReference(fir, expression, analysisSession, session, symbolBuilder)
|
||||
is FirDelegatedConstructorCall ->
|
||||
getSymbolByDelegatedConstructorCall(expression, adjustedResolutionExpression, fir, session, symbolBuilder)
|
||||
is FirResolvable -> getSymbolsByResolvable(fir, expression, session, symbolBuilder)
|
||||
is FirNamedArgumentExpression -> getSymbolsByNameArgumentExpression(expression, analysisSession, symbolBuilder)
|
||||
else -> handleUnknownFirElement(expression, analysisSession, session, symbolBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSymbolByDelegatedConstructorCall(
|
||||
expression: KtSimpleNameExpression,
|
||||
adjustedResolutionExpression: KtElement,
|
||||
fir: FirDelegatedConstructorCall,
|
||||
session: FirSession,
|
||||
symbolBuilder: KtSymbolByFirBuilder
|
||||
): Collection<KtSymbol> {
|
||||
if (expression != adjustedResolutionExpression) {
|
||||
// Type alias detection.
|
||||
//
|
||||
// If we adjusted resolution to get a constructor instead of a class, we need to undo that
|
||||
// if the class is defined as a type alias. We can detect that situation when the constructed type
|
||||
// is different from the return type of the constructor.
|
||||
//
|
||||
// TODO: This seems a little indirect. Is there a better way to do this? For FE1.0 there is
|
||||
// a special `TypeAliasConstructorDescriptor` for this case. For FIR there is
|
||||
// FirConstructor.originalConstructorIfTypeAlias but that doesn't seem to help here as it
|
||||
// is null for the constructors we get.
|
||||
val constructedType = fir.constructedTypeRef.coneType
|
||||
val constructorReturnType = (fir.calleeReference.resolvedSymbol as? FirConstructorSymbol)?.resolvedReturnTypeRef?.type
|
||||
if (constructedType.classId != constructorReturnType?.classId) {
|
||||
return getSymbolsForResolvedTypeRef(fir.constructedTypeRef as FirResolvedTypeRef, expression, session, symbolBuilder)
|
||||
}
|
||||
}
|
||||
return getSymbolsByResolvable(fir, expression, session, symbolBuilder)
|
||||
}
|
||||
|
||||
private fun getSymbolsForPackageDirective(
|
||||
expression: KtSimpleNameExpression,
|
||||
symbolBuilder: KtSymbolByFirBuilder
|
||||
|
||||
+12
@@ -394,6 +394,18 @@ public class FirLibrarySourceReferenceResolveTestGenerated extends AbstractRefer
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/SeveralOverrides.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("SuperTypePrimaryConstructor.kt")
|
||||
public void testSuperTypePrimaryConstructor() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/SuperTypePrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("SuperTypeSecondaryConstructor.kt")
|
||||
public void testSuperTypeSecondaryConstructor() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/SuperTypeSecondaryConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("superWithLabel_caretAtLabel.kt")
|
||||
public void testSuperWithLabel_caretAtLabel() throws Exception {
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
// The frontend resolves to the Test class.
|
||||
//
|
||||
// The analysis API adjusts the resolution to be the constructor as that is what the IDE expects.
|
||||
//
|
||||
// In this particular case, there is no PSI element for the constructor. Therefore PSI
|
||||
// resolution `KtReference.resolve()` returns the KtClass for Test whereas symbol resolution
|
||||
// `KtReference.resolveToSymbol` returns the KtConstructorSymbol for the Test constructor.
|
||||
//
|
||||
// This test is testing symbol resolution, so the resolution result is
|
||||
// the constructor in Test.
|
||||
|
||||
open class Test
|
||||
|
||||
class SomeTest : <caret>Test()
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) open class Test
|
||||
0: (in Test) constructor()
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
class A(i: Int)
|
||||
|
||||
class B: <caret>A(42)
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in A) constructor(i: kotlin.Int)
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
constructor()
|
||||
}
|
||||
|
||||
class B: <caret>A()
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in A) constructor()
|
||||
Reference in New Issue
Block a user