[atomicfu-plugin] Added a FIR checker for property visibility


Merge-request: KT-MR-17641
Merged-by: Maria Sokolova <maria.sokolova@jetbrains.com>
diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
index e4bed43..58b5ebc 100644
--- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
+++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
@@ -44,13 +44,13 @@
 import org.jetbrains.kotlin.scripting.test.AbstractScriptWithCustomDefBlackBoxCodegenTest
 import org.jetbrains.kotlin.scripting.test.AbstractScriptWithCustomDefDiagnosticsTestBase
 import org.jetbrains.kotlin.test.TargetBackend
-import org.jetbrains.kotlinx.atomicfu.AbstractAtomicfuJsFirTest
-import org.jetbrains.kotlinx.atomicfu.AbstractAtomicfuJsIrTest
-import org.jetbrains.kotlinx.atomicfu.AbstractAtomicfuJvmFirLightTreeTest
-import org.jetbrains.kotlinx.atomicfu.AbstractAtomicfuJvmIrTest
+import org.jetbrains.kotlinx.atomicfu.runners.AbstractAtomicfuFirCheckerTest
+import org.jetbrains.kotlinx.atomicfu.runners.AbstractAtomicfuJsFirTest
+import org.jetbrains.kotlinx.atomicfu.runners.AbstractAtomicfuJsIrTest
+import org.jetbrains.kotlinx.atomicfu.runners.AbstractAtomicfuJvmFirLightTreeTest
+import org.jetbrains.kotlinx.atomicfu.runners.AbstractAtomicfuJvmIrTest
 import org.jetbrains.kotlinx.atomicfu.incremental.AbstractIncrementalK2JVMWithAtomicfuRunnerTest
 
