[FIR] Implement extension for providing additional type attributes
This commit is contained in:
committed by
TeamCityServer
parent
32709151c3
commit
38877ba842
+7
-1
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -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<FirSimpleFunctionChecker>
|
||||
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
|
||||
get() = setOf(DummyNameChecker)
|
||||
}
|
||||
|
||||
override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers() {
|
||||
override val functionCallCheckers: Set<FirFunctionCallChecker>
|
||||
get() = setOf(SignedNumberCallChecker)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+2
@@ -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<PsiElement, String>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val ILLEGAL_NUMBER_SIGN by error2<PsiElement, String, String>()
|
||||
}
|
||||
|
||||
+39
@@ -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
|
||||
}
|
||||
}
|
||||
+73
@@ -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<ConeNumberSignAttribute>() {
|
||||
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<out ConeNumberSignAttribute>
|
||||
get() = ConeNumberSignAttribute::class
|
||||
}
|
||||
|
||||
val ConeAttributes.numberSign: ConeNumberSignAttribute? by ConeAttributes.attributeAccessor<ConeNumberSignAttribute>()
|
||||
+62
@@ -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<ConeClassLikeType>()?.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
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -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 <K> id(x: R|K|): R|K| {
|
||||
^id R|<local>/x|
|
||||
}
|
||||
public final fun <K> select(x: R|K|, y: R|K|): R|K| {
|
||||
^select R|<local>/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|<local>/positiveInt|)
|
||||
R|/takeNegative|(R|<local>/negativeDouble|)
|
||||
R|/takeAny|(R|<local>/positiveInt|)
|
||||
R|/takePositive|(R|<local>/negativeDouble|)
|
||||
R|/takeNegative|(R|<local>/positiveInt|)
|
||||
R|/takePositive|(R|/id|<R|@Positive kotlin/Int|>(R|<local>/positiveInt|))
|
||||
R|/takeNegative|(R|/id|<R|@Positive kotlin/Int|>(R|<local>/positiveInt|))
|
||||
R|/takePositive|(R|/select|<R|@Positive kotlin/Int|>(R|<local>/positiveInt|, R|<local>/positiveInt|))
|
||||
R|/takePositive|(R|/select|<R|it(kotlin/Number & kotlin/Comparable<*>)|>(R|<local>/positiveInt|, R|<local>/positiveDouble|))
|
||||
R|/takePositive|(R|/select|<R|it(kotlin/Number & kotlin/Comparable<*>)|>(R|<local>/positiveInt|, R|<local>/negativeDouble|))
|
||||
R|/takeNegative|(R|/select|<R|it(kotlin/Number & kotlin/Comparable<*>)|>(R|<local>/positiveInt|, R|<local>/negativeDouble|))
|
||||
}
|
||||
@@ -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 <K> id(x: K): K = x
|
||||
fun <K> 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(<!ILLEGAL_NUMBER_SIGN!>negativeDouble<!>) // error
|
||||
takeNegative(<!ILLEGAL_NUMBER_SIGN!>positiveInt<!>) // error
|
||||
|
||||
takePositive(id(positiveInt)) // ok
|
||||
takeNegative(<!ILLEGAL_NUMBER_SIGN!>id(positiveInt)<!>) // error
|
||||
|
||||
takePositive(select(positiveInt, positiveInt)) // ok
|
||||
// Should be ok, but currently attributes are not passed through common super type calculation
|
||||
takePositive(<!ILLEGAL_NUMBER_SIGN!>select(positiveInt, positiveDouble)<!>)
|
||||
takePositive(<!ILLEGAL_NUMBER_SIGN!>select(positiveInt, negativeDouble)<!>) // error
|
||||
takeNegative(<!ILLEGAL_NUMBER_SIGN!>select(positiveInt, negativeDouble)<!>) // error
|
||||
}
|
||||
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user