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:
+5
-5
@@ -188,7 +188,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
doCheckType(
|
doCheckType(
|
||||||
expressionType,
|
expressionType,
|
||||||
c.expectedType,
|
c.expectedType,
|
||||||
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext()),
|
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c),
|
||||||
c.dataFlowInfo
|
c.dataFlowInfo
|
||||||
) {
|
) {
|
||||||
expectedMustNotBeNull,
|
expectedMustNotBeNull,
|
||||||
@@ -202,7 +202,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
val baseExpression = expression.getBaseExpression()
|
val baseExpression = expression.getBaseExpression()
|
||||||
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
||||||
doIfNotNull(
|
doIfNotNull(
|
||||||
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
|
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c),
|
||||||
c
|
c
|
||||||
) {
|
) {
|
||||||
c.trace.report(Errors.UNNECESSARY_NOT_NULL_ASSERTION.on(expression.getOperationReference(), baseExpressionType))
|
c.trace.report(Errors.UNNECESSARY_NOT_NULL_ASSERTION.on(expression.getOperationReference(), baseExpressionType))
|
||||||
@@ -214,7 +214,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
val baseExpression = expression.getLeft()
|
val baseExpression = expression.getLeft()
|
||||||
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
||||||
doIfNotNull(
|
doIfNotNull(
|
||||||
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
|
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c),
|
||||||
c
|
c
|
||||||
) {
|
) {
|
||||||
c.trace.report(Errors.USELESS_ELVIS.on(expression.getOperationReference(), baseExpressionType))
|
c.trace.report(Errors.USELESS_ELVIS.on(expression.getOperationReference(), baseExpressionType))
|
||||||
@@ -226,7 +226,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
JetTokens.EXCLEQEQEQ -> {
|
JetTokens.EXCLEQEQEQ -> {
|
||||||
if (expression.getLeft() != null && expression.getRight() != null) {
|
if (expression.getLeft() != null && expression.getRight() != null) {
|
||||||
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
||||||
expression, expression.getLeft()!!, expression.getRight()!!, c.trace,
|
expression, expression.getLeft()!!, expression.getRight()!!, c,
|
||||||
{ c.trace.get(BindingContext.EXPRESSION_TYPE, it) },
|
{ c.trace.get(BindingContext.EXPRESSION_TYPE, it) },
|
||||||
{
|
{
|
||||||
value ->
|
value ->
|
||||||
@@ -253,7 +253,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
safeAccess: Boolean,
|
safeAccess: Boolean,
|
||||||
c: CallResolutionContext<*>
|
c: CallResolutionContext<*>
|
||||||
) {
|
) {
|
||||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, c.trace.getBindingContext())
|
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverArgument, c)
|
||||||
if (!safeAccess) {
|
if (!safeAccess) {
|
||||||
doCheckType(
|
doCheckType(
|
||||||
receiverArgument.getType(),
|
receiverArgument.getType(),
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ public class CandidateResolver {
|
|||||||
JetExpression deparenthesizedArgument = getLastElementDeparenthesized(argumentExpression, context);
|
JetExpression deparenthesizedArgument = getLastElementDeparenthesized(argumentExpression, context);
|
||||||
if (deparenthesizedArgument == null || type == null) return type;
|
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;
|
if (!dataFlowValue.isStableIdentifier()) return type;
|
||||||
|
|
||||||
Set<JetType> possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue);
|
Set<JetType> possibleTypes = context.dataFlowInfo.getPossibleTypes(dataFlowValue);
|
||||||
@@ -564,8 +564,7 @@ public class CandidateResolver {
|
|||||||
@NotNull ResolutionContext<?> context
|
@NotNull ResolutionContext<?> context
|
||||||
) {
|
) {
|
||||||
ExpressionReceiver receiverToCast = new ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType);
|
ExpressionReceiver receiverToCast = new ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType);
|
||||||
Collection<JetType> variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver(
|
Collection<JetType> variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver(context, receiverToCast);
|
||||||
context.trace.getBindingContext(), context.dataFlowInfo, receiverToCast);
|
|
||||||
for (JetType possibleType : variants) {
|
for (JetType possibleType : variants) {
|
||||||
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
|
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
|
||||||
return possibleType;
|
return possibleType;
|
||||||
@@ -637,12 +636,12 @@ public class CandidateResolver {
|
|||||||
|
|
||||||
BindingContext bindingContext = trace.getBindingContext();
|
BindingContext bindingContext = trace.getBindingContext();
|
||||||
if (!safeAccess && !receiverParameter.getType().isMarkedNullable() && receiverArgumentType.isMarkedNullable()) {
|
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);
|
context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck);
|
||||||
return UNSAFE_CALL_ERROR;
|
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()) {
|
if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) {
|
||||||
context.tracing.unnecessarySafeCall(trace, receiverArgumentType);
|
context.tracing.unnecessarySafeCall(trace, receiverArgumentType);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -44,8 +44,7 @@ public class TypeApproximator : AdditionalTypeChecker {
|
|||||||
override val presentableText: String
|
override val presentableText: String
|
||||||
get() = StringUtil.trimMiddle(expression.getText(), 50)
|
get() = StringUtil.trimMiddle(expression.getText(), 50)
|
||||||
|
|
||||||
private val dataFlowValue =
|
private val dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c)
|
||||||
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext())
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (approximationInfo != null) {
|
if (approximationInfo != null) {
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ public class DataFlowValue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stable identifier is a non-literal value that is statically known to be immutable
|
* Stable identifier is a non-literal value that is statically known to be immutable
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
public boolean isStableIdentifier() {
|
public boolean isStableIdentifier() {
|
||||||
return stableIdentifier;
|
return stableIdentifier;
|
||||||
|
|||||||
+88
-20
@@ -24,7 +24,9 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
|||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
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.callUtil.CallUtilPackage;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.*;
|
||||||
import org.jetbrains.kotlin.types.JetType;
|
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;
|
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 {
|
public class DataFlowValueFactory {
|
||||||
private DataFlowValueFactory() {}
|
private DataFlowValueFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static DataFlowValue createDataFlowValue(
|
public static DataFlowValue createDataFlowValue(
|
||||||
@NotNull JetExpression expression,
|
@NotNull JetExpression expression,
|
||||||
@NotNull JetType type,
|
@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) {
|
if (expression instanceof JetConstantExpression) {
|
||||||
JetConstantExpression constantExpression = (JetConstantExpression) expression;
|
JetConstantExpression constantExpression = (JetConstantExpression) expression;
|
||||||
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
||||||
}
|
}
|
||||||
if (type.isError()) return DataFlowValue.ERROR;
|
if (type.isError()) return DataFlowValue.ERROR;
|
||||||
if (KotlinBuiltIns.getInstance().getNullableNothingType().equals(type)) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
|
if (KotlinBuiltIns.getInstance().getNullableNothingType().equals(type)) {
|
||||||
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext);
|
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));
|
return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, type, result.isStable, getImmanentNullability(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +78,20 @@ public class DataFlowValueFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@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) {
|
if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) {
|
||||||
// SCRIPT: smartcasts data flow
|
// SCRIPT: smartcasts data flow
|
||||||
JetType type = receiverValue.getType();
|
JetType type = receiverValue.getType();
|
||||||
@@ -69,7 +102,10 @@ public class DataFlowValueFactory {
|
|||||||
return createDataFlowValue((ThisReceiver) receiverValue);
|
return createDataFlowValue((ThisReceiver) receiverValue);
|
||||||
}
|
}
|
||||||
else if (receiverValue instanceof ExpressionReceiver) {
|
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) {
|
else if (receiverValue == ReceiverValue.NO_RECEIVER) {
|
||||||
throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER");
|
throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER");
|
||||||
@@ -80,9 +116,14 @@ public class DataFlowValueFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static DataFlowValue createDataFlowValue(@NotNull VariableDescriptor variableDescriptor) {
|
public static DataFlowValue createDataFlowValue(
|
||||||
|
@NotNull VariableDescriptor variableDescriptor,
|
||||||
|
@Nullable ModuleDescriptor usageContainingModule
|
||||||
|
) {
|
||||||
JetType type = variableDescriptor.getType();
|
JetType type = variableDescriptor.getType();
|
||||||
return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type));
|
return new DataFlowValue(variableDescriptor, type,
|
||||||
|
isStableVariable(variableDescriptor, usageContainingModule),
|
||||||
|
getImmanentNullability(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -133,25 +174,26 @@ public class DataFlowValueFactory {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static IdentifierInfo getIdForStableIdentifier(
|
private static IdentifierInfo getIdForStableIdentifier(
|
||||||
@Nullable JetExpression expression,
|
@Nullable JetExpression expression,
|
||||||
@NotNull BindingContext bindingContext
|
@NotNull BindingContext bindingContext,
|
||||||
|
@NotNull DeclarationDescriptor containingDeclaration
|
||||||
) {
|
) {
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
|
JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
|
||||||
if (expression != deparenthesized) {
|
if (expression != deparenthesized) {
|
||||||
return getIdForStableIdentifier(deparenthesized, bindingContext);
|
return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (expression instanceof JetQualifiedExpression) {
|
if (expression instanceof JetQualifiedExpression) {
|
||||||
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
|
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
|
||||||
JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
|
JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
|
||||||
JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
|
JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
|
||||||
IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext);
|
IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclaration);
|
||||||
IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext);
|
IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclaration);
|
||||||
|
|
||||||
return combineInfo(receiverId, selectorId);
|
return combineInfo(receiverId, selectorId);
|
||||||
}
|
}
|
||||||
if (expression instanceof JetSimpleNameExpression) {
|
if (expression instanceof JetSimpleNameExpression) {
|
||||||
return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext);
|
return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext, containingDeclaration);
|
||||||
}
|
}
|
||||||
else if (expression instanceof JetThisExpression) {
|
else if (expression instanceof JetThisExpression) {
|
||||||
JetThisExpression thisExpression = (JetThisExpression) expression;
|
JetThisExpression thisExpression = (JetThisExpression) expression;
|
||||||
@@ -168,21 +210,24 @@ public class DataFlowValueFactory {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static IdentifierInfo getIdForSimpleNameExpression(
|
private static IdentifierInfo getIdForSimpleNameExpression(
|
||||||
@NotNull JetSimpleNameExpression simpleNameExpression,
|
@NotNull JetSimpleNameExpression simpleNameExpression,
|
||||||
@NotNull BindingContext bindingContext
|
@NotNull BindingContext bindingContext,
|
||||||
|
@NotNull DeclarationDescriptor containingDeclaration
|
||||||
) {
|
) {
|
||||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
|
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
|
||||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
if (declarationDescriptor instanceof VariableDescriptor) {
|
||||||
ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(simpleNameExpression, bindingContext);
|
ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(simpleNameExpression, bindingContext);
|
||||||
|
|
||||||
// todo uncomment assert
|
// todo uncomment assert
|
||||||
// KT-4113
|
// KT-4113
|
||||||
// for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
|
// 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 =
|
IdentifierInfo receiverInfo =
|
||||||
resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
|
resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
|
||||||
|
|
||||||
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||||
return combineInfo(receiverInfo, createInfo(variableDescriptor, isStableVariable(variableDescriptor)));
|
return combineInfo(receiverInfo, createInfo(variableDescriptor,
|
||||||
|
isStableVariable(variableDescriptor, usageModuleDescriptor)));
|
||||||
}
|
}
|
||||||
if (declarationDescriptor instanceof PackageViewDescriptor) {
|
if (declarationDescriptor instanceof PackageViewDescriptor) {
|
||||||
return createPackageInfo(declarationDescriptor);
|
return createPackageInfo(declarationDescriptor);
|
||||||
@@ -207,7 +252,8 @@ public class DataFlowValueFactory {
|
|||||||
private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
|
private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
|
||||||
if (descriptorOfThisReceiver instanceof CallableDescriptor) {
|
if (descriptorOfThisReceiver instanceof CallableDescriptor) {
|
||||||
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
|
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);
|
return createInfo(receiverParameter.getValue(), true);
|
||||||
}
|
}
|
||||||
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
|
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
|
||||||
@@ -216,13 +262,35 @@ public class DataFlowValueFactory {
|
|||||||
return NO_IDENTIFIER_INFO;
|
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.isVar()) return false;
|
||||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
if (variableDescriptor instanceof PropertyDescriptor) {
|
||||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
|
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
|
||||||
if (!invisibleFromOtherModules(propertyDescriptor)) return false;
|
|
||||||
if (!isFinal(propertyDescriptor)) return false;
|
if (!isFinal(propertyDescriptor)) return false;
|
||||||
if (!hasDefaultGetter(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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-11
@@ -22,6 +22,7 @@ import kotlin.Function1;
|
|||||||
import kotlin.KotlinPackage;
|
import kotlin.KotlinPackage;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
|
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
|
||||||
import org.jetbrains.kotlin.psi.JetExpression;
|
import org.jetbrains.kotlin.psi.JetExpression;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
@@ -53,18 +54,19 @@ public class SmartCastUtils {
|
|||||||
@NotNull ReceiverValue receiverToCast,
|
@NotNull ReceiverValue receiverToCast,
|
||||||
@NotNull ResolutionContext context
|
@NotNull ResolutionContext context
|
||||||
) {
|
) {
|
||||||
return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.dataFlowInfo);
|
return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getContainingDeclaration(), context.dataFlowInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static List<JetType> getSmartCastVariants(
|
public static List<JetType> getSmartCastVariants(
|
||||||
@NotNull ReceiverValue receiverToCast,
|
@NotNull ReceiverValue receiverToCast,
|
||||||
@NotNull BindingContext bindingContext,
|
@NotNull BindingContext bindingContext,
|
||||||
|
@NotNull DeclarationDescriptor containingDeclaration,
|
||||||
@NotNull DataFlowInfo dataFlowInfo
|
@NotNull DataFlowInfo dataFlowInfo
|
||||||
) {
|
) {
|
||||||
List<JetType> variants = Lists.newArrayList();
|
List<JetType> variants = Lists.newArrayList();
|
||||||
variants.add(receiverToCast.getType());
|
variants.add(receiverToCast.getType());
|
||||||
variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, dataFlowInfo, receiverToCast));
|
variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclaration, dataFlowInfo, receiverToCast));
|
||||||
return variants;
|
return variants;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +74,11 @@ public class SmartCastUtils {
|
|||||||
public static List<JetType> getSmartCastVariantsWithLessSpecificExcluded(
|
public static List<JetType> getSmartCastVariantsWithLessSpecificExcluded(
|
||||||
@NotNull ReceiverValue receiverToCast,
|
@NotNull ReceiverValue receiverToCast,
|
||||||
@NotNull BindingContext bindingContext,
|
@NotNull BindingContext bindingContext,
|
||||||
|
@NotNull DeclarationDescriptor containingDeclaration,
|
||||||
@NotNull DataFlowInfo dataFlowInfo
|
@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>() {
|
return KotlinPackage.filter(variants, new Function1<JetType, Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public Boolean invoke(final JetType type) {
|
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
|
* @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
|
public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
|
||||||
@NotNull BindingContext bindingContext,
|
@NotNull BindingContext bindingContext,
|
||||||
|
@NotNull DeclarationDescriptor containingDeclaration,
|
||||||
@NotNull DataFlowInfo dataFlowInfo,
|
@NotNull DataFlowInfo dataFlowInfo,
|
||||||
@NotNull ReceiverValue receiverToCast
|
@NotNull ReceiverValue receiverToCast
|
||||||
) {
|
) {
|
||||||
@@ -104,7 +123,8 @@ public class SmartCastUtils {
|
|||||||
return dataFlowInfo.getPossibleTypes(dataFlowValue);
|
return dataFlowInfo.getPossibleTypes(dataFlowValue);
|
||||||
}
|
}
|
||||||
else if (receiverToCast instanceof ExpressionReceiver) {
|
else if (receiverToCast instanceof ExpressionReceiver) {
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverToCast, bindingContext);
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
|
||||||
|
receiverToCast, bindingContext, containingDeclaration);
|
||||||
return dataFlowInfo.getPossibleTypes(dataFlowValue);
|
return dataFlowInfo.getPossibleTypes(dataFlowValue);
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@@ -152,13 +172,12 @@ public class SmartCastUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<JetType> smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(
|
Collection<JetType> smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(context, receiver);
|
||||||
context.trace.getBindingContext(), context.dataFlowInfo, receiver);
|
|
||||||
JetType smartCastSubType = getSmartCastSubType(receiverParameterType, smartCastTypesExcludingReceiver);
|
JetType smartCastSubType = getSmartCastSubType(receiverParameterType, smartCastTypesExcludingReceiver);
|
||||||
if (smartCastSubType == null) return false;
|
if (smartCastSubType == null) return false;
|
||||||
|
|
||||||
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
|
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);
|
recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true);
|
||||||
return true;
|
return true;
|
||||||
@@ -187,12 +206,10 @@ public class SmartCastUtils {
|
|||||||
public static boolean canBeSmartCast(
|
public static boolean canBeSmartCast(
|
||||||
@NotNull ReceiverParameterDescriptor receiverParameter,
|
@NotNull ReceiverParameterDescriptor receiverParameter,
|
||||||
@NotNull ReceiverValue receiver,
|
@NotNull ReceiverValue receiver,
|
||||||
@NotNull BindingContext bindingContext,
|
@NotNull ResolutionContext context) {
|
||||||
@NotNull DataFlowInfo dataFlowInfo
|
|
||||||
) {
|
|
||||||
if (!receiver.getType().isMarkedNullable()) return true;
|
if (!receiver.getType().isMarkedNullable()) return true;
|
||||||
|
|
||||||
List<JetType> smartCastVariants = getSmartCastVariants(receiver, bindingContext, dataFlowInfo);
|
List<JetType> smartCastVariants = getSmartCastVariants(receiver, context);
|
||||||
for (JetType smartCastVariant : smartCastVariants) {
|
for (JetType smartCastVariant : smartCastVariants) {
|
||||||
if (JetTypeChecker.DEFAULT.isSubtypeOf(smartCastVariant, receiverParameter.getType())) return true;
|
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);
|
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, subjectType);
|
||||||
dataFlowInfo = typeInfo.getDataFlowInfo();
|
dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||||
if (operationType == AS_KEYWORD) {
|
if (operationType == AS_KEYWORD) {
|
||||||
DataFlowValue value = createDataFlowValue(left, subjectType, context.trace.getBindingContext());
|
DataFlowValue value = createDataFlowValue(left, subjectType, context);
|
||||||
dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType);
|
dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Collection<JetType> possibleTypes = DataFlowUtils.getAllPossibleTypes(
|
Collection<JetType> possibleTypes = DataFlowUtils.getAllPossibleTypes(
|
||||||
expression.getLeft(), context.dataFlowInfo, actualType, context.trace.getBindingContext());
|
expression.getLeft(), context.dataFlowInfo, actualType, context);
|
||||||
for (JetType possibleType : possibleTypes) {
|
for (JetType possibleType : possibleTypes) {
|
||||||
if (typeChecker.isSubtypeOf(possibleType, targetType)) {
|
if (typeChecker.isSubtypeOf(possibleType, targetType)) {
|
||||||
context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression));
|
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));
|
context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, baseType));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DataFlowValue value = createDataFlowValue(baseExpression, baseType, context.trace.getBindingContext());
|
DataFlowValue value = createDataFlowValue(baseExpression, baseType, context);
|
||||||
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
|
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
|
||||||
}
|
}
|
||||||
// The call to checkType() is only needed here to execute additionalTypeCheckers, hence the NO_EXPECTED_TYPE
|
// 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) {
|
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();
|
return !context.dataFlowInfo.getNullability(dataFlowValue).canBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1194,7 +1194,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
|
|
||||||
DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
|
DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
|
||||||
if (leftType != null && rightType != null && KotlinBuiltIns.isNothingOrNullableNothing(rightType) && !rightType.isMarkedNullable()) {
|
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);
|
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
|
||||||
}
|
}
|
||||||
JetType type = resolvedCall.getResultingDescriptor().getReturnType();
|
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));
|
context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType));
|
||||||
}
|
}
|
||||||
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
||||||
expression, left, right, context.trace,
|
expression, left, right, context,
|
||||||
new Function1<JetExpression, JetType>() {
|
new Function1<JetExpression, JetType>() {
|
||||||
@Override
|
@Override
|
||||||
public JetType invoke(JetExpression expression) {
|
public JetType invoke(JetExpression expression) {
|
||||||
|
|||||||
@@ -53,7 +53,11 @@ public class DataFlowUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@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;
|
if (condition == null) return context.dataFlowInfo;
|
||||||
final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
|
final Ref<DataFlowInfo> result = new Ref<DataFlowInfo>(null);
|
||||||
condition.accept(new JetVisitorVoid() {
|
condition.accept(new JetVisitorVoid() {
|
||||||
@@ -93,9 +97,8 @@ public class DataFlowUtils {
|
|||||||
JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right);
|
||||||
if (rhsType == null) return;
|
if (rhsType == null) return;
|
||||||
|
|
||||||
BindingContext bindingContext = context.trace.getBindingContext();
|
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, context);
|
||||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, lhsType, bindingContext);
|
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, context);
|
||||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rhsType, bindingContext);
|
|
||||||
|
|
||||||
Boolean equals = null;
|
Boolean equals = null;
|
||||||
if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) {
|
||||||
@@ -192,7 +195,7 @@ public class DataFlowUtils {
|
|||||||
return expressionType;
|
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)) {
|
for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||||
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) {
|
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) {
|
||||||
@@ -254,9 +257,9 @@ public class DataFlowUtils {
|
|||||||
@NotNull JetExpression expression,
|
@NotNull JetExpression expression,
|
||||||
@NotNull DataFlowInfo dataFlowInfo,
|
@NotNull DataFlowInfo dataFlowInfo,
|
||||||
@NotNull JetType type,
|
@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);
|
Collection<JetType> possibleTypes = Sets.newHashSet(type);
|
||||||
if (dataFlowValue.isStableIdentifier()) {
|
if (dataFlowValue.isStableIdentifier()) {
|
||||||
possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
|
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.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.resolve.AnnotationResolver;
|
import org.jetbrains.kotlin.resolve.AnnotationResolver;
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.resolve.ModifiersChecker;
|
import org.jetbrains.kotlin.resolve.ModifiersChecker;
|
||||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
|
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
||||||
@@ -141,8 +142,9 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
dataFlowInfo = typeInfo.getDataFlowInfo();
|
dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||||
JetType type = typeInfo.getType();
|
JetType type = typeInfo.getType();
|
||||||
if (property.getTypeReference() == null && type != null) {
|
if (property.getTypeReference() == null && type != null) {
|
||||||
DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue(propertyDescriptor);
|
DataFlowValue variableDataFlowValue = DataFlowValueFactory.createDataFlowValue(
|
||||||
DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context.trace.getBindingContext());
|
propertyDescriptor, DescriptorUtils.getContainingModuleOrNull(scope.getContainingDeclaration()));
|
||||||
|
DataFlowValue initializerDataFlowValue = DataFlowValueFactory.createDataFlowValue(initializer, type, context);
|
||||||
dataFlowInfo = dataFlowInfo.equate(variableDataFlowValue, initializerDataFlowValue);
|
dataFlowInfo = dataFlowInfo.equate(variableDataFlowValue, initializerDataFlowValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,8 +343,8 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
dataFlowInfo = rightInfo.getDataFlowInfo();
|
dataFlowInfo = rightInfo.getDataFlowInfo();
|
||||||
JetType rightType = rightInfo.getType();
|
JetType rightType = rightInfo.getType();
|
||||||
if (left != null && leftType != null && rightType != null) {
|
if (left != null && leftType != null && rightType != null) {
|
||||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context.trace.getBindingContext());
|
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context);
|
||||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context.trace.getBindingContext());
|
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||||
dataFlowInfo = dataFlowInfo.equate(leftValue, rightValue);
|
dataFlowInfo = dataFlowInfo.equate(leftValue, rightValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -56,8 +56,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
JetType knownType = typeInfo.getType();
|
JetType knownType = typeInfo.getType();
|
||||||
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
|
DataFlowInfo dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||||
if (expression.getTypeReference() != null) {
|
if (expression.getTypeReference() != null) {
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType,
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(leftHandSide, knownType, context);
|
||||||
context.trace.getBindingContext());
|
|
||||||
DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeReference(), dataFlowValue).thenInfo;
|
DataFlowInfo conditionInfo = checkTypeForIs(context, knownType, expression.getTypeReference(), dataFlowValue).thenInfo;
|
||||||
DataFlowInfo newDataFlowInfo = conditionInfo.and(dataFlowInfo);
|
DataFlowInfo newDataFlowInfo = conditionInfo.and(dataFlowInfo);
|
||||||
context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo);
|
context.trace.record(BindingContext.DATAFLOW_INFO_AFTER_CONDITION, expression, newDataFlowInfo);
|
||||||
@@ -87,7 +86,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
|
context = context.replaceDataFlowInfo(typeInfo.getDataFlowInfo());
|
||||||
}
|
}
|
||||||
DataFlowValue subjectDataFlowValue = subjectExpression != null
|
DataFlowValue subjectDataFlowValue = subjectExpression != null
|
||||||
? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context.trace.getBindingContext())
|
? DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context)
|
||||||
: DataFlowValue.NULL;
|
: DataFlowValue.NULL;
|
||||||
|
|
||||||
// TODO : exhaustive patterns
|
// TODO : exhaustive patterns
|
||||||
@@ -263,7 +262,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
checkTypeCompatibility(context, type, subjectType, expression);
|
checkTypeCompatibility(context, type, subjectType, expression);
|
||||||
DataFlowValue expressionDataFlowValue =
|
DataFlowValue expressionDataFlowValue =
|
||||||
DataFlowValueFactory.createDataFlowValue(expression, type, context.trace.getBindingContext());
|
DataFlowValueFactory.createDataFlowValue(expression, type, context);
|
||||||
DataFlowInfos result = noChange(context);
|
DataFlowInfos result = noChange(context);
|
||||||
result = new DataFlowInfos(
|
result = new DataFlowInfos(
|
||||||
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
|
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
|
||||||
|
|||||||
+4
-3
@@ -25,13 +25,14 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
|||||||
import kotlin.platform.platformStatic
|
import kotlin.platform.platformStatic
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
|
|
||||||
object SenselessComparisonChecker {
|
object SenselessComparisonChecker {
|
||||||
platformStatic fun checkSenselessComparisonWithNull(
|
platformStatic fun checkSenselessComparisonWithNull(
|
||||||
expression: JetBinaryExpression,
|
expression: JetBinaryExpression,
|
||||||
left: JetExpression,
|
left: JetExpression,
|
||||||
right: JetExpression,
|
right: JetExpression,
|
||||||
trace: BindingTrace,
|
context: ResolutionContext<*>,
|
||||||
getType: (JetExpression) -> JetType?,
|
getType: (JetExpression) -> JetType?,
|
||||||
getNullability: (DataFlowValue) -> Nullability
|
getNullability: (DataFlowValue) -> Nullability
|
||||||
) {
|
) {
|
||||||
@@ -44,7 +45,7 @@ object SenselessComparisonChecker {
|
|||||||
if (type == null || type.isError()) return
|
if (type == null || type.isError()) return
|
||||||
|
|
||||||
val operationSign = expression.getOperationReference()
|
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 equality = operationSign.getReferencedNameElementType() == JetTokens.EQEQ || operationSign.getReferencedNameElementType() == JetTokens.EQEQEQ
|
||||||
val nullability = getNullability(value)
|
val nullability = getNullability(value)
|
||||||
@@ -55,6 +56,6 @@ object SenselessComparisonChecker {
|
|||||||
else if (nullability == Nullability.IMPOSSIBLE) false
|
else if (nullability == Nullability.IMPOSSIBLE) false
|
||||||
else return
|
else return
|
||||||
|
|
||||||
trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
|
context.trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ package example
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
val p = test.Public()
|
val p = test.Public()
|
||||||
if (p.public is Int) <!SMARTCAST_IMPOSSIBLE!>p.public<!> + 1
|
if (p.public is Int) <!DEBUG_INFO_SMARTCAST!>p.public<!> + 1
|
||||||
if (p.<!INVISIBLE_MEMBER!>protected<!> is Int) <!SMARTCAST_IMPOSSIBLE!>p.<!INVISIBLE_MEMBER!>protected<!><!> + 1
|
if (p.<!INVISIBLE_MEMBER!>protected<!> is Int) <!DEBUG_INFO_SMARTCAST!>p.<!INVISIBLE_MEMBER!>protected<!><!> + 1
|
||||||
if (p.internal is Int) <!DEBUG_INFO_SMARTCAST!>p.internal<!> + 1
|
if (p.internal is Int) <!DEBUG_INFO_SMARTCAST!>p.internal<!> + 1
|
||||||
val i = test.Internal()
|
val i = test.Internal()
|
||||||
if (i.public is Int) <!DEBUG_INFO_SMARTCAST!>i.public<!> + 1
|
if (i.public is Int) <!DEBUG_INFO_SMARTCAST!>i.public<!> + 1
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
public class A() {
|
public open class A() {
|
||||||
public val foo: Int? = 1
|
public open val foo: Int? = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Int.bar(i: Int) = i
|
fun Int.bar(i: Int) = i
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
val p = A()
|
val p = A()
|
||||||
|
// For open value properties, smart casts should not work
|
||||||
if (p.foo is Int) <!SMARTCAST_IMPOSSIBLE!>p.foo<!> bar 11
|
if (p.foo is Int) <!SMARTCAST_IMPOSSIBLE!>p.foo<!> bar 11
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package
|
|||||||
internal fun test(): kotlin.Unit
|
internal fun test(): kotlin.Unit
|
||||||
internal fun kotlin.Int.bar(/*0*/ i: kotlin.Int): kotlin.Int
|
internal fun kotlin.Int.bar(/*0*/ i: kotlin.Int): kotlin.Int
|
||||||
|
|
||||||
public final class A {
|
public open class A {
|
||||||
public constructor A()
|
public constructor A()
|
||||||
public final val foo: kotlin.Int? = 1
|
public open val foo: kotlin.Int? = 1
|
||||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
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 hashCode(): kotlin.Int
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
public trait A {
|
||||||
|
public val x: Any
|
||||||
|
}
|
||||||
|
|
||||||
|
public class B(override public val x: Any) : A {
|
||||||
|
fun foo(): Int {
|
||||||
|
if (x is String) {
|
||||||
|
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public trait A {
|
||||||
|
public abstract val x: kotlin.Any
|
||||||
|
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 B : A {
|
||||||
|
public constructor B(/*0*/ x: kotlin.Any)
|
||||||
|
public open override /*1*/ val x: kotlin.Any
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
internal 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
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
public class X {
|
||||||
|
public val x : String? = null
|
||||||
|
public fun fn(): Int {
|
||||||
|
if (x != null)
|
||||||
|
// Smartcast is possible because it's value property with default getter
|
||||||
|
// used in the same module
|
||||||
|
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public final class X {
|
||||||
|
public constructor X()
|
||||||
|
public final val x: kotlin.String? = null
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final fun fn(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
public class X {
|
||||||
|
private val x : String? = null
|
||||||
|
public val y: CharSequence?
|
||||||
|
get() = x?.subSequence(0, 1)
|
||||||
|
public fun fn(): Int {
|
||||||
|
if (y != null)
|
||||||
|
// With non-default getter smartcast is not possible
|
||||||
|
return y<!UNSAFE_CALL!>.<!>length()
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public final class X {
|
||||||
|
public constructor X()
|
||||||
|
private final val x: kotlin.String? = null
|
||||||
|
public final val y: kotlin.CharSequence?
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final fun fn(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
class Delegate {
|
||||||
|
fun get(thisRef: Any?, prop: PropertyMetadata): String? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Example {
|
||||||
|
private val p: String? by Delegate()
|
||||||
|
|
||||||
|
public val r: String? = "xyz"
|
||||||
|
|
||||||
|
public fun foo(): String {
|
||||||
|
// Smart cast is not possible if property is delegated
|
||||||
|
return if (p != null) <!TYPE_MISMATCH!>p<!> else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun bar(): String {
|
||||||
|
// But is possible for non-delegated value property even if it's public
|
||||||
|
return if (r != null) <!DEBUG_INFO_SMARTCAST!>r<!> else ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal final class Delegate {
|
||||||
|
public constructor Delegate()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
internal final fun get(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.PropertyMetadata): kotlin.String?
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class Example {
|
||||||
|
public constructor Example()
|
||||||
|
private final val p: kotlin.String?
|
||||||
|
public final val r: kotlin.String? = "xyz"
|
||||||
|
public final fun bar(): kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final fun foo(): kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// MODULE: m1
|
||||||
|
// FILE: a.kt
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
public class X {
|
||||||
|
public val x : String? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODULE: m2(m1)
|
||||||
|
// FILE: b.kt
|
||||||
|
|
||||||
|
package b
|
||||||
|
|
||||||
|
import a.X
|
||||||
|
|
||||||
|
public fun X.gav(): Int {
|
||||||
|
if (x != null)
|
||||||
|
// Smart cast is not possible if definition is in another module
|
||||||
|
return x<!UNSAFE_CALL!>.<!>length()
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: c.kt
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
public fun X.gav(): Int {
|
||||||
|
if (x != null)
|
||||||
|
// Even if it's in the same package
|
||||||
|
return x<!UNSAFE_CALL!>.<!>length()
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// -- Module: <m1> --
|
||||||
|
package
|
||||||
|
|
||||||
|
package a {
|
||||||
|
|
||||||
|
public final class X {
|
||||||
|
public constructor X()
|
||||||
|
public final val x: kotlin.String? = null
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -- Module: <m2> --
|
||||||
|
package
|
||||||
|
|
||||||
|
package a {
|
||||||
|
public fun a.X.gav(): kotlin.Int
|
||||||
|
|
||||||
|
public final class X {
|
||||||
|
// -- Module: <m1> --
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package b {
|
||||||
|
public fun a.X.gav(): kotlin.Int
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
public open class X {
|
||||||
|
protected val x : String? = null
|
||||||
|
public fun fn(): Int {
|
||||||
|
if (x != null)
|
||||||
|
// Smartcast is possible for protected value property in the same class
|
||||||
|
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Y: X() {
|
||||||
|
public fun bar(): Int {
|
||||||
|
// Smartcast is possible even in derived class
|
||||||
|
return if (x != null) <!DEBUG_INFO_SMARTCAST!>x<!>.length() else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public open class X {
|
||||||
|
public constructor X()
|
||||||
|
protected final val x: kotlin.String? = null
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final fun fn(): 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 Y : X {
|
||||||
|
public constructor Y()
|
||||||
|
protected final override /*1*/ /*fake_override*/ val x: kotlin.String?
|
||||||
|
public final fun bar(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final override /*1*/ /*fake_override*/ fun fn(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
public class X {
|
||||||
|
public var x : String? = null
|
||||||
|
private var y: String? = "abc"
|
||||||
|
public fun fn(): Int {
|
||||||
|
if (x != null)
|
||||||
|
// Smartcast is not possible for variable properties
|
||||||
|
return x<!UNSAFE_CALL!>.<!>length()
|
||||||
|
else if (y != null)
|
||||||
|
// Even if they are private
|
||||||
|
return y<!UNSAFE_CALL!>.<!>length()
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public final class X {
|
||||||
|
public constructor X()
|
||||||
|
public final var x: kotlin.String?
|
||||||
|
private final var y: kotlin.String?
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final fun fn(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -11018,6 +11018,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@InnerTestClasses({
|
@InnerTestClasses({
|
||||||
SmartCasts.Inference.class,
|
SmartCasts.Inference.class,
|
||||||
|
SmartCasts.Varnotnull.class,
|
||||||
})
|
})
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class SmartCasts extends AbstractJetDiagnosticsTest {
|
public static class SmartCasts extends AbstractJetDiagnosticsTest {
|
||||||
@@ -11177,6 +11178,57 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Varnotnull extends AbstractJetDiagnosticsTest {
|
||||||
|
public void testAllFilesPresentInVarnotnull() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/varnotnull"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt4409.kt")
|
||||||
|
public void testKt4409() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt4409.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt5907.kt")
|
||||||
|
public void testKt5907() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt5907customGetter.kt")
|
||||||
|
public void testKt5907customGetter() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907customGetter.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt5907delegate.kt")
|
||||||
|
public void testKt5907delegate() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907delegate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt5907otherModule.kt")
|
||||||
|
public void testKt5907otherModule() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907otherModule.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt5907protected.kt")
|
||||||
|
public void testKt5907protected() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907protected.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt5907var.kt")
|
||||||
|
public void testKt5907var() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/kt5907var.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/substitutions")
|
@TestMetadata("compiler/testData/diagnostics/tests/substitutions")
|
||||||
|
|||||||
@@ -182,12 +182,17 @@ public class DescriptorUtils {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static ModuleDescriptor getContainingModule(@NotNull DeclarationDescriptor descriptor) {
|
public static ModuleDescriptor getContainingModule(@NotNull DeclarationDescriptor descriptor) {
|
||||||
|
ModuleDescriptor module = getContainingModuleOrNull(descriptor);
|
||||||
|
assert module != null : "Descriptor without a containing module: " + descriptor;
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static ModuleDescriptor getContainingModuleOrNull(@NotNull DeclarationDescriptor descriptor) {
|
||||||
if (descriptor instanceof PackageViewDescriptor) {
|
if (descriptor instanceof PackageViewDescriptor) {
|
||||||
return ((PackageViewDescriptor) descriptor).getModule();
|
return ((PackageViewDescriptor) descriptor).getModule();
|
||||||
}
|
}
|
||||||
ModuleDescriptor module = getParentOfType(descriptor, ModuleDescriptor.class, false);
|
return getParentOfType(descriptor, ModuleDescriptor.class, false);
|
||||||
assert module != null : "Descriptor without a containing module: " + descriptor;
|
|
||||||
return module;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
+4
-1
@@ -100,7 +100,10 @@ public class ReferenceVariantsHelper(
|
|||||||
val receiverValue = ExpressionReceiver(receiverExpression, expressionType)
|
val receiverValue = ExpressionReceiver(receiverExpression, expressionType)
|
||||||
val dataFlowInfo = context.getDataFlowInfo(expression)
|
val dataFlowInfo = context.getDataFlowInfo(expression)
|
||||||
|
|
||||||
for (variant in SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, context, dataFlowInfo)) {
|
for (variant in SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue,
|
||||||
|
context,
|
||||||
|
resolutionScope.getContainingDeclaration(),
|
||||||
|
dataFlowInfo)) {
|
||||||
descriptors.addMembersFromReceiver(variant, callType, kindFilter, nameFilter)
|
descriptors.addMembersFromReceiver(variant, callType, kindFilter, nameFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ public fun CallableDescriptor.substituteExtensionIfCallable(
|
|||||||
dataFlowInfo: DataFlowInfo,
|
dataFlowInfo: DataFlowInfo,
|
||||||
callType: CallType
|
callType: CallType
|
||||||
): Collection<CallableDescriptor> {
|
): Collection<CallableDescriptor> {
|
||||||
val stream = receivers.stream().flatMap { substituteExtensionIfCallable(it, callType, context, dataFlowInfo).stream() }
|
val sequence = receivers.sequence().flatMap { substituteExtensionIfCallable(it, callType, context, dataFlowInfo).sequence() }
|
||||||
if (getTypeParameters().isEmpty()) { // optimization for non-generic callables
|
if (getTypeParameters().isEmpty()) { // optimization for non-generic callables
|
||||||
return stream.firstOrNull()?.let { listOf(it) } ?: listOf()
|
return sequence.firstOrNull()?.let { listOf(it) } ?: listOf()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return stream.toList()
|
return sequence.toList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ public fun CallableDescriptor.substituteExtensionIfCallable(
|
|||||||
if (!receiver.exists()) return listOf()
|
if (!receiver.exists()) return listOf()
|
||||||
if (!callType.canCall(this)) return listOf()
|
if (!callType.canCall(this)) return listOf()
|
||||||
|
|
||||||
var types = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, dataFlowInfo).stream()
|
var types = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, getContainingDeclaration(), dataFlowInfo).sequence()
|
||||||
|
|
||||||
if (callType == CallType.SAFE) {
|
if (callType == CallType.SAFE) {
|
||||||
types = types.map { it.makeNotNullable() }
|
types = types.map { it.makeNotNullable() }
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
|
|||||||
val (receivers, callType) = referenceVariantsHelper!!.getReferenceVariantsReceivers(expression)
|
val (receivers, callType) = referenceVariantsHelper!!.getReferenceVariantsReceivers(expression)
|
||||||
val dataFlowInfo = bindingContext!!.getDataFlowInfo(expression)
|
val dataFlowInfo = bindingContext!!.getDataFlowInfo(expression)
|
||||||
var receiverTypes = receivers.flatMap {
|
var receiverTypes = receivers.flatMap {
|
||||||
SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(it, bindingContext, dataFlowInfo)
|
SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(it, bindingContext, moduleDescriptor, dataFlowInfo)
|
||||||
}
|
}
|
||||||
if (callType == CallType.SAFE) {
|
if (callType == CallType.SAFE) {
|
||||||
receiverTypes = receiverTypes.map { it.makeNotNullable() }
|
receiverTypes = receiverTypes.map { it.makeNotNullable() }
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ class SmartCompletion(
|
|||||||
else
|
else
|
||||||
filteredExpectedInfos
|
filteredExpectedInfos
|
||||||
|
|
||||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = SmartCastCalculator(bindingContext).calculate(expressionWithType, receiver)
|
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = SmartCastCalculator(
|
||||||
|
bindingContext, moduleDescriptor).calculate(expressionWithType, receiver)
|
||||||
|
|
||||||
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
||||||
|
|
||||||
@@ -419,7 +420,8 @@ class SmartCompletion(
|
|||||||
tail: Tail?,
|
tail: Tail?,
|
||||||
typeFilter: (FuzzyType) -> Boolean
|
typeFilter: (FuzzyType) -> Boolean
|
||||||
): Result {
|
): Result {
|
||||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = SmartCastCalculator(bindingContext).calculate(position, receiver)
|
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = SmartCastCalculator(
|
||||||
|
bindingContext, moduleDescriptor).calculate(position, receiver)
|
||||||
|
|
||||||
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
||||||
val types = descriptor.fuzzyTypes(smartCastTypes)
|
val types = descriptor.fuzzyTypes(smartCastTypes)
|
||||||
|
|||||||
+7
-19
@@ -16,34 +16,21 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.intentions.branchedTransformations
|
package org.jetbrains.kotlin.idea.intentions.branchedTransformations
|
||||||
|
|
||||||
import org.jetbrains.kotlin.psi.JetExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetBlockExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetBinaryExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetIfExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetPsiUtil
|
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens
|
import org.jetbrains.kotlin.lexer.JetTokens
|
||||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
||||||
import org.jetbrains.kotlin.psi.JetSafeQualifiedExpression
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
|
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
|
||||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils
|
import org.jetbrains.kotlin.resolve.BindingContextUtils
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import org.jetbrains.kotlin.psi.JetDeclaration
|
|
||||||
import org.jetbrains.kotlin.psi.JetThrowExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetPostfixExpression
|
|
||||||
import org.jetbrains.kotlin.psi.JetCallExpression
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.psi.JetElement
|
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
|
||||||
import org.jetbrains.kotlin.psi.JetProperty
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
val NULL_PTR_EXCEPTION = "NullPointerException"
|
val NULL_PTR_EXCEPTION = "NullPointerException"
|
||||||
val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException"
|
val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException"
|
||||||
@@ -65,9 +52,9 @@ fun JetExpression.extractExpressionIfSingle(): JetExpression? {
|
|||||||
val innerExpression = JetPsiUtil.deparenthesize(this)
|
val innerExpression = JetPsiUtil.deparenthesize(this)
|
||||||
if (innerExpression is JetBlockExpression) {
|
if (innerExpression is JetBlockExpression) {
|
||||||
return if (innerExpression.getStatements().size() == 1)
|
return if (innerExpression.getStatements().size() == 1)
|
||||||
JetPsiUtil.deparenthesize(innerExpression.getStatements().first as? JetExpression)
|
JetPsiUtil.deparenthesize(innerExpression.getStatements().firstOrNull() as? JetExpression)
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
return innerExpression
|
return innerExpression
|
||||||
@@ -86,7 +73,7 @@ fun JetBinaryExpression.getNonNullExpression(): JetExpression? = when {
|
|||||||
|
|
||||||
fun JetExpression.isNullExpression(): Boolean = this.extractExpressionIfSingle()?.getText() == "null"
|
fun JetExpression.isNullExpression(): Boolean = this.extractExpressionIfSingle()?.getText() == "null"
|
||||||
|
|
||||||
fun JetExpression.isNullExpressionOrEmptyBlock(): Boolean = this.isNullExpression() || this is JetBlockExpression && this.getStatements().empty
|
fun JetExpression.isNullExpressionOrEmptyBlock(): Boolean = this.isNullExpression() || this is JetBlockExpression && this.getStatements().isEmpty()
|
||||||
|
|
||||||
fun JetExpression.isThrowExpression(): Boolean = this.extractExpressionIfSingle() is JetThrowExpression
|
fun JetExpression.isThrowExpression(): Boolean = this.extractExpressionIfSingle() is JetThrowExpression
|
||||||
|
|
||||||
@@ -171,5 +158,6 @@ fun JetPostfixExpression.inlineBaseExpressionIfApplicableWithPrompt(editor: Edit
|
|||||||
fun JetExpression.isStableVariable(): Boolean {
|
fun JetExpression.isStableVariable(): Boolean {
|
||||||
val context = this.analyze()
|
val context = this.analyze()
|
||||||
val descriptor = BindingContextUtils.extractVariableDescriptorIfAny(context, this, false)
|
val descriptor = BindingContextUtils.extractVariableDescriptorIfAny(context, this, false)
|
||||||
return descriptor is VariableDescriptor && DataFlowValueFactory.isStableVariable(descriptor)
|
return descriptor is VariableDescriptor &&
|
||||||
|
DataFlowValueFactory.isStableVariable(descriptor, DescriptorUtils.getContainingModule(descriptor))
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -94,7 +94,7 @@ fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
|
|||||||
|
|
||||||
fun JetExpression.guessTypes(
|
fun JetExpression.guessTypes(
|
||||||
context: BindingContext,
|
context: BindingContext,
|
||||||
module: ModuleDescriptor?,
|
module: ModuleDescriptor,
|
||||||
coerceUnusedToUnit: Boolean = true
|
coerceUnusedToUnit: Boolean = true
|
||||||
): Array<JetType> {
|
): Array<JetType> {
|
||||||
val builtIns = KotlinBuiltIns.getInstance()
|
val builtIns = KotlinBuiltIns.getInstance()
|
||||||
@@ -108,7 +108,7 @@ fun JetExpression.guessTypes(
|
|||||||
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
|
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
|
||||||
if (theType1 != null) {
|
if (theType1 != null) {
|
||||||
val dataFlowInfo = context[BindingContext.EXPRESSION_DATA_FLOW_INFO, this]
|
val dataFlowInfo = context[BindingContext.EXPRESSION_DATA_FLOW_INFO, this]
|
||||||
val possibleTypes = dataFlowInfo?.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context))
|
val possibleTypes = dataFlowInfo?.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(this, theType1, context, module))
|
||||||
return if (possibleTypes != null && possibleTypes.isNotEmpty()) possibleTypes.copyToArray() else array(theType1)
|
return if (possibleTypes != null && possibleTypes.isNotEmpty()) possibleTypes.copyToArray() else array(theType1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.psi.JetExpression
|
|||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
||||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import org.jetbrains.kotlin.types.JetTypeImpl
|
import org.jetbrains.kotlin.types.JetTypeImpl
|
||||||
@@ -40,9 +41,9 @@ object CreateIteratorFunctionActionFactory : JetIntentionActionsFactory() {
|
|||||||
val iterableType = TypeInfo(iterableExpr, Variance.IN_VARIANCE)
|
val iterableType = TypeInfo(iterableExpr, Variance.IN_VARIANCE)
|
||||||
val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType()
|
val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType()
|
||||||
|
|
||||||
val context = file.analyzeFully()
|
val analysisResult = file.analyzeFullyAndGetResult()
|
||||||
val returnJetTypeParameterTypes = variableExpr.guessTypes(context, null)
|
val returnJetTypeParameterTypes = variableExpr.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor)
|
||||||
if (returnJetTypeParameterTypes.size != 1) return null
|
if (returnJetTypeParameterTypes.size() != 1) return null
|
||||||
|
|
||||||
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
||||||
val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType)
|
val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType)
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
|||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
import java.util.HashSet
|
import java.util.HashSet
|
||||||
import com.intellij.openapi.util.Pair
|
import com.intellij.openapi.util.Pair
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
|
|
||||||
class SmartCastCalculator(val bindingContext: BindingContext) {
|
class SmartCastCalculator(val bindingContext: BindingContext, val containingDeclaration: DeclarationDescriptor) {
|
||||||
public fun calculate(position: JetExpression, receiver: JetExpression?): (VariableDescriptor) -> Collection<JetType> {
|
public fun calculate(position: JetExpression, receiver: JetExpression?): (VariableDescriptor) -> Collection<JetType> {
|
||||||
val dataFlowInfo = bindingContext.getDataFlowInfo(position)
|
val dataFlowInfo = bindingContext.getDataFlowInfo(position)
|
||||||
val (variableToTypes, notNullVariables) = processDataFlowInfo(dataFlowInfo, receiver)
|
val (variableToTypes, notNullVariables) = processDataFlowInfo(dataFlowInfo, receiver)
|
||||||
@@ -63,14 +65,14 @@ class SmartCastCalculator(val bindingContext: BindingContext) {
|
|||||||
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
|
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver] ?: return ProcessDataFlowInfoResult()
|
val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver] ?: return ProcessDataFlowInfoResult()
|
||||||
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext).getId()
|
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext, containingDeclaration).getId()
|
||||||
dataFlowValueToVariable = {(value) ->
|
dataFlowValueToVariable = { value ->
|
||||||
val id = value.getId()
|
val id = value.getId()
|
||||||
if (id is Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
|
if (id is Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dataFlowValueToVariable = {(value) ->
|
dataFlowValueToVariable = { value ->
|
||||||
val id = value.getId()
|
val id = value.getId()
|
||||||
when {
|
when {
|
||||||
id is VariableDescriptor -> id
|
id is VariableDescriptor -> id
|
||||||
|
|||||||
+3
-3
@@ -161,7 +161,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
handler.selectElements(editor, file) {(elements, previousSibling) ->
|
handler.selectElements(editor, file) { elements, previousSibling ->
|
||||||
handler.doInvoke(editor, file, elements, explicitPreviousSibling ?: previousSibling)
|
handler.doInvoke(editor, file, elements, explicitPreviousSibling ?: previousSibling)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
|||||||
val mainFileName = mainFile.getName()
|
val mainFileName = mainFile.getName()
|
||||||
val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName)
|
val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName)
|
||||||
mainFile.getParentFile()
|
mainFile.getParentFile()
|
||||||
.listFiles {(file, name) ->
|
.listFiles { file, name ->
|
||||||
name != mainFileName && name.startsWith("$mainFileBaseName.") && (name.endsWith(".kt") || name.endsWith(".java"))
|
name != mainFileName && name.startsWith("$mainFileBaseName.") && (name.endsWith(".kt") || name.endsWith(".java"))
|
||||||
}.forEach {
|
}.forEach {
|
||||||
fixture.configureByFile(it.getName())
|
fixture.configureByFile(it.getName())
|
||||||
@@ -192,7 +192,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
|||||||
try {
|
try {
|
||||||
action(file)
|
action(file)
|
||||||
|
|
||||||
assert(!conflictFile.exists())
|
assert(!conflictFile.exists(), "Conflict file $conflictFile should not exist")
|
||||||
JetTestUtils.assertEqualsToFile(afterFile, file.getText()!!)
|
JetTestUtils.assertEqualsToFile(afterFile, file.getText()!!)
|
||||||
}
|
}
|
||||||
catch(e: Exception) {
|
catch(e: Exception) {
|
||||||
|
|||||||
Reference in New Issue
Block a user