Conversion to Kotlin (step 2)
This commit is contained in:
+143
-181
@@ -14,229 +14,191 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.smartcasts;
|
package org.jetbrains.kotlin.resolve.calls.smartcasts
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets
|
||||||
import kotlin.collections.CollectionsKt;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import kotlin.jvm.functions.Function1;
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
import org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST
|
||||||
import org.jetbrains.kotlin.psi.KtExpression;
|
import org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
|
||||||
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.TypeIntersector
|
||||||
import org.jetbrains.kotlin.types.TypeIntersector;
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.TypeUtils;
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE;
|
|
||||||
import static org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST;
|
|
||||||
import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST;
|
|
||||||
|
|
||||||
// We do not want to make methods static to keep SmartCastManager as a component
|
// We do not want to make methods static to keep SmartCastManager as a component
|
||||||
@SuppressWarnings("MethodMayBeStatic")
|
@SuppressWarnings("MethodMayBeStatic")
|
||||||
public class SmartCastManager {
|
class SmartCastManager {
|
||||||
|
|
||||||
@NotNull
|
fun getSmartCastVariants(
|
||||||
public List<KotlinType> getSmartCastVariants(
|
receiverToCast: ReceiverValue,
|
||||||
@NotNull ReceiverValue receiverToCast,
|
context: ResolutionContext<*>): List<KotlinType> {
|
||||||
@NotNull ResolutionContext context
|
return getSmartCastVariants(receiverToCast, context.trace.bindingContext, context.scope.ownerDescriptor, context.dataFlowInfo)
|
||||||
) {
|
|
||||||
return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getOwnerDescriptor(), context.dataFlowInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
fun getSmartCastVariants(
|
||||||
public List<KotlinType> getSmartCastVariants(
|
receiverToCast: ReceiverValue,
|
||||||
@NotNull ReceiverValue receiverToCast,
|
bindingContext: BindingContext,
|
||||||
@NotNull BindingContext bindingContext,
|
containingDeclarationOrModule: DeclarationDescriptor,
|
||||||
@NotNull DeclarationDescriptor containingDeclarationOrModule,
|
dataFlowInfo: DataFlowInfo): List<KotlinType> {
|
||||||
@NotNull DataFlowInfo dataFlowInfo
|
val variants = Lists.newArrayList<KotlinType>()
|
||||||
) {
|
variants.add(receiverToCast.type)
|
||||||
List<KotlinType> variants = Lists.newArrayList();
|
variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast))
|
||||||
variants.add(receiverToCast.getType());
|
return variants
|
||||||
variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast));
|
|
||||||
return variants;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
fun getSmartCastVariantsWithLessSpecificExcluded(
|
||||||
public List<KotlinType> getSmartCastVariantsWithLessSpecificExcluded(
|
receiverToCast: ReceiverValue,
|
||||||
@NotNull ReceiverValue receiverToCast,
|
bindingContext: BindingContext,
|
||||||
@NotNull BindingContext bindingContext,
|
containingDeclarationOrModule: DeclarationDescriptor,
|
||||||
@NotNull DeclarationDescriptor containingDeclarationOrModule,
|
dataFlowInfo: DataFlowInfo): List<KotlinType> {
|
||||||
@NotNull DataFlowInfo dataFlowInfo
|
val variants = getSmartCastVariants(receiverToCast, bindingContext, containingDeclarationOrModule, dataFlowInfo).distinct()
|
||||||
) {
|
return variants.filter { type -> variants.none { another -> another !== type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type) } }
|
||||||
final List<KotlinType> variants = CollectionsKt.distinct(
|
|
||||||
getSmartCastVariants(receiverToCast, bindingContext, containingDeclarationOrModule, dataFlowInfo));
|
|
||||||
return CollectionsKt.filter(variants, new Function1<KotlinType, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean invoke(final KotlinType type) {
|
|
||||||
return CollectionsKt.none(variants, new Function1<KotlinType, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean invoke(KotlinType another) {
|
|
||||||
return another != type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
|
* @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
|
||||||
*/
|
*/
|
||||||
@NotNull
|
fun getSmartCastVariantsExcludingReceiver(
|
||||||
public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
|
context: ResolutionContext<*>,
|
||||||
@NotNull ResolutionContext context,
|
receiverToCast: ReceiverValue): Collection<KotlinType> {
|
||||||
@NotNull ReceiverValue receiverToCast
|
return getSmartCastVariantsExcludingReceiver(context.trace.bindingContext,
|
||||||
) {
|
context.scope.ownerDescriptor,
|
||||||
return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(),
|
|
||||||
context.scope.getOwnerDescriptor(),
|
|
||||||
context.dataFlowInfo,
|
context.dataFlowInfo,
|
||||||
receiverToCast);
|
receiverToCast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
|
* @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
|
||||||
*/
|
*/
|
||||||
@NotNull
|
fun getSmartCastVariantsExcludingReceiver(
|
||||||
public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
|
bindingContext: BindingContext,
|
||||||
@NotNull BindingContext bindingContext,
|
containingDeclarationOrModule: DeclarationDescriptor,
|
||||||
@NotNull DeclarationDescriptor containingDeclarationOrModule,
|
dataFlowInfo: DataFlowInfo,
|
||||||
@NotNull DataFlowInfo dataFlowInfo,
|
receiverToCast: ReceiverValue): Collection<KotlinType> {
|
||||||
@NotNull ReceiverValue receiverToCast
|
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(
|
||||||
) {
|
receiverToCast, bindingContext, containingDeclarationOrModule)
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
|
|
||||||
receiverToCast, bindingContext, containingDeclarationOrModule
|
|
||||||
);
|
|
||||||
|
|
||||||
return dataFlowInfo.getCollectedTypes(dataFlowValue);
|
return dataFlowInfo.getCollectedTypes(dataFlowValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSubTypeBySmartCastIgnoringNullability(
|
fun isSubTypeBySmartCastIgnoringNullability(
|
||||||
@NotNull ReceiverValue receiverArgument,
|
receiverArgument: ReceiverValue,
|
||||||
@NotNull KotlinType receiverParameterType,
|
receiverParameterType: KotlinType,
|
||||||
@NotNull ResolutionContext context
|
context: ResolutionContext<*>): Boolean {
|
||||||
) {
|
val smartCastTypes = getSmartCastVariants(receiverArgument, context)
|
||||||
List<KotlinType> smartCastTypes = getSmartCastVariants(receiverArgument, context);
|
return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null
|
||||||
return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private fun getSmartCastSubType(
|
||||||
private KotlinType getSmartCastSubType(
|
receiverParameterType: KotlinType,
|
||||||
@NotNull KotlinType receiverParameterType,
|
smartCastTypes: Collection<KotlinType>): KotlinType? {
|
||||||
@NotNull Collection<KotlinType> smartCastTypes
|
val subTypes = Sets.newHashSet<KotlinType>()
|
||||||
) {
|
for (smartCastType in smartCastTypes) {
|
||||||
Set<KotlinType> subTypes = Sets.newHashSet();
|
|
||||||
for (KotlinType smartCastType : smartCastTypes) {
|
|
||||||
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
|
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
|
||||||
subTypes.add(smartCastType);
|
subTypes.add(smartCastType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (subTypes.isEmpty()) return null;
|
if (subTypes.isEmpty()) return null
|
||||||
|
|
||||||
KotlinType intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes);
|
val intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes)
|
||||||
if (intersection == null || !intersection.getConstructor().isDenotable()) {
|
if (intersection == null || !intersection.constructor.isDenotable) {
|
||||||
return receiverParameterType;
|
return receiverParameterType
|
||||||
}
|
}
|
||||||
return intersection;
|
return intersection
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void recordCastOrError(
|
companion object {
|
||||||
@NotNull KtExpression expression,
|
|
||||||
@NotNull KotlinType type,
|
|
||||||
@NotNull BindingTrace trace,
|
|
||||||
@NotNull DataFlowValue dataFlowValue,
|
|
||||||
boolean recordExpressionType
|
|
||||||
) {
|
|
||||||
if (KotlinBuiltIns.isNullableNothing(type)) return;
|
|
||||||
if (dataFlowValue.isPredictable()) {
|
|
||||||
trace.record(SMARTCAST, expression, type);
|
|
||||||
if (recordExpressionType) {
|
|
||||||
//TODO
|
|
||||||
//Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
|
|
||||||
trace.recordType(expression, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText(), dataFlowValue.getKind().getDescription()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
private fun recordCastOrError(
|
||||||
public static SmartCastResult checkAndRecordPossibleCast(
|
expression: KtExpression,
|
||||||
@NotNull DataFlowValue dataFlowValue,
|
type: KotlinType,
|
||||||
@NotNull KotlinType expectedType,
|
trace: BindingTrace,
|
||||||
@Nullable KtExpression expression,
|
dataFlowValue: DataFlowValue,
|
||||||
@NotNull ResolutionContext c,
|
recordExpressionType: Boolean) {
|
||||||
@Nullable KtExpression calleeExpression,
|
if (KotlinBuiltIns.isNullableNothing(type)) return
|
||||||
boolean recordExpressionType
|
if (dataFlowValue.isPredictable) {
|
||||||
) {
|
trace.record(SMARTCAST, expression, type)
|
||||||
return checkAndRecordPossibleCast(
|
if (recordExpressionType) {
|
||||||
dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType);
|
//TODO
|
||||||
}
|
//Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
|
||||||
|
trace.recordType(expression, type)
|
||||||
@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)) {
|
|
||||||
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)
|
|
||||||
&& (additionalPredicate == null || additionalPredicate.invoke(possibleType))) {
|
|
||||||
if (expression != null) {
|
|
||||||
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType);
|
|
||||||
}
|
}
|
||||||
else if (calleeExpression != null && dataFlowValue.isPredictable()) {
|
}
|
||||||
c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType);
|
else {
|
||||||
}
|
trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.text, dataFlowValue.kind.description))
|
||||||
return new SmartCastResult(possibleType, dataFlowValue.isPredictable());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable()) {
|
fun checkAndRecordPossibleCast(
|
||||||
// Handling cases like:
|
dataFlowValue: DataFlowValue,
|
||||||
// fun bar(x: Any) {}
|
expectedType: KotlinType,
|
||||||
// fun <T : Any?> foo(x: T) {
|
expression: KtExpression?,
|
||||||
// if (x != null) {
|
c: ResolutionContext<*>,
|
||||||
// bar(x) // Should be allowed with smart cast
|
calleeExpression: KtExpression?,
|
||||||
// }
|
recordExpressionType: Boolean): SmartCastResult? {
|
||||||
// }
|
return checkAndRecordPossibleCast(
|
||||||
//
|
dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType)
|
||||||
// It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T.
|
}
|
||||||
// But at the same time we're sure that `x` can't be null and just check for such cases manually
|
|
||||||
|
|
||||||
// E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds
|
fun checkAndRecordPossibleCast(
|
||||||
// x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression)
|
dataFlowValue: DataFlowValue,
|
||||||
boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull();
|
expectedType: KotlinType,
|
||||||
KotlinType nullableExpectedType = TypeUtils.makeNullable(expectedType);
|
additionalPredicate: Function1<KotlinType, Boolean>?,
|
||||||
|
expression: KtExpression?,
|
||||||
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType)
|
c: ResolutionContext<*>,
|
||||||
&& (additionalPredicate == null || additionalPredicate.invoke(dataFlowValue.getType()))) {
|
calleeExpression: KtExpression?,
|
||||||
if (!immanentlyNotNull) {
|
recordExpressionType: Boolean): SmartCastResult? {
|
||||||
|
for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue)) {
|
||||||
|
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) && (additionalPredicate == null || additionalPredicate.invoke(possibleType))) {
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue, recordExpressionType);
|
recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType)
|
||||||
}
|
}
|
||||||
|
else if (calleeExpression != null && dataFlowValue.isPredictable) {
|
||||||
|
c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType)
|
||||||
|
}
|
||||||
|
return SmartCastResult(possibleType, dataFlowValue.isPredictable)
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SmartCastResult(dataFlowValue.getType(), immanentlyNotNull || dataFlowValue.isPredictable());
|
|
||||||
}
|
}
|
||||||
return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable) {
|
||||||
|
// Handling cases like:
|
||||||
|
// fun bar(x: Any) {}
|
||||||
|
// fun <T : Any?> foo(x: T) {
|
||||||
|
// if (x != null) {
|
||||||
|
// bar(x) // Should be allowed with smart cast
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T.
|
||||||
|
// But at the same time we're sure that `x` can't be null and just check for such cases manually
|
||||||
|
|
||||||
|
// E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds
|
||||||
|
// x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression)
|
||||||
|
val immanentlyNotNull = !dataFlowValue.immanentNullability.canBeNull()
|
||||||
|
val nullableExpectedType = TypeUtils.makeNullable(expectedType)
|
||||||
|
|
||||||
|
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.type, nullableExpectedType) && (additionalPredicate == null || additionalPredicate.invoke(dataFlowValue.type))) {
|
||||||
|
if (!immanentlyNotNull) {
|
||||||
|
if (expression != null) {
|
||||||
|
recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, recordExpressionType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isPredictable)
|
||||||
|
}
|
||||||
|
return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ public class DataFlowAnalyzer {
|
|||||||
) {
|
) {
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
|
||||||
|
|
||||||
return SmartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false);
|
return SmartCastManager.Companion.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordExpectedType(@NotNull BindingTrace trace, @NotNull KtExpression expression, @NotNull KotlinType expectedType) {
|
public void recordExpectedType(@NotNull BindingTrace trace, @NotNull KtExpression expression, @NotNull KotlinType expectedType) {
|
||||||
|
|||||||
Reference in New Issue
Block a user