From 646c89b3d48a1c9ea8f04483d86e7a4a919276fd Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 17 Aug 2017 08:02:07 +0200 Subject: [PATCH] dfa: support fields initialized in class initializers --- .../dataFlow/value/DfaExpressionFactory.java | 34 +++++++++++++++++-- .../FieldUsedBeforeInitialization.java | 23 ++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java index bc357b44028c..5cfcea47be2c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInspection.dataFlow.DfaPsiUtil; import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.codeInspection.dataFlow.SpecialField; +import com.intellij.lang.jvm.JvmModifier; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; @@ -27,6 +28,8 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.impl.light.LightVariableBuilder; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -167,10 +170,35 @@ public class DfaExpressionFactory { PsiField placeField = PsiTreeUtil.getParentOfType(ref, PsiField.class, true, PsiClass.class, PsiLambdaExpression.class); if (placeField == null) return false; + PsiClass placeClass = placeField.getContainingClass(); PsiElement target = ref.resolve(); - return target instanceof PsiField && - placeField.getContainingClass() == ((PsiField)target).getContainingClass() && - ((PsiField)target).getInitializer() == null; + if (target instanceof PsiField) { + PsiField targetField = (PsiField)target; + if (placeClass != null && placeClass == targetField.getContainingClass() && targetField.getInitializer() == null) { + if (!targetField.hasModifier(JvmModifier.FINAL)) return true; + + if (!placeField.hasModifier(JvmModifier.STATIC) && targetField.hasModifier(JvmModifier.STATIC)) { + return false; + } + + return !isWrittenInClassInitializer(placeClass, targetField, ref.getTextRange().getStartOffset()); + } + } + return false; + } + + private static boolean isWrittenInClassInitializer(PsiClass placeClass, PsiField field, int beforeOffset) { + for (PsiReference reference : ReferencesSearch.search(field, new LocalSearchScope(placeClass)).findAll()) { + if (reference instanceof PsiReferenceExpression) { + PsiReferenceExpression expr = (PsiReferenceExpression)reference; + if (PsiUtil.isAccessedForWriting(expr) && + PsiTreeUtil.getParentOfType(expr, PsiClassInitializer.class) != null && + (expr).getTextRange().getStartOffset() < beforeOffset) { + return true; + } + } + } + return false; } @Nullable diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/FieldUsedBeforeInitialization.java b/java/java-tests/testData/inspection/dataFlow/fixture/FieldUsedBeforeInitialization.java index dff3eda945b1..346309bd016f 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/FieldUsedBeforeInitialization.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/FieldUsedBeforeInitialization.java @@ -11,4 +11,25 @@ class Foo { Foo() { field = "x"; } -} \ No newline at end of file +} + +class StaticFieldTest { + static final String FOO; + static { FOO = ""; } + + static final String bar = FOO.trim(); +} + +class InstanceFieldTest { + final String FOO; + { FOO = ""; } + + String bar = FOO.trim(); +} + +class MixedFieldTest { + String bar = FOO.trim(); + + static final String FOO; + static { FOO = ""; } +}