Refine type from property setter parameter on assignment
In most cases these types are equals, the only known exception is var-property contained in projected type member scope (see test data)
This commit is contained in:
@@ -48,6 +48,16 @@ public class BindingContextUtils {
|
|||||||
private BindingContextUtils() {
|
private BindingContextUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static VariableDescriptor extractVariableFromResolvedCall(
|
||||||
|
@NotNull BindingContext bindingContext,
|
||||||
|
@Nullable KtElement callElement
|
||||||
|
) {
|
||||||
|
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(callElement, bindingContext);
|
||||||
|
if (resolvedCall == null || !(resolvedCall.getResultingDescriptor() instanceof VariableDescriptor)) return null;
|
||||||
|
return (VariableDescriptor) resolvedCall.getResultingDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull BindingContext bindingContext, @Nullable KtElement element, boolean onlyReference) {
|
public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull BindingContext bindingContext, @Nullable KtElement element, boolean onlyReference) {
|
||||||
DeclarationDescriptor descriptor = null;
|
DeclarationDescriptor descriptor = null;
|
||||||
|
|||||||
+31
-13
@@ -21,16 +21,13 @@ import com.intellij.psi.tree.IElementType;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||||
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.DeclarationsCheckerKt;
|
import org.jetbrains.kotlin.resolve.*;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||||
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
|
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
|
||||||
@@ -318,16 +315,36 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
basic.resolveArrayAccessSetMethod((KtArrayAccessExpression) left, right, contextForResolve, context.trace);
|
basic.resolveArrayAccessSetMethod((KtArrayAccessExpression) left, right, contextForResolve, context.trace);
|
||||||
}
|
}
|
||||||
rightInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(leftInfo.getDataFlowInfo()));
|
rightInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(leftInfo.getDataFlowInfo()));
|
||||||
components.dataFlowAnalyzer.checkType(binaryOperationType, expression, context.replaceExpectedType(leftType).replaceDataFlowInfo(rightInfo.getDataFlowInfo()));
|
|
||||||
|
KotlinType expectedType = refineTypeFromPropertySetterIfPossible(context.trace.getBindingContext(), leftOperand, leftType);
|
||||||
|
|
||||||
|
components.dataFlowAnalyzer.checkType(binaryOperationType, expression, context.replaceExpectedType(expectedType)
|
||||||
|
.replaceDataFlowInfo(rightInfo.getDataFlowInfo()));
|
||||||
basic.checkLValue(context.trace, context, leftOperand, right);
|
basic.checkLValue(context.trace, context, leftOperand, right);
|
||||||
}
|
}
|
||||||
temporary.commit();
|
temporary.commit();
|
||||||
return rightInfo.replaceType(checkAssignmentType(type, expression, contextWithExpectedType));
|
return rightInfo.replaceType(checkAssignmentType(type, expression, contextWithExpectedType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static KotlinType refineTypeFromPropertySetterIfPossible(
|
||||||
|
@NotNull BindingContext bindingContext,
|
||||||
|
@Nullable KtElement leftOperand,
|
||||||
|
@Nullable KotlinType leftOperandType
|
||||||
|
) {
|
||||||
|
VariableDescriptor descriptor = BindingContextUtils.extractVariableFromResolvedCall(bindingContext, leftOperand);
|
||||||
|
|
||||||
|
if (descriptor instanceof PropertyDescriptor) {
|
||||||
|
PropertySetterDescriptor setter = ((PropertyDescriptor) descriptor).getSetter();
|
||||||
|
if (setter != null) return setter.getValueParameters().get(0).getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftOperandType;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected KotlinTypeInfo visitAssignment(KtBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
|
protected KotlinTypeInfo visitAssignment(KtBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
|
||||||
final ExpressionTypingContext context =
|
ExpressionTypingContext context =
|
||||||
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceScope(scope).replaceContextDependency(INDEPENDENT);
|
contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceScope(scope).replaceContextDependency(INDEPENDENT);
|
||||||
KtExpression leftOperand = expression.getLeft();
|
KtExpression leftOperand = expression.getLeft();
|
||||||
if (leftOperand instanceof KtAnnotatedExpression) {
|
if (leftOperand instanceof KtAnnotatedExpression) {
|
||||||
@@ -346,15 +363,16 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
return typeInfo.replaceType(checkAssignmentType(typeInfo.getType(), expression, contextWithExpectedType));
|
return typeInfo.replaceType(checkAssignmentType(typeInfo.getType(), expression, contextWithExpectedType));
|
||||||
}
|
}
|
||||||
KotlinTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(left, context, facade);
|
KotlinTypeInfo leftInfo = ExpressionTypingUtils.getTypeInfoOrNullType(left, context, facade);
|
||||||
KotlinType leftType = leftInfo.getType();
|
KotlinType expectedType = refineTypeFromPropertySetterIfPossible(context.trace.getBindingContext(), leftOperand, leftInfo.getType());
|
||||||
DataFlowInfo dataFlowInfo = leftInfo.getDataFlowInfo();
|
DataFlowInfo dataFlowInfo = leftInfo.getDataFlowInfo();
|
||||||
KotlinTypeInfo resultInfo;
|
KotlinTypeInfo resultInfo;
|
||||||
if (right != null) {
|
if (right != null) {
|
||||||
resultInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo).replaceExpectedType(leftType));
|
resultInfo = facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo).replaceExpectedType(expectedType));
|
||||||
|
|
||||||
dataFlowInfo = resultInfo.getDataFlowInfo();
|
dataFlowInfo = resultInfo.getDataFlowInfo();
|
||||||
KotlinType rightType = resultInfo.getType();
|
KotlinType rightType = resultInfo.getType();
|
||||||
if (left != null && leftType != null && rightType != null) {
|
if (left != null && expectedType != null && rightType != null) {
|
||||||
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, leftType, context);
|
DataFlowValue leftValue = DataFlowValueFactory.createDataFlowValue(left, expectedType, context);
|
||||||
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
DataFlowValue rightValue = DataFlowValueFactory.createDataFlowValue(right, rightType, context);
|
||||||
// We cannot say here anything new about rightValue except it has the same value as leftValue
|
// We cannot say here anything new about rightValue except it has the same value as leftValue
|
||||||
resultInfo = resultInfo.replaceDataFlowInfo(dataFlowInfo.assign(leftValue, rightValue));
|
resultInfo = resultInfo.replaceDataFlowInfo(dataFlowInfo.assign(leftValue, rightValue));
|
||||||
@@ -363,7 +381,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
|
|||||||
else {
|
else {
|
||||||
resultInfo = leftInfo;
|
resultInfo = leftInfo;
|
||||||
}
|
}
|
||||||
if (leftType != null && leftOperand != null) { //if leftType == null, some other error has been generated
|
if (expectedType != null && leftOperand != null) { //if expectedType == null, some other error has been generated
|
||||||
basic.checkLValue(context.trace, context, leftOperand, right);
|
basic.checkLValue(context.trace, context, leftOperand, right);
|
||||||
}
|
}
|
||||||
return resultInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(expression, contextWithExpectedType));
|
return resultInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(expression, contextWithExpectedType));
|
||||||
|
|||||||
+1
-1
@@ -4,6 +4,6 @@ interface Tr<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test(t: Tr<*>) {
|
fun test(t: Tr<*>) {
|
||||||
t.v = t
|
t.v = <!TYPE_MISMATCH!>t<!>
|
||||||
t.v checkType { _<Tr<*>>() }
|
t.v checkType { _<Tr<*>>() }
|
||||||
}
|
}
|
||||||
+3
-1
@@ -6,6 +6,8 @@ interface Tr<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test(t: Tr<*>) {
|
fun test(t: Tr<*>) {
|
||||||
t.<!SETTER_PROJECTED_OUT!>v<!> = null!!
|
t.v = null!!
|
||||||
|
t.v = <!TYPE_MISMATCH!>""<!>
|
||||||
|
t.v = <!NULL_FOR_NONNULL_TYPE!>null<!>
|
||||||
t.v checkType { _<Any?>() }
|
t.v checkType { _<Any?>() }
|
||||||
}
|
}
|
||||||
Vendored
+3
-1
@@ -4,5 +4,7 @@ interface Tr<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test(t: Tr<out String>) {
|
fun test(t: Tr<out String>) {
|
||||||
t.<!SETTER_PROJECTED_OUT!>v<!> += null!!
|
// resolved as t.v = t.v + null!!, where type of right operand is String,
|
||||||
|
// so TYPE_MISMATCH: String is not <: of Captured(out String)
|
||||||
|
<!TYPE_MISMATCH!>t.v += null!!<!>
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user