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()
|
declarations += prepareJavaConstructor(isPrimary = true).build()
|
||||||
}
|
}
|
||||||
for (javaConstructor in javaClassDeclaredConstructors) {
|
for (javaConstructor in javaClassDeclaredConstructors) {
|
||||||
|
|||||||
+2
@@ -100,6 +100,8 @@ class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass)
|
|||||||
return constructors(psi.constructors.filter { method -> method.isConstructor })
|
return constructors(psi.constructors.filter { method -> method.isConstructor })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasDefaultConstructor() = !isInterface && constructors.isEmpty()
|
||||||
|
|
||||||
override val isAbstract: Boolean
|
override val isAbstract: Boolean
|
||||||
get() = JavaElementUtil.isAbstract(this)
|
get() = JavaElementUtil.isAbstract(this)
|
||||||
|
|
||||||
|
|||||||
+1
@@ -47,6 +47,7 @@ class BinaryJavaClass(
|
|||||||
override val methods = arrayListOf<JavaMethod>()
|
override val methods = arrayListOf<JavaMethod>()
|
||||||
override val fields = arrayListOf<JavaField>()
|
override val fields = arrayListOf<JavaField>()
|
||||||
override val constructors = arrayListOf<JavaConstructor>()
|
override val constructors = arrayListOf<JavaConstructor>()
|
||||||
|
override fun hasDefaultConstructor() = false // never: all constructors explicit in bytecode
|
||||||
|
|
||||||
override val annotationsByFqName by buildLazyValueForMap()
|
override val annotationsByFqName by buildLazyValueForMap()
|
||||||
|
|
||||||
|
|||||||
+1
@@ -159,6 +159,7 @@ class MockKotlinClassifier(override val classId: ClassId,
|
|||||||
override val methods get() = shouldNotBeCalled()
|
override val methods get() = shouldNotBeCalled()
|
||||||
override val fields get() = shouldNotBeCalled()
|
override val fields get() = shouldNotBeCalled()
|
||||||
override val constructors get() = shouldNotBeCalled()
|
override val constructors get() = shouldNotBeCalled()
|
||||||
|
override fun hasDefaultConstructor() = shouldNotBeCalled()
|
||||||
override val annotations get() = shouldNotBeCalled()
|
override val annotations get() = shouldNotBeCalled()
|
||||||
override val isDeprecatedInJavaDoc get() = shouldNotBeCalled()
|
override val isDeprecatedInJavaDoc get() = shouldNotBeCalled()
|
||||||
override fun findAnnotation(fqName: FqName) = shouldNotBeCalled()
|
override fun findAnnotation(fqName: FqName) = shouldNotBeCalled()
|
||||||
|
|||||||
+2
@@ -73,6 +73,8 @@ class FakeSymbolBasedClass(
|
|||||||
|
|
||||||
override val constructors: Collection<JavaConstructor> get() = emptyList()
|
override val constructors: Collection<JavaConstructor> get() = emptyList()
|
||||||
|
|
||||||
|
override fun hasDefaultConstructor() = false
|
||||||
|
|
||||||
override val innerClassNames: Collection<Name> get() = emptyList()
|
override val innerClassNames: Collection<Name> get() = emptyList()
|
||||||
|
|
||||||
override val virtualFile: VirtualFile? by lazy {
|
override val virtualFile: VirtualFile? by lazy {
|
||||||
|
|||||||
+2
@@ -133,6 +133,8 @@ class SymbolBasedClass(
|
|||||||
.filter { it.kind == ElementKind.CONSTRUCTOR }
|
.filter { it.kind == ElementKind.CONSTRUCTOR }
|
||||||
.map { SymbolBasedConstructor(it as ExecutableElement, this, javac) }
|
.map { SymbolBasedConstructor(it as ExecutableElement, this, javac) }
|
||||||
|
|
||||||
|
override fun hasDefaultConstructor() = false // default constructors are explicit in symbols
|
||||||
|
|
||||||
override val innerClassNames: Collection<Name>
|
override val innerClassNames: Collection<Name>
|
||||||
get() = innerClasses.keys
|
get() = innerClasses.keys
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,8 @@ class TreeBasedClass(
|
|||||||
TreeBasedConstructor(constructor as JCTree.JCMethodDecl, compilationUnit, this, javac)
|
TreeBasedConstructor(constructor as JCTree.JCMethodDecl, compilationUnit, this, javac)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasDefaultConstructor() = !isInterface && constructors.isEmpty()
|
||||||
|
|
||||||
override val innerClassNames: Collection<Name>
|
override val innerClassNames: Collection<Name>
|
||||||
get() = innerClasses.keys
|
get() = innerClasses.keys
|
||||||
|
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
fun foo(msg: String) = msg
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
compiler/testData/compileKotlinAgainstCustomBinaries/classfileWithoutConstructors/shouldNotCompile.kt:6:9: error: unresolved reference: TopLevelKt
|
||||||
|
TopLevelKt() // error here
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
+10
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public class ClassDefaultConstructor {
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
fun useClassDefaultConstructor() = ClassDefaultConstructor()
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
public fun useClassDefaultConstructor(): test.ClassDefaultConstructor
|
||||||
|
|
||||||
|
public open class ClassDefaultConstructor {
|
||||||
|
public constructor ClassDefaultConstructor()
|
||||||
|
}
|
||||||
+31
@@ -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() {
|
fun testReplaceAnnotationClassWithInterface() {
|
||||||
val library1 = compileLibrary("library-1")
|
val library1 = compileLibrary("library-1")
|
||||||
val usage = compileLibrary("usage", extraClassPath = listOf(library1))
|
val usage = compileLibrary("usage", extraClassPath = listOf(library1))
|
||||||
|
|||||||
Generated
+10
@@ -70,6 +70,11 @@ public class CompileKotlinAgainstJavaTestGenerated extends AbstractCompileKotlin
|
|||||||
runTest("compiler/testData/compileKotlinAgainstJava/Class.kt");
|
runTest("compiler/testData/compileKotlinAgainstJava/Class.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ClassDefaultConstructor.kt")
|
||||||
|
public void testClassDefaultConstructor() throws Exception {
|
||||||
|
runTest("compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ClassWithNestedEnum.kt")
|
@TestMetadata("ClassWithNestedEnum.kt")
|
||||||
public void testClassWithNestedEnum() throws Exception {
|
public void testClassWithNestedEnum() throws Exception {
|
||||||
runTest("compiler/testData/compileKotlinAgainstJava/ClassWithNestedEnum.kt");
|
runTest("compiler/testData/compileKotlinAgainstJava/ClassWithNestedEnum.kt");
|
||||||
@@ -353,6 +358,11 @@ public class CompileKotlinAgainstJavaTestGenerated extends AbstractCompileKotlin
|
|||||||
runTest("compiler/testData/compileKotlinAgainstJava/Class.kt");
|
runTest("compiler/testData/compileKotlinAgainstJava/Class.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ClassDefaultConstructor.kt")
|
||||||
|
public void testClassDefaultConstructor() throws Exception {
|
||||||
|
runTest("compiler/testData/compileKotlinAgainstJava/ClassDefaultConstructor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ClassWithNestedEnum.kt")
|
@TestMetadata("ClassWithNestedEnum.kt")
|
||||||
public void testClassWithNestedEnum() throws Exception {
|
public void testClassWithNestedEnum() throws Exception {
|
||||||
runTest("compiler/testData/compileKotlinAgainstJava/ClassWithNestedEnum.kt");
|
runTest("compiler/testData/compileKotlinAgainstJava/ClassWithNestedEnum.kt");
|
||||||
|
|||||||
+1
-1
@@ -619,7 +619,7 @@ class LazyJavaClassMemberScope(
|
|||||||
|
|
||||||
private fun createDefaultConstructor(): ClassConstructorDescriptor? {
|
private fun createDefaultConstructor(): ClassConstructorDescriptor? {
|
||||||
val isAnnotation: Boolean = jClass.isAnnotationType
|
val isAnnotation: Boolean = jClass.isAnnotationType
|
||||||
if (jClass.isInterface && !isAnnotation)
|
if ((jClass.isInterface || !jClass.hasDefaultConstructor()) && !isAnnotation)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
val classDescriptor = ownerDescriptor
|
val classDescriptor = ownerDescriptor
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ interface JavaClass : JavaClassifier, JavaTypeParameterListOwner, JavaModifierLi
|
|||||||
val methods: Collection<JavaMethod>
|
val methods: Collection<JavaMethod>
|
||||||
val fields: Collection<JavaField>
|
val fields: Collection<JavaField>
|
||||||
val constructors: Collection<JavaConstructor>
|
val constructors: Collection<JavaConstructor>
|
||||||
|
fun hasDefaultConstructor(): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
val JavaClass.classId: ClassId?
|
val JavaClass.classId: ClassId?
|
||||||
|
|||||||
+2
@@ -96,6 +96,8 @@ class ReflectJavaClass(
|
|||||||
.map(::ReflectJavaConstructor)
|
.map(::ReflectJavaConstructor)
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
|
override fun hasDefaultConstructor() = false // any default constructor is returned by constructors
|
||||||
|
|
||||||
override val lightClassOriginKind: LightClassOriginKind?
|
override val lightClassOriginKind: LightClassOriginKind?
|
||||||
get() = null
|
get() = null
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user