IDEA-163213 Method contract is not inferred when it returns the result of string concatenation

This commit is contained in:
peter
2016-10-27 18:01:46 +02:00
parent 9686420b3b
commit 19ae8cdcdb
3 changed files with 43 additions and 11 deletions
@@ -117,17 +117,7 @@ class ContractInferenceInterpreter {
IElementType type = expr.getTokenType();
if (type == POLYADIC_EXPRESSION || type == BINARY_EXPRESSION) {
List<LighterASTNode> operands = getExpressionChildren(myTree, expr);
if (operands.size() == 2) {
boolean equality = firstChildOfType(myTree, expr, JavaTokenType.EQEQ) != null;
if (equality || firstChildOfType(myTree, expr, JavaTokenType.NE) != null) {
return asPreContracts(visitEqualityComparison(states, operands.get(0), operands.get(1), equality));
}
}
boolean logicalAnd = firstChildOfType(myTree, expr, JavaTokenType.ANDAND) != null;
if (logicalAnd || firstChildOfType(myTree, expr, JavaTokenType.OROR) != null) {
return asPreContracts(visitLogicalOperation(operands, logicalAnd, states));
}
return visitPolyadic(states, expr);
}
if (type == CONDITIONAL_EXPRESSION) {
@@ -190,6 +180,26 @@ class ContractInferenceInterpreter {
return Collections.emptyList();
}
@NotNull
private List<PreContract> visitPolyadic(List<ValueConstraint[]> states, @NotNull LighterASTNode expr) {
if (firstChildOfType(myTree, expr, JavaTokenType.PLUS) != null) {
return asPreContracts(ContainerUtil.map(states, s -> new MethodContract(s, NOT_NULL_VALUE)));
}
List<LighterASTNode> operands = getExpressionChildren(myTree, expr);
if (operands.size() == 2) {
boolean equality = firstChildOfType(myTree, expr, JavaTokenType.EQEQ) != null;
if (equality || firstChildOfType(myTree, expr, JavaTokenType.NE) != null) {
return asPreContracts(visitEqualityComparison(states, operands.get(0), operands.get(1), equality));
}
}
boolean logicalAnd = firstChildOfType(myTree, expr, JavaTokenType.ANDAND) != null;
if (logicalAnd || firstChildOfType(myTree, expr, JavaTokenType.OROR) != null) {
return asPreContracts(visitLogicalOperation(operands, logicalAnd, states));
}
return Collections.emptyList();
}
@NotNull
private static List<PreContract> asPreContracts(List<MethodContract> contracts) {
return ContainerUtil.map(contracts, KnownContract::new);
@@ -438,6 +438,24 @@ class ContractInferenceFromSourceTest extends LightCodeInsightFixtureTestCase {
assert c == ['null -> null']
}
void "test string concatenation"() {
def c = inferContracts("""
public static String test(String s1, String s2) {
return s1 != null ? s1.trim()+s2.trim() : unknown();
}
""")
assert c == ['!null, _ -> !null']
}
void "test int addition"() {
def c = inferContracts("""
public static int test(int a, int b) {
return a + b;
}
""")
assert c == []
}
void "test compare with string literal"() {
def c = inferContracts("""
String s(String s) {
@@ -73,6 +73,10 @@ String bar() { return "z"; }
assert inferNullity(parse('String bar() { return equals(2) ? "a" : equals(3) ? null : "a"; }; ')) == NULLABLE
}
void "test string concatenation"() {
assert inferNullity(parse('String bar(String s1, String s2) { return s1 + s2; }; ')) == NOT_NULL
}
void "test delegation to nullable means nothing"() {
assert inferNullity(parse('String foo() { return bar("2"); }; String bar(String s) { if (s != "2") return null; return "a"; }; ')) == UNKNOWN
}