diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/CompilerConeAttributes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/CompilerConeAttributes.kt index 51b77c8182b..d8bda6c5a4d 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/CompilerConeAttributes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/CompilerConeAttributes.kt @@ -89,6 +89,11 @@ object CompilerConeAttributes { UnsafeVariance.ANNOTATION_CLASS_ID to UnsafeVariance ) + val classIdByCompilerAttribute: Map, ClassId> = compilerAttributeByClassId.entries.associateBy( + keySelector = { it.value }, + valueTransform = { it.key } + ) + val compilerAttributeByFqName: Map> = compilerAttributeByClassId.mapKeys { it.key.asSingleFqName() } } diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeAttributes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeAttributes.kt index 657eba11336..8e613445f7f 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeAttributes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeAttributes.kt @@ -25,7 +25,7 @@ abstract class ConeAttribute> : AnnotationMarker { * * This function must be symmetrical: a.add(b) == b.add(a) */ - abstract fun add(other: @UnsafeVariance T?): T + abstract fun add(other: @UnsafeVariance T?): T? abstract fun isSubtypeOf(other: @UnsafeVariance T?): Boolean abstract override fun toString(): String diff --git a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt index a838220f52e..cd2e42e4da7 100644 --- a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt +++ b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt @@ -609,7 +609,7 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) { private fun ProtoBuf.Type.toTypeRef(context: FirDeserializationContext): FirTypeRef { return buildResolvedTypeRef { annotations += context.annotationDeserializer.loadTypeAnnotations(this@toTypeRef, context.nameResolver) - val attributes = annotations.computeTypeAttributes() + val attributes = annotations.computeTypeAttributes(context.session) type = context.typeDeserializer.type(this@toTypeRef, attributes) } } diff --git a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirTypeDeserializer.kt b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirTypeDeserializer.kt index 706602c0ff6..346673b8f89 100644 --- a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirTypeDeserializer.kt +++ b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirTypeDeserializer.kt @@ -109,7 +109,7 @@ class FirTypeDeserializer( fun type(proto: ProtoBuf.Type): ConeKotlinType { val annotations = annotationDeserializer.loadTypeAnnotations(proto, nameResolver) - val attributes = annotations.computeTypeAttributes() + val attributes = annotations.computeTypeAttributes(moduleData.session) return type(proto, attributes) } diff --git a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt index 1dbe3aa0d3e..0f009be68c2 100644 --- a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt +++ b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirElementSerializer.kt @@ -23,6 +23,8 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyAnnotationArgumentMapping +import org.jetbrains.kotlin.fir.extensions.extensionService +import org.jetbrains.kotlin.fir.extensions.typeAttributeExtensions import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.varargElementType import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType @@ -34,9 +36,11 @@ import org.jetbrains.kotlin.fir.serialization.constant.EnumValue import org.jetbrains.kotlin.fir.serialization.constant.IntValue import org.jetbrains.kotlin.fir.serialization.constant.StringValue import org.jetbrains.kotlin.fir.serialization.constant.toConstantValue +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.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.Flags @@ -606,9 +610,41 @@ class FirElementSerializer private constructor( fun typeId(type: ConeKotlinType): Int = typeTable[typeProto(type)] private fun typeProto(typeRef: FirTypeRef, toSuper: Boolean = false): ProtoBuf.Type.Builder { - return typeProto(typeRef.coneType, toSuper, correspondingTypeRef = typeRef).also { - for (annotation in typeRef.annotations) { - extension.serializeTypeAnnotation(annotation, it) + val coneType = typeRef.coneType + return typeProto(coneType, toSuper, correspondingTypeRef = typeRef).also { typeProto -> + for (annotation in coneType.attributes.customAnnotations) { + extension.serializeTypeAnnotation(annotation, typeProto) + } + + val compilerAttributes = mutableListOf>() + val extensionAttributes = mutableListOf>() + for (attribute in coneType.attributes) { + when (attribute) { + is CustomAnnotationTypeAttribute -> continue + in CompilerConeAttributes.classIdByCompilerAttribute -> compilerAttributes += attribute + else -> extensionAttributes += attribute + } + } + + for (attribute in compilerAttributes) { + val annotation = buildAnnotation { + annotationTypeRef = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + ConeClassLikeLookupTagImpl(CompilerConeAttributes.classIdByCompilerAttribute.getValue(attribute)), + emptyArray(), + isNullable = false + ) + } + argumentMapping = FirEmptyAnnotationArgumentMapping + } + extension.serializeTypeAnnotation(annotation, typeProto) + } + + for (attributeExtension in session.extensionService.typeAttributeExtensions) { + for (attribute in extensionAttributes) { + val annotation = attributeExtension.convertAttributeToAnnotation(attribute) ?: continue + extension.serializeTypeAnnotation(annotation, typeProto) + } } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt index 78786e6880d..2be0e11d75f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt @@ -15,7 +15,8 @@ abstract class FirExtensionRegistrar { FirStatusTransformerExtension::class, FirDeclarationGenerationExtension::class, AbstractFirAdditionalCheckersExtension::class, - FirSupertypeGenerationExtension::class + FirSupertypeGenerationExtension::class, + FirTypeAttributeExtension::class, ) } @@ -44,6 +45,11 @@ abstract class FirExtensionRegistrar { operator fun ((FirSession) -> FirSupertypeGenerationExtension).unaryPlus() { registerExtension(FirSupertypeGenerationExtension::class, FirSupertypeGenerationExtension.Factory { this.invoke(it) }) } + + @JvmName("plusTypeAttributeExtension") + operator fun ((FirSession) -> FirTypeAttributeExtension).unaryPlus() { + registerExtension(FirTypeAttributeExtension::class, FirTypeAttributeExtension.Factory { this.invoke(it) }) + } } @OptIn(PluginServicesInitialization::class) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt index 067798f4eb9..f736b76cb07 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt @@ -243,13 +243,16 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() { } } - return symbol.constructType(allTypeArguments.toTypedArray(), typeRef.isMarkedNullable, typeRef.annotations.computeTypeAttributes()) - .also { - val lookupTag = it.lookupTag - if (lookupTag is ConeClassLikeLookupTagImpl && symbol is FirClassLikeSymbol<*>) { - lookupTag.bindSymbolToLookupTag(session, symbol) - } + return symbol.constructType( + allTypeArguments.toTypedArray(), + typeRef.isMarkedNullable, + typeRef.annotations.computeTypeAttributes(session) + ).also { + val lookupTag = it.lookupTag + if (lookupTag is ConeClassLikeLookupTagImpl && symbol is FirClassLikeSymbol<*>) { + lookupTag.bindSymbolToLookupTag(session, symbol) } + } } @OptIn(SymbolInternals::class) @@ -352,7 +355,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() { } else { StandardClassIds.FunctionN(typeRef.parametersCount) } - val attributes = typeRef.annotations.computeTypeAttributes() + val attributes = typeRef.annotations.computeTypeAttributes(session) val symbol = resolveBuiltInQualified(classId, session) return ConeClassLikeTypeImpl( symbol.toLookupTag().also { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirTypeAttributeExtension.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirTypeAttributeExtension.kt new file mode 100644 index 00000000000..accce46a0c6 --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirTypeAttributeExtension.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.extensions + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.types.ConeAttribute +import kotlin.reflect.KClass + +/** + * Note that when you declare your own attribute you HAVE TO declare accessor to it using following syntax: + * + * class MyAttribute : ConeAttribute() {...} + * + * val ConeAttributes.myAttribute: MyAttribute? by ConeAttributes.attributeAccessor() + */ +abstract class FirTypeAttributeExtension(session: FirSession) : FirExtension(session) { + companion object { + val NAME = FirExtensionPointName("AdditionalTypeAttributeExtension") + } + + final override val name: FirExtensionPointName + get() = NAME + + final override val extensionType: KClass + get() = FirTypeAttributeExtension::class + + abstract fun extractAttributeFromAnnotation(annotation: FirAnnotation): ConeAttribute<*>? + + /** + * Please don't convert attributes which you didn't create + * If [attribute] came from compiler or another plugin just return null + */ + abstract fun convertAttributeToAnnotation(attribute: ConeAttribute<*>): FirAnnotation? + + fun interface Factory : FirExtension.Factory +} + +val FirExtensionService.typeAttributeExtensions: List by FirExtensionService.registeredExtensions() diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt index 0e298b89367..02a0b376f4d 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirTypeUtils.kt @@ -6,10 +6,13 @@ package org.jetbrains.kotlin.fir.types import org.jetbrains.kotlin.builtins.functions.FunctionClassKind +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirConstExpression import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast +import org.jetbrains.kotlin.fir.extensions.extensionService +import org.jetbrains.kotlin.fir.extensions.typeAttributeExtensions import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef import org.jetbrains.kotlin.name.ClassId @@ -109,26 +112,25 @@ fun ConeClassLikeType.toConstKind(): ConstantValueKind<*>? = when (lookupTag.cla else -> null } -fun List.computeTypeAttributes( - additionalProcessor: MutableList>.(ClassId) -> Unit = {} -): ConeAttributes { +fun List.computeTypeAttributes(session: FirSession): ConeAttributes { if (this.isEmpty()) return ConeAttributes.Empty val attributes = mutableListOf>() val customAnnotations = mutableListOf() for (annotation in this) { val type = annotation.annotationTypeRef.coneTypeSafe() ?: continue - when (val classId = type.lookupTag.classId) { + when (type.lookupTag.classId) { CompilerConeAttributes.Exact.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.Exact CompilerConeAttributes.NoInfer.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.NoInfer CompilerConeAttributes.ExtensionFunctionType.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.ExtensionFunctionType CompilerConeAttributes.UnsafeVariance.ANNOTATION_CLASS_ID -> attributes += CompilerConeAttributes.UnsafeVariance else -> { - val annotationAttributes = mutableListOf>() - additionalProcessor.invoke(annotationAttributes, classId) - if (annotationAttributes.isEmpty()) { - customAnnotations += annotation + val attributeFromPlugin = session.extensionService.typeAttributeExtensions.firstNotNullOfOrNull { + it.extractAttributeFromAnnotation(annotation) + } + if (attributeFromPlugin != null) { + attributes += attributeFromPlugin } else { - attributes += annotationAttributes + customAnnotations += annotation } } } diff --git a/plugins/fir/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt b/plugins/fir/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt index 63fb1180963..362c95641f2 100644 --- a/plugins/fir/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt +++ b/plugins/fir/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt @@ -20,6 +20,12 @@ annotation class G annotation class AllPublic(val visibility: Visibility) +@Target(AnnotationTarget.TYPE) +annotation class Positive + +@Target(AnnotationTarget.TYPE) +annotation class Negative + enum class Visibility { Public, Internal, Private, Protected -} \ No newline at end of file +} diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/AllOpenAdditionalCheckers.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/AllOpenAdditionalCheckers.kt index 8d918fe5bc3..d3da070ec83 100644 --- a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/AllOpenAdditionalCheckers.kt +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/AllOpenAdditionalCheckers.kt @@ -8,16 +8,24 @@ package org.jetbrains.kotlin.fir.plugin import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirSimpleFunctionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension import org.jetbrains.kotlin.fir.declarations.FirPluginKey import org.jetbrains.kotlin.fir.plugin.checkers.DummyNameChecker +import org.jetbrains.kotlin.fir.plugin.checkers.SignedNumberCallChecker class AllOpenAdditionalCheckers(session: FirSession) : FirAdditionalCheckersExtension(session) { override val key: FirPluginKey get() = AllOpenPluginKey override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() { - override val simpleFunctionCheckers: Set + override val simpleFunctionCheckers: Set get() = setOf(DummyNameChecker) } + + override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers() { + override val functionCallCheckers: Set + get() = setOf(SignedNumberCallChecker) + } } diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt index 73cc266ad3c..fb30f377f8e 100644 --- a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.plugin import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.plugin.generators.* +import org.jetbrains.kotlin.fir.plugin.types.FirNumberSignAttributeExtension class FirAllOpenComponentRegistrar : FirExtensionRegistrar() { override fun ExtensionRegistrarContext.configurePlugin() { @@ -14,6 +15,7 @@ class FirAllOpenComponentRegistrar : FirExtensionRegistrar() { +::AllOpenVisibilityTransformer +::AllOpenSupertypeGenerator +::AllOpenAdditionalCheckers + +::FirNumberSignAttributeExtension // Declaration generators +::AllOpenTopLevelDeclarationsGenerator diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/checkers/AllOpenErrors.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/checkers/AllOpenErrors.kt index 099c3bd8825..5922a40ab77 100644 --- a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/checkers/AllOpenErrors.kt +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/checkers/AllOpenErrors.kt @@ -7,8 +7,10 @@ package org.jetbrains.kotlin.fir.plugin.checkers import com.intellij.psi.PsiElement import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStrategies +import org.jetbrains.kotlin.fir.analysis.diagnostics.error2 import org.jetbrains.kotlin.fir.analysis.diagnostics.warning1 object AllOpenErrors { val FUNCTION_WITH_DUMMY_NAME by warning1(SourceElementPositioningStrategies.DECLARATION_NAME) + val ILLEGAL_NUMBER_SIGN by error2() } diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/checkers/SignedNumberCallChecker.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/checkers/SignedNumberCallChecker.kt new file mode 100644 index 00000000000..a587f645ede --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/checkers/SignedNumberCallChecker.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin.checkers + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.InternalDiagnosticFactoryMethod +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.argumentMapping +import org.jetbrains.kotlin.fir.plugin.types.ConeNumberSignAttribute +import org.jetbrains.kotlin.fir.plugin.types.numberSign +import org.jetbrains.kotlin.fir.types.coneType + +object SignedNumberCallChecker : FirFunctionCallChecker() { + @OptIn(InternalDiagnosticFactoryMethod::class) + override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) { + val argumentMapping = expression.argumentMapping ?: return + for ((argument, parameter) in argumentMapping.entries) { + val expectedSign = parameter.returnTypeRef.coneType.attributes.numberSign ?: continue + val actualSign = argument.typeRef.coneType.attributes.numberSign + if (expectedSign != actualSign) { + withSuppressedDiagnostics(argument, context) { + reporter.reportOn(argument.source, AllOpenErrors.ILLEGAL_NUMBER_SIGN, expectedSign.asString(), actualSign.asString(), it) + } + } + } + } + + private fun ConeNumberSignAttribute?.asString(): String = when (this?.sign) { + null -> "None" + else -> sign.name + } +} diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/ConeNumberSignAttribute.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/ConeNumberSignAttribute.kt new file mode 100644 index 00000000000..a4d704d8af9 --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/ConeNumberSignAttribute.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin.types + +import org.jetbrains.kotlin.fir.types.ConeAttribute +import org.jetbrains.kotlin.fir.types.ConeAttributes +import kotlin.reflect.KClass + +class ConeNumberSignAttribute private constructor(val sign: Sign) : ConeAttribute() { + companion object { + private val Positive = ConeNumberSignAttribute(Sign.Positive) + private val Negative = ConeNumberSignAttribute(Sign.Negative) + + fun fromSign(sign: Sign?): ConeNumberSignAttribute? { + return when (sign) { + Sign.Positive -> Positive + Sign.Negative -> Negative + null -> null + } + } + } + + enum class Sign { + Positive { + override fun combine(other: Sign?): Sign? = when (other) { + Positive -> Positive + Negative, + null -> null + } + }, + Negative { + override fun combine(other: Sign?): Sign? = when (other) { + Negative -> Negative + Positive, + null -> null + } + }; + + abstract fun combine(other: Sign?): Sign? + } + + private fun combine(other: ConeNumberSignAttribute?): ConeNumberSignAttribute? { + return fromSign(sign.combine(other?.sign)) + } + + override fun union(other: ConeNumberSignAttribute?): ConeNumberSignAttribute? { + return combine(other) + } + + override fun intersect(other: ConeNumberSignAttribute?): ConeNumberSignAttribute? { + return combine(other) + } + + override fun add(other: ConeNumberSignAttribute?): ConeNumberSignAttribute? { + return combine(other) + } + + override fun isSubtypeOf(other: ConeNumberSignAttribute?): Boolean { + return true + } + + override fun toString(): String { + return "@${sign.name}" + } + + override val key: KClass + get() = ConeNumberSignAttribute::class +} + +val ConeAttributes.numberSign: ConeNumberSignAttribute? by ConeAttributes.attributeAccessor() diff --git a/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/FirNumberSignAttributeExtension.kt b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/FirNumberSignAttributeExtension.kt new file mode 100644 index 00000000000..0981043c85a --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/types/FirNumberSignAttributeExtension.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin.types + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirPluginKey +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation +import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyAnnotationArgumentMapping +import org.jetbrains.kotlin.fir.extensions.FirTypeAttributeExtension +import org.jetbrains.kotlin.fir.plugin.AllOpenPluginKey +import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl +import org.jetbrains.kotlin.fir.types.ConeAttribute +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneTypeSafe +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +class FirNumberSignAttributeExtension(session: FirSession) : FirTypeAttributeExtension(session) { + companion object { + private val PACKAGE_FQN = FqName("org.jetbrains.kotlin.fir.plugin") + private val PositiveClassId = ClassId(PACKAGE_FQN, Name.identifier("Positive")) + private val NegativeClassId = ClassId(PACKAGE_FQN, Name.identifier("Negative")) + } + + override val key: FirPluginKey + get() = AllOpenPluginKey + + override fun extractAttributeFromAnnotation(annotation: FirAnnotation): ConeAttribute<*>? { + val sign = when (annotation.annotationTypeRef.coneTypeSafe()?.classId) { + PositiveClassId -> ConeNumberSignAttribute.Sign.Positive + NegativeClassId -> ConeNumberSignAttribute.Sign.Negative + else -> return null + } + return ConeNumberSignAttribute.fromSign(sign) + } + + override fun convertAttributeToAnnotation(attribute: ConeAttribute<*>): FirAnnotation? { + if (attribute !is ConeNumberSignAttribute) return null + val classId = when (attribute.sign) { + ConeNumberSignAttribute.Sign.Positive -> PositiveClassId + ConeNumberSignAttribute.Sign.Negative -> NegativeClassId + } + return buildAnnotation { + annotationTypeRef = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + ConeClassLikeLookupTagImpl(classId), + emptyArray(), + isNullable = false + ) + } + argumentMapping = FirEmptyAnnotationArgumentMapping + } + } +} diff --git a/plugins/fir/fir-plugin-prototype/testData/checkers/signedNumbersCheckers.fir.txt b/plugins/fir/fir-plugin-prototype/testData/checkers/signedNumbersCheckers.fir.txt new file mode 100644 index 00000000000..94ea040886c --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/testData/checkers/signedNumbersCheckers.fir.txt @@ -0,0 +1,26 @@ +FILE: signedNumbersCheckers.kt + public final fun takePositive(x: R|@Positive kotlin/Number|): R|kotlin/Unit| { + } + public final fun takeNegative(x: R|@Negative kotlin/Number|): R|kotlin/Unit| { + } + public final fun takeAny(x: R|kotlin/Number|): R|kotlin/Unit| { + } + public final fun id(x: R|K|): R|K| { + ^id R|/x| + } + public final fun select(x: R|K|, y: R|K|): R|K| { + ^select R|/x| + } + public final fun test(positiveInt: R|@Positive kotlin/Int|, positiveDouble: R|@Positive kotlin/Double|, negativeDouble: R|@Negative kotlin/Double|): R|kotlin/Unit| { + R|/takePositive|(R|/positiveInt|) + R|/takeNegative|(R|/negativeDouble|) + R|/takeAny|(R|/positiveInt|) + R|/takePositive|(R|/negativeDouble|) + R|/takeNegative|(R|/positiveInt|) + R|/takePositive|(R|/id|(R|/positiveInt|)) + R|/takeNegative|(R|/id|(R|/positiveInt|)) + R|/takePositive|(R|/select|(R|/positiveInt|, R|/positiveInt|)) + R|/takePositive|(R|/select|)|>(R|/positiveInt|, R|/positiveDouble|)) + R|/takePositive|(R|/select|)|>(R|/positiveInt|, R|/negativeDouble|)) + R|/takeNegative|(R|/select|)|>(R|/positiveInt|, R|/negativeDouble|)) + } diff --git a/plugins/fir/fir-plugin-prototype/testData/checkers/signedNumbersCheckers.kt b/plugins/fir/fir-plugin-prototype/testData/checkers/signedNumbersCheckers.kt new file mode 100644 index 00000000000..44b67fa98b7 --- /dev/null +++ b/plugins/fir/fir-plugin-prototype/testData/checkers/signedNumbersCheckers.kt @@ -0,0 +1,31 @@ +import org.jetbrains.kotlin.fir.plugin.Positive +import org.jetbrains.kotlin.fir.plugin.Negative + +fun takePositive(x: @Positive Number) {} +fun takeNegative(x: @Negative Number) {} +fun takeAny(x: Number) {} + +fun id(x: K): K = x +fun select(x: K, y: K): K = x + +fun test( + positiveInt: @Positive Int, + positiveDouble: @Positive Double, + negativeDouble: @Negative Double +) { + takePositive(positiveInt) // ok + takeNegative(negativeDouble) // ok + takeAny(positiveInt) + + takePositive(negativeDouble) // error + takeNegative(positiveInt) // error + + takePositive(id(positiveInt)) // ok + takeNegative(id(positiveInt)) // error + + takePositive(select(positiveInt, positiveInt)) // ok + // Should be ok, but currently attributes are not passed through common super type calculation + takePositive(select(positiveInt, positiveDouble)) + takePositive(select(positiveInt, negativeDouble)) // error + takeNegative(select(positiveInt, negativeDouble)) // error +} diff --git a/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java b/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java index 89591d5c3b5..9a2e95c099f 100644 --- a/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java +++ b/plugins/fir/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/FirAllOpenDiagnosticTestGenerated.java @@ -33,6 +33,12 @@ public class FirAllOpenDiagnosticTestGenerated extends AbstractFirAllOpenDiagnos KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/fir/fir-plugin-prototype/testData/checkers"), Pattern.compile("^(.+)\\.kt$"), null, true); } + @Test + @TestMetadata("signedNumbersCheckers.kt") + public void testSignedNumbersCheckers() throws Exception { + runTest("plugins/fir/fir-plugin-prototype/testData/checkers/signedNumbersCheckers.kt"); + } + @Test @TestMetadata("simple.kt") public void testSimple() throws Exception {