diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index bed3e5b67ac8..bca5390a66ee 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -35,6 +35,7 @@ import com.intellij.codeInspection.dataFlow.instructions.*; import com.intellij.codeInspection.dataFlow.value.DfaConstValue; import com.intellij.codeInspection.dataFlow.value.DfaValue; import com.intellij.codeInspection.nullable.NullableStuffInspectionBase; +import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; @@ -43,14 +44,12 @@ import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.extractMethod.ExtractMethodUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.ArrayUtilRt; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.SmartList; +import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jdom.Element; @@ -157,11 +156,42 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { while (element != null) { element = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClassInitializer.class); if (element instanceof PsiClassInitializer) return true; - if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()) return true; + if (element instanceof PsiMethod) { + if (((PsiMethod)element).isConstructor()) return true; + + final PsiClass containingClass = ((PsiMethod)element).getContainingClass(); + return !InheritanceUtil.processSupers(containingClass, true, new Processor() { + @Override + public boolean process(PsiClass psiClass) { + return !canCallMethodsInConstructors(psiClass, psiClass != containingClass); + } + }); + + } } return false; } + private static boolean canCallMethodsInConstructors(PsiClass aClass, boolean virtual) { + for (PsiMethod constructor : aClass.getConstructors()) { + if (!constructor.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return true; + + PsiCodeBlock body = constructor.getBody(); + if (body == null) continue; + + for (PsiMethodCallExpression call : SyntaxTraverser.psiTraverser().withRoot(body).filter(PsiMethodCallExpression.class)) { + PsiReferenceExpression methodExpression = call.getMethodExpression(); + if (methodExpression instanceof PsiThisExpression || methodExpression instanceof PsiSuperExpression) continue; + if (!virtual) return true; + + PsiMethod target = call.resolveMethod(); + if (target != null && PsiUtil.canBeOverriden(target)) return true; + } + } + + return false; + } + private void analyzeDfaWithNestedClosures(PsiElement scope, ProblemsHolder holder, StandardDataFlowRunner dfaRunner, diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java index 987054ad360a..4c78c4807069 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java @@ -70,6 +70,10 @@ public class DfaPsiUtil { return Nullness.UNKNOWN; } + if (owner instanceof PsiEnumConstant) { + return Nullness.NOT_NULL; + } + if (resultType != null) { NullableNotNullManager nnn = NullableNotNullManager.getInstance(owner.getProject()); for (PsiAnnotation annotation : resultType.getAnnotations()) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java index fd45fdfc8b5a..f8f6d16aed13 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java @@ -24,7 +24,6 @@ */ package com.intellij.codeInspection.dataFlow.value; -import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.codeInspection.dataFlow.DfaPsiUtil; import com.intellij.codeInspection.dataFlow.Nullness; @@ -195,19 +194,11 @@ public class DfaVariableValue extends DfaValue { boolean hasUnknowns = false; for (PsiExpression expression : initializers) { - if (!(expression instanceof PsiReferenceExpression)) { - hasUnknowns = true; - continue; - } - PsiElement target = ((PsiReferenceExpression)expression).resolve(); - if (!(target instanceof PsiParameter)) { - hasUnknowns = true; - continue; - } - if (NullableNotNullManager.isNullable((PsiParameter)target)) { + Nullness nullness = getFieldInitializerNullness(expression); + if (nullness == Nullness.NULLABLE) { return Nullness.NULLABLE; } - if (!NullableNotNullManager.isNotNull((PsiParameter)target)) { + if (nullness == Nullness.UNKNOWN) { hasUnknowns = true; } } @@ -225,6 +216,20 @@ public class DfaVariableValue extends DfaValue { return defaultNullability; } + private static Nullness getFieldInitializerNullness(@NotNull PsiExpression expression) { + if (expression.textMatches(PsiKeyword.NULL)) return Nullness.NULLABLE; + if (expression instanceof PsiNewExpression || expression instanceof PsiLiteralExpression || expression instanceof PsiPolyadicExpression) return Nullness.NOT_NULL; + if (expression instanceof PsiReferenceExpression) { + PsiElement target = ((PsiReferenceExpression)expression).resolve(); + return DfaPsiUtil.getElementNullability(null, (PsiModifierListOwner)target); + } + if (expression instanceof PsiMethodCallExpression) { + PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod(); + return method != null ? DfaPsiUtil.getElementNullability(null, method) : Nullness.UNKNOWN; + } + return Nullness.UNKNOWN; + } + public boolean isFlushableByCalls() { if (myVariable instanceof PsiLocalVariable || myVariable instanceof PsiParameter) return false; if (myVariable instanceof PsiVariable && myVariable.hasModifierProperty(PsiModifier.FINAL)) { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/FinalFieldNotDuringInitialization.java b/java/java-tests/testData/inspection/dataFlow/fixture/FinalFieldNotDuringInitialization.java new file mode 100644 index 000000000000..91b8d23ee0b2 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/FinalFieldNotDuringInitialization.java @@ -0,0 +1,79 @@ +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +class Super { + public Super() { + this(2); + } + + public Super(int a) { + staticMethod(); + } + + static void staticMethod() {} + +} + +class Test { + @Nullable + private static Object getNull() { + return null; + } + + private static final Object CONST = getNull(); + + public static void test() { + System.out.println(CONST.toString()); + } +} + +class Test2 extends Super { + @NotNull + private static Object getNotNull() { + return new Object(); + } + + private static final Object CONST = getNotNull(); + + public static void test() { + System.out.println(CONST.toString()); + } +} + +class Test3 { + + private static final Object CONST = ""; + + public static void test() { + System.out.println(CONST.toString()); + } +} + +class Test4 { + + public enum Day { + SUNDAY, MONDAY, TUESDAY, WEDNESDAY, + THURSDAY, FRIDAY, SATURDAY + } + + private static final Day CONST = Day.FRIDAY; + + public static void test() { + System.out.println(CONST.toString()); + } +} + +class Test5 { + private final String something = new String("something"); + private final String somethingElse = "somethingElse"; + + public Integer someLength() { + //May produce nullpointer warning + return something.length(); + } + + public Integer someElseLength() { + //No warning + return somethingElse.length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 362c04ee0766..3c5913134981 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -196,6 +196,16 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { public void testFinalFieldDuringInitialization() { doTest(); } public void testFinalFieldDuringSuperInitialization() { doTest(); } public void testFinalFieldInConstructorAnonymous() { doTest(); } + + public void testFinalFieldNotDuringInitialization() { + final DataFlowInspection inspection = new DataFlowInspection(); + inspection.TREAT_UNKNOWN_MEMBERS_AS_NULLABLE = true; + inspection.REPORT_CONSTANT_REFERENCE_VALUES = false; + myFixture.enableInspections(inspection); + myFixture.testHighlighting(true, false, true, getTestName(false) + ".java"); + } + + public void _testSymmetricUncheckedCast() { doTest(); } // https://youtrack.jetbrains.com/issue/IDEABKL-6871 public void testNullCheckDoesntAffectUncheckedCast() { doTest(); } public void testThrowNull() { doTest(); }