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 86c0ae6fb35..b63fd747a35 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 @@ -26,5 +26,7 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") { val MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND by error { parameter("exceptionName") } + val INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY by error() + val INAPPLICABLE_SHARED_IMMUTABLE_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 82f5f06d732..d02c8181ef9 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 @@ -24,6 +24,8 @@ object FirNativeErrors { val INCOMPATIBLE_THROWS_OVERRIDE by error1() val INCOMPATIBLE_THROWS_INHERITED by error1>() val MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND by error1() + val INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY by error0() + val INAPPLICABLE_SHARED_IMMUTABLE_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 6d7e96a9086..c6d000156a2 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 @@ -11,6 +11,8 @@ import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS 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.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 @@ -25,6 +27,11 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() { MISSING_EXCEPTION_IN_THROWS_ON_SUSPEND, "@Throws on suspend declaration must have {0} (or any of its superclasses) listed", TO_STRING ) + map.put( + INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY, + "@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.checkMissingMessages(FirNativeErrors) } 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 new file mode 100644 index 00000000000..7147bd98329 --- /dev/null +++ b/compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeSharedImmutableChecker.kt @@ -0,0 +1,54 @@ +/* + * 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.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +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.impl.FirPrimaryConstructor +import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField +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")) + + 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) + } + } + } + } 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) + } + + return + } + + 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) + } + } + } +} \ No newline at end of file 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 f90ec895714..f89ba45190e 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 @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclaratio object NativeDeclarationCheckers : DeclarationCheckers() { override val basicDeclarationCheckers: Set get() = setOf( - FirNativeThrowsChecker + FirNativeThrowsChecker, + FirNativeSharedImmutableChecker ) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/nativeTests/sharedImmutable.fir.kt b/compiler/testData/diagnostics/nativeTests/sharedImmutable.fir.kt deleted file mode 100644 index 1fbe4c85f9a..00000000000 --- a/compiler/testData/diagnostics/nativeTests/sharedImmutable.fir.kt +++ /dev/null @@ -1,145 +0,0 @@ -// FILE: annotation.kt -package kotlin.native.concurrent - -import kotlin.reflect.KProperty - -@Target(AnnotationTarget.PROPERTY) -@Retention(AnnotationRetention.BINARY) -annotation class SharedImmutable - -@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.BINARY) -annotation class ThreadLocal - -// FILE: test.kt -import kotlin.native.concurrent.SharedImmutable -import kotlin.native.concurrent.ThreadLocal -import kotlin.reflect.KProperty - -fun println(value: Int) {} -fun println(value: String) {} -fun println(value: Point) {} - -data class Point(val x: Double, val y: Double) -@SharedImmutable -val point1 = Point(1.0, 1.0) - -@SharedImmutable -var point2 = Point(2.0, 2.0) - -class Date(@SharedImmutable val month: Int, @SharedImmutable var day:Int) -class Person(val name: String) { - @SharedImmutable - var surname: String? = null -} - -class Figure { - @SharedImmutable - val cornerPoint: Point - get() = point1 -} - -@SharedImmutable -var age = 20 - get() { - println("Age is: $field") - return field - } - set(value) { - println(value) - } - -var globalAge = 30 -@SharedImmutable -var age1 = 20 - get() { - println("Age is: $field") - return field - } - set(value) { - globalAge = value - } - -@SharedImmutable -val age2 = 20 - get() { - println("Age is: $field") - return field - } - -@SharedImmutable -var point3: Point - get() = point2 - set(value) { - point2 = value - } - -@SharedImmutable -var point4: Point - get() = point2 - set(value) { - println(value) - } - -@ThreadLocal -var point0 = Point(2.0, 2.0) - -@SharedImmutable -var point5: Point - get() = point0 - set(value) { - point0 = value - } - - -class Delegate { - var value = 20 - operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { - println("Get") - return value - } - - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { - println("Set") - } -} - -@SharedImmutable -var property: Int by Delegate() - -class Delegate1 { - var value = 20 - operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return value - } - - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { - this.value = value - } -} - -@SharedImmutable -var property1: Int by Delegate1() - -var globalValue: Int = 20 - -class Delegate2 { - - operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { - return globalValue - } - - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { - println(value) - } -} - -@SharedImmutable -var property2: Int by Delegate2() - -@SharedImmutable -val someValue: Int - get() = 20 - -@SharedImmutable -val someValueWithDelegate by Delegate() diff --git a/compiler/testData/diagnostics/nativeTests/sharedImmutable.kt b/compiler/testData/diagnostics/nativeTests/sharedImmutable.kt index 46a0966042b..851d9b7fd50 100644 --- a/compiler/testData/diagnostics/nativeTests/sharedImmutable.kt +++ b/compiler/testData/diagnostics/nativeTests/sharedImmutable.kt @@ -1,3 +1,5 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_PARAMETER // FILE: annotation.kt package kotlin.native.concurrent @@ -16,9 +18,9 @@ import kotlin.native.concurrent.SharedImmutable import kotlin.native.concurrent.ThreadLocal import kotlin.reflect.KProperty -fun println(value: Int) {} -fun println(value: String) {} -fun println(value: Point) {} +fun println(value: Int) {} +fun println(value: String) {} +fun println(value: Point) {} data class Point(val x: Double, val y: Double) @SharedImmutable