mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
only stub default constructor when compiling against .java source files
This commit is contained in:
committed by
Alexander Udalov
parent
c83860187c
commit
7448761dfd
@@ -250,7 +250,9 @@ class JavaSymbolProvider(
|
||||
}
|
||||
}
|
||||
|
||||
if (javaClassDeclaredConstructors.isEmpty() && javaClass.classKind == ClassKind.CLASS) {
|
||||
if (javaClassDeclaredConstructors.isEmpty()
|
||||
&& javaClass.classKind == ClassKind.CLASS
|
||||
&& javaClass.hasDefaultConstructor()) {
|
||||
declarations += prepareJavaConstructor(isPrimary = true).build()
|
||||
}
|
||||
for (javaConstructor in javaClassDeclaredConstructors) {
|
||||
|
||||
@@ -100,6 +100,8 @@ class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass)
|
||||
return constructors(psi.constructors.filter { method -> method.isConstructor })
|
||||
}
|
||||
|
||||
override fun hasDefaultConstructor() = !isInterface && constructors.isEmpty()
|
||||
|
||||
override val isAbstract: Boolean
|
||||
get() = JavaElementUtil.isAbstract(this)
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ class BinaryJavaClass(
|
||||
override val methods = arrayListOf<JavaMethod>()
|
||||
override val fields = arrayListOf<JavaField>()
|
||||
override val constructors = arrayListOf<JavaConstructor>()
|
||||
override fun hasDefaultConstructor() = false // never: all constructors explicit in bytecode
|
||||
|
||||
override val annotationsByFqName by buildLazyValueForMap()
|
||||
|
||||
|
||||
@@ -159,6 +159,7 @@ class MockKotlinClassifier(override val classId: ClassId,
|
||||
override val methods get() = shouldNotBeCalled()
|
||||
override val fields get() = shouldNotBeCalled()
|
||||
override val constructors get() = shouldNotBeCalled()
|
||||
override fun hasDefaultConstructor() = shouldNotBeCalled()
|
||||
override val annotations get() = shouldNotBeCalled()
|
||||
override val isDeprecatedInJavaDoc get() = shouldNotBeCalled()
|
||||
override fun findAnnotation(fqName: FqName) = shouldNotBeCalled()
|
||||
|
||||
@@ -73,6 +73,8 @@ class FakeSymbolBasedClass(
|
||||
|
||||
override val constructors: Collection<JavaConstructor> get() = emptyList()
|
||||
|
||||
override fun hasDefaultConstructor() = false
|
||||
|
||||
override val innerClassNames: Collection<Name> get() = emptyList()
|
||||
|
||||
override val virtualFile: VirtualFile? by lazy {
|
||||
|
||||
@@ -133,6 +133,8 @@ class SymbolBasedClass(
|
||||
.filter { it.kind == ElementKind.CONSTRUCTOR }
|
||||
.map { SymbolBasedConstructor(it as ExecutableElement, this, javac) }
|
||||
|
||||
override fun hasDefaultConstructor() = false // default constructors are explicit in symbols
|
||||
|
||||
override val innerClassNames: Collection<Name>
|
||||
get() = innerClasses.keys
|
||||
|
||||
|
||||
@@ -134,6 +134,8 @@ class TreeBasedClass(
|
||||
TreeBasedConstructor(constructor as JCTree.JCMethodDecl, compilationUnit, this, javac)
|
||||
}
|
||||
|
||||
override fun hasDefaultConstructor() = !isInterface && constructors.isEmpty()
|
||||
|
||||
override val innerClassNames: Collection<Name>
|
||||
get() = innerClasses.keys
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
fun foo(msg: String) = msg
|
||||
@@ -0,0 +1 @@
|
||||
OK
|
||||
4
compiler/testData/compileKotlinAgainstCustomBinaries/classfileWithoutConstructors/output.txt
vendored
Normal file
4
compiler/testData/compileKotlinAgainstCustomBinaries/classfileWithoutConstructors/output.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
compiler/testData/compileKotlinAgainstCustomBinaries/classfileWithoutConstructors/shouldNotCompile.kt:6:9: error: unresolved reference: TopLevelKt
|
||||
TopLevelKt() // error here
|
||||
^
|
||||
COMPILATION_ERROR
|
||||
@@ -0,0 +1,10 @@
|
||||
package test
|
||||
|
||||
public class B {
|
||||
|
||||
public fun test(): String {
|
||||
TopLevelKt() // error here
|
||||
return TopLevelKt.foo("OK") // no error here: can still call static functions
|
||||
}
|
||||
|
||||
}
|
||||
4
compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.java
vendored
Normal file
4
compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.java
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
package test;
|
||||
|
||||
public class ClassDefaultConstructor {
|
||||
}
|
||||
3
compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.kt
vendored
Normal file
3
compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.kt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
fun useClassDefaultConstructor() = ClassDefaultConstructor()
|
||||
7
compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.txt
vendored
Normal file
7
compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
public fun useClassDefaultConstructor(): test.ClassDefaultConstructor
|
||||
|
||||
public open class ClassDefaultConstructor {
|
||||
public constructor ClassDefaultConstructor()
|
||||
}
|
||||
@@ -458,6 +458,37 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
||||
}
|
||||
}
|
||||
|
||||
/* Regression test for KT-37107: compile against .class file without any constructors. */
|
||||
fun testClassfileWithoutConstructors() {
|
||||
compileKotlin("TopLevel.kt", tmpdir, expectedFileName = "TopLevel.txt")
|
||||
|
||||
val inlineFunClass = File(tmpdir.absolutePath, "test/TopLevelKt.class")
|
||||
val cw = ClassWriter(Opcodes.API_VERSION)
|
||||
ClassReader(inlineFunClass.readBytes()).accept(object : ClassVisitor(Opcodes.API_VERSION, cw) {
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? =
|
||||
if (desc == JvmAnnotationNames.METADATA_DESC) null else super.visitAnnotation(desc, visible)
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
assertEquals("foo", name) // test sanity: shouldn't see any constructors, only the "foo" method
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
}
|
||||
}, 0)
|
||||
|
||||
assert(inlineFunClass.delete())
|
||||
assert(!inlineFunClass.exists())
|
||||
|
||||
inlineFunClass.writeBytes(cw.toByteArray())
|
||||
|
||||
val (_, exitCode) = compileKotlin("shouldNotCompile.kt", tmpdir, listOf(tmpdir))
|
||||
assertEquals(1, exitCode.code) // double-check that we failed :) output.txt also says so
|
||||
}
|
||||
|
||||
fun testReplaceAnnotationClassWithInterface() {
|
||||
val library1 = compileLibrary("library-1")
|
||||
val usage = compileLibrary("usage", extraClassPath = listOf(library1))
|
||||
|
||||
@@ -70,6 +70,11 @@ public class CompileKotlinAgainstJavaTestGenerated extends AbstractCompileKotlin
|
||||
runTest("compiler/testData/compileKotlinAgainstJava/Class.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassDefaultConstructor.kt")
|
||||
public void testClassDefaultConstructor() throws Exception {
|
||||
runTest("compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassWithNestedEnum.kt")
|
||||
public void testClassWithNestedEnum() throws Exception {
|
||||
runTest("compiler/testData/compileKotlinAgainstJava/ClassWithNestedEnum.kt");
|
||||
@@ -353,6 +358,11 @@ public class CompileKotlinAgainstJavaTestGenerated extends AbstractCompileKotlin
|
||||
runTest("compiler/testData/compileKotlinAgainstJava/Class.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassDefaultConstructor.kt")
|
||||
public void testClassDefaultConstructor() throws Exception {
|
||||
runTest("compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassWithNestedEnum.kt")
|
||||
public void testClassWithNestedEnum() throws Exception {
|
||||
runTest("compiler/testData/compileKotlinAgainstJava/ClassWithNestedEnum.kt");
|
||||
|
||||
@@ -619,7 +619,7 @@ class LazyJavaClassMemberScope(
|
||||
|
||||
private fun createDefaultConstructor(): ClassConstructorDescriptor? {
|
||||
val isAnnotation: Boolean = jClass.isAnnotationType
|
||||
if (jClass.isInterface && !isAnnotation)
|
||||
if ((jClass.isInterface || !jClass.hasDefaultConstructor()) && !isAnnotation)
|
||||
return null
|
||||
|
||||
val classDescriptor = ownerDescriptor
|
||||
|
||||
@@ -90,6 +90,7 @@ interface JavaClass : JavaClassifier, JavaTypeParameterListOwner, JavaModifierLi
|
||||
val methods: Collection<JavaMethod>
|
||||
val fields: Collection<JavaField>
|
||||
val constructors: Collection<JavaConstructor>
|
||||
fun hasDefaultConstructor(): Boolean
|
||||
}
|
||||
|
||||
val JavaClass.classId: ClassId?
|
||||
|
||||
@@ -96,6 +96,8 @@ class ReflectJavaClass(
|
||||
.map(::ReflectJavaConstructor)
|
||||
.toList()
|
||||
|
||||
override fun hasDefaultConstructor() = false // any default constructor is returned by constructors
|
||||
|
||||
override val lightClassOriginKind: LightClassOriginKind?
|
||||
get() = null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user