Implementation of smart casts for public / protected immutable properties that are not open and used in the same module.
DataFlowValueFactory and its environment refactoring: containing declaration is added into factory functions as an argument and used to determine identifier stability. A few minor fixes. #KT-5907 Fixed. #KT-4450 Fixed. #KT-4409 Fixed. New tests for KT-4409, KT-4450, KT-5907 (public and protected value properties used from the same module or not, open properties, variable properties, delegated properties, properties with non-default getter). Public val test and KT-362 test changed accordingly.
This commit is contained in:
@@ -444,7 +444,7 @@ public class CandidateResolver {
|
||||
JetExpression deparenthesizedArgument = getLastElementDeparenthesized(argumentExpression, context);
|
||||
if (deparenthesizedArgument == null || type == null) return type;
|
||||
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context.trace.getBindingContext());
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(deparenthesizedArgument, type, context);
|
||||
if (!dataFlowValue.isStableIdentifier()) return type;
|
||||
|
||||
Set<JetType> possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue);
|
||||
@@ -564,8 +564,7 @@ public class CandidateResolver {
|
||||
@NotNull ResolutionContext<?> context
|
||||
) {
|
||||
ExpressionReceiver receiverToCast = new ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType);
|
||||
Collection<JetType> variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver(
|
||||
context.trace.getBindingContext(), context.dataFlowInfo, receiverToCast);
|
||||
Collection<JetType> variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver(context, receiverToCast);
|
||||
for (JetType possibleType : variants) {
|
||||
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
|
||||
return possibleType;
|
||||
@@ -637,12 +636,12 @@ public class CandidateResolver {
|
||||
|
||||
BindingContext bindingContext = trace.getBindingContext();
|
||||
if (!safeAccess && !receiverParameter.getType().isMarkedNullable() && receiverArgumentType.isMarkedNullable()) {
|
||||
if (!SmartCastUtils.canBeSmartCast(receiverParameter, receiverArgument, bindingContext, context.dataFlowInfo)) {
|
||||
if (!SmartCastUtils.canBeSmartCast(receiverParameter, receiverArgument, context)) {
|
||||
context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck);
|
||||
return UNSAFE_CALL_ERROR;
|
||||
}
|
||||
}
|
||||
DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, bindingContext);
|
||||
DataFlowValue receiverValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, bindingContext, context.scope.getContainingDeclaration());
|
||||
if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) {
|
||||
context.tracing.unnecessarySafeCall(trace, receiverArgumentType);
|
||||
}
|
||||
|
||||
+1
-2
@@ -44,8 +44,7 @@ public class TypeApproximator : AdditionalTypeChecker {
|
||||
override val presentableText: String
|
||||
get() = StringUtil.trimMiddle(expression.getText(), 50)
|
||||
|
||||
private val dataFlowValue =
|
||||
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext())
|
||||
private val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c)
|
||||
}
|
||||
)
|
||||
if (approximationInfo != null) {
|
||||
|
||||
@@ -53,7 +53,6 @@ public class DataFlowValue {
|
||||
|
||||
/**
|
||||
* Stable identifier is a non-literal value that is statically known to be immutable
|
||||
* @return
|
||||
*/
|
||||
public boolean isStableIdentifier() {
|
||||
return stableIdentifier;
|
||||
|
||||
+88
-20
@@ -24,7 +24,9 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
@@ -32,22 +34,40 @@ import org.jetbrains.kotlin.types.TypeUtils;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
|
||||
|
||||
/**
|
||||
* This class is intended to create data flow values for different kind of expressions.
|
||||
* Then data flow values serve as keys to obtain data flow information for these expressions.
|
||||
*/
|
||||
public class DataFlowValueFactory {
|
||||
private DataFlowValueFactory() {}
|
||||
private DataFlowValueFactory() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DataFlowValue createDataFlowValue(
|
||||
@NotNull JetExpression expression,
|
||||
@NotNull JetType type,
|
||||
@NotNull BindingContext bindingContext
|
||||
@NotNull ResolutionContext resolutionContext
|
||||
) {
|
||||
return createDataFlowValue(expression, type, resolutionContext.trace.getBindingContext(),
|
||||
resolutionContext.scope.getContainingDeclaration());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DataFlowValue createDataFlowValue(
|
||||
@NotNull JetExpression expression,
|
||||
@NotNull JetType type,
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor containingDeclaration
|
||||
) {
|
||||
if (expression instanceof JetConstantExpression) {
|
||||
JetConstantExpression constantExpression = (JetConstantExpression) expression;
|
||||
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
||||
}
|
||||
if (type.isError()) return DataFlowValue.ERROR;
|
||||
if (KotlinBuiltIns.getInstance().getNullableNothingType().equals(type)) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
|
||||
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext);
|
||||
if (KotlinBuiltIns.getInstance().getNullableNothingType().equals(type)) {
|
||||
return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
|
||||
}
|
||||
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclaration);
|
||||
return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, type, result.isStable, getImmanentNullability(type));
|
||||
}
|
||||
|
||||
@@ -58,7 +78,20 @@ public class DataFlowValueFactory {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DataFlowValue createDataFlowValue(@NotNull ReceiverValue receiverValue, @NotNull BindingContext bindingContext) {
|
||||
public static DataFlowValue createDataFlowValue(
|
||||
@NotNull ReceiverValue receiverValue,
|
||||
@NotNull ResolutionContext resolutionContext
|
||||
) {
|
||||
return createDataFlowValue(receiverValue, resolutionContext.trace.getBindingContext(),
|
||||
resolutionContext.scope.getContainingDeclaration());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DataFlowValue createDataFlowValue(
|
||||
@NotNull ReceiverValue receiverValue,
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor containingDeclaration
|
||||
) {
|
||||
if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) {
|
||||
// SCRIPT: smartcasts data flow
|
||||
JetType type = receiverValue.getType();
|
||||
@@ -69,7 +102,10 @@ public class DataFlowValueFactory {
|
||||
return createDataFlowValue((ThisReceiver) receiverValue);
|
||||
}
|
||||
else if (receiverValue instanceof ExpressionReceiver) {
|
||||
return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(), receiverValue.getType(), bindingContext);
|
||||
return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(),
|
||||
receiverValue.getType(),
|
||||
bindingContext,
|
||||
containingDeclaration);
|
||||
}
|
||||
else if (receiverValue == ReceiverValue.NO_RECEIVER) {
|
||||
throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER");
|
||||
@@ -80,9 +116,14 @@ public class DataFlowValueFactory {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DataFlowValue createDataFlowValue(@NotNull VariableDescriptor variableDescriptor) {
|
||||
public static DataFlowValue createDataFlowValue(
|
||||
@NotNull VariableDescriptor variableDescriptor,
|
||||
@Nullable ModuleDescriptor usageContainingModule
|
||||
) {
|
||||
JetType type = variableDescriptor.getType();
|
||||
return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type));
|
||||
return new DataFlowValue(variableDescriptor, type,
|
||||
isStableVariable(variableDescriptor, usageContainingModule),
|
||||
getImmanentNullability(type));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -133,25 +174,26 @@ public class DataFlowValueFactory {
|
||||
@NotNull
|
||||
private static IdentifierInfo getIdForStableIdentifier(
|
||||
@Nullable JetExpression expression,
|
||||
@NotNull BindingContext bindingContext
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor containingDeclaration
|
||||
) {
|
||||
if (expression != null) {
|
||||
JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
|
||||
if (expression != deparenthesized) {
|
||||
return getIdForStableIdentifier(deparenthesized, bindingContext);
|
||||
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclaration);
|
||||
}
|
||||
}
|
||||
if (expression instanceof JetQualifiedExpression) {
|
||||
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
|
||||
JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
|
||||
JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
|
||||
IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext);
|
||||
IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext);
|
||||
IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclaration);
|
||||
IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclaration);
|
||||
|
||||
return combineInfo(receiverId, selectorId);
|
||||
}
|
||||
if (expression instanceof JetSimpleNameExpression) {
|
||||
return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext);
|
||||
return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext, containingDeclaration);
|
||||
}
|
||||
else if (expression instanceof JetThisExpression) {
|
||||
JetThisExpression thisExpression = (JetThisExpression) expression;
|
||||
@@ -168,21 +210,24 @@ public class DataFlowValueFactory {
|
||||
@NotNull
|
||||
private static IdentifierInfo getIdForSimpleNameExpression(
|
||||
@NotNull JetSimpleNameExpression simpleNameExpression,
|
||||
@NotNull BindingContext bindingContext
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor containingDeclaration
|
||||
) {
|
||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
|
||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
||||
ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(simpleNameExpression, bindingContext);
|
||||
|
||||
// todo uncomment assert
|
||||
// KT-4113
|
||||
// for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
|
||||
// assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for " + declarationDescriptor;
|
||||
|
||||
// assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
|
||||
ModuleDescriptor usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclaration);
|
||||
IdentifierInfo receiverInfo =
|
||||
resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
|
||||
|
||||
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||
return combineInfo(receiverInfo, createInfo(variableDescriptor, isStableVariable(variableDescriptor)));
|
||||
return combineInfo(receiverInfo, createInfo(variableDescriptor,
|
||||
isStableVariable(variableDescriptor, usageModuleDescriptor)));
|
||||
}
|
||||
if (declarationDescriptor instanceof PackageViewDescriptor) {
|
||||
return createPackageInfo(declarationDescriptor);
|
||||
@@ -207,7 +252,8 @@ public class DataFlowValueFactory {
|
||||
private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
|
||||
if (descriptorOfThisReceiver instanceof CallableDescriptor) {
|
||||
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
|
||||
assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + descriptorOfThisReceiver;
|
||||
assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " +
|
||||
descriptorOfThisReceiver;
|
||||
return createInfo(receiverParameter.getValue(), true);
|
||||
}
|
||||
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
|
||||
@@ -216,13 +262,35 @@ public class DataFlowValueFactory {
|
||||
return NO_IDENTIFIER_INFO;
|
||||
}
|
||||
|
||||
public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) {
|
||||
/**
|
||||
* Determines whether a variable with a given descriptor is stable or not at the given usage place.
|
||||
* <p/>
|
||||
* Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
|
||||
* <p/>
|
||||
* If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
|
||||
* the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
|
||||
* (it's private OR internal OR used at the same module where it's defined).
|
||||
* The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
|
||||
*
|
||||
* @param variableDescriptor descriptor of a considered variable
|
||||
* @param usageModule a module with a considered usage place, or null if it's not known (not recommended)
|
||||
* @return true if variable is stable, false otherwise
|
||||
*/
|
||||
public static boolean isStableVariable(
|
||||
@NotNull VariableDescriptor variableDescriptor,
|
||||
@Nullable ModuleDescriptor usageModule
|
||||
) {
|
||||
if (variableDescriptor.isVar()) return false;
|
||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
|
||||
if (!invisibleFromOtherModules(propertyDescriptor)) return false;
|
||||
if (!isFinal(propertyDescriptor)) return false;
|
||||
if (!hasDefaultGetter(propertyDescriptor)) return false;
|
||||
if (!invisibleFromOtherModules(propertyDescriptor)) {
|
||||
ModuleDescriptor declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor);
|
||||
if (usageModule == null || !usageModule.equals(declarationModule)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+28
-11
@@ -22,6 +22,7 @@ import kotlin.Function1;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
|
||||
import org.jetbrains.kotlin.psi.JetExpression;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
@@ -53,18 +54,19 @@ public class SmartCastUtils {
|
||||
@NotNull ReceiverValue receiverToCast,
|
||||
@NotNull ResolutionContext context
|
||||
) {
|
||||
return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.dataFlowInfo);
|
||||
return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getContainingDeclaration(), context.dataFlowInfo);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<JetType> getSmartCastVariants(
|
||||
@NotNull ReceiverValue receiverToCast,
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor containingDeclaration,
|
||||
@NotNull DataFlowInfo dataFlowInfo
|
||||
) {
|
||||
List<JetType> variants = Lists.newArrayList();
|
||||
variants.add(receiverToCast.getType());
|
||||
variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, dataFlowInfo, receiverToCast));
|
||||
variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclaration, dataFlowInfo, receiverToCast));
|
||||
return variants;
|
||||
}
|
||||
|
||||
@@ -72,9 +74,11 @@ public class SmartCastUtils {
|
||||
public static List<JetType> getSmartCastVariantsWithLessSpecificExcluded(
|
||||
@NotNull ReceiverValue receiverToCast,
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor containingDeclaration,
|
||||
@NotNull DataFlowInfo dataFlowInfo
|
||||
) {
|
||||
final List<JetType> variants = getSmartCastVariants(receiverToCast, bindingContext, dataFlowInfo);
|
||||
final List<JetType> variants = getSmartCastVariants(receiverToCast, bindingContext,
|
||||
containingDeclaration, dataFlowInfo);
|
||||
return KotlinPackage.filter(variants, new Function1<JetType, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(final JetType type) {
|
||||
@@ -88,12 +92,27 @@ public class SmartCastUtils {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
|
||||
*/
|
||||
@NotNull
|
||||
public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
|
||||
@NotNull ResolutionContext context,
|
||||
@NotNull ReceiverValue receiverToCast
|
||||
) {
|
||||
return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(),
|
||||
context.scope.getContainingDeclaration(),
|
||||
context.dataFlowInfo,
|
||||
receiverToCast);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
|
||||
*/
|
||||
@NotNull
|
||||
public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DeclarationDescriptor containingDeclaration,
|
||||
@NotNull DataFlowInfo dataFlowInfo,
|
||||
@NotNull ReceiverValue receiverToCast
|
||||
) {
|
||||
@@ -104,7 +123,8 @@ public class SmartCastUtils {
|
||||
return dataFlowInfo.getPossibleTypes(dataFlowValue);
|
||||
}
|
||||
else if (receiverToCast instanceof ExpressionReceiver) {
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverToCast, bindingContext);
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
|
||||
receiverToCast, bindingContext, containingDeclaration);
|
||||
return dataFlowInfo.getPossibleTypes(dataFlowValue);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
@@ -152,13 +172,12 @@ public class SmartCastUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
Collection<JetType> smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(
|
||||
context.trace.getBindingContext(), context.dataFlowInfo, receiver);
|
||||
Collection<JetType> smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(context, receiver);
|
||||
JetType smartCastSubType = getSmartCastSubType(receiverParameterType, smartCastTypesExcludingReceiver);
|
||||
if (smartCastSubType == null) return false;
|
||||
|
||||
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext());
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context);
|
||||
|
||||
recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true);
|
||||
return true;
|
||||
@@ -187,12 +206,10 @@ public class SmartCastUtils {
|
||||
public static boolean canBeSmartCast(
|
||||
@NotNull ReceiverParameterDescriptor receiverParameter,
|
||||
@NotNull ReceiverValue receiver,
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DataFlowInfo dataFlowInfo
|
||||
) {
|
||||
@NotNull ResolutionContext context) {
|
||||
if (!receiver.getType().isMarkedNullable()) return true;
|
||||
|
||||
List<JetType> smartCastVariants = getSmartCastVariants(receiver, bindingContext, dataFlowInfo);
|
||||
List<JetType> smartCastVariants = getSmartCastVariants(receiver, context);
|
||||
for (JetType smartCastVariant : smartCastVariants) {
|
||||
if (JetTypeChecker.DEFAULT.isSubtypeOf(smartCastVariant, receiverParameter.getType())) return true;
|
||||
}
|
||||
|
||||
+6
-6
@@ -170,7 +170,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, subjectType);
|
||||
dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||
if (operationType == AS_KEYWORD) {
|
||||
DataFlowValue value = createDataFlowValue(left, subjectType, context.trace.getBindingContext());
|
||||
DataFlowValue value = createDataFlowValue(left, subjectType, context);
|
||||
dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType);
|
||||
}
|
||||
}
|
||||
@@ -224,7 +224,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
return;
|
||||
}
|
||||
Collection<JetType> possibleTypes = DataFlowUtils.getAllPossibleTypes(
|
||||
expression.getLeft(), context.dataFlowInfo, actualType, context.trace.getBindingContext());
|
||||
expression.getLeft(), context.dataFlowInfo, actualType, context);
|
||||
for (JetType possibleType : possibleTypes) {
|
||||
if (typeChecker.isSubtypeOf(possibleType, targetType)) {
|
||||
context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression));
|
||||
@@ -890,7 +890,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, baseType));
|
||||
}
|
||||
else {
|
||||
DataFlowValue value = createDataFlowValue(baseExpression, baseType, context.trace.getBindingContext());
|
||||
DataFlowValue value = createDataFlowValue(baseExpression, baseType, context);
|
||||
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
|
||||
}
|
||||
// The call to checkType() is only needed here to execute additionalTypeCheckers, hence the NO_EXPECTED_TYPE
|
||||
@@ -932,7 +932,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
|
||||
private static boolean isKnownToBeNotNull(JetExpression expression, JetType jetType, ExpressionTypingContext context) {
|
||||
DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context.trace.getBindingContext());
|
||||
DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context);
|
||||
return !context.dataFlowInfo.getNullability(dataFlowValue).canBeNull();
|
||||
}
|
||||
|
||||
@@ -1194,7 +1194,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
|
||||
DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
|
||||
if (leftType != null && rightType != null && KotlinBuiltIns.isNothingOrNullableNothing(rightType) && !rightType.isMarkedNullable()) {
|
||||
DataFlowValue value = createDataFlowValue(left, leftType, context.trace.getBindingContext());
|
||||
DataFlowValue value = createDataFlowValue(left, leftType, context);
|
||||
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
|
||||
}
|
||||
JetType type = resolvedCall.getResultingDescriptor().getReturnType();
|
||||
@@ -1262,7 +1262,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType));
|
||||
}
|
||||
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
||||
expression, left, right, context.trace,
|
||||
expression, left, right, context,
|
||||
new Function1<JetExpression, JetType>() {
|
||||
@Override
|
||||
public JetType invoke(JetExpression expression) {
|
||||
|
||||
@@ -53,7 +53,11 @@ public class DataFlowUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, final ExpressionTypingContext context) {
|
||||
public static DataFlowInfo extractDataFlowInfoFromCondition(
|
||||
@Nullable JetExpression condition,
|
||||
final boolean conditionValue,
|
||||
final ExpressionTypingContext context
|
||||
) {
|
||||
if (condition == null) return context.dataFlowInfo;
|
||||
final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
|
||||
condition.accept(new JetVisitorVoid() {
|
||||
@@ -93,9 +97,8 @@ public class DataFlowUtils {
|
||||
JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
||||
if (rhsType == null) return;
|
||||
|
||||
BindingContext bindingContext = context.trace.getBindingContext();
|
||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, bindingContext);
|
||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, bindingContext);
|
||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, context);
|
||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, context);
|
||||
|
||||
Boolean equals = null;
|
||||
if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
||||
@@ -192,7 +195,7 @@ public class DataFlowUtils {
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext());
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c);
|
||||
|
||||
for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) {
|
||||
@@ -254,9 +257,9 @@ public class DataFlowUtils {
|
||||
@NotNull JetExpression expression,
|
||||
@NotNull DataFlowInfo dataFlowInfo,
|
||||
@NotNull JetType type,
|
||||
@NotNull BindingContext bindingContext
|
||||
@NotNull ResolutionContext c
|
||||
) {
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, bindingContext);
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
|
||||
Collection<JetType> possibleTypes = Sets.newHashSet(type);
|
||||
if (dataFlowValue.isStableIdentifier()) {
|
||||
possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
|
||||
|
||||
+6
-4
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.AnnotationResolver;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.ModifiersChecker;
|
||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
||||
@@ -141,8 +142,9 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
||||
dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||
JetType type = typeInfo.getType();
|
||||
if (property.getTypeReference() == null && type != null) {
|
||||
DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue(propertyDescriptor);
|
||||
DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context.trace.getBindingContext());
|
||||
DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue(
|
||||
propertyDescriptor, DescriptorUtils.getContainingModuleOrNull(scope.getContainingDeclaration()));
|
||||
DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context);
|
||||
dataFlowInfo = dataFlowInfo.equate(variableDataFlowValue, initializerDataFlowValue);
|
||||
}
|
||||
}
|
||||
@@ -341,8 +343,8 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
||||
dataFlowInfo = rightInfo.getDataFlowInfo();
|
||||
JetType rightType = rightInfo.getType();
|
||||
if (left != null && leftType != null && rightType != null) {
|
||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context.trace.getBindingContext());
|
||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context.trace.getBindingContext());
|
||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context);
|
||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||
dataFlowInfo = dataFlowInfo.equate(leftValue, rightValue);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -56,8 +56,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
JetType knownType = typeInfo.getType();
|
||||
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||
if (expression.getTypeReference() != null) {
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType,
|
||||
context.trace.getBindingContext());
|
||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context);
|
||||
DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeReference(), dataFlowValue).thenInfo;
|
||||
DataFlowInfo newDataFlowInfo = conditionInfo.and(dataFlowInfo);
|
||||
context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo);
|
||||
@@ -87,7 +86,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
|
||||
}
|
||||
DataFlowValue subjectDataFlowValue = subjectExpression != null
|
||||
? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context.trace.getBindingContext())
|
||||
? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context)
|
||||
: DataFlowValue.NULL;
|
||||
|
||||
// TODO : exhaustive patterns
|
||||
@@ -263,7 +262,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
checkTypeCompatibility(context, type, subjectType, expression);
|
||||
DataFlowValue expressionDataFlowValue =
|
||||
DataFlowValueFactory.createDataFlowValue(expression, type, context.trace.getBindingContext());
|
||||
DataFlowValueFactory.createDataFlowValue(expression, type, context);
|
||||
DataFlowInfos result = noChange(context);
|
||||
result = new DataFlowInfos(
|
||||
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
|
||||
|
||||
+4
-3
@@ -25,13 +25,14 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import kotlin.platform.platformStatic
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
|
||||
object SenselessComparisonChecker {
|
||||
platformStatic fun checkSenselessComparisonWithNull(
|
||||
expression: JetBinaryExpression,
|
||||
left: JetExpression,
|
||||
right: JetExpression,
|
||||
trace: BindingTrace,
|
||||
context: ResolutionContext<*>,
|
||||
getType: (JetExpression) -> JetType?,
|
||||
getNullability: (DataFlowValue) -> Nullability
|
||||
) {
|
||||
@@ -44,7 +45,7 @@ object SenselessComparisonChecker {
|
||||
if (type == null || type.isError()) return
|
||||
|
||||
val operationSign = expression.getOperationReference()
|
||||
val value = DataFlowValueFactory.createDataFlowValue(expr, type, trace.getBindingContext())
|
||||
val value = DataFlowValueFactory.createDataFlowValue(expr, type, context)
|
||||
|
||||
val equality = operationSign.getReferencedNameElementType() == JetTokens.EQEQ || operationSign.getReferencedNameElementType() == JetTokens.EQEQEQ
|
||||
val nullability = getNullability(value)
|
||||
@@ -55,6 +56,6 @@ object SenselessComparisonChecker {
|
||||
else if (nullability == Nullability.IMPOSSIBLE) false
|
||||
else return
|
||||
|
||||
trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
|
||||
context.trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user