Light classes: do not rely on dummy context when data class autogenerated members conflict with declared members

Fix related issues with nested classes
This commit is contained in:
Pavel V. Talanov
2017-04-05 20:49:32 +03:00
parent 69c250a1b7
commit 2f159bb64b
9 changed files with 115 additions and 11 deletions
@@ -0,0 +1,20 @@
// p.Wrapper
package p
class Wrapper {
data class Equals(val code: G) {
override fun equals(other: Any?): Boolean = true
}
data class HashCode(val code: G) {
override fun hashCode() = 3
}
data class ToString(val code: G) {
override fun toString() = "b"
}
}
class G
// LAZINESS:NoLaziness
@@ -0,0 +1,13 @@
public final class B {
public B() { /* compiled code */ }
public static final class A implements p.I {
private final p.I f;
public A(@org.jetbrains.annotations.NotNull p.I f) { /* compiled code */ }
public void f() { /* compiled code */ }
public void g() { /* compiled code */ }
}
}
@@ -0,0 +1,15 @@
// p.B
package p
class B {
class A(private val f: I) : I by f {
}
}
interface I {
fun g()
fun f()
}
// LAZINESS:NoLaziness
@@ -42,6 +42,18 @@ public class CompilerLightClassTestGenerated extends AbstractCompilerLightClassT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("DataClassWithCustomImplementedMembers.kt")
public void testDataClassWithCustomImplementedMembers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/DataClassWithCustomImplementedMembers.kt");
doTest(fileName);
}
@TestMetadata("DelegatedNested.kt")
public void testDelegatedNested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/DelegatedNested.kt");
doTest(fileName);
}
@TestMetadata("Delegation.kt") @TestMetadata("Delegation.kt")
public void testDelegation() throws Exception { public void testDelegation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/Delegation.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/Delegation.kt");
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope.Companion.sourceAndClassFiles import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope.Companion.sourceAndClassFiles
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -70,12 +71,10 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
) )
} }
else { else {
val hasDelegatedMembers = classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }
val dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) }
LazyLightClassDataHolder.ForClass( LazyLightClassDataHolder.ForClass(
builder, builder,
exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) }, exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) },
dummyContextProvider = if (!hasDelegatedMembers) dummyContextProvider else null dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) }
) )
} }
} }
@@ -125,7 +125,9 @@ object IDELightClassContexts {
return IDELightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor, EXACT) return IDELightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor, EXACT)
} }
fun lightContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext { fun lightContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext? {
if (!isDummyResolveApplicable(classOrObject)) return null
val resolveSession = setupAdHocResolve(classOrObject.project, classOrObject.getResolutionFacade().moduleDescriptor, listOf(classOrObject.containingKtFile)) val resolveSession = setupAdHocResolve(classOrObject.project, classOrObject.getResolutionFacade().moduleDescriptor, listOf(classOrObject.containingKtFile))
ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(classOrObject)) ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(classOrObject))
@@ -133,6 +135,24 @@ object IDELightClassContexts {
return IDELightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor, LIGHT) return IDELightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor, LIGHT)
} }
private fun isDummyResolveApplicable(classOrObject: KtClassOrObject): Boolean {
val hasDelegatedMembers = classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }
val dataClassWithGeneratedMembersOverridden = classOrObject.declarations.filterIsInstance<KtFunction>().any {
isGeneratedForDataClass(it.nameAsSafeName)
}
return !hasDelegatedMembers && !dataClassWithGeneratedMembersOverridden
&& classOrObject.declarations.filterIsInstance<KtClassOrObject>().all { isDummyResolveApplicable(it) }
}
private fun isGeneratedForDataClass(name: Name): Boolean {
return name == DataClassDescriptorResolver.EQUALS_METHOD_NAME ||
// known failure is related to equals override, checking for other methods 'just in case'
name == DataClassDescriptorResolver.COPY_METHOD_NAME ||
name == DataClassDescriptorResolver.HASH_CODE_METHOD_NAME ||
name == DataClassDescriptorResolver.TO_STRING_METHOD_NAME ||
DataClassDescriptorResolver.isComponentLike(name)
}
fun lightContextForFacade(files: List<KtFile>): LightClassConstructionContext { fun lightContextForFacade(files: List<KtFile>): LightClassConstructionContext {
val representativeFile = files.first() val representativeFile = files.first()
val resolveSession = setupAdHocResolve(representativeFile.project, representativeFile.getResolutionFacade().moduleDescriptor, files) val resolveSession = setupAdHocResolve(representativeFile.project, representativeFile.getResolutionFacade().moduleDescriptor, files)
@@ -329,6 +349,6 @@ object IDELightClassContexts {
} }
private val notImplemented: Nothing private val notImplemented: Nothing
get() = error("Should not be called") get() = error("Should not be called")
} }
@@ -38,12 +38,13 @@ import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedDeclaration
typealias LightClassContextProvider = () -> LightClassConstructionContext typealias ExactLightClassContextProvider = () -> LightClassConstructionContext
typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?)?
sealed class LazyLightClassDataHolder( sealed class LazyLightClassDataHolder(
builder: LightClassBuilder, builder: LightClassBuilder,
exactContextProvider: LightClassContextProvider, exactContextProvider: ExactLightClassContextProvider,
dummyContextProvider: LightClassContextProvider? dummyContextProvider: DummyLightClassContextProvider
) : LightClassDataHolder { ) : LightClassDataHolder {
private val exactResultLazyValue = lazyPub { builder(exactContextProvider()) } private val exactResultLazyValue = lazyPub { builder(exactContextProvider()) }
@@ -51,7 +52,7 @@ sealed class LazyLightClassDataHolder(
private val exactResult: LightClassBuilderResult by exactResultLazyValue private val exactResult: LightClassBuilderResult by exactResultLazyValue
private val lazyInexactResult by lazyPub { private val lazyInexactResult by lazyPub {
dummyContextProvider?.let { builder.invoke(it()) } dummyContextProvider?.let { provider -> provider()?.let { context -> builder.invoke(context) } }
} }
private val inexactResult: LightClassBuilderResult? private val inexactResult: LightClassBuilderResult?
@@ -67,7 +68,7 @@ sealed class LazyLightClassDataHolder(
} }
class ForClass( class ForClass(
builder: LightClassBuilder, exactContextProvider: LightClassContextProvider, dummyContextProvider: LightClassContextProvider? builder: LightClassBuilder, exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForClass { ) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForClass {
override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData = override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData =
LazyLightClassData(relyOnDummySupertypes = classOrObject.getSuperTypeList() == null) { lightClassBuilderResult -> LazyLightClassData(relyOnDummySupertypes = classOrObject.getSuperTypeList() == null) { lightClassBuilderResult ->
@@ -76,7 +77,7 @@ sealed class LazyLightClassDataHolder(
} }
class ForFacade( class ForFacade(
builder: LightClassBuilder, exactContextProvider: LightClassContextProvider, dummyContextProvider: LightClassContextProvider? builder: LightClassBuilder, exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForFacade ) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForFacade
private inner class LazyLightClassData( private inner class LazyLightClassData(
@@ -42,6 +42,18 @@ public class IdeCompiledLightClassTestGenerated extends AbstractIdeCompiledLight
doTest(fileName); doTest(fileName);
} }
@TestMetadata("DataClassWithCustomImplementedMembers.kt")
public void testDataClassWithCustomImplementedMembers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/DataClassWithCustomImplementedMembers.kt");
doTest(fileName);
}
@TestMetadata("DelegatedNested.kt")
public void testDelegatedNested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/DelegatedNested.kt");
doTest(fileName);
}
@TestMetadata("Delegation.kt") @TestMetadata("Delegation.kt")
public void testDelegation() throws Exception { public void testDelegation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/Delegation.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/Delegation.kt");
@@ -42,6 +42,18 @@ public class IdeLightClassTestGenerated extends AbstractIdeLightClassTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("DataClassWithCustomImplementedMembers.kt")
public void testDataClassWithCustomImplementedMembers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/DataClassWithCustomImplementedMembers.kt");
doTest(fileName);
}
@TestMetadata("DelegatedNested.kt")
public void testDelegatedNested() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/DelegatedNested.kt");
doTest(fileName);
}
@TestMetadata("Delegation.kt") @TestMetadata("Delegation.kt")
public void testDelegation() throws Exception { public void testDelegation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/Delegation.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/Delegation.kt");