[FE 1.0] Prohibit implicit Java actualization in K1

^KT-58545 Fixed
Review: https://jetbrains.team/p/kt/reviews/10561

It's not yet supported in K2 KT-59213

Related tests:
- ApiTest.testStdlib
- RuntimePublicAPITest.kotlinStdlibRuntimeMerged
- KotlinProjectViewTestGenerated.test_Arrays (in Kotlin plugin)
This commit is contained in:
Nikita Bobko
2023-06-02 18:06:11 +02:00
parent d0452d569f
commit f4ba5aaf9a
18 changed files with 268 additions and 5 deletions
@@ -878,6 +878,24 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
runTest("compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.kt");
}
@Test
@TestMetadata("implicitJavaActualizationAllowed.kt")
public void testImplicitJavaActualizationAllowed() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualizationAllowed.kt");
}
@Test
@TestMetadata("implicitJavaActualizationDisallowed.kt")
public void testImplicitJavaActualizationDisallowed() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualizationDisallowed.kt");
}
@Test
@TestMetadata("implicitJavaActualization_multipleActuals.kt")
public void testImplicitJavaActualization_multipleActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualization_multipleActuals.kt");
}
@Test
@TestMetadata("inheritedJavaMembers.kt")
public void testInheritedJavaMembers() throws Exception {
@@ -878,6 +878,24 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
runTest("compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.kt");
}
@Test
@TestMetadata("implicitJavaActualizationAllowed.kt")
public void testImplicitJavaActualizationAllowed() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualizationAllowed.kt");
}
@Test
@TestMetadata("implicitJavaActualizationDisallowed.kt")
public void testImplicitJavaActualizationDisallowed() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualizationDisallowed.kt");
}
@Test
@TestMetadata("implicitJavaActualization_multipleActuals.kt")
public void testImplicitJavaActualization_multipleActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualization_multipleActuals.kt");
}
@Test
@TestMetadata("inheritedJavaMembers.kt")
public void testInheritedJavaMembers() throws Exception {
@@ -817,6 +817,8 @@ public interface Errors {
DiagnosticFactory3<KtNamedDeclaration, MemberDescriptor, ModuleDescriptor,
Map<Incompatible<MemberDescriptor>, Collection<MemberDescriptor>>> NO_ACTUAL_FOR_EXPECT =
DiagnosticFactory3.create(ERROR, INCOMPATIBLE_DECLARATION);
DiagnosticFactory2<KtNamedDeclaration, MemberDescriptor, ModuleDescriptor> IMPLICIT_JVM_ACTUALIZATION =
DiagnosticFactory2.create(ERROR, INCOMPATIBLE_DECLARATION);
DiagnosticFactory2<KtNamedDeclaration, MemberDescriptor,
Map<Incompatible<MemberDescriptor>, Collection<MemberDescriptor>>> ACTUAL_WITHOUT_EXPECT =
DiagnosticFactory2.create(ERROR, INCOMPATIBLE_DECLARATION);
@@ -374,6 +374,10 @@ public class DefaultErrorMessages {
MAP.put(NO_ACTUAL_FOR_EXPECT, "Expected {0} has no actual declaration in module {1}{2}", DECLARATION_NAME_WITH_KIND,
MODULE_WITH_PLATFORM, adaptGenerics1(PlatformIncompatibilityDiagnosticRenderer.TEXT));
MAP.put(IMPLICIT_JVM_ACTUALIZATION, "Expected {0} is implicitly actualized by Java declaration in module {1}. " +
"Please migrate to explicit ''actual typealias''. See: https://youtrack.jetbrains.com/issue/KT-58545",
DECLARATION_NAME_WITH_KIND,
MODULE_WITH_PLATFORM);
MAP.put(ACTUAL_WITHOUT_EXPECT, "{0} has no corresponding expected declaration{1}", CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM,
adaptGenerics1(PlatformIncompatibilityDiagnosticRenderer.TEXT));
MAP.put(AMBIGUOUS_ACTUALS, "{0} has several compatible actual declarations in modules {1}", CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM, CommonRenderers.commaSeparated(
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.mpp.MppJavaImplicitActualizatorMarker
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.resolve.*
@@ -40,6 +42,8 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.cast
import java.io.File
private val implicitlyActualizedAnnotationFqn = FqName("kotlin.jvm.ImplicitlyActualizedByJvmDeclaration")
class ExpectedActualDeclarationChecker(
val moduleStructureOracle: ModuleStructureOracle,
val argumentExtractors: Iterable<ActualAnnotationArgumentExtractor>
@@ -64,7 +68,7 @@ class ExpectedActualDeclarationChecker(
if (descriptor.isExpect) {
checkExpectedDeclarationHasProperActuals(
declaration, descriptor, context.trace,
checkActualModifier, context.expectActualTracker
checkActualModifier, context
)
checkOptInAnnotation(declaration, descriptor, descriptor, context.trace)
}
@@ -93,7 +97,7 @@ class ExpectedActualDeclarationChecker(
descriptor: MemberDescriptor,
trace: BindingTrace,
checkActualModifier: Boolean,
expectActualTracker: ExpectActualTracker
context: DeclarationCheckerContext
) {
val allActualizationPaths = moduleStructureOracle.findAllReversedDependsOnPaths(descriptor.module)
val allLeafModules = allActualizationPaths.map { it.nodes.last() }.toSet()
@@ -102,15 +106,41 @@ class ExpectedActualDeclarationChecker(
val actuals = ExpectedActualResolver.findActualForExpected(descriptor, leafModule) ?: return@forEach
checkExpectedDeclarationHasAtLeastOneActual(
reportOn, descriptor, actuals, trace, leafModule, checkActualModifier, expectActualTracker
reportOn, descriptor, actuals, trace, leafModule, checkActualModifier, context.expectActualTracker
)
checkImplicitJavaActualization(reportOn, descriptor, actuals, leafModule, context)
checkExpectedDeclarationHasAtMostOneActual(
reportOn, descriptor, actuals, allActualizationPaths, trace
)
}
}
private fun checkImplicitJavaActualization(
expectPsi: KtNamedDeclaration,
expect: MemberDescriptor,
actuals: ActualsMap,
module: ModuleDescriptor,
context: DeclarationCheckerContext
) {
val actualMembers = actuals
.filter { (compatibility, _) -> compatibility.isCompatibleOrWeakCompatible() }
.flatMap { (_, members) -> members }
.takeIf(List<MemberDescriptor>::isNotEmpty)
?: return
if (actualMembers.any {
it is MppJavaImplicitActualizatorMarker &&
with(OptInUsageChecker) {
!expectPsi.isDeclarationAnnotatedWith(implicitlyActualizedAnnotationFqn, context.trace.bindingContext)
}
}
) {
context.trace.report(Errors.IMPLICIT_JVM_ACTUALIZATION.on(expectPsi, expect, module))
}
}
private fun checkExpectedDeclarationHasAtMostOneActual(
reportOn: KtNamedDeclaration,
expectDescriptor: MemberDescriptor,
@@ -343,7 +343,7 @@ class OptInUsageChecker(project: Project) : CallChecker {
}
}
private fun PsiElement.isDeclarationAnnotatedWith(annotationFqName: FqName, bindingContext: BindingContext): Boolean {
internal fun PsiElement.isDeclarationAnnotatedWith(annotationFqName: FqName, bindingContext: BindingContext): Boolean {
if (this !is KtDeclaration) return false
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, this)
@@ -0,0 +1,20 @@
// WITH_STDLIB
// MODULE: m1-common
// FILE: common.kt
import kotlin.jvm.ImplicitlyActualizedByJvmDeclaration
<!NO_ACTUAL_FOR_EXPECT{JVM}!>@OptIn(ExperimentalMultiplatform::class)
@ImplicitlyActualizedByJvmDeclaration
expect class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>Foo<!>() {
fun foo()
}<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: Foo.java
public class Foo {
public void foo() {
}
}
@@ -0,0 +1,20 @@
// WITH_STDLIB
// MODULE: m1-common
// FILE: common.kt
import kotlin.jvm.ImplicitlyActualizedByJvmDeclaration
@OptIn(ExperimentalMultiplatform::class)
@ImplicitlyActualizedByJvmDeclaration
expect class Foo() {
fun foo()
}
// MODULE: m2-jvm()()(m1-common)
// FILE: Foo.java
public class Foo {
public void foo() {
}
}
@@ -0,0 +1,14 @@
// MODULE: m1-common
// FILE: common.kt
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>Foo<!>() {
fun foo()
}<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: Foo.java
public class Foo {
public void foo() {
}
}
@@ -0,0 +1,14 @@
// MODULE: m1-common
// FILE: common.kt
expect class <!IMPLICIT_JVM_ACTUALIZATION{JVM}!>Foo<!>() {
fun foo()
}
// MODULE: m2-jvm()()(m1-common)
// FILE: Foo.java
public class Foo {
public void foo() {
}
}
@@ -0,0 +1,20 @@
// MODULE: m1-common
// FILE: common.kt
<!INCOMPATIBLE_MATCHING{JVM}!>expect class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>Foo<!>(i: Int) {
fun foo()
}<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: Foo.java
public class Foo {
public Foo(int i) {}
public void foo() {}
}
// FILE: jvm.kt
class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>Foo<!><T>(t: T) {
fun foo() {}
}
@@ -0,0 +1,20 @@
// MODULE: m1-common
// FILE: common.kt
expect class <!IMPLICIT_JVM_ACTUALIZATION{JVM}!>Foo<!>(i: Int) {
fun <!AMBIGUOUS_ACTUALS{JVM}!>foo<!>()
}
// MODULE: m2-jvm()()(m1-common)
// FILE: Foo.java
public class Foo {
public Foo(int i) {}
public void foo() {}
}
// FILE: jvm.kt
class Foo<T>(t: T) {
fun foo() {}
}
@@ -23441,6 +23441,24 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/flexibleTypes.kt");
}
@Test
@TestMetadata("implicitJavaActualizationAllowed.kt")
public void testImplicitJavaActualizationAllowed() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualizationAllowed.kt");
}
@Test
@TestMetadata("implicitJavaActualizationDisallowed.kt")
public void testImplicitJavaActualizationDisallowed() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualizationDisallowed.kt");
}
@Test
@TestMetadata("implicitJavaActualization_multipleActuals.kt")
public void testImplicitJavaActualization_multipleActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualization_multipleActuals.kt");
}
@Test
@TestMetadata("inheritedJavaMembers.kt")
public void testInheritedJavaMembers() throws Exception {