KT-2422 Invalid warning "Condition is always true"
#KT-2422 fixed
This commit is contained in:
+137
-33
@@ -18,17 +18,20 @@ package org.jetbrains.jet.lang.resolve.calls.autocasts;
|
|||||||
|
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.JetNodeTypes;
|
import org.jetbrains.jet.JetNodeTypes;
|
||||||
import org.jetbrains.jet.lang.descriptors.*;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.JetModuleUtil;
|
import org.jetbrains.jet.lang.resolve.JetModuleUtil;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
|
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
|
||||||
|
import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
|
||||||
|
|
||||||
public class DataFlowValueFactory {
|
public class DataFlowValueFactory {
|
||||||
public static final DataFlowValueFactory INSTANCE = new DataFlowValueFactory();
|
public static final DataFlowValueFactory INSTANCE = new DataFlowValueFactory();
|
||||||
@@ -42,8 +45,8 @@ public class DataFlowValueFactory {
|
|||||||
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
|
||||||
}
|
}
|
||||||
if (TypeUtils.equalTypes(type, KotlinBuiltIns.getInstance().getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
|
if (TypeUtils.equalTypes(type, KotlinBuiltIns.getInstance().getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
|
||||||
Pair<Object, Boolean> result = getIdForStableIdentifier(expression, bindingContext, false);
|
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, false);
|
||||||
return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type));
|
return new DataFlowValue(result.id == null ? expression : result.id, type, result.isStable, getImmanentNullability(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -104,55 +107,156 @@ public class DataFlowValueFactory {
|
|||||||
return type.isNullable() || TypeUtils.hasNullableSuperType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
|
return type.isNullable() || TypeUtils.hasNullableSuperType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class IdentifierInfo {
|
||||||
|
public final Object id;
|
||||||
|
public final boolean isStable;
|
||||||
|
public final boolean isNamespace;
|
||||||
|
|
||||||
|
private IdentifierInfo(Object id, boolean isStable, boolean isNamespace) {
|
||||||
|
this.id = id;
|
||||||
|
this.isStable = isStable;
|
||||||
|
this.isNamespace = isNamespace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final IdentifierInfo ERROR_IDENTIFIER_INFO = new IdentifierInfo(null, false, false);
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static Pair<Object, Boolean> getIdForStableIdentifier(@NotNull JetExpression expression, @NotNull BindingContext bindingContext, boolean allowNamespaces) {
|
private static IdentifierInfo createInfo(Object id, boolean isStable) {
|
||||||
|
return new IdentifierInfo(id, isStable, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static IdentifierInfo createNamespaceInfo(Object id, boolean isStable) {
|
||||||
|
return new IdentifierInfo(id, isStable, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static IdentifierInfo combineInfo(@Nullable IdentifierInfo receiverInfo, @NotNull IdentifierInfo selectorInfo) {
|
||||||
|
if (receiverInfo == null || receiverInfo == ERROR_IDENTIFIER_INFO || receiverInfo.isNamespace) {
|
||||||
|
return selectorInfo;
|
||||||
|
}
|
||||||
|
return createInfo(Pair.create(receiverInfo.id, selectorInfo.id), receiverInfo.isStable && selectorInfo.isStable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static IdentifierInfo getIdForStableIdentifier(
|
||||||
|
@Nullable JetExpression expression,
|
||||||
|
@NotNull BindingContext bindingContext,
|
||||||
|
boolean allowNamespaces
|
||||||
|
) {
|
||||||
if (expression instanceof JetParenthesizedExpression) {
|
if (expression instanceof JetParenthesizedExpression) {
|
||||||
JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression) expression;
|
JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression) expression;
|
||||||
JetExpression innerExpression = parenthesizedExpression.getExpression();
|
JetExpression innerExpression = parenthesizedExpression.getExpression();
|
||||||
if (innerExpression == null) {
|
|
||||||
return Pair.create(null, false);
|
|
||||||
}
|
|
||||||
return getIdForStableIdentifier(innerExpression, bindingContext, allowNamespaces);
|
return getIdForStableIdentifier(innerExpression, bindingContext, allowNamespaces);
|
||||||
}
|
}
|
||||||
else if (expression instanceof JetQualifiedExpression) {
|
else if (expression instanceof JetQualifiedExpression) {
|
||||||
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
|
JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
|
||||||
|
JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
|
||||||
JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
|
JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
|
||||||
if (selectorExpression == null) {
|
IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, true);
|
||||||
return Pair.create(null, false);
|
IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, allowNamespaces);
|
||||||
}
|
|
||||||
Pair<Object, Boolean> receiverId = getIdForStableIdentifier(qualifiedExpression.getReceiverExpression(), bindingContext, true);
|
return combineInfo(receiverId, selectorId);
|
||||||
Pair<Object, Boolean> selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, allowNamespaces);
|
|
||||||
return receiverId.second ? selectorId : Pair.create(receiverId.first, false);
|
|
||||||
}
|
}
|
||||||
if (expression instanceof JetSimpleNameExpression) {
|
if (expression instanceof JetSimpleNameExpression) {
|
||||||
JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) expression;
|
return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext, allowNamespaces);
|
||||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
|
|
||||||
if (declarationDescriptor instanceof VariableDescriptor) {
|
|
||||||
return Pair.create((Object) declarationDescriptor, isStableVariable((VariableDescriptor) declarationDescriptor));
|
|
||||||
}
|
|
||||||
if (declarationDescriptor instanceof NamespaceDescriptor) {
|
|
||||||
return Pair.create((Object) declarationDescriptor, allowNamespaces);
|
|
||||||
}
|
|
||||||
if (declarationDescriptor instanceof ClassDescriptor) {
|
|
||||||
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
|
|
||||||
return Pair.create((Object) classDescriptor, classDescriptor.isClassObjectAValue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (expression instanceof JetThisExpression) {
|
else if (expression instanceof JetThisExpression) {
|
||||||
JetThisExpression thisExpression = (JetThisExpression) expression;
|
JetThisExpression thisExpression = (JetThisExpression) expression;
|
||||||
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
|
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
|
||||||
if (declarationDescriptor instanceof CallableDescriptor) {
|
|
||||||
return Pair.create((Object) ((CallableDescriptor) declarationDescriptor).getReceiverParameter().getValue(), true);
|
return getIdForThisReceiver(declarationDescriptor);
|
||||||
}
|
|
||||||
if (declarationDescriptor instanceof ClassDescriptor) {
|
|
||||||
return Pair.create((Object) ((ClassDescriptor) declarationDescriptor).getThisAsReceiverParameter().getValue(), true);
|
|
||||||
}
|
|
||||||
return Pair.create(null, true);
|
|
||||||
}
|
}
|
||||||
else if (expression instanceof JetRootNamespaceExpression) {
|
else if (expression instanceof JetRootNamespaceExpression) {
|
||||||
return Pair.create((Object) JetModuleUtil.getRootNamespaceType(expression), allowNamespaces);
|
return createNamespaceInfo(JetModuleUtil.getRootNamespaceType(expression), allowNamespaces);
|
||||||
}
|
}
|
||||||
return Pair.create(null, false);
|
return ERROR_IDENTIFIER_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static IdentifierInfo getIdForSimpleNameExpression(
|
||||||
|
@NotNull JetSimpleNameExpression simpleNameExpression,
|
||||||
|
@NotNull BindingContext bindingContext,
|
||||||
|
boolean allowNamespaces
|
||||||
|
) {
|
||||||
|
DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
|
||||||
|
if (declarationDescriptor instanceof VariableDescriptor) {
|
||||||
|
ResolvedCall<?> resolvedCall = bindingContext.get(RESOLVED_CALL, simpleNameExpression);
|
||||||
|
// todo return assert
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
IdentifierInfo receiverInfo = resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getThisObject(), simpleNameExpression) : null;
|
||||||
|
|
||||||
|
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
|
||||||
|
return combineInfo(receiverInfo, createInfo(variableDescriptor, isStableVariable(variableDescriptor)));
|
||||||
|
}
|
||||||
|
if (declarationDescriptor instanceof NamespaceDescriptor) {
|
||||||
|
return createNamespaceInfo(declarationDescriptor, allowNamespaces);
|
||||||
|
}
|
||||||
|
if (declarationDescriptor instanceof ClassDescriptor) {
|
||||||
|
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
|
||||||
|
return createInfo(classDescriptor, classDescriptor.isClassObjectAValue());
|
||||||
|
}
|
||||||
|
return ERROR_IDENTIFIER_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static IdentifierInfo getIdForImplicitReceiver(@NotNull ReceiverValue receiverValue, @Nullable final JetExpression expression) {
|
||||||
|
return receiverValue.accept(new ReceiverValueVisitor<IdentifierInfo, Void>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierInfo visitNoReceiver(ReceiverValue noReceiver, Void data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierInfo visitTransientReceiver(TransientReceiver receiver, Void data) {
|
||||||
|
assert false: "Transient receiver is implicit for an explicit expression: " + expression + ". Receiver: " + receiver;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierInfo visitExtensionReceiver(ExtensionReceiver receiver, Void data) {
|
||||||
|
return getIdForThisReceiver(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierInfo visitExpressionReceiver(ExpressionReceiver receiver, Void data) {
|
||||||
|
// there is an explicit "this" expression and it was analyzed earlier
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierInfo visitClassReceiver(ClassReceiver receiver, Void data) {
|
||||||
|
return getIdForThisReceiver(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierInfo visitScriptReceiver(ScriptReceiver receiver, Void data) {
|
||||||
|
return getIdForThisReceiver(receiver);
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static IdentifierInfo getIdForThisReceiver(@NotNull ThisReceiver thisReceiver) {
|
||||||
|
return getIdForThisReceiver(thisReceiver.getDeclarationDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
|
||||||
|
if (descriptorOfThisReceiver instanceof CallableDescriptor) {
|
||||||
|
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getReceiverParameter();
|
||||||
|
assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + descriptorOfThisReceiver;
|
||||||
|
return createInfo(receiverParameter.getValue(), true);
|
||||||
|
}
|
||||||
|
if (descriptorOfThisReceiver instanceof ClassDescriptor) {
|
||||||
|
return createInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue(), true);
|
||||||
|
}
|
||||||
|
return ERROR_IDENTIFIER_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) {
|
public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package bar
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
val foo: Int? = null
|
||||||
|
fun foo(o: Test) = foo == null && o.foo == null // ERROR warning: o.test == null is always true
|
||||||
|
|
||||||
|
fun bar(a: Test, b: Test) {
|
||||||
|
if (a.foo != null) {
|
||||||
|
useInt(<!TYPE_MISMATCH!>b.foo<!>)
|
||||||
|
}
|
||||||
|
if (a.foo != null) {
|
||||||
|
useInt(<!TYPE_MISMATCH!>foo<!>)
|
||||||
|
}
|
||||||
|
if (this.foo != null) {
|
||||||
|
useInt(foo)
|
||||||
|
}
|
||||||
|
if (foo != null) {
|
||||||
|
useInt(this.foo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useInt(i: Int) = i
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
//FILE: bar.kt
|
||||||
|
package bar
|
||||||
|
|
||||||
|
val i: Int? = 2
|
||||||
|
|
||||||
|
//FILE: foo.kt
|
||||||
|
package foo
|
||||||
|
|
||||||
|
val i: Int? = 1
|
||||||
|
|
||||||
|
class A(val i: Int?) {
|
||||||
|
fun testUseFromClass() {
|
||||||
|
if (foo.i != null) {
|
||||||
|
useInt(<!TYPE_MISMATCH!>i<!>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testUseFromOtherPackage() {
|
||||||
|
if (bar.i != null) {
|
||||||
|
useInt(<!TYPE_MISMATCH!>i<!>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useInt(i: Int) = i
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class A(val i: Int?) {
|
||||||
|
fun test1() {
|
||||||
|
if (this@A.i != null) {
|
||||||
|
useInt(this.i)
|
||||||
|
useInt(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class B {
|
||||||
|
fun test2() {
|
||||||
|
if (i != null) {
|
||||||
|
useInt(this@A.i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun A.foo() {
|
||||||
|
if (this@foo.i != null) {
|
||||||
|
useInt(this.i)
|
||||||
|
useInt(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test3() {
|
||||||
|
useFunction {
|
||||||
|
if(i != null) {
|
||||||
|
useInt(this.i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useInt(i: Int) = i
|
||||||
|
fun useFunction(f: A.() -> Unit) = f
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
class C(val i: Int?) {}
|
||||||
|
|
||||||
|
class A(val c: C) {
|
||||||
|
fun test1() {
|
||||||
|
if (this@A.c.i != null) {
|
||||||
|
useInt(this.c.i)
|
||||||
|
useInt(c.i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class B {
|
||||||
|
fun test2() {
|
||||||
|
if (c.i != null) {
|
||||||
|
useInt(this@A.c.i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun A.foo() {
|
||||||
|
if (this@foo.c.i != null) {
|
||||||
|
useInt(this.c.i)
|
||||||
|
useInt(c.i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test3() {
|
||||||
|
useFunction {
|
||||||
|
if(c.i != null) {
|
||||||
|
useInt(this.c.i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useInt(i: Int) = i
|
||||||
|
fun useFunction(f: A.() -> Unit) = f
|
||||||
|
|
||||||
@@ -4722,6 +4722,26 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
doTest("compiler/testData/diagnostics/tests/smartCasts/kt1461.kt");
|
doTest("compiler/testData/diagnostics/tests/smartCasts/kt1461.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt2422.kt")
|
||||||
|
public void testKt2422() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/smartCasts/kt2422.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noErrorCheckForPackageLevelVal.kt")
|
||||||
|
public void testNoErrorCheckForPackageLevelVal() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/smartCasts/noErrorCheckForPackageLevelVal.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisWithLabel.kt")
|
||||||
|
public void testThisWithLabel() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/smartCasts/thisWithLabel.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisWithLabelAsReceiverPart.kt")
|
||||||
|
public void testThisWithLabelAsReceiverPart() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/smartCasts/thisWithLabelAsReceiverPart.kt");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/substitutions")
|
@TestMetadata("compiler/testData/diagnostics/tests/substitutions")
|
||||||
|
|||||||
Reference in New Issue
Block a user