From a44d5bf52f0569fa8f80d127d49d3ab7580dc017 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Wed, 29 Jun 2022 18:14:49 +0300 Subject: [PATCH] [FIR] Implement FirNativeThreadLocalChecker * INAPPLICABLE_THREAD_LOCAL * INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL --- .../diagnostics/FirNativeDiagnosticsList.kt | 2 + .../diagnostics/native/FirNativeErrors.kt | 2 + .../native/FirNativeErrorsDefaultMessages.kt | 7 ++ .../native/checkers/FirNativeHelpers.kt | 26 +++++++ .../FirNativeSharedImmutableChecker.kt | 32 +++++---- .../checkers/FirNativeThreadLocalChecker.kt | 36 ++++++++++ .../native/checkers/FirNativeThrowsChecker.kt | 6 +- .../checkers/NativeDeclarationCheckers.kt | 3 +- .../nativeTests/threadLocal.fir.kt | 71 ------------------- .../diagnostics/nativeTests/threadLocal.kt | 1 + .../nativeTests/topLevelSingleton.fir.kt | 2 +- 11 files changed, 99 insertions(+), 89 deletions(-) create mode 100644 compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeHelpers.kt create mode 100644 compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThreadLocalChecker.kt delete mode 100644 compiler/testData/diagnostics/nativeTests/threadLocal.fir.kt diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt index b63fd747a35..6a9c36b03c9 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirNativeDiagnosticsList.kt @@ -28,5 +28,7 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") { } val INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY by error() val INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL by error() + val INAPPLICABLE_THREAD_LOCAL by error() + val INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL by error() } } \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt index d02c8181ef9..999edcfc298 100644 --- a/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt +++ b/compiler/fir/checkers/checkers.native/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrors.kt @@ -26,6 +26,8 @@ object FirNativeErrors { val MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND by error1() val INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY by error0() val INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL by error0() + val INAPPLICABLE_THREAD_LOCAL by error0() + val INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL by error0() init { RootDiagnosticRendererFactory.registerFactory(FirNativeErrorsDefaultMessages) diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt index c6d000156a2..bce1b85a91d 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/diagnostics/native/FirNativeErrorsDefaultMessages.kt @@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMB import org.jetbrains.kotlin.fir.analysis.diagnostics.checkMissingMessages import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_THREAD_LOCAL +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCOMPATIBLE_THROWS_INHERITED import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCOMPATIBLE_THROWS_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND @@ -32,6 +34,11 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() { "@SharedImmutable is applicable only to val with backing field or to property with delegation" ) map.put(INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL, "@SharedImmutable is applicable only to top level declarations") + map.put( + INAPPLICABLE_THREAD_LOCAL, + "@ThreadLocal is applicable only to property with backing field, to property with delegation or to objects" + ) + map.put(INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL, "@ThreadLocal is applicable only to top level declarations") map.checkMissingMessages(FirNativeErrors) } diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeHelpers.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeHelpers.kt new file mode 100644 index 00000000000..e04b9aa9285 --- /dev/null +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeHelpers.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2022 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.analysis.native.checkers + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory0 +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId +import org.jetbrains.kotlin.name.ClassId + +fun DiagnosticReporter.reportIfHasAnnotation( +declaration: FirDeclaration, + annotationClassId: ClassId, + error: KtDiagnosticFactory0, + context: CheckerContext +) { + val annotation = declaration.getAnnotationByClassId(annotationClassId) + if (annotation != null) { + reportOn(annotation.source, error, context) + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeSharedImmutableChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeSharedImmutableChecker.kt index 7147bd98329..d4b820370ae 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeSharedImmutableChecker.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeSharedImmutableChecker.kt @@ -18,26 +18,30 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName object FirNativeSharedImmutableChecker : FirBasicDeclarationChecker() { - private val sharedImmutableFqName = ClassId.topLevel(FqName("kotlin.native.concurrent.SharedImmutable")) + private val sharedImmutableClassId = ClassId.topLevel(FqName("kotlin.native.concurrent.SharedImmutable")) override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { if (declaration is FirVariable) { if (declaration !is FirValueParameter || context.containingDeclarations.lastOrNull() !is FirPrimaryConstructor) { val hasBackingField = declaration is FirProperty && declaration.hasBackingField if ((declaration.isVar || !hasBackingField) && declaration.delegate == null) { - val annotation = declaration.getAnnotationByClassId(sharedImmutableFqName) - if (annotation != null) { - reporter.reportOn(annotation.source, FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY, context) - } + reporter.reportIfHasAnnotation( + declaration, + sharedImmutableClassId, + FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY, + context + ) } } } else { if (declaration.source?.kind is KtFakeSourceElementKind) return - val annotation = declaration.getAnnotationByClassId(sharedImmutableFqName) - if (annotation != null) { - reporter.reportOn(annotation.source, FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY, context) - } + reporter.reportIfHasAnnotation( + declaration, + sharedImmutableClassId, + FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY, + context + ) return } @@ -45,10 +49,12 @@ object FirNativeSharedImmutableChecker : FirBasicDeclarationChecker() { if (declaration.source?.kind is KtFakeSourceElementKind) return if (context.containingDeclarations.lastOrNull() !is FirFile) { - val annotation = declaration.getAnnotationByClassId(sharedImmutableFqName) - if (annotation != null) { - reporter.reportOn(annotation.source, FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL, context) - } + reporter.reportIfHasAnnotation( + declaration, + sharedImmutableClassId, + FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_TOP_LEVEL, + context + ) } } } \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThreadLocalChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThreadLocalChecker.kt new file mode 100644 index 00000000000..a34b21d2477 --- /dev/null +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThreadLocalChecker.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2022 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.analysis.native.checkers + +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName + +object FirNativeThreadLocalChecker : FirBasicDeclarationChecker() { + private val threadLocalClassId = ClassId.topLevel(FqName("kotlin.native.concurrent.ThreadLocal")) + + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + val isObject = declaration is FirClass && declaration.classKind == ClassKind.OBJECT + val isOk = declaration is FirVariable && + (declaration is FirProperty && declaration.hasBackingField || declaration.delegate != null) || isObject + if (!isOk) { + reporter.reportIfHasAnnotation(declaration, threadLocalClassId, FirNativeErrors.INAPPLICABLE_THREAD_LOCAL, context) + } + + if (declaration.source?.kind is KtFakeSourceElementKind) return + + if (context.containingDeclarations.lastOrNull() !is FirFile && !isObject) { + reporter.reportIfHasAnnotation(declaration, threadLocalClassId, FirNativeErrors.INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL, context) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt index 7f301fab7f2..7d584bc8ba6 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeThrowsChecker.kt @@ -30,7 +30,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.annotations.KOTLIN_THROWS_ANNOTATION_FQ_NAME object FirNativeThrowsChecker : FirBasicDeclarationChecker() { - private val throwsFqName = ClassId.topLevel(KOTLIN_THROWS_ANNOTATION_FQ_NAME) + private val throwsClassId = ClassId.topLevel(KOTLIN_THROWS_ANNOTATION_FQ_NAME) private val cancellationExceptionFqName = FqName("kotlin.coroutines.cancellation.CancellationException") @@ -43,7 +43,7 @@ object FirNativeThrowsChecker : FirBasicDeclarationChecker() { ) override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { - val throwsAnnotation = declaration.getAnnotationByClassId(throwsFqName) as? FirAnnotationCall + val throwsAnnotation = declaration.getAnnotationByClassId(throwsClassId) as? FirAnnotationCall if (!checkInheritance(declaration, throwsAnnotation, context, reporter)) return @@ -121,7 +121,7 @@ object FirNativeThrowsChecker : FirBasicDeclarationChecker() { val annotation = if (overriddenFunction.isSubstitutionOrIntersectionOverride) { null } else { - overriddenFunction.getAnnotationByClassId(throwsFqName) as? FirAnnotationCall + overriddenFunction.getAnnotationByClassId(throwsClassId) as? FirAnnotationCall } getInheritedThrows(annotation, overriddenFunction) } diff --git a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt index f89ba45190e..1d1bcbd9f7d 100644 --- a/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeDeclarationCheckers.kt @@ -12,6 +12,7 @@ object NativeDeclarationCheckers : DeclarationCheckers() { override val basicDeclarationCheckers: Set get() = setOf( FirNativeThrowsChecker, - FirNativeSharedImmutableChecker + FirNativeSharedImmutableChecker, + FirNativeThreadLocalChecker ) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/nativeTests/threadLocal.fir.kt b/compiler/testData/diagnostics/nativeTests/threadLocal.fir.kt deleted file mode 100644 index 498524e9446..00000000000 --- a/compiler/testData/diagnostics/nativeTests/threadLocal.fir.kt +++ /dev/null @@ -1,71 +0,0 @@ -// FILE: annotation.kt -package kotlin.native.concurrent - -@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.BINARY) -annotation class ThreadLocal - -// FILE: test.kt -import kotlin.native.concurrent.ThreadLocal -import kotlin.reflect.KProperty - -data class Point(val x: Double, val y: Double) - -class Person(val name: String) { - @ThreadLocal - var surname: String? = null -} - -abstract class Information { - abstract var field: String -} - -@ThreadLocal -class Person1(val name: String) { - var surname: String? = null - @ThreadLocal - val extraInfo: Information = object : Information() { - override var field: String = "extra info" - } -} - -@ThreadLocal -val extraInfo: Information = object : Information() { - override var field: String = "extra info" -} - -@ThreadLocal -val point1 = Point(1.0, 1.0) - -@ThreadLocal -val cornerPoint: Point - get() = point1 - -@ThreadLocal -val person = Person1("aaaaa") - -class Delegate { - val value: Int = 10 - operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return value - } - - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { - } -} - -@ThreadLocal -var field1: Int by Delegate() - -@ThreadLocal -object WithDelegate { - var field1: Int by Delegate() -} - -class Bar { - @ThreadLocal - object SomeObject { - var field1: Int = 10 - var field2: String? = null - } -} diff --git a/compiler/testData/diagnostics/nativeTests/threadLocal.kt b/compiler/testData/diagnostics/nativeTests/threadLocal.kt index 68c8686af20..36bb629a309 100644 --- a/compiler/testData/diagnostics/nativeTests/threadLocal.kt +++ b/compiler/testData/diagnostics/nativeTests/threadLocal.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // FILE: annotation.kt package kotlin.native.concurrent diff --git a/compiler/testData/diagnostics/nativeTests/topLevelSingleton.fir.kt b/compiler/testData/diagnostics/nativeTests/topLevelSingleton.fir.kt index ef896bced45..b6608b330fb 100644 --- a/compiler/testData/diagnostics/nativeTests/topLevelSingleton.fir.kt +++ b/compiler/testData/diagnostics/nativeTests/topLevelSingleton.fir.kt @@ -65,7 +65,7 @@ class Bar2 { } } -@ThreadLocal +@ThreadLocal enum class Color(var rgb: Int) { RED(0xFF0000), GREEN(0x00FF00),