-
 private class ExcludePattern {
     companion object {
         private const val MEMBER_ALIAS = "(^removeMemberTypeAlias)|(^addMemberTypeAlias)"
@@ -272,7 +272,7 @@
         }
 
         testGroup(
-            "plugins/atomicfu/atomicfu-compiler/test",
+            "plugins/atomicfu/atomicfu-compiler/tests-gen",
             "plugins/atomicfu/atomicfu-compiler/testData",
             testRunnerMethodName = "runTest0"
         ) {
@@ -286,10 +286,14 @@
         }
 
         testGroup(
-            "plugins/atomicfu/atomicfu-compiler/test",
+            "plugins/atomicfu/atomicfu-compiler/tests-gen",
             "plugins/atomicfu/atomicfu-compiler/testData",
             testRunnerMethodName = "runTest0"
         ) {
+            testClass<AbstractAtomicfuFirCheckerTest> {
+                model("diagnostics/")
+            }
+
             testClass<AbstractAtomicfuJvmIrTest> {
                 model("box/")
             }
diff --git a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
index ad54765..81394b7 100644
--- a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
+++ b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
@@ -59,6 +59,14 @@
     compileOnly(intellijCore())
     compileOnly(libs.intellij.asm)
 
+    compileOnly(project(":compiler:fir:cones"))
+    compileOnly(project(":compiler:fir:tree"))
+    compileOnly(project(":compiler:fir:resolve"))
+    compileOnly(project(":compiler:fir:plugin-utils"))
+    compileOnly(project(":compiler:fir:checkers"))
+    compileOnly(project(":compiler:fir:fir2ir"))
+    compileOnly(project(":compiler:fir:entrypoint"))
+
     compileOnly(project(":compiler:plugin-api"))
     compileOnly(project(":compiler:cli-common"))
     compileOnly(project(":compiler:frontend"))
@@ -157,9 +165,14 @@
 optInToExperimentalCompilerApi()
 optInToUnsafeDuringIrConstructionAPI()
 
+val generationRoot = projectDir.resolve("tests-gen")
+
 sourceSets {
     "main" { projectDefault() }
-    "test" { projectDefault() }
+    "test" {
+        projectDefault()
+        this.java.srcDir(generationRoot.name)
+    }
 }
 
 testsJar()
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/common/AbstractAtomicfuTransformer.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/common/AbstractAtomicfuTransformer.kt
index 419eb8c..6972c76 100644
--- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/common/AbstractAtomicfuTransformer.kt
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/common/AbstractAtomicfuTransformer.kt
@@ -29,43 +29,11 @@
 import org.jetbrains.kotlin.ir.visitors.*
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
+import org.jetbrains.kotlinx.atomicfu.compiler.diagnostic.AtomicfuErrorMessages.CONSTRAINTS_MESSAGE
 
 abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
 
     companion object {
-        const val CONSTRAINTS_MESSAGE =
-            "\nPlease make sure that you follow these constraints for using atomic properties:\n" +
-                    "   * To ensure that atomic properties are not accessed out of the current Kotlin module, it is necessary to declare atomic properties as private or internal.\n" +
-                    "     Alternatively, you can make the containing class private or internal.\n" +
-                    "     If you need to expose the atomic property value to the public, consider using a delegated property declared within the same scope:\n" +
-                    "       ```\n" +
-                    "       private val _a = atomic<T>(initial) \n" +
-                    "       public var a: T by _a \n" +
-                    "       ```\n" +
-                    "   * Directly invoke operations on atomic properties, like this:\n" +
-                    "       ```\n" +
-                    "       val top = atomic<Node?>(null)\n" +
-                    "       top.compareAndSet(null, Node(1)) // OK\n" +
-                    "       ```\n" +
-                    "   * Refrain from invoking atomic operations on local variables:\n" +
-                    "       ```\n" +
-                    "       val top = atomic<Node?>(null)\n" +
-                    "       val tmp = top\n" +
-                    "       tmp.compareAndSet(null, Node(1)) // DON'T DO THIS\n" +
-                    "       ```\n" +
-                    "   * Avoid leaking references to atomic values in other ways, such as returning them or passing them as parameters.\n" +
-                    "   * Be cautious with the complexity of data flow within parameters of atomic operations.\n" +
-                    "     For instance, instead of using intricate expression directly as an argument, e.g.:\n" +
-                    "       ```\n" +
-                    "       top.compareAndSet(cur, <complex_expression>)\n" +
-                    "       ```\n" +
-                    "     create a separate variable to hold the complex expression's value and then perform the operation:\n" +
-                    "       ```\n" +
-                    "       val newValue = <complex_expression>\n" +
-                    "       top.compareAndSet(cur, newValue) \n" +
-                    "       ```\n" +
-                    "\n"
-
         internal const val VOLATILE = "\$volatile"
         internal const val ATOMICFU = "atomicfu"
         internal const val ARRAY = "array"
@@ -158,8 +126,6 @@
             val isTopLevel = parentContainer is IrFile || (parentContainer is IrClass && parentContainer.kind == ClassKind.OBJECT)
             when {
                 atomicProperty.isAtomic() -> {
-                    atomicProperty.checkIsFinal(isArray = false)
-                    atomicProperty.checkVisibility(isArray = false)
                     if (isTopLevel) {
                         parentContainer.addTransformedStaticAtomic(atomicProperty, index)
                     } else {
@@ -169,8 +135,6 @@
                     }
                 }
                 atomicProperty.isAtomicArray() -> {
-                    atomicProperty.checkIsFinal(isArray = true)
-                    atomicProperty.checkVisibility(isArray = true)
                     parentContainer.addTransformedAtomicArray(atomicProperty, index).also {
                         declarationsToBeRemoved.add(atomicProperty)
                     }
@@ -452,32 +416,6 @@
             )
         }
 
-        private fun IrProperty.checkIsFinal(isArray: Boolean) =
-            check(!isVar) {
-                "Please consider declaring [${this.atomicfuRender()}] from [${this.parent.render()}] as a private val or internal val.\n" +
-                        if (!isArray) "If you need to declare a variable with accessors delegated to the atomic property value, you can use a delegated property declared within the same scope, e.g:\n" +
-                                "```\n" +
-                                "private val _a = atomic<T>(initial) \n" +
-                                "public var a: T by _a \n" +
-                                "```\n" else ""
-            }
-
-        private fun IrProperty.checkVisibility(isArray: Boolean) =
-            check(
-                (visibility == DescriptorVisibilities.PRIVATE || visibility == DescriptorVisibilities.INTERNAL) ||
-                        (parent is IrClass &&
-                                (parentAsClass.visibility == DescriptorVisibilities.PRIVATE || parentAsClass.visibility == DescriptorVisibilities.INTERNAL))
-            ) {
-                "To ensure that atomic properties are not accessed out of the current Kotlin module, it is necessary to declare atomic properties as private or internal.\n" +
-                        "Please consider declaring [${this.atomicfuRender()}] from [${this.parent.render()}] as a private or internal property.\n" +
-                        (if (parent is IrClass) "You may also make the containing class [${parentAsClass.render()}] private or internal.\n" else "") +
-                        if (!isArray) "Alternatively, if you need to expose the atomic property value to the public, you can use a delegated property declared within the same scope, e.g:\n" +
-                                "```\n" +
-                                "private val _a = atomic<T>(initial) \n" +
-                                "public var a: T by _a \n" +
-                                "```\n" else ""
-            }
-
         protected fun IrProperty.getMinVisibility(): DescriptorVisibility {
             // To protect atomic properties from leaking out of the current sourceSet, they are required to be internal or private,
             // or the containing class may be internal or private.
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuJvmIrTransformer.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuJvmIrTransformer.kt
index 6f4cfce..0dea857 100644
--- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuJvmIrTransformer.kt
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuJvmIrTransformer.kt
@@ -19,6 +19,8 @@
 import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuIrBuilder
 import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuTransformer
 import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuTransformer.Companion.ATOMICFU
+import org.jetbrains.kotlinx.atomicfu.compiler.diagnostic.AtomicfuErrorMessages.CONSTRAINTS_MESSAGE
+
 import kotlin.collections.set
 
 private const val DISPATCH_RECEIVER = "dispatchReceiver\$$ATOMICFU"
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt
index c1f8c61..d71a0c5 100644
--- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt
@@ -24,6 +24,7 @@
 import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuIrBuilder
 import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuTransformer
 import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuTransformer.Companion.ATOMICFU
+import org.jetbrains.kotlinx.atomicfu.compiler.diagnostic.AtomicfuErrorMessages.CONSTRAINTS_MESSAGE
 
 private const val REF_GETTER = "refGetter\$$ATOMICFU"
 private const val ATOMIC_ARRAY = "atomicArray\$$ATOMICFU"
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuErrorMessages.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuErrorMessages.kt
new file mode 100644
index 0000000..9dd56a7
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuErrorMessages.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2010-2024 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.kotlinx.atomicfu.compiler.diagnostic
+
+import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
+import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
+import org.jetbrains.kotlin.diagnostics.rendering.Renderers
+
+object AtomicfuErrorMessages : BaseDiagnosticRendererFactory() {
+
+    const val CONSTRAINTS_MESSAGE =
+        "\nPlease make sure that you follow these constraints for using atomic properties:\n" +
+                "   * To ensure that atomic properties are not accessed out of the current Kotlin module, it is necessary to declare atomic properties as private or internal.\n" +
+                "     Alternatively, you can make the containing class private or internal.\n" +
+                "     If you need to expose the atomic property value to the public, consider using a delegated property declared within the same scope:\n" +
+                "       ```\n" +
+                "       private val _a = atomic<T>(initial) \n" +
+                "       public var a: T by _a \n" +
+                "       ```\n" +
+                "   * Directly invoke operations on atomic properties, like this:\n" +
+                "       ```\n" +
+                "       val top = atomic<Node?>(null)\n" +
+                "       top.compareAndSet(null, Node(1)) // OK\n" +
+                "       ```\n" +
+                "   * Refrain from invoking atomic operations on local variables:\n" +
+                "       ```\n" +
+                "       val top = atomic<Node?>(null)\n" +
+                "       val tmp = top\n" +
+                "       tmp.compareAndSet(null, Node(1)) // DON'T DO THIS\n" +
+                "       ```\n" +
+                "   * Avoid leaking references to atomic values in other ways, such as returning them or passing them as parameters.\n" +
+                "   * Be cautious with the complexity of data flow within parameters of atomic operations.\n" +
+                "     For instance, instead of using intricate expression directly as an argument, e.g.:\n" +
+                "       ```\n" +
+                "       top.compareAndSet(cur, <complex_expression>)\n" +
+                "       ```\n" +
+                "     create a separate variable to hold the complex expression's value and then perform the operation:\n" +
+                "       ```\n" +
+                "       val newValue = <complex_expression>\n" +
+                "       top.compareAndSet(cur, newValue) \n" +
+                "       ```\n" +
+                "\n"
+
+    private const val PUBLIC_ATOMICS_ARE_FORBIDDEN_MESSAGE =
+        "\nTo prevent atomic properties from being referenced outside the current Kotlin module, they should be declared as either private or internal. " +
+                "Note, that `@kotlin.PublishedApi` annotation, when applied to a class or a member with internal visibility, makes it effectively public.\n" +
+                "Please consider setting the visibility of the property `''{0}''` to private or internal or limit the scope of the containing class. \n" +
+                "Alternatively, if you need to expose the atomic property value to the public, you can use a delegated property declared within the same scope, e.g:\n" +
+                "```\n" +
+                "private val _a = atomic<T>(initial) \n" +
+                "public val a: T by _a \n" +
+                "```\n"
+
+    private const val ATOMIC_PROEPRTIES_SHOULD_BE_VAL_MESSAGE = "Please consider declaring `''{0}''` as a private val or internal val.\n" +
+            "If you need to declare a variable with accessors delegated to the atomic property value, you can use a delegated property declared within the same scope, e.g:\n" +
+            "```\n" +
+            "private val _a = atomic<T>(initial) \n" +
+            "public var a: T by _a \n" +
+            "```\n"
+
+    override val MAP: KtDiagnosticFactoryToRendererMap = KtDiagnosticFactoryToRendererMap("Atomicfu Plugin").also { map ->
+        map.put(
+            AtomicfuErrors.PUBLIC_ATOMICS_ARE_FORBIDDEN, PUBLIC_ATOMICS_ARE_FORBIDDEN_MESSAGE, Renderers.TO_STRING
+        )
+        map.put(
+            AtomicfuErrors.PUBLISHED_API_ATOMICS_ARE_FORBIDDEN, PUBLIC_ATOMICS_ARE_FORBIDDEN_MESSAGE, Renderers.TO_STRING
+        )
+        map.put(
+            AtomicfuErrors.ATOMIC_PROPERTIES_SHOULD_BE_VAL, ATOMIC_PROEPRTIES_SHOULD_BE_VAL_MESSAGE, Renderers.TO_STRING
+        )
+    }
+
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuErrors.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuErrors.kt
new file mode 100644
index 0000000..a2c2fe2
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuErrors.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2024 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.kotlinx.atomicfu.compiler.diagnostic
+
+import org.jetbrains.kotlin.diagnostics.*
+import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
+import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
+import org.jetbrains.kotlin.psi.KtProperty
+
+object AtomicfuErrors {
+    val PUBLIC_ATOMICS_ARE_FORBIDDEN = KtDiagnosticFactory1<String>("PUBLIC_ATOMICS_ARE_FORBIDDEN",
+        Severity.ERROR, SourceElementPositioningStrategies.VISIBILITY_MODIFIER, KtProperty::class)
+
+    // This diagnostic should be upgraded to error in the next release: KT-70982
+    val PUBLISHED_API_ATOMICS_ARE_FORBIDDEN = KtDiagnosticFactory1<String>("PUBLISHED_API_ATOMICS_ARE_FORBIDDEN",
+        Severity.WARNING, SourceElementPositioningStrategies.VISIBILITY_MODIFIER, KtProperty::class)
+
+    val ATOMIC_PROPERTIES_SHOULD_BE_VAL = KtDiagnosticFactory1<String>("ATOMIC_PROPERTIES_SHOULD_BE_VAL",
+        Severity.ERROR, SourceElementPositioningStrategies.VAL_OR_VAR_NODE, KtProperty::class)
+
+    init {
+        RootDiagnosticRendererFactory.registerFactory(AtomicfuErrorMessages)
+    }
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuPropertyChecker.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuPropertyChecker.kt
new file mode 100644
index 0000000..b0d6de1
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/diagnostic/AtomicfuPropertyChecker.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010-2024 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.kotlinx.atomicfu.compiler.diagnostic
+
+import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
+import org.jetbrains.kotlin.diagnostics.reportOn
+import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirPropertyChecker
+import org.jetbrains.kotlin.fir.declarations.FirProperty
+import org.jetbrains.kotlin.fir.declarations.utils.visibility
+import org.jetbrains.kotlin.fir.expressions.FirAnnotation
+import org.jetbrains.kotlin.fir.resolve.toClassLikeSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
+import org.jetbrains.kotlin.fir.types.classId
+import org.jetbrains.kotlin.fir.types.coneType
+import org.jetbrains.kotlin.text
+
+private const val KOTLINX_ATOMICFU = "kotlinx.atomicfu"
+private const val PUBLISHED_API = "kotlin.PublishedApi"
+
+private fun FirProperty.isKotlinxAtomicfu(): Boolean = returnTypeRef.coneType.classId?.packageFqName?.asString() == KOTLINX_ATOMICFU
+
+private fun FirProperty.isPublishedApi(): Boolean = annotations.any(::isMarkedWithPublishedApi)
+
+private fun FirClassLikeSymbol<*>.isPublishedApi(): Boolean = annotations.any(::isMarkedWithPublishedApi)
+private fun FirClassLikeSymbol<*>.isPublic(): Boolean = resolvedStatus.visibility.isPublicAPI
+
+private fun isMarkedWithPublishedApi(a: FirAnnotation): Boolean =
+    a.annotationTypeRef.coneType.classId?.asFqNameString() == PUBLISHED_API
+
+object AtomicfuPropertyChecker: FirPropertyChecker(MppCheckerKind.Common) {
+    override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) {
+        if (!declaration.isKotlinxAtomicfu()) return
+        val containingClassSymbol = declaration.dispatchReceiverType?.toClassLikeSymbol(context.session)
+        if (declaration.visibility.isPublicAPI &&
+            (containingClassSymbol == null || containingClassSymbol.isPublic())) {
+            reporter.reportOn(
+                declaration.source,
+                AtomicfuErrors.PUBLIC_ATOMICS_ARE_FORBIDDEN,
+                declaration.source.text.toString(),
+                context
+            )
+        } else {
+            if ((declaration.visibility.isPublicAPI || declaration.isPublishedApi()) &&
+                (containingClassSymbol == null || containingClassSymbol.isPublic() || containingClassSymbol.isPublishedApi())) {
+                reporter.reportOn(
+                    declaration.source,
+                    AtomicfuErrors.PUBLISHED_API_ATOMICS_ARE_FORBIDDEN,
+                    declaration.source.text.toString(),
+                    context
+                )
+            }
+        }
+        if (declaration.isVar) {
+            reporter.reportOn(
+                declaration.source,
+                AtomicfuErrors.ATOMIC_PROPERTIES_SHOULD_BE_VAL,
+                declaration.source.text.toString(),
+                context
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuComponentRegistrar.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuComponentRegistrar.kt
index 355b601..76aecd3 100644
--- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuComponentRegistrar.kt
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuComponentRegistrar.kt
@@ -19,10 +19,13 @@
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
 import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
 import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
+import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
+import org.jetbrains.kotlinx.atomicfu.compiler.diagnostic.AtomicfuFirDeclarationCheckers
 
 class AtomicfuComponentRegistrar : CompilerPluginRegistrar() {
     override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
-        Companion.registerExtensions(this)
+        registerExtensions(this)
     }
 
     override val supportsK2: Boolean
@@ -30,7 +33,14 @@
 
     companion object {
         fun registerExtensions(extensionStorage: ExtensionStorage) = with(extensionStorage) {
+            FirExtensionRegistrarAdapter.registerExtension(AtomicfuFirExtensionRegistrar())
             IrGenerationExtension.registerExtension(AtomicfuLoweringExtension())
         }
     }
 }
+
+class AtomicfuFirExtensionRegistrar : FirExtensionRegistrar() {
+    override fun ExtensionRegistrarContext.configurePlugin() {
+        +::AtomicfuFirDeclarationCheckers
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuFirDeclarationCheckers.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuFirDeclarationCheckers.kt
new file mode 100644
index 0000000..95af767
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuFirDeclarationCheckers.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2010-2024 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.kotlinx.atomicfu.compiler.diagnostic
+
+import org.jetbrains.kotlin.fir.FirSession
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirPropertyChecker
+import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
+
+class AtomicfuFirDeclarationCheckers(session: FirSession) : FirAdditionalCheckersExtension(session) {
+    override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() {
+        override val propertyCheckers: Set<FirPropertyChecker>
+            get() = setOf(AtomicfuPropertyChecker)
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJvmIrTest.kt b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJvmIrTest.kt
deleted file mode 100644
index 72de0b5..0000000
--- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJvmIrTest.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.kotlinx.atomicfu
-
-import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
-import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar.ExtensionStorage
-import org.jetbrains.kotlin.config.CompilerConfiguration
-import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
-import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
-import org.jetbrains.kotlin.test.model.TestModule
-import org.jetbrains.kotlin.test.runners.codegen.AbstractFirLightTreeBlackBoxCodegenTest
-import org.jetbrains.kotlin.test.runners.codegen.AbstractIrBlackBoxCodegenTest
-import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
-import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider
-import org.jetbrains.kotlin.utils.PathUtil
-import org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuComponentRegistrar
-import java.io.File
-
-private val librariesPaths = getLibrariesPaths()
-
-open class AbstractAtomicfuJvmIrTest : AbstractIrBlackBoxCodegenTest() {
-    override fun configure(builder: TestConfigurationBuilder) {
-        super.configure(builder)
-        builder.configureForKotlinxAtomicfu(librariesPaths)
-    }
-}
-
-open class AbstractAtomicfuJvmFirLightTreeTest : AbstractFirLightTreeBlackBoxCodegenTest() {
-    override fun configure(builder: TestConfigurationBuilder) {
-        super.configure(builder)
-        builder.configureForKotlinxAtomicfu(librariesPaths)
-    }
-}
-
-private fun getLibraryJar(classToDetect: String): File? = try {
-    PathUtil.getResourcePathForClass(Class.forName(classToDetect))
-} catch (e: ClassNotFoundException) {
-    null
-}
-
-private fun getLibrariesPaths(): List<File> {
-    val coreLibraryPath = getLibraryJar("kotlinx.atomicfu.AtomicFU") ?: error("kotlinx.atomicfu library is not found")
-    val kotlinTestPath = getLibraryJar("kotlin.test.AssertionsKt") ?: error("kotlin.test is not found")
-    val kotlinJvm = getLibraryJar("kotlin.jvm.JvmField") ?: error("kotlin-stdlib is not found")
-    return listOf(coreLibraryPath, kotlinTestPath, kotlinJvm)
-}
-
-private fun TestConfigurationBuilder.configureForKotlinxAtomicfu(librariesPaths: List<File>) {
-    useConfigurators(
-        { services ->
-            object : EnvironmentConfigurator(services) {
-                override fun configureCompilerConfiguration(
-                    configuration: CompilerConfiguration,
-                    module: TestModule
-                ) {
-                    configuration.addJvmClasspathRoots(librariesPaths)
-                }
-
-                override fun ExtensionStorage.registerCompilerExtensions(module: TestModule, configuration: CompilerConfiguration) {
-                    AtomicfuComponentRegistrar.registerExtensions(this)
-                }
-            }
-        })
-
-    useCustomRuntimeClasspathProviders(
-        {
-            object : RuntimeClasspathProvider(it) {
-                override fun runtimeClassPaths(module: TestModule): List<File> = librariesPaths
-            }
-        }
-    )
-
-    defaultDirectives {
-        +CodegenTestDirectives.CHECK_BYTECODE_LISTING
-    }
-}
-
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJsIrTest.kt b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AbstractAtomicfuJsIrTest.kt
similarity index 98%
rename from plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJsIrTest.kt
rename to plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AbstractAtomicfuJsIrTest.kt
index 269ce9c..7f71594 100644
--- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJsIrTest.kt
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AbstractAtomicfuJsIrTest.kt
@@ -3,7 +3,7 @@
  * 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.kotlinx.atomicfu
+package org.jetbrains.kotlinx.atomicfu.runners
 
 import com.intellij.openapi.project.Project
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AbstractAtomicfuPluginTestRunners.kt b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AbstractAtomicfuPluginTestRunners.kt
new file mode 100644
index 0000000..3c1404b
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AbstractAtomicfuPluginTestRunners.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2024 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.kotlinx.atomicfu.runners
+
+import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
+import org.jetbrains.kotlin.test.frontend.fir.FirFailingTestSuppressor
+import org.jetbrains.kotlin.test.runners.AbstractFirPsiDiagnosticTest
+import org.jetbrains.kotlin.test.runners.codegen.*
+
+open class AbstractAtomicfuJvmIrTest : AbstractIrBlackBoxCodegenTest() {
+    override fun configure(builder: TestConfigurationBuilder) {
+        super.configure(builder)
+        builder.configureForKotlinxAtomicfu()
+    }
+}
+
+open class AbstractAtomicfuJvmFirLightTreeTest : AbstractFirLightTreeBlackBoxCodegenTest() {
+    override fun configure(builder: TestConfigurationBuilder) {
+        super.configure(builder)
+        builder.configureForKotlinxAtomicfu()
+    }
+}
+
+abstract class AbstractAtomicfuFirCheckerTest : AbstractFirPsiDiagnosticTest() {
+    override fun configure(builder: TestConfigurationBuilder) {
+        super.configure(builder)
+        with(builder) {
+            configureForKotlinxAtomicfu()
+            useAfterAnalysisCheckers(::FirFailingTestSuppressor)
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuExtensionRegistrarConfigurator.kt b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuExtensionRegistrarConfigurator.kt
new file mode 100644
index 0000000..952b65321
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuExtensionRegistrarConfigurator.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2010-2024 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.kotlinx.atomicfu.runners
+
+import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
+import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
+import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar.ExtensionStorage
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
+import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
+import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
+import org.jetbrains.kotlin.test.model.TestModule
+import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
+import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider
+import org.jetbrains.kotlin.test.services.TestServices
+import org.jetbrains.kotlin.utils.PathUtil
+import org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuFirExtensionRegistrar
+import org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuLoweringExtension
+import java.io.File
+
+internal fun TestConfigurationBuilder.configureForKotlinxAtomicfu() {
+    useConfigurators(
+        ::AtomicfuExtensionRegistrarConfigurator
+    )
+
+    useCustomRuntimeClasspathProviders(
+        {
+            object : RuntimeClasspathProvider(it) {
+                override fun runtimeClassPaths(module: TestModule): List<File> = getLibrariesPaths()
+            }
+        }
+    )
+
+    defaultDirectives {
+        +CodegenTestDirectives.CHECK_BYTECODE_LISTING
+    }
+}
+
+class AtomicfuExtensionRegistrarConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
+    override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule) {
+        configuration.addJvmClasspathRoots(getLibrariesPaths())
+    }
+
+    override fun ExtensionStorage.registerCompilerExtensions(module: TestModule, configuration: CompilerConfiguration) {
+        FirExtensionRegistrarAdapter.registerExtension(AtomicfuFirExtensionRegistrar())
+        IrGenerationExtension.registerExtension(AtomicfuLoweringExtension())
+    }
+}
+
+private fun getLibraryJar(classToDetect: String): File? = try {
+    PathUtil.getResourcePathForClass(Class.forName(classToDetect))
+} catch (e: ClassNotFoundException) {
+    null
+}
+
+private fun getLibrariesPaths(): List<File> {
+    val coreLibraryPath = getLibraryJar("kotlinx.atomicfu.AtomicFU") ?: error("kotlinx.atomicfu library is not found")
+    val kotlinTestPath = getLibraryJar("kotlin.test.AssertionsKt") ?: error("kotlin.test is not found")
+    val kotlinJvm = getLibraryJar("kotlin.jvm.JvmField") ?: error("kotlin-stdlib is not found")
+    return listOf(coreLibraryPath, kotlinTestPath, kotlinJvm)
+}
+
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/diagnostics/CheckAtomicVisibilityTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/diagnostics/CheckAtomicVisibilityTest.kt
new file mode 100644
index 0000000..e5cf12f
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/diagnostics/CheckAtomicVisibilityTest.kt
@@ -0,0 +1,53 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class A {
+    <!PUBLIC_ATOMICS_ARE_FORBIDDEN!>public<!> val a = atomic(true)
+    @PublishedApi <!PUBLISHED_API_ATOMICS_ARE_FORBIDDEN!>internal<!> val b = atomic(false)
+    <!PUBLIC_ATOMICS_ARE_FORBIDDEN!>public<!> <!ATOMIC_PROPERTIES_SHOULD_BE_VAL!>var<!> c = atomic(false)
+    internal val d = atomic(0)
+
+    public inline fun update(newValue: Boolean, lambda: (Boolean) -> Unit) {
+        val oldValue = b.getAndSet(newValue)
+        lambda(oldValue)
+    }
+}
+
+@PublishedApi
+internal class A1 {
+    val <!PUBLISHED_API_ATOMICS_ARE_FORBIDDEN!>a<!> = atomic(0)
+    @PublishedApi <!PUBLISHED_API_ATOMICS_ARE_FORBIDDEN!>internal<!> val b = atomic(false)
+    internal val c = atomic(0)
+}
+
+internal class A2 {
+    val a = atomic(0)
+    @PublishedApi internal val b = atomic(0)
+    internal val c = atomic(0)
+}
+
+private class A3 {
+    val a = atomic(0)
+    @PublishedApi internal val b = atomic(0)
+    internal val c = atomic(0)
+}
+
+abstract class Base {
+    val <!PUBLIC_ATOMICS_ARE_FORBIDDEN!>a<!> = atomic(0)
+    @PublishedApi <!PUBLISHED_API_ATOMICS_ARE_FORBIDDEN!>internal<!> val b = atomic(0)
+    internal val c = atomic(0)
+    <!PUBLIC_ATOMICS_ARE_FORBIDDEN!>protected<!> val d = atomic(0)
+
+    protected class Nested {
+        val <!PUBLIC_ATOMICS_ARE_FORBIDDEN!>nestedA<!> = atomic(0)
+        @PublishedApi <!PUBLISHED_API_ATOMICS_ARE_FORBIDDEN!>internal<!> val nestedB = atomic(0)
+        internal val nestedC = atomic(0)
+        <!PUBLIC_ATOMICS_ARE_FORBIDDEN!>protected<!> val nestedD = atomic(0)
+    }
+}
+
+fun box(): String {
+    val aClass = A()
+    aClass.update(true) { i -> assertFalse(i) }
+    return "OK"
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuFirCheckerTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuFirCheckerTestGenerated.java
new file mode 100644
index 0000000..b4c870d
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuFirCheckerTestGenerated.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010-2024 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.kotlinx.atomicfu.runners;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.test.util.KtTestUtil;
+import org.jetbrains.kotlin.test.TargetBackend;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateTestsKt}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/diagnostics")
+@TestDataPath("$PROJECT_ROOT")
+public class AtomicfuFirCheckerTestGenerated extends AbstractAtomicfuFirCheckerTest {
+  @Test
+  public void testAllFilesPresentInDiagnostics() {
+    KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null, true);
+  }
+
+  @Test
+  @TestMetadata("CheckAtomicVisibilityTest.kt")
+  public void testCheckAtomicVisibilityTest() {
+    runTest("plugins/atomicfu/atomicfu-compiler/testData/diagnostics/CheckAtomicVisibilityTest.kt");
+  }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsFirTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJsFirTestGenerated.java
similarity index 99%
rename from plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsFirTestGenerated.java
rename to plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJsFirTestGenerated.java
index 5ab65cc..3417b98 100644
--- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsFirTestGenerated.java
+++ b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJsFirTestGenerated.java
@@ -3,7 +3,7 @@
  * 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.kotlinx.atomicfu;
+package org.jetbrains.kotlinx.atomicfu.runners;
 
 import com.intellij.testFramework.TestDataPath;
 import org.jetbrains.kotlin.test.util.KtTestUtil;
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsIrTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJsIrTestGenerated.java
similarity index 99%
rename from plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsIrTestGenerated.java
rename to plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJsIrTestGenerated.java
index cdcfb6b..6f176dd 100644
--- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsIrTestGenerated.java
+++ b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJsIrTestGenerated.java
@@ -3,7 +3,7 @@
  * 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.kotlinx.atomicfu;
+package org.jetbrains.kotlinx.atomicfu.runners;
 
 import com.intellij.testFramework.TestDataPath;
 import org.jetbrains.kotlin.test.util.KtTestUtil;
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmFirLightTreeTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJvmFirLightTreeTestGenerated.java
similarity index 99%
rename from plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmFirLightTreeTestGenerated.java
rename to plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJvmFirLightTreeTestGenerated.java
index 8faaf0a..2120205 100644
--- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmFirLightTreeTestGenerated.java
+++ b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJvmFirLightTreeTestGenerated.java
@@ -3,7 +3,7 @@
  * 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.kotlinx.atomicfu;
+package org.jetbrains.kotlinx.atomicfu.runners;
 
 import com.intellij.testFramework.TestDataPath;
 import org.jetbrains.kotlin.test.util.KtTestUtil;
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmIrTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJvmIrTestGenerated.java
similarity index 99%
rename from plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmIrTestGenerated.java
rename to plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJvmIrTestGenerated.java
index f7dea97..6d8332d 100644
--- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmIrTestGenerated.java
+++ b/plugins/atomicfu/atomicfu-compiler/tests-gen/org/jetbrains/kotlinx/atomicfu/runners/AtomicfuJvmIrTestGenerated.java
@@ -3,7 +3,7 @@
  * 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.kotlinx.atomicfu;
+package org.jetbrains.kotlinx.atomicfu.runners;
 
 import com.intellij.testFramework.TestDataPath;
 import org.jetbrains.kotlin.test.util.KtTestUtil;