JVM, JVM IR: report error if not all parts of multifile class are @JvmSynthetic
#KT-41884 Fixed
This commit is contained in:
@@ -34,9 +34,13 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtTypeAlias
|
import org.jetbrains.kotlin.psi.KtTypeAlias
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.MemberComparator
|
import org.jetbrains.kotlin.resolve.MemberComparator
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClassPart
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClassPart
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
@@ -125,8 +129,19 @@ class MultifileClassCodegenImpl(
|
|||||||
val superClassForFacade = if (shouldGeneratePartHierarchy) partInternalNamesSorted.last() else J_L_OBJECT
|
val superClassForFacade = if (shouldGeneratePartHierarchy) partInternalNamesSorted.last() else J_L_OBJECT
|
||||||
|
|
||||||
state.factory.newVisitor(MultifileClass(files.firstOrNull(), actualPackageFragment), facadeClassType, files).apply {
|
state.factory.newVisitor(MultifileClass(files.firstOrNull(), actualPackageFragment), facadeClassType, files).apply {
|
||||||
|
var attributes = FACADE_CLASS_ATTRIBUTES
|
||||||
|
|
||||||
|
val nonJvmSyntheticParts = files.filterNot { it.isJvmSynthetic() }
|
||||||
|
if (nonJvmSyntheticParts.isEmpty()) {
|
||||||
|
attributes = attributes or Opcodes.ACC_SYNTHETIC
|
||||||
|
} else if (nonJvmSyntheticParts.size < files.size) {
|
||||||
|
for (part in nonJvmSyntheticParts) {
|
||||||
|
state.diagnostics.report(ErrorsJvm.NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC.on(part.packageDirective ?: part))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defineClass(
|
defineClass(
|
||||||
singleSourceFile, state.classFileVersion, FACADE_CLASS_ATTRIBUTES,
|
singleSourceFile, state.classFileVersion, attributes,
|
||||||
facadeClassType.internalName, null, superClassForFacade, emptyArray()
|
facadeClassType.internalName, null, superClassForFacade, emptyArray()
|
||||||
)
|
)
|
||||||
if (singleSourceFile != null) {
|
if (singleSourceFile != null) {
|
||||||
@@ -146,6 +161,13 @@ class MultifileClassCodegenImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtFile.isJvmSynthetic(): Boolean {
|
||||||
|
return annotationEntries.any { entry ->
|
||||||
|
val descriptor = state.bindingContext[BindingContext.ANNOTATION, entry]
|
||||||
|
descriptor?.annotationClass?.let(DescriptorUtils::getFqNameSafe) == JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun generate() {
|
override fun generate() {
|
||||||
assert(delegateGenerationTasks.isEmpty()) { "generate() is called twice for facade class $facadeFqName" }
|
assert(delegateGenerationTasks.isEmpty()) { "generate() is called twice for facade class $facadeFqName" }
|
||||||
|
|
||||||
|
|||||||
+1
@@ -78,6 +78,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
|||||||
MAP.put(JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES, "''@JvmPackageName'' annotation is not supported for files with class declarations");
|
MAP.put(JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES, "''@JvmPackageName'' annotation is not supported for files with class declarations");
|
||||||
|
|
||||||
MAP.put(STATE_IN_MULTIFILE_CLASS, "Non-const property with backing field or delegate is not allowed in a multi-file class if -Xmultifile-parts-inherit is enabled");
|
MAP.put(STATE_IN_MULTIFILE_CLASS, "Non-const property with backing field or delegate is not allowed in a multi-file class if -Xmultifile-parts-inherit is enabled");
|
||||||
|
MAP.put(NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC, "All of multi-file class parts should be annotated with @JvmSynthetic if at least one of them is");
|
||||||
|
|
||||||
MAP.put(NO_REFLECTION_IN_CLASS_PATH, "Call uses reflection API which is not found in compilation classpath. " +
|
MAP.put(NO_REFLECTION_IN_CLASS_PATH, "Call uses reflection API which is not found in compilation classpath. " +
|
||||||
"Make sure you have kotlin-reflect.jar in the classpath");
|
"Make sure you have kotlin-reflect.jar in the classpath");
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ public interface ErrorsJvm {
|
|||||||
DiagnosticFactory0<KtAnnotationEntry> JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtAnnotationEntry> JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<KtDeclaration> STATE_IN_MULTIFILE_CLASS = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtDeclaration> STATE_IN_MULTIFILE_CLASS = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<PsiElement> NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<PsiElement> INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+18
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.backend.common.phaser.makeCustomPhase
|
|||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.fileParent
|
import org.jetbrains.kotlin.backend.jvm.codegen.fileParent
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.ir.getKtFile
|
||||||
import org.jetbrains.kotlin.config.JvmAnalysisFlags
|
import org.jetbrains.kotlin.config.JvmAnalysisFlags
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
@@ -42,6 +43,8 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
|||||||
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.resolve.inline.INLINE_ONLY_ANNOTATION_FQ_NAME
|
import org.jetbrains.kotlin.resolve.inline.INLINE_ONLY_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||||
|
|
||||||
internal val generateMultifileFacadesPhase = makeCustomPhase<JvmBackendContext, IrModuleFragment>(
|
internal val generateMultifileFacadesPhase = makeCustomPhase<JvmBackendContext, IrModuleFragment>(
|
||||||
name = "GenerateMultifileFacades",
|
name = "GenerateMultifileFacades",
|
||||||
@@ -133,7 +136,22 @@ private fun generateMultifileFacades(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val nonJvmSyntheticParts = partClasses.filterNot { it.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) }
|
||||||
|
if (nonJvmSyntheticParts.isEmpty()) {
|
||||||
|
annotations = annotations + partClasses.first().getAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)!!.deepCopyWithSymbols()
|
||||||
|
} else if (nonJvmSyntheticParts.size < partClasses.size) {
|
||||||
|
for (part in nonJvmSyntheticParts) {
|
||||||
|
val partFile = part.fileParent.getKtFile() ?: error("Not a KtFile: ${part.render()} ${part.fileParent}")
|
||||||
|
// If at least one of parts is annotated with @JvmSynthetic, then all other parts should also be annotated.
|
||||||
|
// We report this error on the package directive for each non-@JvmSynthetic part.
|
||||||
|
context.state.diagnostics.report(
|
||||||
|
ErrorsJvm.NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC.on(partFile.packageDirective ?: partFile)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.declarations.add(facadeClass)
|
file.declarations.add(facadeClass)
|
||||||
|
|
||||||
for (partClass in partClasses) {
|
for (partClass in partClasses) {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// FILE: f.kt
|
||||||
|
|
||||||
|
@file:JvmName("Foo")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
@file:JvmSynthetic
|
||||||
|
package test
|
||||||
|
|
||||||
|
fun f() {}
|
||||||
|
|
||||||
|
// FILE: g.kt
|
||||||
|
|
||||||
|
@file:JvmName("Foo")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
@file:JvmSynthetic
|
||||||
|
package test
|
||||||
|
|
||||||
|
val g = ""
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public synthetic final class test/Foo {
|
||||||
|
public final static method f(): void
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method getG(): java.lang.String
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
synthetic final class test/Foo__FKt {
|
||||||
|
// source: 'f.kt'
|
||||||
|
public final static method f(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
synthetic final class test/Foo__GKt {
|
||||||
|
// source: 'g.kt'
|
||||||
|
private final static @org.jetbrains.annotations.NotNull field g: java.lang.String
|
||||||
|
static method <clinit>(): void
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method getG(): java.lang.String
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// FILE: f.kt
|
||||||
|
|
||||||
|
@file:JvmName("Foo")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
<!NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC!>package test<!>
|
||||||
|
|
||||||
|
fun f() {}
|
||||||
|
|
||||||
|
// FILE: g.kt
|
||||||
|
|
||||||
|
@file:JvmName("Foo")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
@file:JvmSynthetic
|
||||||
|
package test
|
||||||
|
|
||||||
|
val g = ""
|
||||||
|
|
||||||
|
// FILE: h.kt
|
||||||
|
|
||||||
|
@file:JvmName("Foo")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
<!NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC!>package test<!>
|
||||||
|
|
||||||
|
fun h() {}
|
||||||
|
|
||||||
|
// FILE: z.kt
|
||||||
|
|
||||||
|
@file:JvmName("Bar")
|
||||||
|
@file:JvmMultifileClass
|
||||||
|
package test
|
||||||
|
|
||||||
|
fun z() {}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
package test {
|
||||||
|
public val g: kotlin.String = ""
|
||||||
|
public fun f(): kotlin.Unit
|
||||||
|
public fun h(): kotlin.Unit
|
||||||
|
public fun z(): kotlin.Unit
|
||||||
|
}
|
||||||
+16
@@ -641,6 +641,22 @@ public class DiagnosticsTestWithJvmIrBackendGenerated extends AbstractDiagnostic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/multifileClasses")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
public class MultifileClasses {
|
||||||
|
@Test
|
||||||
|
public void testAllFilesPresentInMultifileClasses() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("jvmSynthetic.kt")
|
||||||
|
public void testJvmSynthetic() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithJvmBackend/multifileClasses/jvmSynthetic.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
|
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
+16
@@ -629,6 +629,22 @@ public class DiagnosticsTestWithOldJvmBackendGenerated extends AbstractDiagnosti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/multifileClasses")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
public class MultifileClasses {
|
||||||
|
@Test
|
||||||
|
public void testAllFilesPresentInMultifileClasses() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_OLD, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("jvmSynthetic.kt")
|
||||||
|
public void testJvmSynthetic() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithJvmBackend/multifileClasses/jvmSynthetic.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
|
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
+5
@@ -1548,6 +1548,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/emptyMultifileFacade.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/emptyMultifileFacade.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("jvmSynthetic.kt")
|
||||||
|
public void testJvmSynthetic() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/jvmSynthetic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt43519.kt")
|
@TestMetadata("kt43519.kt")
|
||||||
public void testKt43519() throws Exception {
|
public void testKt43519() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/kt43519.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/kt43519.kt");
|
||||||
|
|||||||
+5
@@ -1548,6 +1548,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
|
|||||||
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/emptyMultifileFacade.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/emptyMultifileFacade.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("jvmSynthetic.kt")
|
||||||
|
public void testJvmSynthetic() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/jvmSynthetic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt43519.kt")
|
@TestMetadata("kt43519.kt")
|
||||||
public void testKt43519() throws Exception {
|
public void testKt43519() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/kt43519.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/kt43519.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user