Conversion to Kotlin (step 2)

This commit is contained in:
Valentin Kipyatkov
2016-05-10 19:50:57 +03:00
parent ccd7ae901c
commit 83714ae9c9
2 changed files with 144 additions and 182 deletions
@@ -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) {