[FIR] Introduce special phase for resolve of enums and class literals

This commit is contained in:
Dmitriy Novozhilov
2021-07-28 15:17:17 +03:00
committed by TeamCityServer
parent afb85026c4
commit 71def0666e
12 changed files with 218 additions and 50 deletions
@@ -29,6 +29,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("annotationOnDeclarationWithDifferentArguments.kt")
public void testAnnotationOnDeclarationWithDifferentArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt");
}
@TestMetadata("annotationUsedAsAnnotationArgument.kt")
public void testAnnotationUsedAsAnnotationArgument() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/annotationUsedAsAnnotationArgument.kt");
@@ -0,0 +1,54 @@
FILE: annotationOnDeclarationWithDifferentArguments.kt
public final enum class SomeEnum : R|kotlin/Enum<SomeEnum>| {
private constructor(): R|SomeEnum| {
super<R|kotlin/Enum<SomeEnum>|>()
}
public final static enum entry A: R|SomeEnum|
public final static enum entry B: R|SomeEnum|
public final static fun values(): R|kotlin/Array<SomeEnum>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|SomeEnum| {
}
}
public final annotation class MyAnnotation : R|kotlin/Annotation| {
public constructor(intValue: R|kotlin/Int|, stringValue: R|kotlin/String|, enumValue: R|SomeEnum|, kClasses: R|kotlin/Array<out kotlin/reflect/KClass<*>>|, annotation: R|MyOtherAnnotation|): R|MyAnnotation| {
super<R|kotlin/Any|>()
}
public final val intValue: R|kotlin/Int| = R|<local>/intValue|
public get(): R|kotlin/Int|
public final val stringValue: R|kotlin/String| = R|<local>/stringValue|
public get(): R|kotlin/String|
public final val enumValue: R|SomeEnum| = R|<local>/enumValue|
public get(): R|SomeEnum|
public final val kClasses: R|kotlin/Array<out kotlin/reflect/KClass<*>>| = R|<local>/kClasses|
public get(): R|kotlin/Array<out kotlin/reflect/KClass<*>>|
public final val annotation: R|MyOtherAnnotation| = R|<local>/annotation|
public get(): R|MyOtherAnnotation|
}
public final annotation class MyOtherAnnotation : R|kotlin/Annotation| {
public constructor(intValue: R|kotlin/Int|, stringValue: R|kotlin/String|): R|MyOtherAnnotation| {
super<R|kotlin/Any|>()
}
public final val intValue: R|kotlin/Int| = R|<local>/intValue|
public get(): R|kotlin/Int|
public final val stringValue: R|kotlin/String| = R|<local>/stringValue|
public get(): R|kotlin/String|
}
public final const val constInt: R|kotlin/Int| = Int(10)
public get(): R|kotlin/Int|
public final const val constString: R|kotlin/String| = String()
public get(): R|kotlin/String|
@R|MyAnnotation|(intValue = Int(10), stringValue = R|/constString|, enumValue = Q|SomeEnum|.R|/SomeEnum.A|, kClasses = <implicitArrayOf>(<getClass>(Q|kotlin/String|), <getClass>(R|/constString|)), annotation = R|/MyOtherAnnotation.MyOtherAnnotation|(intValue = R|/constInt|, stringValue = String(hello))) public final fun foo(): R|kotlin/Unit| {
}
@@ -0,0 +1,30 @@
import kotlin.reflect.KClass
enum class SomeEnum {
A, B
}
annotation class MyAnnotation(
val intValue: Int,
val stringValue: String,
val enumValue: SomeEnum,
val kClasses: Array<out KClass<*>>,
val annotation: MyOtherAnnotation
)
annotation class MyOtherAnnotation(val intValue: Int, val stringValue: String)
const val constInt = 10
const val constString = ""
@MyAnnotation(
intValue = 10,
stringValue = constString,
enumValue = SomeEnum.A,
kClasses = [String::class, <!ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL!>constString::class<!>],
annotation = MyOtherAnnotation(
intValue = constInt,
stringValue = "hello"
)
)
fun foo() {}
@@ -26,6 +26,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("annotationOnDeclarationWithDifferentArguments.kt")
public void testAnnotationOnDeclarationWithDifferentArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt");
}
@Test
@TestMetadata("annotationUsedAsAnnotationArgument.kt")
public void testAnnotationUsedAsAnnotationArgument() throws Exception {
@@ -26,6 +26,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("annotationOnDeclarationWithDifferentArguments.kt")
public void testAnnotationOnDeclarationWithDifferentArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt");
}
@Test
@TestMetadata("annotationUsedAsAnnotationArgument.kt")
public void testAnnotationUsedAsAnnotationArgument() throws Exception {
@@ -200,6 +200,20 @@ class FirCallResolver(
}
fun <T : FirQualifiedAccess> resolveVariableAccessAndSelectCandidate(qualifiedAccess: T): FirStatement {
return resolveVariableAccessAndSelectCandidateImpl(qualifiedAccess) { true }
}
fun resolveOnlyEnumOrQualifierAccessAndSelectCandidate(qualifiedAccess: FirQualifiedAccessExpression): FirStatement {
return resolveVariableAccessAndSelectCandidateImpl(qualifiedAccess) accept@{ candidates ->
val symbol = candidates.singleOrNull()?.symbol ?: return@accept false
symbol is FirEnumEntrySymbol || symbol is FirRegularClassSymbol
}
}
private fun <T : FirQualifiedAccess> resolveVariableAccessAndSelectCandidateImpl(
qualifiedAccess: T,
acceptCandidates: (Collection<Candidate>) -> Boolean
): FirStatement {
val callee = qualifiedAccess.calleeReference as? FirSimpleNamedReference ?: return qualifiedAccess
qualifiedResolver.initProcessingQualifiedAccess(callee, qualifiedAccess.typeArguments)
@@ -233,6 +247,8 @@ class FirCallResolver(
}
val reducedCandidates = result.candidates
if (!acceptCandidates(reducedCandidates)) return qualifiedAccess
val nameReference = createResolvedNamedReference(
callee,
callee.name,
@@ -28,7 +28,7 @@ fun FirResolvePhase.createCompilerProcessorByPhase(
SUPER_TYPES -> FirSupertypeResolverProcessor(session, scopeSession)
SEALED_CLASS_INHERITORS -> FirSealedClassInheritorsProcessor(session, scopeSession)
TYPES -> FirTypeResolveProcessor(session, scopeSession)
ARGUMENTS_OF_PLUGIN_ANNOTATIONS -> FirAnnotationArgumentsResolveProcessor(session, scopeSession)
ARGUMENTS_OF_ANNOTATIONS -> FirAnnotationArgumentsResolveProcessor(session, scopeSession)
EXTENSION_STATUS_UPDATE -> FirGlobalExtensionStatusProcessor(session, scopeSession)
STATUS -> FirStatusResolveProcessor(session, scopeSession)
CONTRACTS -> FirContractResolveProcessor(session, scopeSession)
@@ -121,7 +121,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
qualifiedAccessExpression
}
else -> {
val transformedCallee = callResolver.resolveVariableAccessAndSelectCandidate(qualifiedAccessExpression)
val transformedCallee = resolveQualifiedAccessAndSelectCandidate(qualifiedAccessExpression)
// NB: here we can get raw expression because of dropped qualifiers (see transform callee),
// so candidate existence must be checked before calling completion
if (transformedCallee is FirQualifiedAccessExpression && transformedCallee.candidate() != null) {
@@ -147,6 +147,10 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
return result
}
protected open fun resolveQualifiedAccessAndSelectCandidate(qualifiedAccessExpression: FirQualifiedAccessExpression): FirStatement {
return callResolver.resolveVariableAccessAndSelectCandidate(qualifiedAccessExpression)
}
fun transformSuperReceiver(
superReference: FirSuperReference,
superReferenceContainer: FirQualifiedAccessExpression,
@@ -833,6 +837,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
symbol.constructType(emptyArray(), isNullable = false)
}
else -> {
if (!shouldComputeTypeOfGetClassCallWithNotQualifierInLhs(getClassCall)) return transformedGetClassCall
val resultType = lhs.resultType
if (resultType is FirErrorTypeRef) {
resultType.coneType
@@ -850,6 +855,10 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
return transformedGetClassCall
}
protected open fun shouldComputeTypeOfGetClassCallWithNotQualifierInLhs(getClassCall: FirGetClassCall): Boolean {
return true
}
override fun <T> transformConstExpression(
constExpression: FirConstExpression<T>,
data: ResolutionMode,
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.transformers.plugin
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.extensions.registeredPluginAnnotations
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.AdapterForResolveProcessor
@@ -27,15 +25,12 @@ class FirAnnotationArgumentsResolveProcessor(
@AdapterForResolveProcessor
class FirAnnotationArgumentsResolveTransformerAdapter(session: FirSession, scopeSession: ScopeSession) : FirTransformer<Any?>() {
private val transformer = FirAnnotationArgumentsResolveTransformer(session, scopeSession)
private val hasAnnotations = session.registeredPluginAnnotations.annotations.isNotEmpty()
private val predicateBasedProvider = session.predicateBasedProvider
override fun <E : FirElement> transformElement(element: E, data: Any?): E {
return element
}
override fun transformFile(file: FirFile, data: Any?): FirFile {
if (!hasAnnotations || !predicateBasedProvider.fileHasPluginAnnotations(file)) return file
return file.transform(transformer, ResolutionMode.ContextIndependent)
}
}
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.transformers.plugin
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.extensions.AnnotationFqn
import org.jetbrains.kotlin.fir.extensions.registeredPluginAnnotations
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.ScopeSession
@@ -17,8 +15,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveCon
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
class FirAnnotationArgumentsResolveTransformer(
session: FirSession,
@@ -26,15 +22,12 @@ class FirAnnotationArgumentsResolveTransformer(
outerBodyResolveContext: BodyResolveContext? = null
) : FirBodyResolveTransformer(
session,
FirResolvePhase.ARGUMENTS_OF_PLUGIN_ANNOTATIONS,
FirResolvePhase.ARGUMENTS_OF_ANNOTATIONS,
implicitTypeOnly = false,
scopeSession,
outerBodyResolveContext = outerBodyResolveContext
) {
override val expressionsTransformer: FirExpressionsResolveTransformer = FirExpressionsResolveTransformerForSpecificAnnotations(
this,
session.registeredPluginAnnotations.annotations
)
override val expressionsTransformer: FirExpressionsResolveTransformer = FirExpressionsResolveTransformerForSpecificAnnotations(this)
override val declarationsTransformer: FirDeclarationsResolveTransformer = FirDeclarationsResolveTransformerForArgumentAnnotations(this)
}
@@ -42,15 +35,16 @@ class FirAnnotationArgumentsResolveTransformer(
private class FirDeclarationsResolveTransformerForArgumentAnnotations(
transformer: FirBodyResolveTransformer
) : FirDeclarationsResolveTransformer(transformer) {
override fun transformWrappedDelegateExpression(
wrappedDelegateExpression: FirWrappedDelegateExpression,
data: ResolutionMode
): FirStatement {
return wrappedDelegateExpression
}
override fun transformRegularClass(regularClass: FirRegularClass, data: ResolutionMode): FirStatement {
return regularClass.transformAnnotations(this, data).transformDeclarations(this, data)
regularClass.transformAnnotations(this, data)
context.withContainingClass(regularClass) {
regularClass
.transformTypeParameters(transformer, data)
.transformSuperTypeRefs(transformer, data)
.transformDeclarations(transformer, data)
.transformCompanionObject(transformer, data)
}
return regularClass
}
override fun transformAnonymousInitializer(
@@ -64,21 +58,38 @@ private class FirDeclarationsResolveTransformerForArgumentAnnotations(
simpleFunction: FirSimpleFunction,
data: ResolutionMode
): FirSimpleFunction {
return simpleFunction.transformAnnotations(this, data)
simpleFunction
.transformReturnTypeRef(transformer, data)
.transformReceiverTypeRef(transformer, data)
.transformValueParameters(transformer, data)
.transformAnnotations(transformer, data)
return simpleFunction
}
override fun transformConstructor(constructor: FirConstructor, data: ResolutionMode): FirConstructor {
return constructor.transformAnnotations(this, data)
constructor
.transformReturnTypeRef(transformer, data)
.transformReceiverTypeRef(transformer, data)
.transformValueParameters(transformer, data)
.transformAnnotations(transformer, data)
return constructor
}
override fun transformValueParameter(valueParameter: FirValueParameter, data: ResolutionMode): FirStatement {
return valueParameter.transformAnnotations(this, data)
valueParameter
.transformAnnotations(transformer, data)
.transformReturnTypeRef(transformer, data)
return valueParameter
}
override fun transformProperty(property: FirProperty, data: ResolutionMode): FirProperty {
property.transformAnnotations(this, data)
property.transformGetter(this, data)
property.transformSetter(this, data)
property
.transformAnnotations(transformer, data)
.transformReceiverTypeRef(transformer, data)
.transformReturnTypeRef(transformer, data)
.transformGetter(transformer, data)
.transformSetter(transformer, data)
.transformTypeParameters(transformer, data)
return property
}
@@ -86,42 +97,63 @@ private class FirDeclarationsResolveTransformerForArgumentAnnotations(
propertyAccessor: FirPropertyAccessor,
data: ResolutionMode
): FirPropertyAccessor {
propertyAccessor.transformAnnotations(this, data)
propertyAccessor
.transformValueParameters(transformer, data)
.transformReturnTypeRef(transformer, data)
.transformReceiverTypeRef(transformer, data)
.transformReturnTypeRef(transformer, data)
.transformAnnotations(transformer, data)
return propertyAccessor
}
override fun transformDeclarationStatus(declarationStatus: FirDeclarationStatus, data: ResolutionMode): FirDeclarationStatus {
return declarationStatus
}
override fun transformEnumEntry(enumEntry: FirEnumEntry, data: ResolutionMode): FirEnumEntry {
context.forEnumEntry {
enumEntry
.transformAnnotations(transformer, data)
.transformReceiverTypeRef(transformer, data)
.transformReturnTypeRef(transformer, data)
.transformTypeParameters(transformer, data)
}
return enumEntry
}
override fun transformField(field: FirField, data: ResolutionMode): FirField {
return field.transformAnnotations(transformer, data)
}
override fun transformTypeAlias(typeAlias: FirTypeAlias, data: ResolutionMode): FirTypeAlias {
typeAlias.transformAnnotations(transformer, data)
return typeAlias
}
}
private class FirExpressionsResolveTransformerForSpecificAnnotations(
transformer: FirBodyResolveTransformer,
private val annotations: Set<AnnotationFqn>
transformer: FirBodyResolveTransformer
) : FirExpressionsResolveTransformer(transformer) {
private var annotationArgumentsMode: Boolean = false
override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: ResolutionMode): FirStatement {
if (annotationArgumentsMode) {
return resolveAnnotationCall(annotationCall, FirAnnotationResolveStatus.PartiallyResolved)
}
annotationCall.transformAnnotationTypeRef(transformer, data)
val classId = annotationCall.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId
?: return annotationCall
if (classId.asSingleFqName() !in annotations) {
return annotationCall
}
annotationArgumentsMode = true
return resolveAnnotationCall(annotationCall, FirAnnotationResolveStatus.PartiallyResolved).also {
annotationArgumentsMode = false
}
dataFlowAnalyzer.enterAnnotationCall(annotationCall)
annotationCall.transformChildren(transformer, ResolutionMode.ContextDependent)
dataFlowAnalyzer.exitAnnotationCall(annotationCall)
return annotationCall
}
override fun transformExpression(expression: FirExpression, data: ResolutionMode): FirStatement {
return expression
return expression.transformChildren(transformer, data) as FirStatement
}
override fun FirQualifiedAccessExpression.isAcceptableResolvedQualifiedAccess(): Boolean {
return calleeReference !is FirErrorNamedReference
}
override fun resolveQualifiedAccessAndSelectCandidate(qualifiedAccessExpression: FirQualifiedAccessExpression): FirStatement {
return callResolver.resolveOnlyEnumOrQualifierAccessAndSelectCandidate(qualifiedAccessExpression)
}
override fun transformFunctionCall(functionCall: FirFunctionCall, data: ResolutionMode): FirStatement {
return functionCall
}
@@ -192,4 +224,13 @@ private class FirExpressionsResolveTransformerForSpecificAnnotations(
): FirStatement {
return augmentedArraySetCall
}
override fun transformArrayOfCall(arrayOfCall: FirArrayOfCall, data: ResolutionMode): FirStatement {
arrayOfCall.transformChildren(transformer, data)
return arrayOfCall
}
override fun shouldComputeTypeOfGetClassCallWithNotQualifierInLhs(getClassCall: FirGetClassCall): Boolean {
return false
}
}
@@ -13,7 +13,7 @@ enum class FirResolvePhase(val pluginPhase: Boolean = false, val noProcessor: Bo
SUPER_TYPES,
SEALED_CLASS_INHERITORS,
TYPES,
ARGUMENTS_OF_PLUGIN_ANNOTATIONS(pluginPhase = true),
ARGUMENTS_OF_ANNOTATIONS,
EXTENSION_STATUS_UPDATE(pluginPhase = true),
STATUS,
CONTRACTS,
@@ -26,6 +26,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("annotationOnDeclarationWithDifferentArguments.kt")
public void testAnnotationOnDeclarationWithDifferentArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt");
}
@Test
@TestMetadata("annotationUsedAsAnnotationArgument.kt")
public void testAnnotationUsedAsAnnotationArgument() throws Exception {