Clarify Visibility.isVisible contract for null as receiverValue

See comment
This commit is contained in:
Denis Zharkov
2016-03-25 19:01:13 +03:00
parent 60511f010a
commit 182b349492
15 changed files with 69 additions and 19 deletions
@@ -183,7 +183,7 @@ class BuilderFactoryForDuplicateSignatureDiagnostics(
descriptor.getParentJavaStaticClassScope()?.run { descriptor.getParentJavaStaticClassScope()?.run {
getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)
.filter { .filter {
it is FunctionDescriptor && Visibilities.isVisibleWithIrrelevantReceiver(it, descriptor) it is FunctionDescriptor && Visibilities.isVisibleIgnoringReceiver(it, descriptor)
} }
.forEach(::processMember) .forEach(::processMember)
} }
@@ -74,7 +74,7 @@ class JavaTypeAccessibilityChecker : AdditionalTypeChecker {
if (visitedTypeConstructors.contains(typeConstructor)) return if (visitedTypeConstructors.contains(typeConstructor)) return
visitedTypeConstructors.add(typeConstructor) visitedTypeConstructors.add(typeConstructor)
if (typeConstructor is JavaClassDescriptor && !Visibilities.isVisibleWithIrrelevantReceiver(typeConstructor, scopeOwner)) { if (typeConstructor is JavaClassDescriptor && !Visibilities.isVisibleIgnoringReceiver(typeConstructor, scopeOwner)) {
inaccessibleTypes.add(type) inaccessibleTypes.add(type)
} }
for (typeProjection in type.arguments) { for (typeProjection in type.arguments) {
@@ -36,7 +36,7 @@ fun syntheticExtensionVisibility(originalDescriptor: DeclarationDescriptorWithVi
else -> object : Visibility(originalVisibility.name, originalVisibility.isPublicAPI) { else -> object : Visibility(originalVisibility.name, originalVisibility.isPublicAPI) {
override fun isVisible(receiver: ReceiverValue?, what: DeclarationDescriptorWithVisibility, from: DeclarationDescriptor) override fun isVisible(receiver: ReceiverValue?, what: DeclarationDescriptorWithVisibility, from: DeclarationDescriptor)
= originalVisibility.isVisible(receiver, originalDescriptor, from) = originalVisibility.isVisible(Visibilities.ALWAYS_SUITABLE_RECEIVER, originalDescriptor, from)
override fun mustCheckInImports() override fun mustCheckInImports()
= throw UnsupportedOperationException("Should never be called for this visibility") = throw UnsupportedOperationException("Should never be called for this visibility")
@@ -1027,7 +1027,7 @@ public class OverrideResolver {
all.addAll((Collection) supertype.getMemberScope().getContributedVariables(declared.getName(), NoLookupLocation.WHEN_CHECK_OVERRIDES)); all.addAll((Collection) supertype.getMemberScope().getContributedVariables(declared.getName(), NoLookupLocation.WHEN_CHECK_OVERRIDES));
for (CallableMemberDescriptor fromSuper : all) { for (CallableMemberDescriptor fromSuper : all) {
if (OverridingUtil.DEFAULT.isOverridableBy(fromSuper, declared, null).getResult() == OVERRIDABLE) { if (OverridingUtil.DEFAULT.isOverridableBy(fromSuper, declared, null).getResult() == OVERRIDABLE) {
if (Visibilities.isVisibleWithIrrelevantReceiver(fromSuper, declared)) { if (Visibilities.isVisibleIgnoringReceiver(fromSuper, declared)) {
throw new IllegalStateException("Descriptor " + fromSuper + " is overridable by " + declared + throw new IllegalStateException("Descriptor " + fromSuper + " is overridable by " + declared +
" and visible but does not appear in its getOverriddenDescriptors()"); " and visible but does not appear in its getOverriddenDescriptors()");
} }
@@ -609,7 +609,7 @@ class QualifiedExpressionResolver(val symbolUsageValidator: SymbolUsageValidator
if (Visibilities.isPrivate(visibility)) return false if (Visibilities.isPrivate(visibility)) return false
if (!visibility.mustCheckInImports()) return true if (!visibility.mustCheckInImports()) return true
} }
return Visibilities.isVisibleWithIrrelevantReceiver(descriptor, shouldBeVisibleFrom) return Visibilities.isVisibleIgnoringReceiver(descriptor, shouldBeVisibleFrom)
} }
} }
@@ -160,7 +160,7 @@ open class FileScopeProviderImpl(
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? { override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
if (name in excludedNames) return null if (name in excludedNames) return null
val classifier = scope.getContributedClassifier(name, location) ?: return null val classifier = scope.getContributedClassifier(name, location) ?: return null
val visible = Visibilities.isVisibleWithIrrelevantReceiver(classifier as ClassDescriptor, fromDescriptor) val visible = Visibilities.isVisibleIgnoringReceiver(classifier as ClassDescriptor, fromDescriptor)
return classifier.check { filteringKind == if (visible) FilteringKind.VISIBLE_CLASSES else FilteringKind.INVISIBLE_CLASSES } return classifier.check { filteringKind == if (visible) FilteringKind.VISIBLE_CLASSES else FilteringKind.INVISIBLE_CLASSES }
} }
@@ -180,7 +180,7 @@ class LazyImportScope(
val visibility = descriptor.visibility val visibility = descriptor.visibility
val includeVisible = filteringKind == FilteringKind.VISIBLE_CLASSES val includeVisible = filteringKind == FilteringKind.VISIBLE_CLASSES
if (!visibility.mustCheckInImports()) return includeVisible if (!visibility.mustCheckInImports()) return includeVisible
return Visibilities.isVisibleWithIrrelevantReceiver(descriptor, importResolver.moduleDescriptor) == includeVisible return Visibilities.isVisibleIgnoringReceiver(descriptor, importResolver.moduleDescriptor) == includeVisible
} }
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? { override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.util.ModuleVisibilityHelper; import org.jetbrains.kotlin.util.ModuleVisibilityHelper;
import org.jetbrains.kotlin.utils.CollectionsKt; import org.jetbrains.kotlin.utils.CollectionsKt;
@@ -88,6 +89,10 @@ public class Visibilities {
@Override @Override
public boolean isVisible(@Nullable ReceiverValue thisObject, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { public boolean isVisible(@Nullable ReceiverValue thisObject, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
if (PRIVATE.isVisible(thisObject, what, from)) { if (PRIVATE.isVisible(thisObject, what, from)) {
// See Visibility.isVisible contract
if (thisObject == ALWAYS_SUITABLE_RECEIVER) return true;
if (thisObject == IRRELEVANT_RECEIVER) return false;
DeclarationDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class); DeclarationDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class);
if (classDescriptor != null && thisObject instanceof ThisClassReceiver) { if (classDescriptor != null && thisObject instanceof ThisClassReceiver) {
@@ -223,13 +228,21 @@ public class Visibilities {
} }
/** /**
* Receiver used only for visibility PRIVATE_TO_THIS. * @see Visibility.isVisible contract
* For all other visibilities this method give correct result.
*/ */
public static boolean isVisibleWithIrrelevantReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { public static boolean isVisibleIgnoringReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
return findInvisibleMember(null, what, from) == null; return findInvisibleMember(ALWAYS_SUITABLE_RECEIVER, what, from) == null;
} }
/**
* @see Visibility.isVisible contract
* @see Visibilities.RECEIVER_DOES_NOT_EXIST
*/
public static boolean isVisibleWithAnyReceiver(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
return findInvisibleMember(IRRELEVANT_RECEIVER, what, from) == null;
}
@Nullable @Nullable
public static DeclarationDescriptorWithVisibility findInvisibleMember( public static DeclarationDescriptorWithVisibility findInvisibleMember(
@Nullable ReceiverValue receiver, @Nullable ReceiverValue receiver,
@@ -285,6 +298,30 @@ public class Visibilities {
public static final Visibility DEFAULT_VISIBILITY = PUBLIC; public static final Visibility DEFAULT_VISIBILITY = PUBLIC;
/**
* This value should be used for receiverValue parameter of Visibility.isVisible
* iff there is intention to determine if member is visible for any receiver.
*/
private static final ReceiverValue IRRELEVANT_RECEIVER = new ReceiverValue() {
@NotNull
@Override
public KotlinType getType() {
throw new IllegalStateException("This method should not be called");
}
};
/**
* This value should be used for receiverValue parameter of Visibility.isVisible
* iff there is intention to determine if member is visible without receiver related checks being performed.
*/
public static final ReceiverValue ALWAYS_SUITABLE_RECEIVER = new ReceiverValue() {
@NotNull
@Override
public KotlinType getType() {
throw new IllegalStateException("This method should not be called");
}
};
public static boolean isPrivate(@NotNull Visibility visibility) { public static boolean isPrivate(@NotNull Visibility visibility) {
return visibility == PRIVATE || visibility == PRIVATE_TO_THIS; return visibility == PRIVATE || visibility == PRIVATE_TO_THIS;
} }
@@ -22,6 +22,19 @@ abstract class Visibility protected constructor(
val name: String, val name: String,
val isPublicAPI: Boolean val isPublicAPI: Boolean
) { ) {
/**
* @param receiver can be used to determine callee accessibility for some special receiver value
*
* 'null'-value basically means that receiver is absent in current call
*
* In case if it's needed to perform basic checks ignoring ones considering receiver (e.g. when checks happen beyond any call),
* special value Visibilities.ALWAYS_SUITABLE_RECEIVER should be used.
* If it's needed to determine whether visibility accepts any receiver, Visibilities.IRRELEVANT_RECEIVER should be used.
*
* NB: Currently Visibilities.IRRELEVANT_RECEIVER has the same effect as 'null'
*
* Also it's important that implementation that take receiver into account do aware about these special values.
*/
abstract fun isVisible(receiver: ReceiverValue?, what: DeclarationDescriptorWithVisibility, from: DeclarationDescriptor): Boolean abstract fun isVisible(receiver: ReceiverValue?, what: DeclarationDescriptorWithVisibility, from: DeclarationDescriptor): Boolean
/** /**
@@ -337,7 +337,7 @@ public class OverridingUtil {
for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) { for (CallableMemberDescriptor fromSupertype : descriptorsFromSuper) {
OverrideCompatibilityInfo.Result result = DEFAULT.isOverridableBy(fromSupertype, fromCurrent, current).getResult(); OverrideCompatibilityInfo.Result result = DEFAULT.isOverridableBy(fromSupertype, fromCurrent, current).getResult();
boolean isVisible = Visibilities.isVisibleWithIrrelevantReceiver(fromSupertype, current); boolean isVisible = Visibilities.isVisibleIgnoringReceiver(fromSupertype, current);
switch (result) { switch (result) {
case OVERRIDABLE: case OVERRIDABLE:
if (isVisible) { if (isVisible) {
@@ -567,7 +567,7 @@ public class OverridingUtil {
public Boolean invoke(CallableMemberDescriptor descriptor) { public Boolean invoke(CallableMemberDescriptor descriptor) {
//nested class could capture private member, so check for private visibility added //nested class could capture private member, so check for private visibility added
return !Visibilities.isPrivate(descriptor.getVisibility()) && return !Visibilities.isPrivate(descriptor.getVisibility()) &&
Visibilities.isVisibleWithIrrelevantReceiver(descriptor, current); Visibilities.isVisibleIgnoringReceiver(descriptor, current);
} }
}); });
} }
@@ -54,7 +54,7 @@ private fun DeclarationDescriptorWithVisibility.isVisible(
bindingContext: BindingContext? = null, bindingContext: BindingContext? = null,
resolutionScope: LexicalScope? = null resolutionScope: LexicalScope? = null
): Boolean { ): Boolean {
if (Visibilities.isVisibleWithIrrelevantReceiver(this, from)) return true if (Visibilities.isVisibleWithAnyReceiver(this, from)) return true
if (bindingContext == null || resolutionScope == null) return false if (bindingContext == null || resolutionScope == null) return false
@@ -242,8 +242,8 @@ class MoveKotlinDeclarationsProcessor(
fun DeclarationDescriptor.isVisibleIn(where: DeclarationDescriptor): Boolean { fun DeclarationDescriptor.isVisibleIn(where: DeclarationDescriptor): Boolean {
return when { return when {
this !is DeclarationDescriptorWithVisibility -> true this !is DeclarationDescriptorWithVisibility -> true
!Visibilities.isVisibleWithIrrelevantReceiver(this, where) -> false !Visibilities.isVisibleIgnoringReceiver(this, where) -> false
this is ConstructorDescriptor -> Visibilities.isVisibleWithIrrelevantReceiver(containingDeclaration, where) this is ConstructorDescriptor -> Visibilities.isVisibleIgnoringReceiver(containingDeclaration, where)
else -> true else -> true
} }
} }
@@ -150,7 +150,7 @@ private fun KotlinPullUpData.checkVisibility(
if (targetDescriptor in memberDescriptors.values) return if (targetDescriptor in memberDescriptors.values) return
val target = (targetDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return val target = (targetDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return
if (targetDescriptor is DeclarationDescriptorWithVisibility if (targetDescriptor is DeclarationDescriptorWithVisibility
&& !Visibilities.isVisibleWithIrrelevantReceiver(targetDescriptor, targetClassDescriptor)) { && !Visibilities.isVisibleIgnoringReceiver(targetDescriptor, targetClassDescriptor)) {
val message = RefactoringBundle.message( val message = RefactoringBundle.message(
"0.uses.1.which.is.not.accessible.from.the.superclass", "0.uses.1.which.is.not.accessible.from.the.superclass",
memberDescriptor.renderForConflicts(), memberDescriptor.renderForConflicts(),
@@ -196,7 +196,7 @@ private fun checkVisibility(
fun reportConflictIfAny(targetDescriptor: DeclarationDescriptor) { fun reportConflictIfAny(targetDescriptor: DeclarationDescriptor) {
val target = (targetDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return val target = (targetDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return
if (targetDescriptor is DeclarationDescriptorWithVisibility if (targetDescriptor is DeclarationDescriptorWithVisibility
&& !Visibilities.isVisibleWithIrrelevantReceiver(targetDescriptor, targetClassDescriptor)) { && !Visibilities.isVisibleIgnoringReceiver(targetDescriptor, targetClassDescriptor)) {
val message = "${context.memberDescriptors[member]!!.renderForConflicts()} " + val message = "${context.memberDescriptors[member]!!.renderForConflicts()} " +
"uses ${targetDescriptor.renderForConflicts()}, " + "uses ${targetDescriptor.renderForConflicts()}, " +
"which is not accessible from the ${targetClassDescriptor.renderForConflicts()}" "which is not accessible from the ${targetClassDescriptor.renderForConflicts()}"
@@ -253,7 +253,7 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
fun isVisible(descriptor: DeclarationDescriptor): Boolean { fun isVisible(descriptor: DeclarationDescriptor): Boolean {
if (descriptor !is DeclarationDescriptorWithVisibility) return true if (descriptor !is DeclarationDescriptorWithVisibility) return true
val visibility = descriptor.visibility val visibility = descriptor.visibility
return !visibility.mustCheckInImports() || Visibilities.isVisibleWithIrrelevantReceiver(descriptor, filePackage) return !visibility.mustCheckInImports() || Visibilities.isVisibleIgnoringReceiver(descriptor, filePackage)
} }
val classNamesToImport = scopeToImport val classNamesToImport = scopeToImport