Respect receiver-dependent visibility when selecting smart-cast type
This commit is contained in:
@@ -466,7 +466,7 @@ public class ControlFlowInformationProvider {
|
|||||||
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());
|
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());
|
||||||
ReceiverValue receiverValue = null;
|
ReceiverValue receiverValue = null;
|
||||||
if (resolvedCall != null) {
|
if (resolvedCall != null) {
|
||||||
receiverValue = resolvedCall.getDispatchReceiver();
|
receiverValue = ResolvedCallUtilKt.getDispatchReceiverWithSmartCast(resolvedCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor) && setterDescriptor != null
|
if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor) && setterDescriptor != null
|
||||||
|
|||||||
@@ -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.results.ResolutionStatus.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager
|
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.calls.util.FakeCallableDescriptorForObject
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver
|
||||||
@@ -76,7 +77,7 @@ class CandidateResolver(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!context.isDebuggerContext) {
|
if (!context.isDebuggerContext) {
|
||||||
checkVisibility()
|
checkVisibilityWithoutReceiver()
|
||||||
}
|
}
|
||||||
|
|
||||||
when (checkArguments) {
|
when (checkArguments) {
|
||||||
@@ -169,9 +170,17 @@ class CandidateResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CallCandidateResolutionContext<*>.checkVisibility() = checkAndReport {
|
private fun CallCandidateResolutionContext<*>.checkVisibilityWithoutReceiver() = checkAndReport {
|
||||||
val invisibleMember = Visibilities.findInvisibleMember(candidateCall.dispatchReceiver, candidateDescriptor, scope.ownerDescriptor)
|
checkVisibilityWithDispatchReceiver(Visibilities.ALWAYS_SUITABLE_RECEIVER, null)
|
||||||
if (invisibleMember != 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)
|
tracing.invisibleMember(trace, invisibleMember)
|
||||||
OTHER_ERROR
|
OTHER_ERROR
|
||||||
} else {
|
} 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 {
|
private fun CallCandidateResolutionContext<*>.checkExtensionReceiver() = checkAndReport {
|
||||||
val receiverParameter = candidateCall.getCandidateDescriptor().extensionReceiverParameter
|
val receiverParameter = candidateCall.getCandidateDescriptor().extensionReceiverParameter
|
||||||
val receiverArgument = candidateCall.getExtensionReceiver()
|
val receiverArgument = candidateCall.getExtensionReceiver()
|
||||||
@@ -430,13 +453,25 @@ class CandidateResolver(
|
|||||||
candidateCall,
|
candidateCall,
|
||||||
candidateCall.getResultingDescriptor().extensionReceiverParameter,
|
candidateCall.getResultingDescriptor().extensionReceiverParameter,
|
||||||
candidateCall.extensionReceiver as ReceiverValue?,
|
candidateCall.extensionReceiver as ReceiverValue?,
|
||||||
candidateCall.getExplicitReceiverKind().isExtensionReceiver, false))
|
candidateCall.explicitReceiverKind.isExtensionReceiver,
|
||||||
|
implicitInvokeCheck = false, isDispatchReceiver = false))
|
||||||
|
|
||||||
resultStatus = resultStatus.combine(context.checkReceiver(candidateCall,
|
resultStatus = resultStatus.combine(context.checkReceiver(candidateCall,
|
||||||
candidateCall.getResultingDescriptor().dispatchReceiverParameter, candidateCall.getDispatchReceiver(),
|
candidateCall.getResultingDescriptor().dispatchReceiverParameter, candidateCall.getDispatchReceiver(),
|
||||||
candidateCall.getExplicitReceiverKind().isDispatchReceiver,
|
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
|
// 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
|
return resultStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +480,9 @@ class CandidateResolver(
|
|||||||
receiverParameter: ReceiverParameterDescriptor?,
|
receiverParameter: ReceiverParameterDescriptor?,
|
||||||
receiverArgument: ReceiverValue?,
|
receiverArgument: ReceiverValue?,
|
||||||
isExplicitReceiver: Boolean,
|
isExplicitReceiver: Boolean,
|
||||||
implicitInvokeCheck: Boolean): ResolutionStatus {
|
implicitInvokeCheck: Boolean,
|
||||||
|
isDispatchReceiver: Boolean
|
||||||
|
): ResolutionStatus {
|
||||||
if (receiverParameter == null || receiverArgument == null) return SUCCESS
|
if (receiverParameter == null || receiverArgument == null) return SUCCESS
|
||||||
val candidateDescriptor = candidateCall.candidateDescriptor
|
val candidateDescriptor = candidateCall.candidateDescriptor
|
||||||
if (TypeUtils.dependsOnTypeParameters(receiverParameter.type, candidateDescriptor.typeParameters)) return SUCCESS
|
if (TypeUtils.dependsOnTypeParameters(receiverParameter.type, candidateDescriptor.typeParameters)) return SUCCESS
|
||||||
@@ -465,11 +502,11 @@ class CandidateResolver(
|
|||||||
val call = candidateCall.call
|
val call = candidateCall.call
|
||||||
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall()
|
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall()
|
||||||
val expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type
|
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
|
var reportUnsafeCall = false
|
||||||
|
|
||||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, this)
|
|
||||||
val nullability = dataFlowInfo.getPredictableNullability(dataFlowValue)
|
|
||||||
var nullableImplicitInvokeReceiver = false
|
var nullableImplicitInvokeReceiver = false
|
||||||
var receiverArgumentType = receiverArgument.type
|
var receiverArgumentType = receiverArgument.type
|
||||||
if (implicitInvokeCheck && call is CallForImplicitInvoke && call.isSafeCall()) {
|
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
|
val expression = (receiverArgument as? ExpressionReceiver)?.expression
|
||||||
if (nullability.canBeNull() && !nullability.canBeNonNull()) {
|
if (nullability.canBeNull() && !nullability.canBeNonNull()) {
|
||||||
if (!TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
if (!TypeUtils.isNullableType(expectedReceiverParameterType)) {
|
||||||
@@ -496,14 +536,20 @@ class CandidateResolver(
|
|||||||
// Look if smart cast has some useful nullability info
|
// Look if smart cast has some useful nullability info
|
||||||
|
|
||||||
val smartCastResult = SmartCastManager.checkAndRecordPossibleCast(
|
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) {
|
if (smartCastResult == null) {
|
||||||
reportUnsafeCall = true
|
if (notNullReceiverExpected) {
|
||||||
|
reportUnsafeCall = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
candidateCall.setSmartCastDispatchReceiverType(smartCastResult.resultType)
|
if (isDispatchReceiver) {
|
||||||
|
candidateCall.setSmartCastDispatchReceiverType(smartCastResult.resultType)
|
||||||
|
}
|
||||||
if (!smartCastResult.isCorrect) {
|
if (!smartCastResult.isCorrect) {
|
||||||
// Error about unstable smart cast reported within checkAndRecordPossibleCast
|
// Error about unstable smart cast reported within checkAndRecordPossibleCast
|
||||||
return OTHER_ERROR
|
return OTHER_ERROR
|
||||||
|
|||||||
@@ -16,15 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.resolvedCallUtil
|
package org.jetbrains.kotlin.resolve.calls.resolvedCallUtil
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||||
import org.jetbrains.kotlin.psi.KtThisExpression
|
import org.jetbrains.kotlin.psi.KtThisExpression
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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.context.CallResolutionContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
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.calls.tasks.ExplicitReceiverKind
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getOwnerForEffectiveDispatchReceiverParameter
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getOwnerForEffectiveDispatchReceiverParameter
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
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<*>) =
|
fun ResolvedCall<*>.makeNullableTypeIfSafeReceiver(type: KotlinType?, context: CallResolutionContext<*>) =
|
||||||
type?.let { TypeUtils.makeNullableIfNeeded(type, hasSafeNullableReceiver(context)) }
|
type?.let { TypeUtils.makeNullableIfNeeded(type, hasSafeNullableReceiver(context)) }
|
||||||
|
|
||||||
fun ResolvedCall<*>.hasBothReceivers() = dispatchReceiver != null && extensionReceiver != null
|
fun ResolvedCall<*>.hasBothReceivers() = dispatchReceiver != null && extensionReceiver != null
|
||||||
|
|
||||||
|
fun ResolvedCall<*>.getDispatchReceiverWithSmartCast(): ReceiverValue?
|
||||||
|
= getReceiverValueWithSmartCast(dispatchReceiver, smartCastDispatchReceiverType)
|
||||||
|
|||||||
+18
-2
@@ -179,9 +179,24 @@ public class SmartCastManager {
|
|||||||
@NotNull ResolutionContext c,
|
@NotNull ResolutionContext c,
|
||||||
@Nullable KtExpression calleeExpression,
|
@Nullable KtExpression calleeExpression,
|
||||||
boolean recordExpressionType
|
boolean recordExpressionType
|
||||||
|
) {
|
||||||
|
return checkAndRecordPossibleCast(
|
||||||
|
dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static SmartCastResult checkAndRecordPossibleCast(
|
||||||
|
@NotNull DataFlowValue dataFlowValue,
|
||||||
|
@NotNull KotlinType expectedType,
|
||||||
|
@Nullable Function1<KotlinType, Boolean> additionalPredicate,
|
||||||
|
@Nullable KtExpression expression,
|
||||||
|
@NotNull ResolutionContext c,
|
||||||
|
@Nullable KtExpression calleeExpression,
|
||||||
|
boolean recordExpressionType
|
||||||
) {
|
) {
|
||||||
for (KotlinType possibleType : c.dataFlowInfo.getCollectedTypes(dataFlowValue)) {
|
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) {
|
if (expression != null) {
|
||||||
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType);
|
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType);
|
||||||
}
|
}
|
||||||
@@ -209,7 +224,8 @@ public class SmartCastManager {
|
|||||||
boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull();
|
boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull();
|
||||||
KotlinType nullableExpectedType = TypeUtils.makeNullable(expectedType);
|
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 (!immanentlyNotNull) {
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue, recordExpressionType);
|
recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue, recordExpressionType);
|
||||||
|
|||||||
+29
@@ -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
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.tower
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||||
import org.jetbrains.kotlin.name.Name
|
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.calls.util.FakeCallableDescriptorForObject
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
|
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassValueDescriptor
|
import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassValueDescriptor
|
||||||
@@ -65,7 +66,8 @@ internal abstract class AbstractScopeTowerLevel(
|
|||||||
val shouldSkipVisibilityCheck = scopeTower.isDebuggerContext
|
val shouldSkipVisibilityCheck = scopeTower.isDebuggerContext
|
||||||
if (!shouldSkipVisibilityCheck) {
|
if (!shouldSkipVisibilityCheck) {
|
||||||
Visibilities.findInvisibleMember(
|
Visibilities.findInvisibleMember(
|
||||||
dispatchReceiver, descriptor,
|
getReceiverValueWithSmartCast(dispatchReceiver, dispatchReceiverSmartCastType),
|
||||||
|
descriptor,
|
||||||
scopeTower.lexicalScope.ownerDescriptor
|
scopeTower.lexicalScope.ownerDescriptor
|
||||||
)?.let { diagnostics.add(VisibilityError(it)) }
|
)?.let { diagnostics.add(VisibilityError(it)) }
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -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.<!INVISIBLE_MEMBER!>y<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -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
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
abstract class A<T : Any> {
|
||||||
|
abstract protected fun T.foo()
|
||||||
|
|
||||||
|
fun bar(x: T?) {
|
||||||
|
if (x != null) {
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public abstract class A</*0*/ T : kotlin.Any> {
|
||||||
|
public constructor A</*0*/ T : kotlin.Any>()
|
||||||
|
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
|
||||||
|
}
|
||||||
+18
@@ -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 { _<String>() } // Resolved to extension
|
||||||
|
<!SMARTCAST_IMPOSSIBLE!>foo.base<!>.bar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -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
|
||||||
|
}
|
||||||
+30
@@ -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.<!INVISIBLE_MEMBER!>foo<!>()
|
||||||
|
x.<!INVISIBLE_MEMBER!>bar<!>()
|
||||||
|
|
||||||
|
x.<!INVISIBLE_MEMBER!>x<!> = x.<!INVISIBLE_MEMBER!>x<!> + 1
|
||||||
|
<!INVISIBLE_SETTER!>x.y<!> = x.y + 1
|
||||||
|
|
||||||
|
if (x is Derived) {
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.foo()
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.bar()
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.baz(x)
|
||||||
|
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.x = <!DEBUG_INFO_SMARTCAST!>x<!>.x + 1
|
||||||
|
// TODO: Should be smart cast
|
||||||
|
<!INVISIBLE_SETTER!>x.y<!> = x.y + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -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
|
||||||
|
}
|
||||||
@@ -15476,6 +15476,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("javaInheritedInKotlin.kt")
|
||||||
public void testJavaInheritedInKotlin() throws Exception {
|
public void testJavaInheritedInKotlin() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.kt");
|
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");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.kt");
|
||||||
doTest(fileName);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.CheckArgumentTypesMode
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
|
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
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.calls.results.ResolutionStatus
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||||
@@ -136,7 +137,7 @@ fun Call.resolveCandidates(
|
|||||||
|
|
||||||
if (filterOutByVisibility) {
|
if (filterOutByVisibility) {
|
||||||
candidates = candidates.filter {
|
candidates = candidates.filter {
|
||||||
Visibilities.isVisible(it.dispatchReceiver, it.resultingDescriptor, inDescriptor)
|
Visibilities.isVisible(it.getDispatchReceiverWithSmartCast(), it.resultingDescriptor, inDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user