From 5bf336474d61cca77da1d0731cef93e758d0e7ce Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 25 Mar 2016 13:59:52 +0300 Subject: [PATCH] Respect receiver-dependent visibility when selecting smart-cast type --- .../cfg/ControlFlowInformationProvider.java | 2 +- .../kotlin/resolve/calls/CandidateResolver.kt | 72 +++++++++++++++---- .../kotlin/resolve/calls/resolvedCallUtil.kt | 8 ++- .../calls/smartcasts/SmartCastManager.java | 20 +++++- .../resolve/calls/smartcasts/smartCastUtil.kt | 29 ++++++++ .../kotlin/resolve/calls/tower/TowerLevels.kt | 4 +- .../innerProtectedClass.kt | 13 ++++ .../innerProtectedClass.txt | 27 +++++++ .../smartcastOnExtensionReceiver.kt | 9 +++ .../smartcastOnExtensionReceiver.txt | 10 +++ .../protectedVisibility/unstableSmartCast.kt | 18 +++++ .../protectedVisibility/unstableSmartCast.txt | 30 ++++++++ .../protectedVisibility/withSmartcast.kt | 30 ++++++++ .../protectedVisibility/withSmartcast.txt | 24 +++++++ .../checkers/DiagnosticsTestGenerated.java | 24 +++++++ .../org/jetbrains/kotlin/idea/core/Utils.kt | 3 +- 16 files changed, 302 insertions(+), 21 deletions(-) create mode 100644 compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/smartCastUtil.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.txt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.txt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.txt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java index 914de61b4be..110b74ad7e8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java @@ -466,7 +466,7 @@ public class ControlFlowInformationProvider { ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext()); ReceiverValue receiverValue = null; if (resolvedCall != null) { - receiverValue = resolvedCall.getDispatchReceiver(); + receiverValue = ResolvedCallUtilKt.getDispatchReceiverWithSmartCast(resolvedCall); } if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor) && setterDescriptor != null diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index de8ad0e5629..f909a2b808c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -43,6 +43,7 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.* import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager +import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver @@ -76,7 +77,7 @@ class CandidateResolver( } if (!context.isDebuggerContext) { - checkVisibility() + checkVisibilityWithoutReceiver() } when (checkArguments) { @@ -169,9 +170,17 @@ class CandidateResolver( } } - private fun CallCandidateResolutionContext<*>.checkVisibility() = checkAndReport { - val invisibleMember = Visibilities.findInvisibleMember(candidateCall.dispatchReceiver, candidateDescriptor, scope.ownerDescriptor) - if (invisibleMember != null) { + private fun CallCandidateResolutionContext<*>.checkVisibilityWithoutReceiver() = checkAndReport { + checkVisibilityWithDispatchReceiver(Visibilities.ALWAYS_SUITABLE_RECEIVER, null) + } + + private fun CallCandidateResolutionContext<*>.checkVisibilityWithDispatchReceiver( + receiverArgument: ReceiverValue?, + smartCastType: KotlinType? + ): ResolutionStatus { + val invisibleMember = Visibilities.findInvisibleMember( + getReceiverValueWithSmartCast(receiverArgument, smartCastType), candidateDescriptor, scope.ownerDescriptor) + return if (invisibleMember != null) { tracing.invisibleMember(trace, invisibleMember) OTHER_ERROR } else { @@ -179,6 +188,20 @@ class CandidateResolver( } } + private fun CallCandidateResolutionContext<*>.isCandidateVisibleOrExtensionReceiver( + receiverArgument: ReceiverValue?, + smartCastType: KotlinType?, + isDispatchReceiver: Boolean + ) = !isDispatchReceiver || isCandidateVisible(receiverArgument, smartCastType) + + private fun CallCandidateResolutionContext<*>.isCandidateVisible( + receiverArgument: ReceiverValue?, + smartCastType: KotlinType? + ) = Visibilities.findInvisibleMember( + getReceiverValueWithSmartCast(receiverArgument, smartCastType), + candidateDescriptor, scope.ownerDescriptor + ) == null + private fun CallCandidateResolutionContext<*>.checkExtensionReceiver() = checkAndReport { val receiverParameter = candidateCall.getCandidateDescriptor().extensionReceiverParameter val receiverArgument = candidateCall.getExtensionReceiver() @@ -430,13 +453,25 @@ class CandidateResolver( candidateCall, candidateCall.getResultingDescriptor().extensionReceiverParameter, candidateCall.extensionReceiver as ReceiverValue?, - candidateCall.getExplicitReceiverKind().isExtensionReceiver, false)) + candidateCall.explicitReceiverKind.isExtensionReceiver, + implicitInvokeCheck = false, isDispatchReceiver = false)) resultStatus = resultStatus.combine(context.checkReceiver(candidateCall, candidateCall.getResultingDescriptor().dispatchReceiverParameter, candidateCall.getDispatchReceiver(), candidateCall.getExplicitReceiverKind().isDispatchReceiver, // for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' if there is unsafe call error - context.call is CallForImplicitInvoke)) + implicitInvokeCheck = context.call is CallForImplicitInvoke, + isDispatchReceiver = true)) + + if (!context.isDebuggerContext + && candidateCall.dispatchReceiver != null + // Do not report error if it's already reported when checked without receiver + && context.isCandidateVisible(receiverArgument = Visibilities.ALWAYS_SUITABLE_RECEIVER, smartCastType = null)) { + resultStatus = resultStatus.combine( + context.checkVisibilityWithDispatchReceiver( + candidateCall.dispatchReceiver, candidateCall.smartCastDispatchReceiverType)) + } + return resultStatus } @@ -445,7 +480,9 @@ class CandidateResolver( receiverParameter: ReceiverParameterDescriptor?, receiverArgument: ReceiverValue?, isExplicitReceiver: Boolean, - implicitInvokeCheck: Boolean): ResolutionStatus { + implicitInvokeCheck: Boolean, + isDispatchReceiver: Boolean + ): ResolutionStatus { if (receiverParameter == null || receiverArgument == null) return SUCCESS val candidateDescriptor = candidateCall.candidateDescriptor if (TypeUtils.dependsOnTypeParameters(receiverParameter.type, candidateDescriptor.typeParameters)) return SUCCESS @@ -465,11 +502,11 @@ class CandidateResolver( val call = candidateCall.call val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall() val expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type - val smartCastNeeded = !ArgumentTypeResolver.isSubtypeOfForArgumentType(receiverArgument.type, expectedReceiverParameterType) + val notNullReceiverExpected = !ArgumentTypeResolver.isSubtypeOfForArgumentType(receiverArgument.type, expectedReceiverParameterType) + val smartCastNeeded = + notNullReceiverExpected || !isCandidateVisibleOrExtensionReceiver(receiverArgument, null, isDispatchReceiver) var reportUnsafeCall = false - val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this) - val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue) var nullableImplicitInvokeReceiver = false var receiverArgumentType = receiverArgument.type if (implicitInvokeCheck && call is CallForImplicitInvoke && call.isSafeCall()) { @@ -483,6 +520,9 @@ class CandidateResolver( } } } + + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this) + val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue) val expression = (receiverArgument as? ExpressionReceiver)?.expression if (nullability.canBeNull() && !nullability.canBeNonNull()) { if (!TypeUtils.isNullableType(expectedReceiverParameterType)) { @@ -496,14 +536,20 @@ class CandidateResolver( // Look if smart cast has some useful nullability info val smartCastResult = SmartCastManager.checkAndRecordPossibleCast( - dataFlowValue, expectedReceiverParameterType, expression, this, candidateCall.call.calleeExpression, /*recordType =*/true + dataFlowValue, expectedReceiverParameterType, + { possibleSmartCast -> isCandidateVisibleOrExtensionReceiver(receiverArgument, possibleSmartCast, isDispatchReceiver) }, + expression, this, candidateCall.call.calleeExpression, /*recordType =*/true ) if (smartCastResult == null) { - reportUnsafeCall = true + if (notNullReceiverExpected) { + reportUnsafeCall = true + } } else { - candidateCall.setSmartCastDispatchReceiverType(smartCastResult.resultType) + if (isDispatchReceiver) { + candidateCall.setSmartCastDispatchReceiverType(smartCastResult.resultType) + } if (!smartCastResult.isCorrect) { // Error about unstable smart cast reported within checkAndRecordPossibleCast return OTHER_ERROR diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt index a62f5fb432c..ba558f4f954 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt @@ -16,15 +16,14 @@ package org.jetbrains.kotlin.resolve.calls.resolvedCallUtil -import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.psi.KtThisExpression import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.descriptorUtil.getOwnerForEffectiveDispatchReceiverParameter import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver @@ -88,4 +87,7 @@ private fun ResolvedCall<*>.hasSafeNullableReceiver(context: CallResolutionConte fun ResolvedCall<*>.makeNullableTypeIfSafeReceiver(type: KotlinType?, context: CallResolutionContext<*>) = type?.let { TypeUtils.makeNullableIfNeeded(type, hasSafeNullableReceiver(context)) } -fun ResolvedCall<*>.hasBothReceivers() = dispatchReceiver != null && extensionReceiver != null \ No newline at end of file +fun ResolvedCall<*>.hasBothReceivers() = dispatchReceiver != null && extensionReceiver != null + +fun ResolvedCall<*>.getDispatchReceiverWithSmartCast(): ReceiverValue? + = getReceiverValueWithSmartCast(dispatchReceiver, smartCastDispatchReceiverType) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java index 0d49a299991..e2a3161977a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java @@ -179,9 +179,24 @@ public class SmartCastManager { @NotNull ResolutionContext c, @Nullable KtExpression calleeExpression, boolean recordExpressionType + ) { + return checkAndRecordPossibleCast( + dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType); + } + + @Nullable + public static SmartCastResult checkAndRecordPossibleCast( + @NotNull DataFlowValue dataFlowValue, + @NotNull KotlinType expectedType, + @Nullable Function1 additionalPredicate, + @Nullable KtExpression expression, + @NotNull ResolutionContext c, + @Nullable KtExpression calleeExpression, + boolean recordExpressionType ) { for (KotlinType possibleType : c.dataFlowInfo.getCollectedTypes(dataFlowValue)) { - if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)) { + if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) + && (additionalPredicate == null || additionalPredicate.invoke(possibleType))) { if (expression != null) { recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType); } @@ -209,7 +224,8 @@ public class SmartCastManager { boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull(); KotlinType nullableExpectedType = TypeUtils.makeNullable(expectedType); - if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType)) { + if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType) + && (additionalPredicate == null || additionalPredicate.invoke(dataFlowValue.getType()))) { if (!immanentlyNotNull) { if (expression != null) { recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue, recordExpressionType); diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/smartCastUtil.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/smartCastUtil.kt new file mode 100644 index 00000000000..c51d5d298b3 --- /dev/null +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/smartcasts/smartCastUtil.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.smartcasts + +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType + +fun getReceiverValueWithSmartCast( + receiverArgument: ReceiverValue?, + smartCastType: KotlinType? +) = smartCastType?.let { SmartCastReceiverValue(it) } ?: receiverArgument + +private class SmartCastReceiverValue(private val type: KotlinType) : ReceiverValue { + override fun getType() = type +} diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt index 68ba8983676..6c6a7d8ba3d 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.tower import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassValueDescriptor @@ -65,7 +66,8 @@ internal abstract class AbstractScopeTowerLevel( val shouldSkipVisibilityCheck = scopeTower.isDebuggerContext if (!shouldSkipVisibilityCheck) { Visibilities.findInvisibleMember( - dispatchReceiver, descriptor, + getReceiverValueWithSmartCast(dispatchReceiver, dispatchReceiverSmartCastType), + descriptor, scopeTower.lexicalScope.ownerDescriptor )?.let { diagnostics.add(VisibilityError(it)) } } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.kt new file mode 100644 index 00000000000..0a81bb0111a --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.kt @@ -0,0 +1,13 @@ +open class BaseClass() { + protected class Nested(val x: Int, protected val y: Int) + + protected fun foo() = Nested(1, 2) +} + +class Foo : BaseClass() { + fun bar() { + val f = foo() + f.x + f.y + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.txt new file mode 100644 index 00000000000..6e3e6611986 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.txt @@ -0,0 +1,27 @@ +package + +public open class BaseClass { + public constructor BaseClass() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected final fun foo(): BaseClass.Nested + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + protected final class Nested { + public constructor Nested(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val x: kotlin.Int + protected final val y: kotlin.Int + 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 Foo : BaseClass { + public constructor Foo() + public final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected final override /*1*/ /*fake_override*/ fun foo(): BaseClass.Nested + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.kt new file mode 100644 index 00000000000..04007b27c49 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.kt @@ -0,0 +1,9 @@ +abstract class A { + abstract protected fun T.foo() + + fun bar(x: T?) { + if (x != null) { + x.foo() + } + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.txt new file mode 100644 index 00000000000..4e6ae044cc8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.txt @@ -0,0 +1,10 @@ +package + +public abstract class A { + public constructor A() + public final fun bar(/*0*/ x: T?): kotlin.Unit + 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 + protected abstract fun T.foo(): kotlin.Unit +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.kt new file mode 100644 index 00000000000..2617fc06257 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.kt @@ -0,0 +1,18 @@ +// !CHECK_TYPE +open class BaseOuter { + protected fun foo() = 1 + protected fun bar() { } +} + +class Foo(var base: BaseOuter) + +fun BaseOuter.foo(): String = "" + +class Derived : BaseOuter() { + fun test(foo: Foo) { + if (foo.base is Derived) { + foo.base.foo() checkType { _() } // Resolved to extension + foo.base.bar() + } + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.txt new file mode 100644 index 00000000000..4af1e8cd969 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.txt @@ -0,0 +1,30 @@ +package + +public fun BaseOuter.foo(): kotlin.String + +public open class BaseOuter { + public constructor BaseOuter() + protected final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected final fun foo(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Derived : BaseOuter { + public constructor Derived() + protected final override /*1*/ /*fake_override*/ fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test(/*0*/ foo: Foo): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Foo { + public constructor Foo(/*0*/ base: BaseOuter) + public final var base: BaseOuter + 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 +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt new file mode 100644 index 00000000000..e796f0c82c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt @@ -0,0 +1,30 @@ +open class Base { + open protected fun foo() {} + open protected fun bar() {} + + open protected var x: Int = 1 + open var y: Int = 1 + protected set +} + +class Derived : Base() { + override fun bar() { } + + protected fun baz(x: Base) { + x.foo() + x.bar() + + x.x = x.x + 1 + x.y = x.y + 1 + + if (x is Derived) { + x.foo() + x.bar() + x.baz(x) + + x.x = x.x + 1 + // TODO: Should be smart cast + x.y = x.y + 1 + } + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.txt new file mode 100644 index 00000000000..1ed29e90330 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.txt @@ -0,0 +1,24 @@ +package + +public open class Base { + public constructor Base() + protected open var x: kotlin.Int + public open var y: kotlin.Int + protected open fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected open fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Derived : Base { + public constructor Derived() + protected open override /*1*/ /*fake_override*/ var x: kotlin.Int + public open override /*1*/ /*fake_override*/ var y: kotlin.Int + protected open override /*1*/ fun bar(): kotlin.Unit + protected final fun baz(/*0*/ x: Base): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected open override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 0135f5469dc..8faff68d66b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -15476,6 +15476,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("innerProtectedClass.kt") + public void testInnerProtectedClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerProtectedClass.kt"); + doTest(fileName); + } + @TestMetadata("javaInheritedInKotlin.kt") public void testJavaInheritedInKotlin() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.kt"); @@ -15493,6 +15499,24 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.kt"); doTest(fileName); } + + @TestMetadata("smartcastOnExtensionReceiver.kt") + public void testSmartcastOnExtensionReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/smartcastOnExtensionReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("unstableSmartCast.kt") + public void testUnstableSmartCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.kt"); + doTest(fileName); + } + + @TestMetadata("withSmartcast.kt") + public void testWithSmartcast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); + doTest(fileName); + } } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt index 7dc02975b78..e609e5bd4c8 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode import org.jetbrains.kotlin.resolve.calls.context.ContextDependency import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getDispatchReceiverWithSmartCast import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver @@ -136,7 +137,7 @@ fun Call.resolveCandidates( if (filterOutByVisibility) { candidates = candidates.filter { - Visibilities.isVisible(it.dispatchReceiver, it.resultingDescriptor, inDescriptor) + Visibilities.isVisible(it.getDispatchReceiverWithSmartCast(), it.resultingDescriptor, inDescriptor) } }