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.KtTypeAlias
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
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.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.MultifileClassPart
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
@@ -125,8 +129,19 @@ class MultifileClassCodegenImpl(
|
||||
val superClassForFacade = if (shouldGeneratePartHierarchy) partInternalNamesSorted.last() else J_L_OBJECT
|
||||
|
||||
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(
|
||||
singleSourceFile, state.classFileVersion, FACADE_CLASS_ATTRIBUTES,
|
||||
singleSourceFile, state.classFileVersion, attributes,
|
||||
facadeClassType.internalName, null, superClassForFacade, emptyArray()
|
||||
)
|
||||
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() {
|
||||
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(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. " +
|
||||
"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<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> 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.JvmLoweredDeclarationOrigin
|
||||
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.descriptors.DescriptorVisibilities
|
||||
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.resolve.inline.INLINE_ONLY_ANNOTATION_FQ_NAME
|
||||
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>(
|
||||
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)
|
||||
|
||||
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
|
||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
|
||||
@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
|
||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+5
@@ -1548,6 +1548,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
|
||||
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")
|
||||
public void testKt43519() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("jvmSynthetic.kt")
|
||||
public void testJvmSynthetic() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/jvmSynthetic.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt43519.kt")
|
||||
public void testKt43519() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/multifileClasses/kt43519.kt");
|
||||
|
||||
Reference in New Issue
Block a user