From 76c393a14521c0a56a26c7aa6241f6a7132f6529 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 10 Nov 2010 22:49:32 +0300 Subject: [PATCH] nullity: check if possible to detect NotNull return value from conditional checks (IDEA-60693) --- .../inferNullity/NullityInferrer.java | 55 +++++++++++++++++-- .../afterConditionalReturnNotNull.java | 9 +++ .../afterParameterCheckedForNull.java | 2 +- .../beforeConditionalReturnNotNull.java | 5 ++ .../codeInsight/NullityInferrerTest.java | 4 ++ 5 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/nullityinferrer/afterConditionalReturnNotNull.java create mode 100644 java/java-tests/testData/codeInsight/nullityinferrer/beforeConditionalReturnNotNull.java diff --git a/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java b/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java index 392a749c5991..46b0c784876b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java +++ b/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java @@ -199,8 +199,16 @@ public class NullityInferrer { @Override public void visitConditionalExpression(@NotNull PsiConditionalExpression expression) { - neverNull = expressionIsNeverNull(expression.getThenExpression()) && - expressionIsNeverNull(expression.getElseExpression()); + final PsiExpression condition = expression.getCondition(); + final PsiExpression thenExpression = expression.getThenExpression(); + final PsiExpression elseExpression = expression.getElseExpression(); + if (canTrunkImpossibleBrunch(condition, elseExpression)) { + neverNull = expressionIsNeverNull(thenExpression); + return; + } + + neverNull = expressionIsNeverNull(thenExpression) || + expressionIsNeverNull(elseExpression); } @Override @@ -241,6 +249,37 @@ public class NullityInferrer { } } + private static boolean trunkImpossibleBrunch(PsiExpression condition, + PsiExpression elseExpression, + PsiExpression rOperand, + PsiExpression lOperand) { + if (rOperand instanceof PsiLiteralExpression && "null".equals(rOperand.getText())) { + if (lOperand instanceof PsiReferenceExpression) { + final PsiElement resolve = ((PsiReferenceExpression)lOperand).resolve(); + if (resolve instanceof PsiVariable) { + if (((PsiBinaryExpression)condition).getOperationTokenType() == JavaTokenType.EQEQ) { + if (elseExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)elseExpression).resolve() == resolve) { + return true; + } + } + } + } + } + return false; + } + + private static boolean canTrunkImpossibleBrunch(PsiExpression condition, PsiExpression elseExpression) { + if (condition instanceof PsiBinaryExpression) { + final PsiExpression rOperand = ((PsiBinaryExpression)condition).getROperand(); + final PsiExpression lOperand = ((PsiBinaryExpression)condition).getLOperand(); + if (trunkImpossibleBrunch(condition, elseExpression, rOperand, lOperand) || + trunkImpossibleBrunch(condition, elseExpression, lOperand, rOperand)) { + return true; + } + } + return false; + } + private class ExpressionIsSometimesNullVisitor extends JavaRecursiveElementWalkingVisitor{ private boolean sometimesNull = false; @@ -262,8 +301,16 @@ public class NullityInferrer { @Override public void visitConditionalExpression(@NotNull PsiConditionalExpression expression) { - sometimesNull = expressionIsSometimesNull(expression.getThenExpression()) || - expressionIsSometimesNull(expression.getElseExpression()); + final PsiExpression condition = expression.getCondition(); + final PsiExpression thenExpression = expression.getThenExpression(); + final PsiExpression elseExpression = expression.getElseExpression(); + if (canTrunkImpossibleBrunch(condition, elseExpression)) { + sometimesNull = expressionIsSometimesNull(thenExpression); + return; + } + + sometimesNull = expressionIsSometimesNull(thenExpression) || + expressionIsSometimesNull(elseExpression); } @Override diff --git a/java/java-tests/testData/codeInsight/nullityinferrer/afterConditionalReturnNotNull.java b/java/java-tests/testData/codeInsight/nullityinferrer/afterConditionalReturnNotNull.java new file mode 100644 index 000000000000..5a89e47550ad --- /dev/null +++ b/java/java-tests/testData/codeInsight/nullityinferrer/afterConditionalReturnNotNull.java @@ -0,0 +1,9 @@ +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +class Test { + @NotNull + public String noNull( @Nullable String text) { + return text == null ? "" : text; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/nullityinferrer/afterParameterCheckedForNull.java b/java/java-tests/testData/codeInsight/nullityinferrer/afterParameterCheckedForNull.java index 264201b0308c..4ca28d85db02 100644 --- a/java/java-tests/testData/codeInsight/nullityinferrer/afterParameterCheckedForNull.java +++ b/java/java-tests/testData/codeInsight/nullityinferrer/afterParameterCheckedForNull.java @@ -23,7 +23,7 @@ class Test { return ((String)str); } - @Nullable + @NotNull String fram(@Nullable String str, boolean b) { if (str != null) { return b ? str : "not null strimg"; diff --git a/java/java-tests/testData/codeInsight/nullityinferrer/beforeConditionalReturnNotNull.java b/java/java-tests/testData/codeInsight/nullityinferrer/beforeConditionalReturnNotNull.java new file mode 100644 index 000000000000..ae5f1a8524a3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/nullityinferrer/beforeConditionalReturnNotNull.java @@ -0,0 +1,5 @@ +class Test { + public String noNull( String text) { + return text == null ? "" : text; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/NullityInferrerTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/NullityInferrerTest.java index 925f04890fd8..322ace251ae1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/NullityInferrerTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/NullityInferrerTest.java @@ -58,6 +58,10 @@ public class NullityInferrerTest extends CodeInsightTestCase { doTest(true); } + public void testConditionalReturnNotNull() throws Exception { + doTest(false); + } + //-----------------------fields--------------------------------------------------- public void testFieldsAssignment() throws Exception { doTest(false);