mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
[java-inspections] Support simple chaining in NotNullFieldNotInitializedInspection
Fixes IDEA-145194 Do not report 'Not-null fields must be initialized' when field is initialized indirectly GitOrigin-RevId: c3bae51deaedbbbb4d035e5d7d3b55f2ca9b8c03
This commit is contained in:
committed by
intellij-monorepo-bot
parent
41c0081fd8
commit
c22a9186f9
@@ -19,6 +19,9 @@ import com.intellij.codeInspection.util.InspectionMessage;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.util.text.HtmlChunk;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.JavaPsiConstructorUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -28,7 +31,9 @@ import static com.intellij.codeInspection.options.OptPane.checkbox;
|
||||
import static com.intellij.codeInspection.options.OptPane.pane;
|
||||
|
||||
public class NotNullFieldNotInitializedInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
@Language("jvm-field-name")
|
||||
private static final String IGNORE_IMPLICITLY_WRITTEN_FIELDS_NAME = "IGNORE_IMPLICITLY_WRITTEN_FIELDS";
|
||||
@Language("jvm-field-name")
|
||||
private static final String IGNORE_FIELDS_WRITTEN_IN_SETUP_NAME = "IGNORE_FIELDS_WRITTEN_IN_SETUP";
|
||||
public boolean IGNORE_IMPLICITLY_WRITTEN_FIELDS = true;
|
||||
public boolean IGNORE_FIELDS_WRITTEN_IN_SETUP = true;
|
||||
@@ -49,9 +54,10 @@ public class NotNullFieldNotInitializedInspection extends AbstractBaseJavaLocalI
|
||||
public void visitField(@NotNull PsiField field) {
|
||||
NullableNotNullManager manager = NullableNotNullManager.getInstance(holder.getProject());
|
||||
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(field);
|
||||
if (info == null ||
|
||||
info.getNullability() != Nullability.NOT_NULL ||
|
||||
HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field)) {
|
||||
if (info == null || info.getNullability() != Nullability.NOT_NULL) return;
|
||||
|
||||
if (HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field) ||
|
||||
isWrittenIndirectly(field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -95,6 +101,39 @@ public class NotNullFieldNotInitializedInspection extends AbstractBaseJavaLocalI
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isWrittenIndirectly(@NotNull PsiField field) {
|
||||
PsiClass fieldClass = field.getContainingClass();
|
||||
if (fieldClass == null) return false;
|
||||
PsiMethod[] constructors = fieldClass.getConstructors();
|
||||
if (constructors.length == 0) return false;
|
||||
return ContainerUtil.all(constructors, constructor ->
|
||||
JavaPsiConstructorUtil.isChainedConstructorCall(JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor)) ||
|
||||
isWrittenIndirectlyIn(field, constructor));
|
||||
}
|
||||
|
||||
private static boolean isWrittenIndirectlyIn(@NotNull PsiField field, @NotNull PsiMethod constructor) {
|
||||
PsiCodeBlock body = constructor.getBody();
|
||||
if (body == null) return false;
|
||||
PsiStatement[] statements = body.getStatements();
|
||||
for (PsiStatement statement : statements) {
|
||||
if (statement instanceof PsiExpressionStatement expressionStatement &&
|
||||
expressionStatement.getExpression() instanceof PsiMethodCallExpression call) {
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null || qualifier instanceof PsiThisExpression thisExpression && thisExpression.getQualifier() == null) {
|
||||
PsiMethod target = call.resolveMethod();
|
||||
if (target != null && !target.hasModifierProperty(PsiModifier.STATIC) &&
|
||||
target.getContainingClass() == constructor.getContainingClass() && !target.isConstructor()) {
|
||||
PsiCodeBlock targetBody = target.getBody();
|
||||
if (targetBody != null && HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, targetBody)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void reportProblem(@NotNull ProblemsHolder holder,
|
||||
PsiElement anchor,
|
||||
@InspectionMessage String message,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
class Test {
|
||||
@NotNull String str1;
|
||||
<warning descr="Not-null fields must be initialized">@NotNull</warning> String str2;
|
||||
@NotNull String str3;
|
||||
<warning descr="Not-null fields must be initialized">@NotNull</warning> String str4;
|
||||
|
||||
Test() {
|
||||
init();
|
||||
this.initStr3();
|
||||
new Test().initStr4();
|
||||
}
|
||||
|
||||
Test(String s) {
|
||||
this();
|
||||
str2 = s;
|
||||
}
|
||||
|
||||
void initStr3() {
|
||||
str3 = "";
|
||||
}
|
||||
|
||||
void initStr4() {
|
||||
str4 = "";
|
||||
}
|
||||
|
||||
void init() {
|
||||
if (Math.random() > 0.5) {
|
||||
str1 = "";
|
||||
str2 = "";
|
||||
} else {
|
||||
str1 = "123";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NotNullFieldNotInitializedInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
public void testNotNullFieldNotInitialized() { doTest(); }
|
||||
public void testNotNullFieldInitializedIndirectly() { doTest(); }
|
||||
public void testNotNullFieldInitializedInLambda() { doTest(); }
|
||||
public void testNotNullFieldNotInitializedInOneConstructor() { doTest(); }
|
||||
public void testTypeUseNotNullField() {
|
||||
|
||||
Reference in New Issue
Block a user