Introduce deprecation of companion objects nested classes
Introdude deprecation as per KT-21515. Warning is reported on type usage, that soon will became invisible. Quickfix by adding explicit import is added. Idea behind implementation is to mark scopes that are deprecated (see ClassResolutionScopesSupport). Then, during walk along hierarchy of scopes, look at deprecation status of the scope that has provided this classifier. Note that we also have to check if there are *some* non-deprecated visibility paths (because we can see classifier by two paths, e.g. if we've added explicit import) -- then this type reference shouldn't be treated as deprecated.
This commit is contained in:
@@ -106,6 +106,7 @@ public interface Errors {
|
||||
DiagnosticFactory3<KtSimpleNameExpression, DeclarationDescriptor, Visibility, DeclarationDescriptor> INVISIBLE_REFERENCE =
|
||||
DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, DeclarationDescriptor, Visibility, DeclarationDescriptor> INVISIBLE_MEMBER = DiagnosticFactory3.create(ERROR, CALL_ELEMENT);
|
||||
DiagnosticFactory1<KtElement, DeclarationDescriptor> DEPRECATED_ACCESS_BY_SHORT_NAME = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<PsiElement, ConstructorDescriptor> PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
|
||||
+1
@@ -66,6 +66,7 @@ public class DefaultErrorMessages {
|
||||
|
||||
MAP.put(INVISIBLE_REFERENCE, "Cannot access ''{0}'': it is {1} in {2}", NAME, VISIBILITY, NAME_OF_CONTAINING_DECLARATION_OR_FILE);
|
||||
MAP.put(INVISIBLE_MEMBER, "Cannot access ''{0}'': it is {1} in {2}", NAME, VISIBILITY, NAME_OF_CONTAINING_DECLARATION_OR_FILE);
|
||||
MAP.put(DEPRECATED_ACCESS_BY_SHORT_NAME, "Access to this type by short name is deprecated, and soon is going to be removed. Please, add explicit qualifier or import", NAME);
|
||||
|
||||
MAP.put(PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL, "Protected constructor ''{0}'' from other classes can only be used in super-call", Renderers.SHORT_NAMES_IN_TYPES);
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ class SyntheticClassOrObjectDescriptor(
|
||||
|
||||
private val thisDescriptor: SyntheticClassOrObjectDescriptor get() = this // code readability
|
||||
private val typeConstructor = SyntheticTypeConstructor(c.storageManager)
|
||||
private val resolutionScopesSupport = ClassResolutionScopesSupport(thisDescriptor, c.storageManager, { outerScope })
|
||||
private val resolutionScopesSupport = ClassResolutionScopesSupport(thisDescriptor, c.storageManager, c.languageVersionSettings, { outerScope })
|
||||
private val syntheticSupertypes =
|
||||
mutableListOf<KotlinType>().apply { c.syntheticResolveExtension.addSyntheticSupertypes(thisDescriptor, this) }
|
||||
private val unsubstitutedMemberScope =
|
||||
|
||||
@@ -244,6 +244,8 @@ public interface BindingContext {
|
||||
WritableSlice<PsiElement, PropertyDescriptor> PRIMARY_CONSTRUCTOR_PARAMETER = Slices.createSimpleSlice();
|
||||
WritableSlice<PsiElement, TypeAliasDescriptor> TYPE_ALIAS = Slices.createSimpleSlice();
|
||||
|
||||
WritableSlice<PsiElement, Boolean> DEPRECATED_SHORT_NAME_ACCESS = Slices.createSimpleSlice();
|
||||
|
||||
WritableSlice[] DECLARATIONS_TO_DESCRIPTORS = new WritableSlice[] {
|
||||
CLASS, TYPE_PARAMETER, FUNCTION, CONSTRUCTOR, VARIABLE, VALUE_PARAMETER, PROPERTY_ACCESSOR,
|
||||
PRIMARY_CONSTRUCTOR_PARAMETER, SCRIPT, TYPE_ALIAS
|
||||
|
||||
@@ -33,8 +33,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.*
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
|
||||
import org.jetbrains.kotlin.types.expressions.isWithoutValueArguments
|
||||
@@ -62,6 +61,27 @@ class QualifiedExpressionResolver {
|
||||
get() = qualifierParts.flatMap { it.typeArguments?.arguments.orEmpty() }
|
||||
}
|
||||
|
||||
fun LexicalScope.findClassifierAndReportDeprecationIfNeeded(
|
||||
name: Name,
|
||||
lookupLocation: KotlinLookupLocation,
|
||||
reportOn: KtExpression,
|
||||
trace: BindingTrace
|
||||
): ClassifierDescriptor? {
|
||||
val (classifier, isDeprecated) = findFirstClassifierWithDeprecationStatus(name, lookupLocation) ?: return null
|
||||
|
||||
if (isDeprecated) {
|
||||
trace.record(BindingContext.DEPRECATED_SHORT_NAME_ACCESS, reportOn) // For IDE
|
||||
|
||||
// slow-path: we know that closest classifier is imported by the deprecated path, but before reporting
|
||||
// deprecation, we have to recheck if there's some other import path, which isn't deprecated (e.g. explicit import)
|
||||
if (!classifier.canBeResolvedWithoutDeprecation(this, lookupLocation)) {
|
||||
trace.report(Errors.DEPRECATED_ACCESS_BY_SHORT_NAME.on(reportOn, classifier))
|
||||
}
|
||||
}
|
||||
|
||||
return classifier
|
||||
}
|
||||
|
||||
fun resolveDescriptorForType(
|
||||
userType: KtUserType,
|
||||
scope: LexicalScope,
|
||||
@@ -71,7 +91,13 @@ class QualifiedExpressionResolver {
|
||||
val ownerDescriptor = if (!isDebuggerContext) scope.ownerDescriptor else null
|
||||
if (userType.qualifier == null) {
|
||||
val descriptor = userType.referenceExpression?.let { expression ->
|
||||
val classifier = scope.findClassifier(expression.getReferencedNameAsName(), KotlinLookupLocation(expression))
|
||||
val classifier = scope.findClassifierAndReportDeprecationIfNeeded(
|
||||
expression.getReferencedNameAsName(),
|
||||
KotlinLookupLocation(expression),
|
||||
expression,
|
||||
trace
|
||||
)
|
||||
|
||||
checkNotEnumEntry(classifier, trace, expression)
|
||||
storeResult(trace, expression, classifier, ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = false)
|
||||
classifier
|
||||
@@ -143,9 +169,14 @@ class QualifiedExpressionResolver {
|
||||
}
|
||||
|
||||
if (qualifierPartList.size == 1) {
|
||||
val (name, simpleName) = qualifierPartList.single()
|
||||
val descriptor = scope.findClassifier(name, KotlinLookupLocation(simpleName))
|
||||
storeResult(trace, simpleName, descriptor, ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = true)
|
||||
val (name, simpleNameExpression) = qualifierPartList.single()
|
||||
val descriptor = scope.findClassifierAndReportDeprecationIfNeeded(
|
||||
name,
|
||||
KotlinLookupLocation(simpleNameExpression),
|
||||
simpleNameExpression,
|
||||
trace
|
||||
)
|
||||
storeResult(trace, simpleNameExpression, descriptor, ownerDescriptor, position = QualifierPosition.TYPE, isQualifier = true)
|
||||
return TypeQualifierResolutionResult(qualifierPartList, descriptor)
|
||||
}
|
||||
|
||||
|
||||
+43
-4
@@ -18,10 +18,7 @@ package org.jetbrains.kotlin.resolve.calls.tower
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -45,11 +42,14 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.*
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDynamicExtensionAnnotation
|
||||
import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.canBeResolvedWithoutDeprecation
|
||||
import org.jetbrains.kotlin.types.DeferredType
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
@@ -287,15 +287,54 @@ class NewResolutionOldInference(
|
||||
error.message
|
||||
)
|
||||
)
|
||||
|
||||
is NestedClassViaInstanceReference -> tracing.nestedClassAccessViaInstanceReference(
|
||||
resolvedCall.trace,
|
||||
error.classDescriptor,
|
||||
resolvedCall.explicitReceiverKind
|
||||
)
|
||||
|
||||
is ErrorDescriptorDiagnostic -> {
|
||||
// todo
|
||||
// return@map null
|
||||
}
|
||||
|
||||
is ResolvedUsingDeprecatedVisbility -> {
|
||||
resolvedCall.trace.record(
|
||||
BindingContext.DEPRECATED_SHORT_NAME_ACCESS,
|
||||
resolvedCall.call.calleeExpression
|
||||
)
|
||||
|
||||
val candidateDescriptor = resolvedCall.candidateDescriptor
|
||||
val descriptorToLookup: DeclarationDescriptor = when (candidateDescriptor) {
|
||||
is ClassConstructorDescriptor -> candidateDescriptor.containingDeclaration
|
||||
is FakeCallableDescriptorForObject -> candidateDescriptor.classDescriptor
|
||||
else -> error(
|
||||
"Unexpected candidate descriptor of resolved call with " +
|
||||
"ResolvedUsingDeprecatedVisibility-diagnostic: $candidateDescriptor\n" +
|
||||
"Call context: ${resolvedCall.call.callElement.parent?.text}"
|
||||
)
|
||||
}
|
||||
|
||||
// If this descriptor was resolved from HierarchicalScope, then there can be another, non-deprecated path
|
||||
// in parents of base scope
|
||||
val sourceScope = error.baseSourceScope
|
||||
val canBeResolvedWithoutDeprecation = if (sourceScope is HierarchicalScope) {
|
||||
descriptorToLookup.canBeResolvedWithoutDeprecation(
|
||||
sourceScope,
|
||||
error.lookupLocation
|
||||
)
|
||||
} else {
|
||||
// Normally, that should be unreachable, but instead of asserting that, we will report diagnostic
|
||||
false
|
||||
}
|
||||
|
||||
if (!canBeResolvedWithoutDeprecation) {
|
||||
resolvedCall.trace.report(
|
||||
Errors.DEPRECATED_ACCESS_BY_SHORT_NAME.on(resolvedCall.call.callElement, resolvedCall.resultingDescriptor)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-31
@@ -16,9 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.lazy.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
|
||||
import org.jetbrains.kotlin.resolve.scopes.*
|
||||
@@ -30,6 +31,7 @@ import java.util.*
|
||||
class ClassResolutionScopesSupport(
|
||||
private val classDescriptor: ClassDescriptor,
|
||||
storageManager: StorageManager,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
private val getOuterScope: () -> LexicalScope
|
||||
) {
|
||||
private fun scopeWithGenerics(parent: LexicalScope): LexicalScopeImpl {
|
||||
@@ -85,44 +87,50 @@ class ClassResolutionScopesSupport(
|
||||
parent: LexicalScope,
|
||||
ownerDescriptor: DeclarationDescriptor,
|
||||
classDescriptor: ClassDescriptor,
|
||||
withCompanionObject: Boolean = true
|
||||
withCompanionObject: Boolean = true,
|
||||
isDeprecated: Boolean = false
|
||||
): LexicalScope {
|
||||
val companionObjectDescriptor = classDescriptor.companionObjectDescriptor?.takeIf { withCompanionObject }
|
||||
val staticScopes = ArrayList<MemberScope>(3)
|
||||
|
||||
// todo filter fake overrides
|
||||
staticScopes.add(classDescriptor.staticScope)
|
||||
|
||||
staticScopes.add(classDescriptor.unsubstitutedInnerClassesScope)
|
||||
staticScopes.addIfNotNull(companionObjectDescriptor?.getStaticScopeOfCompanionObject(classDescriptor))
|
||||
|
||||
val implicitReceiver: ReceiverParameterDescriptor?
|
||||
val parentForNewScope = companionObjectDescriptor?.packScopesOfCompanionSupertypes(parent, ownerDescriptor) ?: parent
|
||||
|
||||
val parentForNewScope: LexicalScope
|
||||
|
||||
if (withCompanionObject) {
|
||||
staticScopes.addIfNotNull(classDescriptor.companionObjectDescriptor?.unsubstitutedInnerClassesScope)
|
||||
implicitReceiver = classDescriptor.companionObjectDescriptor?.thisAsReceiverParameter
|
||||
|
||||
parentForNewScope = classDescriptor.companionObjectDescriptor?.let {
|
||||
it.getAllSuperclassesWithoutAny().asReversed().fold(parent) { scope, currentClass ->
|
||||
createInheritanceScope(
|
||||
parent = scope,
|
||||
ownerDescriptor = ownerDescriptor,
|
||||
classDescriptor = currentClass,
|
||||
withCompanionObject = false
|
||||
)
|
||||
}
|
||||
} ?: parent
|
||||
} else {
|
||||
implicitReceiver = null
|
||||
parentForNewScope = parent
|
||||
}
|
||||
|
||||
return LexicalChainedScope(
|
||||
parentForNewScope, ownerDescriptor, false,
|
||||
implicitReceiver,
|
||||
LexicalScopeKind.CLASS_INHERITANCE,
|
||||
memberScopes = staticScopes, isStaticScope = true
|
||||
val lexicalChainedScope = LexicalChainedScope(
|
||||
parentForNewScope, ownerDescriptor,
|
||||
isOwnerDescriptorAccessibleByLabel = false,
|
||||
implicitReceiver = companionObjectDescriptor?.thisAsReceiverParameter,
|
||||
kind = LexicalScopeKind.CLASS_INHERITANCE,
|
||||
memberScopes = staticScopes,
|
||||
isStaticScope = true
|
||||
)
|
||||
|
||||
return if (isDeprecated) DeprecatedLexicalScope(lexicalChainedScope) else lexicalChainedScope
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.getStaticScopeOfCompanionObject(companionOwner: ClassDescriptor): MemberScope? {
|
||||
return when {
|
||||
// We always see nesteds from our own companion
|
||||
companionOwner == classDescriptor -> unsubstitutedInnerClassesScope
|
||||
|
||||
// We see nesteds from other companions in hierarchy only in legacy mode
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.ProhibitVisibilityOfNestedClassifiersFromSupertypesOfCompanion) -> null
|
||||
|
||||
else -> DeprecatedMemberScope(unsubstitutedInnerClassesScope)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.packScopesOfCompanionSupertypes(
|
||||
parent: LexicalScope,
|
||||
ownerDescriptor: DeclarationDescriptor
|
||||
): LexicalScope? {
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitVisibilityOfNestedClassifiersFromSupertypesOfCompanion)) return null
|
||||
return getAllSuperclassesWithoutAny().asReversed().fold(parent) { scope, currentClass ->
|
||||
createInheritanceScope(scope, ownerDescriptor, currentClass, withCompanionObject = false, isDeprecated = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> StorageManager.createLazyValue(onRecursion: ((Boolean) -> T), compute: () -> T) =
|
||||
|
||||
+7
-1
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.ReadOnly;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorBase;
|
||||
@@ -221,7 +222,12 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
|
||||
return null;
|
||||
}, null);
|
||||
|
||||
this.resolutionScopesSupport = new ClassResolutionScopesSupport(this, storageManager, this::getOuterScope);
|
||||
this.resolutionScopesSupport = new ClassResolutionScopesSupport(
|
||||
this,
|
||||
storageManager,
|
||||
c.getLanguageVersionSettings(),
|
||||
this::getOuterScope
|
||||
);
|
||||
|
||||
this.parameters = c.getStorageManager().createLazyValue(() -> {
|
||||
KtClassLikeInfo classInfo = declarationProvider.getOwnerInfo();
|
||||
|
||||
Reference in New Issue
Block a user