Write to trace in case class qualifier is a short reference to default object

This allows to fix some cases when there is a difference between explicit and short reference to default object
Fix shorten reference, optimize imports and import insert helper for default objects
ShortenReferences always transforms default object references to shorter form for now
Fix DescriptorUtils#getFqName() for default objects (affects test data mostly)
Fix DescriptorUtils#getImportableDescriptor()
This commit is contained in:
Pavel V. Talanov
2015-02-25 13:19:39 +03:00
parent 7f672f8078
commit 989f07962b
40 changed files with 278 additions and 104 deletions
@@ -96,6 +96,9 @@ public interface BindingContext {
WritableSlice<JetReferenceExpression, DeclarationDescriptor> REFERENCE_TARGET =
new BasicWritableSlice<JetReferenceExpression, DeclarationDescriptor>(DO_NOTHING);
// if 'A' really means 'A.Default' then this slice stores class descriptor for A, REFERENCE_TARGET stores descriptor Default in this case
WritableSlice<JetReferenceExpression, ClassDescriptor> SHORT_REFERENCE_TO_DEFAULT_OBJECT =
new BasicWritableSlice<JetReferenceExpression, ClassDescriptor>(DO_NOTHING);
@KotlinSignature("val RESOLVED_CALL: WritableSlice<Call, ResolvedCall<out CallableDescriptor>>")
WritableSlice<Call, ResolvedCall<?>> RESOLVED_CALL = new BasicWritableSlice<Call, ResolvedCall<?>>(DO_NOTHING);
@@ -58,7 +58,11 @@ public class TracingStrategyImpl extends AbstractTracingStrategy {
descriptor = ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall().getCandidateDescriptor();
}
if (descriptor instanceof FakeCallableDescriptorForObject) {
descriptor = ((FakeCallableDescriptorForObject) descriptor).getReferencedDescriptor();
FakeCallableDescriptorForObject fakeCallableDescriptorForObject = (FakeCallableDescriptorForObject) descriptor;
descriptor = fakeCallableDescriptorForObject.getReferencedDescriptor();
if (fakeCallableDescriptorForObject.getClassDescriptor().getDefaultObjectDescriptor() != null) {
trace.record(SHORT_REFERENCE_TO_DEFAULT_OBJECT, reference, fakeCallableDescriptorForObject.getClassDescriptor());
}
}
DeclarationDescriptor storedReference = trace.get(REFERENCE_TARGET, reference);
if (storedReference == null || !ErrorUtils.isError(descriptor)) {
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.resolve.descriptorUtil.getClassObjectReferenceTarget
public class FakeCallableDescriptorForObject(
private val classDescriptor: ClassDescriptor
public val classDescriptor: ClassDescriptor
) : DeclarationDescriptorWithVisibility by classDescriptor.getClassObjectReferenceTarget(), VariableDescriptor {
{
@@ -148,11 +148,14 @@ private fun QualifierReceiver.resolveAsReceiverInQualifiedExpression(context: Ex
}
private fun QualifierReceiver.resolveAndRecordReferenceTarget(context: ExpressionTypingContext, selector: DeclarationDescriptor?) {
resultingDescriptor = resolveReferenceTarget(selector)
resultingDescriptor = resolveReferenceTarget(context, selector)
context.trace.record(REFERENCE_TARGET, referenceExpression, resultingDescriptor)
}
private fun QualifierReceiver.resolveReferenceTarget(selector: DeclarationDescriptor?): DeclarationDescriptor {
private fun QualifierReceiver.resolveReferenceTarget(
context: ExpressionTypingContext,
selector: DeclarationDescriptor?
): DeclarationDescriptor {
if (classifier is TypeParameterDescriptor) {
return classifier
}
@@ -168,6 +171,9 @@ private fun QualifierReceiver.resolveReferenceTarget(selector: DeclarationDescri
}
if (classifier is ClassDescriptor && classifier.classObjectDescriptor == selectorContainer) {
if (classifier.getDefaultObjectDescriptor() != null) {
context.trace.record(SHORT_REFERENCE_TO_DEFAULT_OBJECT, referenceExpression, classifier)
}
return classifier.getClassObjectReferenceTarget()
}
@@ -1,7 +1,7 @@
package
package f {
internal fun test(): f.A.B
internal fun test(): f.A.Default.B
internal final class A {
public constructor A()
@@ -1,6 +1,6 @@
package
internal fun f1(): A.B.C.Default
internal fun f1(): A.Default.B.C.Default
internal fun f2(): kotlin.Unit
internal final class A {
@@ -12,10 +12,10 @@ internal final class A {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
internal final enum class E : kotlin.Enum<A.E> {
public enum entry ENTRY : A.E {
internal final enum class E : kotlin.Enum<A.Default.E> {
public enum entry ENTRY : A.Default.E {
private constructor ENTRY()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.Default.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
@@ -24,7 +24,7 @@ internal final class A {
}
private constructor E()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.Default.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
@@ -32,8 +32,8 @@ internal final class A {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A.E
public final /*synthesized*/ fun values(): kotlin.Array<A.E>
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A.Default.E
public final /*synthesized*/ fun values(): kotlin.Array<A.Default.E>
}
}
}
@@ -12,10 +12,10 @@ internal final class A {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
internal final enum class E : kotlin.Enum<A.E> {
public enum entry ENTRY2 : A.E {
internal final enum class E : kotlin.Enum<A.Default.E> {
public enum entry ENTRY2 : A.Default.E {
private constructor ENTRY2()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.Default.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
@@ -24,7 +24,7 @@ internal final class A {
}
private constructor E()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.Default.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
@@ -32,8 +32,8 @@ internal final class A {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A.E
public final /*synthesized*/ fun values(): kotlin.Array<A.E>
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A.Default.E
public final /*synthesized*/ fun values(): kotlin.Array<A.Default.E>
}
}
@@ -43,10 +43,10 @@ internal final class A {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
internal final enum class E : kotlin.Enum<A.E> {
public enum entry ENTRY : A.E {
internal final enum class E : kotlin.Enum<A.Default.E> {
public enum entry ENTRY : A.Default.E {
private constructor ENTRY()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.Default.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
@@ -55,7 +55,7 @@ internal final class A {
}
private constructor E()
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A.Default.E): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
@@ -63,8 +63,8 @@ internal final class A {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A.E
public final /*synthesized*/ fun values(): kotlin.Array<A.E>
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A.Default.E
public final /*synthesized*/ fun values(): kotlin.Array<A.Default.E>
}
}
}
@@ -81,8 +81,8 @@ package c {
}
package d {
internal val b: d.A.B
internal val c: d.A.B
internal val b: d.A.Default.B
internal val c: d.A.Default.B
internal final class A {
public constructor A()
@@ -103,7 +103,7 @@ package d {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal object C : d.A.B {
internal object C : d.A.Default.B {
private constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -6,7 +6,7 @@ internal final class Test {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
Test.ClassObjectAnnotation() Test.NestedAnnotation() internal class object Default {
Test.Default.ClassObjectAnnotation() Test.NestedAnnotation() internal class object Default {
private constructor Default()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -64,5 +64,5 @@ fun test(m1: M1) {
d(m1.d)
e(m1.e)
f(m1.f)
g(m1.g)
g(<!TYPE_MISMATCH!>m1.g<!>)
}
@@ -64,10 +64,10 @@ package p {
public final val a: p.A
public final val b: p.A.B
public final val c: p.A.C
public final val d: p.A.D
public final val e: p.A.D.E
public final val d: p.A.Default.D
public final val e: p.A.Default.D.E
public final val f: p.A.F
public final val g: p.A.G
public final val g: p.A.Default.G
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -82,8 +82,8 @@ package p {
public fun a(/*0*/ p: p.A): kotlin.Unit
public fun b(/*0*/ p: p.A.B): kotlin.Unit
public fun c(/*0*/ p: p.A.C): kotlin.Unit
public fun d(/*0*/ p: p.A.D): kotlin.Unit
public fun e(/*0*/ p: p.A.D.E): kotlin.Unit
public fun d(/*0*/ p: p.A.Default.D): kotlin.Unit
public fun e(/*0*/ p: p.A.Default.D.E): kotlin.Unit
public fun f(/*0*/ p: p.A.F): kotlin.Unit
public fun g(/*0*/ p: p.A.G): kotlin.Unit
@@ -154,8 +154,8 @@ package p {
public fun a(/*0*/ p: p.A): kotlin.Unit
public fun b(/*0*/ p: p.A.B): kotlin.Unit
public fun c(/*0*/ p: p.A.C): kotlin.Unit
public fun d(/*0*/ p: p.A.D): kotlin.Unit
public fun e(/*0*/ p: p.A.D.E): kotlin.Unit
public fun d(/*0*/ p: p.A.Default.D): kotlin.Unit
public fun e(/*0*/ p: p.A.Default.D.E): kotlin.Unit
public fun f(/*0*/ p: p.A.F): kotlin.Unit
public fun g(/*0*/ p: p.A.G): kotlin.Unit
@@ -2,7 +2,7 @@ package
internal final class X {
public constructor X()
internal final val y: X.Y
internal final val y: X.Default.Y
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -20,7 +20,7 @@ internal final class Main {
internal class object Default {
private constructor Default()
public final val N: Main.States
public final val N: Main.Default.States
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -20,6 +20,6 @@ internal final class A {
}
}
test.A.Anno1() test.A.B.Anno2() internal final class C {
test.A.Default.Anno1() test.A.Default.B.Anno2() internal final class C {
/*primary*/ public constructor C()
}
@@ -3,7 +3,7 @@ package test
internal final class Some {
/*primary*/ public constructor Some()
test.Some.TestAnnotation() internal class object Default {
test.Some.Default.TestAnnotation() internal class object Default {
/*primary*/ private constructor Default()
internal final annotation class TestAnnotation : kotlin.Annotation {
@@ -104,20 +104,14 @@ public class DescriptorUtils {
@NotNull
private static FqNameUnsafe getFqNameUnsafe(@NotNull DeclarationDescriptor descriptor) {
DeclarationDescriptor containingDeclaration = getContainingDeclarationSkippingClassObjects(descriptor);
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
assert containingDeclaration != null : "Not package/module descriptor doesn't have containing declaration: " + descriptor;
return getFqName(containingDeclaration).child(descriptor.getName());
}
@Nullable
private static DeclarationDescriptor getContainingDeclarationSkippingClassObjects(@NotNull DeclarationDescriptor descriptor) {
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
return isClassObject(containingDeclaration) ? containingDeclaration.getContainingDeclaration() : containingDeclaration;
}
@NotNull
public static FqName getFqNameFromTopLevelClass(@NotNull DeclarationDescriptor descriptor) {
DeclarationDescriptor containingDeclaration = getContainingDeclarationSkippingClassObjects(descriptor);
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
Name name = descriptor.getName();
if (!(containingDeclaration instanceof ClassDescriptor)) {
return FqName.topLevel(name);
@@ -26,7 +26,7 @@ public fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor = ge
public fun DeclarationDescriptor.getImportableDescriptor(): DeclarationDescriptor {
return when {
this is ConstructorDescriptor, DescriptorUtils.isClassObject(this) -> getContainingDeclaration()!!
this is ConstructorDescriptor -> getContainingDeclaration()
this is PropertyAccessorDescriptor -> getCorrespondingProperty()
else -> this
}
@@ -19,16 +19,16 @@ package org.jetbrains.kotlin.idea.imports
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.*
public val DeclarationDescriptor.importableFqName: FqName?
get() {
@@ -60,3 +60,12 @@ public fun JetType.canBeReferencedViaImport(): Boolean {
val descriptor = getConstructor().getDeclarationDescriptor()
return descriptor != null && descriptor.canBeReferencedViaImport()
}
// for cases when class qualifier refers default object treats it like reference to class itself
public fun JetReferenceExpression.getImportableTargets(bindingContext: BindingContext): Collection<DeclarationDescriptor> {
val targets = bindingContext[BindingContext.SHORT_REFERENCE_TO_DEFAULT_OBJECT, this]?.let { listOf(it) }
?: bindingContext[BindingContext.REFERENCE_TARGET, this]?.let { listOf(it) }
?: bindingContext[BindingContext.AMBIGUOUS_REFERENCE_TARGET, this]
?: listOf()
return targets.map { it.getImportableDescriptor() }.toSet()
}
@@ -16,14 +16,11 @@
package org.jetbrains.kotlin.idea.util
import com.intellij.psi.PsiElement;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.analyzer.analyzeInContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
@@ -41,6 +38,8 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
import org.jetbrains.kotlin.idea.util.ShortenReferences.Options
import org.jetbrains.kotlin.idea.imports.*
import org.jetbrains.kotlin.resolve.*
import com.intellij.psi.*
public class ShortenReferences(val options: (JetElement) -> Options = { Options.DEFAULT }) {
public data class Options(
@@ -51,19 +50,14 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
val DEFAULT = Options()
}
}
class object {
val DEFAULT = ShortenReferences()
private fun DeclarationDescriptor.asString()
= DescriptorRenderer.FQ_NAMES_IN_TYPES.render(this)
private fun JetReferenceExpression.targets(context: BindingContext): Collection<DeclarationDescriptor> {
val targets = context[BindingContext.REFERENCE_TARGET, this]?.let { listOf(it) }
?: context[BindingContext.AMBIGUOUS_REFERENCE_TARGET, this]
?: listOf()
return targets.map { it.getImportableDescriptor() }.toSet()
}
private fun JetReferenceExpression.targets(context: BindingContext) = getImportableTargets(context)
private fun mayImport(descriptor: DeclarationDescriptor, file: JetFile): Boolean {
return descriptor.canBeReferencedViaImport()
@@ -152,10 +146,11 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
while (true) {
// Visitor order is important here so that enclosing elements are not shortened before their children are, e.g.
// test.foo(this@A) -> foo(this)
val visitors = listOf(
val visitors: List<ShorteningVisitor<*>> = listOf(
ShortenTypesVisitor(file, elementFilter, failedToImportDescriptors),
ShortenThisExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
ShortenQualifiedExpressionsVisitor(file, elementFilter, failedToImportDescriptors)
ShortenQualifiedExpressionsVisitor(file, elementFilter, failedToImportDescriptors),
RemoveExplicitDefaultObjectReferenceVisitor(file, elementFilter, failedToImportDescriptors)
)
val descriptorsToImport = visitors.flatMap { analyzeReferences(elementsToUse, it) }.toSet()
visitors.forEach { it.shortenElements(elementsToUse) }
@@ -201,7 +196,7 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
protected val failedToImportDescriptors: Set<DeclarationDescriptor>
) : JetVisitorVoid() {
var options: Options = Options.DEFAULT
private val elementsToShorten = ArrayList<T>()
private val descriptorsToImport = LinkedHashSet<DeclarationDescriptor>()
@@ -209,7 +204,7 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
protected fun processQualifiedElement(element: T, target: DeclarationDescriptor, canShortenNow: Boolean) {
if (canShortenNow) {
elementsToShorten.add(element)
addElementToShorten(element)
}
else if (target !in failedToImportDescriptors && mayImport(target, file)) {
descriptorsToImport.add(target)
@@ -219,6 +214,10 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
}
}
protected fun addElementToShorten(element: T) {
elementsToShorten.add(element)
}
protected abstract fun qualifier(element: T): JetElement
protected abstract fun shortenElement(element: T): JetElement
@@ -257,7 +256,7 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
if (filterResult == FilterResult.PROCESS) {
processType(userType)
}
else{
else {
userType.getQualifier()?.accept(this)
}
}
@@ -286,12 +285,11 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
}
}
private class ShortenQualifiedExpressionsVisitor(
private abstract class QualifiedExpressionShorteningVisitor(
file: JetFile,
elementFilter: (PsiElement) -> FilterResult,
failedToImportDescriptors: Set<DeclarationDescriptor>
) : ShorteningVisitor<JetQualifiedExpression>(file, elementFilter, failedToImportDescriptors) {
) : ShorteningVisitor<JetDotQualifiedExpression>(file, elementFilter, failedToImportDescriptors) {
override fun visitDotQualifiedExpression(expression: JetDotQualifiedExpression) {
val filterResult = elementFilter(expression)
if (filterResult == FilterResult.SKIP) return
@@ -305,7 +303,18 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
expression.getReceiverExpression().accept(this)
}
private fun process(qualifiedExpression: JetDotQualifiedExpression): Boolean {
abstract fun process(qualifiedExpression: JetDotQualifiedExpression): Boolean
override fun qualifier(element: JetDotQualifiedExpression) = element.getReceiverExpression()
}
private class ShortenQualifiedExpressionsVisitor(
file: JetFile,
elementFilter: (PsiElement) -> FilterResult,
failedToImportDescriptors: Set<DeclarationDescriptor>
) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) {
override fun process(qualifiedExpression: JetDotQualifiedExpression): Boolean {
val bindingContext = resolutionFacade.analyze(qualifiedExpression)
val receiver = qualifiedExpression.getReceiverExpression()
@@ -317,7 +326,7 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
if (bindingContext[BindingContext.QUALIFIER, receiver] == null) return false
}
}
if (PsiTreeUtil.getParentOfType(
qualifiedExpression,
javaClass<JetImportDirective>(), javaClass<JetPackageDirective>()) != null) return true
@@ -338,10 +347,10 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
val newCall = selectorCopy.getResolvedCall(newContext) ?: return false
val receiverKind = originalCall.getExplicitReceiverKind()
val newReceiver = when (receiverKind) {
ExplicitReceiverKind.BOTH_RECEIVERS, ExplicitReceiverKind.EXTENSION_RECEIVER -> newCall.getExtensionReceiver()
ExplicitReceiverKind.DISPATCH_RECEIVER -> newCall.getDispatchReceiver()
else -> return false
} as? ThisReceiver ?: return false
ExplicitReceiverKind.BOTH_RECEIVERS, ExplicitReceiverKind.EXTENSION_RECEIVER -> newCall.getExtensionReceiver()
ExplicitReceiverKind.DISPATCH_RECEIVER -> newCall.getDispatchReceiver()
else -> return false
} as? ThisReceiver ?: return false
val thisTarget = receiver.getInstanceReference().targets(bindingContext).singleOrNull()
if (newReceiver.getDeclarationDescriptor().asString() != thisTarget?.asString()) return false
@@ -356,9 +365,7 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
return true
}
override fun qualifier(element: JetQualifiedExpression) = element.getReceiverExpression()
override fun shortenElement(element: JetQualifiedExpression): JetElement {
override fun shortenElement(element: JetDotQualifiedExpression): JetElement {
return element.replace(element.getSelectorExpression()!!) as JetElement
}
}
@@ -380,7 +387,7 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
val newContext = simpleThis.analyzeInContext(scope)
val targetAfter = simpleThis.getInstanceReference().targets(newContext).singleOrNull()
if (targetBefore == targetAfter) {
processQualifiedElement(thisExpression, targetBefore, true)
addElementToShorten(thisExpression)
}
}
@@ -398,6 +405,51 @@ public class ShortenReferences(val options: (JetElement) -> Options = { Options.
}
}
private class RemoveExplicitDefaultObjectReferenceVisitor(
file: JetFile,
elementFilter: (PsiElement) -> FilterResult,
failedToImportDescriptors: Set<DeclarationDescriptor>
) : QualifiedExpressionShorteningVisitor(file, elementFilter, failedToImportDescriptors) {
private fun JetExpression.singleTarget(context: BindingContext): DeclarationDescriptor? {
return (getCalleeExpressionIfAny() as? JetReferenceExpression)?.targets(context)?.singleOrNull()
}
override fun process(qualifiedExpression: JetDotQualifiedExpression): Boolean {
val bindingContext = resolutionFacade.analyze(qualifiedExpression)
val receiver = qualifiedExpression.getReceiverExpression()
if (PsiTreeUtil.getParentOfType(
qualifiedExpression,
javaClass<JetImportDirective>(), javaClass<JetPackageDirective>()) != null) return false
val receiverTarget = receiver.singleTarget(bindingContext) ?: return false
if (receiverTarget !is ClassDescriptor) return false
val selectorExpression = qualifiedExpression.getSelectorExpression() ?: return false
val selectorTarget = selectorExpression.singleTarget(bindingContext) ?: return false
if (receiverTarget.getDefaultObjectDescriptor() != selectorTarget) return false
val selectorsSelector = (qualifiedExpression.getParent() as? JetDotQualifiedExpression)?.getSelectorExpression()
if (selectorsSelector == null) {
addElementToShorten(qualifiedExpression)
return true
}
val selectorsSelectorTarget = selectorsSelector.singleTarget(bindingContext) ?: return false
if (selectorsSelectorTarget is ClassDescriptor) return false
addElementToShorten(qualifiedExpression)
return true
}
override fun shortenElement(element: JetDotQualifiedExpression): JetElement {
return element.replace(element.getReceiverExpression()) as JetElement
}
}
// this class is needed to optimize imports only when we actually insert any import (optimization)
private class ImportInserter(val file: JetFile) {
private var optimizeImports = true
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.formatter.JetCodeStyleSettings
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -36,13 +35,13 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import java.util.ArrayList
import java.util.HashMap
import java.util.HashSet
import org.jetbrains.kotlin.idea.caches.resolve.*
import org.jetbrains.kotlin.resolve.*
public class KotlinImportOptimizer() : ImportOptimizer {
@@ -182,12 +181,19 @@ public class KotlinImportOptimizer() : ImportOptimizer {
override fun visitPackageDirective(directive: JetPackageDirective) {
}
private fun JetElement.classForDefaultObjectReference(): ClassDescriptor? {
return analyze()[BindingContext.SHORT_REFERENCE_TO_DEFAULT_OBJECT, this as? JetReferenceExpression]
}
override fun visitJetElement(element: JetElement) {
val reference = element.getReference()
if (reference is JetReference) {
val referencedName = (element as? JetNameReferenceExpression)?.getReferencedNameAsName() //TODO: other types of references
val targets = reference.resolveToDescriptors()
//class qualifiers that refer to default objects should be considered (containing) class references
val targets = element.classForDefaultObjectReference()?.let { listOf(it) }
?: reference.resolveToDescriptors()
for (target in targets) {
if (!target.canBeReferencedViaImport()) continue
if (target is PackageViewDescriptor && target.getFqName().parent() == FqName.ROOT) continue // no need to import top-level packages
@@ -39,8 +39,6 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.imports.importableFqNameSafe
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -54,6 +52,7 @@ import org.jetbrains.kotlin.idea.util.ImportInsertHelper.ImportDescriptorResult
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.refactoring.fqName.isImported
import java.util.*
import org.jetbrains.kotlin.idea.imports.*
public class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper() {
@@ -374,10 +373,7 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert
}
private fun JetReferenceExpression.resolveTargets(): Collection<DeclarationDescriptor> {
val bindingContext = resolutionFacade.analyze(this, BodyResolveMode.PARTIAL)
return bindingContext[BindingContext.REFERENCE_TARGET, this]?.let { listOf(it) }
?: bindingContext[BindingContext.AMBIGUOUS_REFERENCE_TARGET, this]
?: return listOf()
return this.getImportableTargets(resolutionFacade.analyze(this, BodyResolveMode.PARTIAL))
}
private fun addImport(fqName: FqName, allUnder: Boolean): JetImportDirective {
@@ -7,4 +7,8 @@ import a.T
fun g(t: T): Int {
g(A)
B.f()
A
B
A.Default
B.Default
}
@@ -16,6 +16,10 @@ class B {
<selection>fun g(t: T): Int {
g(A)
B.f()
A
B
A.Default
B.Default
}</selection>
@@ -2,5 +2,5 @@ package to
import a.Outer
fun f(i: Outer.Inner, n: Outer.Nested, e: Outer.NestedEnum, o: Outer.NestedObj, t: Outer.NestedTrait, a: Outer.NestedAnnotation) {
fun f(n: Outer.Default.Nested, e: Outer.Default.NestedEnum, o: Outer.Default.NestedObj, t: Outer.Default.NestedTrait, a: Outer.Default.NestedAnnotation) {
}
@@ -1,11 +1,13 @@
package a
import a.Outer.*
import a.Outer.Default.Nested
import a.Outer.Default.NestedEnum
import a.Outer.Default.NestedObj
import a.Outer.Default.NestedTrait
import a.Outer.Default.NestedAnnotation
class Outer {
class object {
inner class Inner {
}
class Nested {
}
enum class NestedEnum {
@@ -18,5 +20,5 @@ class Outer {
}
}
<selection>fun f(i: Inner, n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
<selection>fun f(n: Nested, e: NestedEnum, o: NestedObj, t: NestedTrait, a: NestedAnnotation) {
}</selection>
@@ -1,2 +1,2 @@
<!-- nestedClassShouldBeQualified3 -->
Nested class 'Nested' should be qualified as 'C.D.Nested'
Nested class 'Nested' should be qualified as 'C.Default.D.Nested'
@@ -1,2 +1,2 @@
<!-- nestedClassShouldBeQualified4 -->
Nested class 'Nested' should be qualified as 'C.D::Nested'
Nested class 'Nested' should be qualified as 'C.Default.D::Nested'
@@ -1,2 +1,2 @@
<!-- nestedClassShouldBeQualified5 -->
Nested class object of 'F' should be qualified as 'E.F'
Nested class object of 'F' should be qualified as 'E.F.Default'
@@ -0,0 +1,13 @@
package d
class A {
class object E {
val c: Int
}
}
class B {
class object F {
val c: Int
}
}
@@ -0,0 +1,11 @@
package p
import d.A
import d.A.E
import d.B.F
import d.B
fun main(args: Array<String>) {
F.c
A.c
}
@@ -0,0 +1,9 @@
package p
import d.A
import d.B.F
fun main(args: Array<String>) {
F.c
A.c
}
@@ -1 +1 @@
Property A.t will become invisible after extraction
Property A.Default.t will become invisible after extraction
@@ -3,5 +3,7 @@ package source
import library.*
class Bar {
class object {
val c : Int
}
}
@@ -3,6 +3,7 @@ package target
import library.JavaClass
import library.KtClass
import library.KtObject
import source.Bar
class Foo {
val jv1 = JavaClass.foo()
@@ -13,4 +14,11 @@ class Foo {
val kt2 = KtObject.foo()
val kt3 = KtClass.Inner.foo()
val kt4 = KtObject.Inner.foo()
val kt5 = KtClass.foo()
val kt6 = KtClass
val kt7 = KtClass
val kt8 = Bar
val kt9 = Bar
val kt10 = Bar.c
}
@@ -11,8 +11,17 @@ class <caret>Foo {
val kt2 = KtObject.foo()
val kt3 = KtClass.Inner.foo()
val kt4 = KtObject.Inner.foo()
val kt5 = KtClass.Default.foo()
val kt6 = KtClass.Default
val kt7 = KtClass
val kt8 = Bar
val kt9 = Bar.Default
val kt10 = Bar.Default.c
}
class Bar {
class object {
val c : Int
}
}
+20 -2
View File
@@ -1,16 +1,34 @@
package p.q
<selection>fun foo(): Int {
<selection>fun foo(myC: p.q.MyClass, def: p.q.MyClass.Default, nes: p.q.MyClass.Default.Nested) {
p.q.MyClass.Default.foo()
p.q.MyClass.Default.coProp
p.q.MyClass.Default
p.q.MyClass
p.q.MyClass.coProp
p.q.MyClass.foo()
return p.q.MyClass.coProp + 10
p.q.MyClass.bar(p.q.MyClass.Default)
p.q.MyClass.bar(p.q.MyClass)
p.q.MyClass.Default.Nested.Default
p.q.MyClass.Default.Nested.Default.c
MyClass.Default
}</selection>
class MyClass {
class object {
val coProp = 1
class Nested {
class object {
val c: Int = 1
}
}
fun foo() {
}
fun bar(p: MyClass.Default) {
}
}
}
+20 -2
View File
@@ -1,16 +1,34 @@
package p.q
fun foo(): Int {
fun foo(myC: MyClass, def: MyClass.Default, nes: MyClass.Default.Nested) {
MyClass.foo()
return MyClass.coProp + 10
MyClass.coProp
MyClass
MyClass
MyClass.coProp
MyClass.foo()
MyClass.bar(MyClass)
MyClass.bar(MyClass)
MyClass.Default.Nested
MyClass.Default.Nested.c
MyClass
}
class MyClass {
class object {
val coProp = 1
class Nested {
class object {
val c: Int = 1
}
}
fun foo() {
}
fun bar(p: MyClass.Default) {
}
}
}
@@ -75,6 +75,12 @@ public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
doTest(fileName);
}
@TestMetadata("DefaultObjectReference.kt")
public void testDefaultObjectReference() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/DefaultObjectReference.kt");
doTest(fileName);
}
@TestMetadata("DuplicatedImports.kt")
public void testDuplicatedImports() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/DuplicatedImports.kt");