From 19ae8cdcdbf6887a6a01f03c07b7ffbcb1eed0ee Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 27 Oct 2016 11:15:27 +0200 Subject: [PATCH] IDEA-163213 Method contract is not inferred when it returns the result of string concatenation --- .../ContractInferenceInterpreter.java | 32 ++++++++++++------- .../ContractInferenceFromSourceTest.groovy | 18 +++++++++++ .../NullityInferenceFromSourceTestCase.groovy | 4 +++ 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java index 2f78283ac76b..a56d2c0626f5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java @@ -117,17 +117,7 @@ class ContractInferenceInterpreter { IElementType type = expr.getTokenType(); if (type == POLYADIC_EXPRESSION || type == BINARY_EXPRESSION) { - List 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 visitPolyadic(List states, @NotNull LighterASTNode expr) { + if (firstChildOfType(myTree, expr, JavaTokenType.PLUS) != null) { + return asPreContracts(ContainerUtil.map(states, s -> new MethodContract(s, NOT_NULL_VALUE))); + } + + List 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 asPreContracts(List contracts) { return ContainerUtil.map(contracts, KnownContract::new); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy index 7f67b436b0dd..9f1ec912fa93 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy @@ -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) { diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy index 5dd3136d54fa..dfa86716bf34 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy @@ -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 }