diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 0ab5093cfee..7270870abed 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -61,6 +61,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.kotlin.resolve.calls.util.CallMaker; import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject; +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject; import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt; import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo; import org.jetbrains.kotlin.resolve.constants.*; @@ -1738,6 +1739,9 @@ public class ExpressionCodegen extends KtVisitor impleme resolvedCall, false); } + if (descriptor instanceof FakeCallableDescriptorForTypeAliasObject) { + descriptor = ((FakeCallableDescriptorForTypeAliasObject) descriptor).getTypeAliasDescriptor(); + } if (descriptor instanceof TypeAliasDescriptor) { ClassDescriptor classDescriptor = ((TypeAliasDescriptor) descriptor).getClassDescriptor(); if (classDescriptor == null) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolveUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolveUtil.kt index 6d9f8e7d5ba..047afb317f4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolveUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/QualifiedExpressionResolveUtil.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject import org.jetbrains.kotlin.resolve.descriptorUtil.classValueDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.classValueTypeDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.hasCompanionObject @@ -97,10 +98,10 @@ private fun resolveQualifierReferenceTarget( // Given a class qualifier in expression position, // it should provide a proper REFERENCE_TARGET (with type), // and, in case of implicit companion object reference, SHORT_REFERENCE_TO_COMPANION_OBJECT. - val classValueDescriptor = classifier.classValueDescriptor - if (selectorIsCallable && classValueDescriptor != null) { + val receiverClassifierDescriptor = classifier.getCallableReceiverDescriptorRetainingTypeAliasReference() + if (selectorIsCallable && receiverClassifierDescriptor != null) { val classValueTypeDescriptor = classifier.classValueTypeDescriptor!! - context.trace.record(BindingContext.REFERENCE_TARGET, qualifier.referenceExpression, classValueDescriptor) + context.trace.record(BindingContext.REFERENCE_TARGET, qualifier.referenceExpression, receiverClassifierDescriptor) context.trace.recordType(qualifier.expression, classValueTypeDescriptor.defaultType) if (classifier.hasCompanionObject) { context.trace.record(BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, qualifier.referenceExpression, classifier) @@ -111,3 +112,16 @@ private fun resolveQualifierReferenceTarget( return qualifier.descriptor } + +private fun ClassifierDescriptor.getCallableReceiverDescriptorRetainingTypeAliasReference(): DeclarationDescriptor? = + when (this) { + is ClassDescriptor -> classValueDescriptor + + is TypeAliasDescriptor -> + if (classDescriptor?.classValueDescriptor != null) + FakeCallableDescriptorForTypeAliasObject(this) + else + this + + else -> null + } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt index 895fc1ad217..421c10ae65a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ClassifierUsageChecker.kt @@ -26,7 +26,8 @@ import org.jetbrains.kotlin.psi.KtTreeVisitorVoid import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.DeprecationResolver -import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject +import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject interface ClassifierUsageChecker { fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) @@ -55,16 +56,9 @@ fun checkClassifierUsages( return } - val target = getReferencedClassifier(expression) ?: return - - runCheckersWithTarget(target, expression) - - getReferenceToCompanionViaClassifier(expression, target)?.let { referenceClassifier -> - val outerClass = target.containingDeclaration as ClassDescriptor - runCheckersWithTarget(outerClass, expression) - if (referenceClassifier is TypeAliasDescriptor) { - runCheckersWithTarget(referenceClassifier, expression) - } + val targets = getReferencedClassifiers(expression) + for (target in targets) { + runCheckersWithTarget(target, expression) } } @@ -74,28 +68,49 @@ fun checkClassifierUsages( } } - private fun getReferencedClassifier(expression: KtReferenceExpression): ClassifierDescriptor? { + private fun getReferencedClassifiers(expression: KtReferenceExpression): List { val target = context.trace.get(BindingContext.REFERENCE_TARGET, expression) - if (target is ClassifierDescriptor) return target - if (target is ClassConstructorDescriptor) return target.constructedClass - // "Comparable" in "import java.lang.Comparable" references both a class and a SAM constructor and prevents - // REFERENCE_TARGET from being recorded in favor of AMBIGUOUS_REFERENCE_TARGET. But we must still run checkers - // to report if there's something wrong with the class. We characterize this case below by the following properties: - // 1) Exactly one of the references is a classifier - // 2) All references refer to the same source element, i.e. their source is the same - val targets = context.trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, expression) ?: return null - if (targets.groupBy { (it as? DeclarationDescriptorWithSource)?.source }.size != 1) return null - return targets.filterIsInstance().singleOrNull() + return when (target) { + is ClassifierDescriptor -> + listOfNotNull( + target, + getClassifierUsedToReferenceCompanionObject(target, expression) + ) + + is ClassConstructorDescriptor -> listOf(target.constructedClass) + + is FakeCallableDescriptorForTypeAliasObject -> { + val referencedObject = target.getReferencedObject() + val referencedTypeAlias = target.typeAliasDescriptor + if (referencedObject != referencedTypeAlias.classDescriptor) + listOf(referencedObject, referencedTypeAlias) + else + listOf(referencedTypeAlias) + } + + else -> { + // "Comparable" in "import java.lang.Comparable" references both a class and a SAM constructor and prevents + // REFERENCE_TARGET from being recorded in favor of AMBIGUOUS_REFERENCE_TARGET. But we must still run checkers + // to report if there's something wrong with the class. We characterize this case below by the following properties: + // 1) Exactly one of the references is a classifier + // 2) All references refer to the same source element, i.e. their source is the same + val targets = context.trace.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, expression) ?: return emptyList() + if (targets.groupBy { (it as? DeclarationDescriptorWithSource)?.source }.size != 1) return emptyList() + val targetClassifiers = targets.filterIsInstance() + if (targetClassifiers.size == 1) targetClassifiers else emptyList() + } + } } - private fun getReferenceToCompanionViaClassifier( - expression: KtReferenceExpression, - target: ClassifierDescriptor - ): ClassifierDescriptor? { - if (!DescriptorUtils.isCompanionObject(target)) return null - return context.trace.get(BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression) - } + private fun getClassifierUsedToReferenceCompanionObject( + referencedObject: ClassifierDescriptor, + expression: KtReferenceExpression + ): ClassifierDescriptor? = + if (referencedObject.isCompanionObject()) + context.trace.get(BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression) + else + null } for (declaration in declarations) { diff --git a/compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.kt b/compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.kt new file mode 100644 index 00000000000..a02955adc61 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.kt @@ -0,0 +1,18 @@ +// FILE: 1.kt +package something + +object N + +class WC { + companion object +} + +typealias T = N +typealias TWC = WC + +// FILE: 2.kt +import something.T +import something.TWC + +val test1 = T.hashCode() +val test2 = TWC.hashCode() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.txt b/compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.txt new file mode 100644 index 00000000000..4c369207c39 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.txt @@ -0,0 +1,30 @@ +package + +public val test1: kotlin.Int +public val test2: kotlin.Int + +package something { + + public object N { + private constructor N() + 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 + } + + public final class WC { + public constructor WC() + 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 + + public companion object Companion { + private constructor Companion() + 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 + } + } + public typealias T = something.N + public typealias TWC = something.WC +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index c71cb36c26a..d0bf3b011ac 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -21465,6 +21465,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/typealias/import.kt"); } + @TestMetadata("importForTypealiasObject.kt") + public void testImportForTypealiasObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.kt"); + doTest(fileName); + } + @TestMetadata("importFromTypeAliasObject.kt") public void testImportFromTypeAliasObject() throws Exception { runTest("compiler/testData/diagnostics/tests/typealias/importFromTypeAliasObject.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 24dd04088f7..c22f5b56c53 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -21465,6 +21465,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/typealias/import.kt"); } + @TestMetadata("importForTypealiasObject.kt") + public void testImportForTypealiasObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/importForTypealiasObject.kt"); + doTest(fileName); + } + @TestMetadata("importFromTypeAliasObject.kt") public void testImportFromTypeAliasObject() throws Exception { runTest("compiler/testData/diagnostics/tests/typealias/importFromTypeAliasObject.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index eb016c7af14..57031350060 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind.* import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention -import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor +import org.jetbrains.kotlin.descriptors.impl.DescriptorDerivedFromTypeAlias import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -50,7 +50,7 @@ fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor = companion fun DeclarationDescriptor.getImportableDescriptor(): DeclarationDescriptor = when (this) { - is TypeAliasConstructorDescriptor -> containingDeclaration + is DescriptorDerivedFromTypeAlias -> typeAliasDescriptor is ConstructorDescriptor -> containingDeclaration is PropertyAccessorDescriptor -> correspondingProperty else -> this @@ -97,13 +97,12 @@ val ClassifierDescriptorWithTypeParameters.hasCompanionObject: Boolean val ClassDescriptor.hasClassValueDescriptor: Boolean get() = classValueDescriptor != null -val ClassifierDescriptorWithTypeParameters.classValueDescriptor: ClassDescriptor? - get() = denotedClassDescriptor?.let { - if (it.kind.isSingleton) - it +val ClassDescriptor.classValueDescriptor: ClassDescriptor? + get() = + if (kind.isSingleton) + this else - it.companionObjectDescriptor - } + companionObjectDescriptor val ClassifierDescriptorWithTypeParameters.classValueTypeDescriptor: ClassDescriptor? get() = denotedClassDescriptor?.let { diff --git a/idea/testData/inspections/unusedImport/noUnusedImportForTypeAliasUsedAsObject.Dependency.kt b/idea/testData/inspections/unusedImport/noUnusedImportForTypeAliasUsedAsObject.Dependency.kt new file mode 100644 index 00000000000..6b39fa9378e --- /dev/null +++ b/idea/testData/inspections/unusedImport/noUnusedImportForTypeAliasUsedAsObject.Dependency.kt @@ -0,0 +1,10 @@ +package something + +object N + +class WC { + companion object +} + +typealias T = N +typealias TWC = WC \ No newline at end of file diff --git a/idea/testData/inspections/unusedImport/noUnusedImportForTypeAliasUsedAsObject.kt b/idea/testData/inspections/unusedImport/noUnusedImportForTypeAliasUsedAsObject.kt new file mode 100644 index 00000000000..7bccefa6550 --- /dev/null +++ b/idea/testData/inspections/unusedImport/noUnusedImportForTypeAliasUsedAsObject.kt @@ -0,0 +1,5 @@ +import something.T +import something.TWC + +val test1 = T.hashCode() +val test2 = TWC.hashCode() \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CompanionObjectIntrinsicAccessTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CompanionObjectIntrinsicAccessTranslator.java index 83f439ae2e9..bb5fc75172c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CompanionObjectIntrinsicAccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CompanionObjectIntrinsicAccessTranslator.java @@ -16,10 +16,10 @@ package org.jetbrains.kotlin.js.translate.reference; -import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; +import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.intrinsic.objects.ObjectIntrinsic; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java index 8a2585b5589..ac9205fc786 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtQualifiedExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject; import org.jetbrains.kotlin.types.KotlinType; import java.util.Arrays; @@ -220,7 +221,9 @@ public final class ReferenceTranslator { DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), simpleNameExpression); // Skip ValueParameterDescriptor because sometime we can miss resolved call for it, e.g. when set something to delegated property. - return descriptor instanceof VariableDescriptor && !(descriptor instanceof ValueParameterDescriptor); + return descriptor instanceof VariableDescriptor && + !(descriptor instanceof ValueParameterDescriptor) && + !(descriptor instanceof FakeCallableDescriptorForTypeAliasObject); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java index a8118ac309d..b8824948c7c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.types.KotlinType; @@ -129,6 +130,9 @@ public final class BindingUtils { } return classDescriptor; } + else if (descriptor instanceof FakeCallableDescriptorForTypeAliasObject) { + return ((FakeCallableDescriptorForTypeAliasObject) descriptor).getReferencedObject(); + } else { return descriptor; }