[K2] Fix bug: generated 'copy' method from another module wasn't correctly recognized by FirDataClassCopyUsageWillBecomeInaccessibleChecker

KT-11914
Review: https://jetbrains.team/p/kt/reviews/15489/timeline

The bug: unfortunatelly, `origin` returns `Library` (instead of
`FirDeclarationOrigin.Synthetic.DataClassMember`) for declarations from
external modules, but the Checker only tested for
`FirDeclarationOrigin.Synthetic.DataClassMember`

Moreover, we already had the multi-module test that should have
prevented this bug from happening
`compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase2_internalConstructor.kt`
but there is another bug (or misunderstanding from my side?) in test
infrastructure. In `deprecationPhase2_internalConstructor`, `origin`
returns `FirDeclarationOrigin.Synthetic.DataClassMember` for unknown
reason.

That's why I introduce full blown up Gradle test
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt
index 1e6d130..666241b 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt
@@ -7,16 +7,21 @@
 
 import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
 import org.jetbrains.kotlin.diagnostics.reportOn
+import org.jetbrains.kotlin.fir.FirSession
 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.primaryConstructorSymbol
 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
 import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
 import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
+import org.jetbrains.kotlin.fir.declarations.utils.isData
 import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
 import org.jetbrains.kotlin.fir.references.symbol
 import org.jetbrains.kotlin.fir.symbols.SymbolInternals
 import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
+import org.jetbrains.kotlin.fir.types.classId
 import org.jetbrains.kotlin.fir.types.resolvedType
 import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
 import org.jetbrains.kotlin.fir.visibilityChecker
@@ -24,9 +29,9 @@
 
 object FirDataClassCopyUsageWillBecomeInaccessibleChecker : FirFunctionCallChecker(MppCheckerKind.Common) {
     override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
-        val dataClass = expression.resolvedType.toRegularClassSymbol(context.session) ?: return
+        val dataClass = expression.resolvedType.toRegularClassSymbol(context.session)?.takeIf { it.isData } ?: return
         val copyFunction = expression.calleeReference.symbol as? FirCallableSymbol ?: return
-        if (DataClassResolver.isCopy(copyFunction.name) && copyFunction.origin == FirDeclarationOrigin.Synthetic.DataClassMember) {
+        if (copyFunction.isDataClassCopy(dataClass, context.session)) {
             val dataClassConstructor = dataClass.primaryConstructorSymbol(context.session) ?: return
 
             @OptIn(SymbolInternals::class)
@@ -57,3 +62,18 @@
         }
     }
 }
+
+fun FirCallableSymbol<*>.isDataClassCopy(dataClass: FirRegularClassSymbol, session: FirSession): Boolean {
+    if (DataClassResolver.isCopy(name) && this is FirNamedFunctionSymbol) {
+        if (origin == FirDeclarationOrigin.Synthetic.DataClassMember) {
+            return true
+        }
+        val constructor = dataClass.primaryConstructorSymbol(session)
+        if (constructor != null && origin == FirDeclarationOrigin.Library && receiverParameter == null &&
+            valueParameterSymbols.map { it.resolvedReturnType.classId } == constructor.valueParameterSymbols.map { it.resolvedReturnType.classId }
+        ) {
+            return true
+        }
+    }
+    return false
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt
index e579fcf..38fe60f 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt
@@ -90,6 +90,15 @@
     }
 
     @GradleTest
+    fun testDataClassInternalConstructorUsageWillBecomeInaccessible(gradleVersion: GradleVersion) {
+        project("dataClassInternalConstructorUsageWillBecomeInaccessible", gradleVersion) {
+            buildAndFail("assemble") {
+                assertOutputContains("This 'copy()' exposes the non-public primary constructor of a 'data class'. Please migrate the usage.")
+            }
+        }
+    }
+
+    @GradleTest
     @DisplayName("Should produce '.kotlin_module' file with specified name")
     fun testModuleName(gradleVersion: GradleVersion) {
         project("moduleName", gradleVersion) {
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/build.gradle.kts
new file mode 100644
index 0000000..0e6aecb
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/build.gradle.kts
@@ -0,0 +1,20 @@
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
+import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
+
+plugins {
+    kotlin("jvm")
+}
+
+repositories {
+    mavenLocal()
+    mavenCentral()
+}
+
+dependencies {
+    implementation(project(":lib"))
+}
+
+tasks.withType<KotlinJvmCompile>().configureEach {
+    compilerOptions.progressiveMode.set(true)
+    compilerOptions.languageVersion.set(KotlinVersion.KOTLIN_2_0)
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/lib/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/lib/build.gradle.kts
new file mode 100644
index 0000000..6a6b756
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/lib/build.gradle.kts
@@ -0,0 +1,15 @@
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
+import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
+
+plugins {
+    kotlin("jvm")
+}
+
+repositories {
+    mavenLocal()
+    mavenCentral()
+}
+
+tasks.withType<KotlinJvmCompile>().configureEach {
+    compilerOptions.languageVersion.set(KotlinVersion.KOTLIN_2_0)
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/lib/src/main/kotlin/Lib.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/lib/src/main/kotlin/Lib.kt
new file mode 100644
index 0000000..6b14ef5
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/lib/src/main/kotlin/Lib.kt
@@ -0,0 +1,6 @@
+@ExposedCopyVisibility
+data class Foo private constructor(val x: Int) {
+    companion object {
+        fun new() = Foo(1)
+    }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/settings.gradle.kts
new file mode 100644
index 0000000..408a5e7
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/settings.gradle.kts
@@ -0,0 +1 @@
+include(":lib")
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/src/main/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/src/main/kotlin/main.kt
new file mode 100644
index 0000000..79adf32
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/dataClassInternalConstructorUsageWillBecomeInaccessible/src/main/kotlin/main.kt
@@ -0,0 +1,3 @@
+fun main() {
+    Foo.new().copy()
+}