IDEA-218253 Extract "Not-null field is not initialized" to separate inspection and improve it

GitOrigin-RevId: 0f709e869db16fe75318864e45bda2bed6b9e4f6
This commit is contained in:
Tagir Valeev
2019-07-15 11:05:15 +03:00
committed by intellij-monorepo-bot
parent 6f6ef13cd3
commit 6ebd80cf9f
28 changed files with 318 additions and 131 deletions
@@ -3,7 +3,6 @@ package com.intellij.codeInspection.nullable;
import com.intellij.codeInsight.*;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
import com.intellij.codeInspection.*;
@@ -50,7 +49,6 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL = true;
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_GETTER = true;
@SuppressWarnings({"WeakerAccess"}) public boolean IGNORE_EXTERNAL_SUPER_NOTNULL;
@SuppressWarnings({"WeakerAccess"}) public boolean REQUIRE_NOTNULL_FIELDS_INITIALIZED = true;
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED;
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_SETTER_PARAMETER = true;
@Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = true; // remains for test
@@ -119,10 +117,6 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
checkConstructorParameters(field, annotated, manager, anno, annoToRemove, holder);
}
if (REQUIRE_NOTNULL_FIELDS_INITIALIZED && !annotated.isDeclaredNullable) {
checkNotNullFieldsInitialized(field, manager, holder);
}
}
@Override
@@ -283,7 +277,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
private void checkCollectionNullityOnAssignment(@NotNull PsiElement errorElement,
@Nullable PsiType expectedType,
@Nullable PsiType assignedType) {
if (isNullableNotNullCollectionConflict(errorElement, expectedType, assignedType, new HashSet<>())) {
if (isNullableNotNullCollectionConflict(expectedType, assignedType, new HashSet<>())) {
holder.registerProblem(errorElement,
"Assigning a collection of nullable elements into a collection of non-null elements",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
@@ -291,8 +285,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
}
private boolean isNullableNotNullCollectionConflict(PsiElement place,
@Nullable PsiType expectedType,
private boolean isNullableNotNullCollectionConflict(@Nullable PsiType expectedType,
@Nullable PsiType assignedType,
@NotNull Set<? super Couple<PsiType>> visited) {
if (!visited.add(Couple.of(expectedType, assignedType))) return false;
@@ -307,7 +300,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
PsiType expectedArg = PsiUtil.substituteTypeParameter(expectedType, CommonClassNames.JAVA_UTIL_MAP, i, false);
PsiType assignedArg = PsiUtil.substituteTypeParameter(assignedType, CommonClassNames.JAVA_UTIL_MAP, i, false);
if (isNullityConflict(expectedArg, assignedArg) ||
expectedArg != null && assignedArg != null && isNullableNotNullCollectionConflict(place, expectedArg, assignedArg, visited)) {
expectedArg != null && assignedArg != null && isNullableNotNullCollectionConflict(expectedArg, assignedArg, visited)) {
return true;
}
}
@@ -502,21 +495,6 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
LOG.assertTrue(parameter.isPhysical(), setter.getText());
}
private static void checkNotNullFieldsInitialized(PsiField field, NullableNotNullManager manager, @NotNull ProblemsHolder holder) {
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(field);
if (info == null ||
info.getNullability() != Nullability.NOT_NULL ||
HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field)) {
return;
}
boolean byDefault = info.isContainer();
PsiAnnotation annotation = info.getAnnotation();
PsiJavaCodeReferenceElement name = annotation.getNameReferenceElement();
holder.registerProblem(annotation.isPhysical() && !byDefault ? annotation : field.getNameIdentifier(),
(byDefault && name != null ? "@" + name.getReferenceName() : "Not-null") + " fields must be initialized");
}
private void checkConstructorParameters(PsiField field,
Annotated annotated,
NullableNotNullManager manager,
@@ -276,7 +276,7 @@ convert.to.string.text=Convert to string literal
convert.to.raw.string.text=Convert to raw string literal
initialize.final.field.in.constructor.name=Initialize in constructor
initialize.final.field.in.constructor.choose.dialog.title=Choose constructors to add initialization to
initialize.final.field.in.constructor.choose.dialog.title=Choose Constructors to Add Initialization to
remove.redundant.arguments.text=Remove redundant arguments to call ''{0}''
remove.redundant.arguments.family=Remove redundant arguments
+5 -2
View File
@@ -1506,9 +1506,12 @@
<localInspection groupPath="Java" language="JAVA" shortName="WrongPackageStatement" bundle="messages.InspectionsBundle" key="wrong.package.statement" groupKey="group.names.probable.bugs"
enabledByDefault="true" level="ERROR"
implementationClass="com.intellij.codeInspection.wrongPackageStatement.WrongPackageStatementInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="NullableProblems" bundle="messages.InspectionsBundle" key="inspection.nullable.problems.display.name"
groupKey="group.names.probable.bugs" enabledByDefault="true" level="WARNING"
<localInspection groupPath="Java,Probable bugs" language="JAVA" shortName="NullableProblems" bundle="messages.InspectionsBundle" key="inspection.nullable.problems.display.name"
groupKey="group.names.nullability.problems" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.nullable.NullableStuffInspection" />
<localInspection groupPath="Java,Probable bugs" language="JAVA" shortName="NotNullFieldNotInitialized" bundle="messages.InspectionsBundle" key="inspection.notnull.field.not.initialized.display.name"
groupKey="group.names.nullability.problems" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.nullable.NotNullFieldNotInitializedInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="ParameterCanBeLocal" key="inspection.parameter.can.be.local.display.name"
groupKey="group.names.class.structure" enabledByDefault="true" level="WARNING" bundle="messages.InspectionsBundle"
implementationClass="com.intellij.codeInspection.varScopeCanBeNarrowed.ParameterCanBeLocalInspection" />
@@ -2,12 +2,14 @@
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInsight.lookup.ExpressionLookupItem;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.template.*;
import com.intellij.codeInsight.template.PsiElementResult;
import com.intellij.codeInsight.template.TemplateBuilderFactory;
import com.intellij.codeInsight.template.TemplateBuilderImpl;
import com.intellij.codeInsight.template.impl.ConstantNode;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
@@ -17,7 +19,6 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
@@ -28,18 +29,18 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class AddVariableInitializerFix implements IntentionAction {
public class AddVariableInitializerFix extends LocalQuickFixAndIntentionActionOnPsiElement {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.AddReturnFix");
private final PsiVariable myVariable;
public AddVariableInitializerFix(@NotNull PsiVariable variable) {
myVariable = variable;
super(variable);
}
@Override
@NotNull
public String getText() {
return CodeInsightBundle.message("quickfix.add.variable.text", myVariable.getName());
PsiVariable variable = ObjectUtils.tryCast(myStartElement.getElement(), PsiVariable.class);
return variable == null ? getFamilyName() : CodeInsightBundle.message("quickfix.add.variable.text", variable.getName());
}
@Override
@@ -49,29 +50,40 @@ public class AddVariableInitializerFix implements IntentionAction {
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return myVariable.isValid() &&
BaseIntentionAction.canModify(myVariable) &&
!myVariable.hasInitializer() &&
!(myVariable instanceof PsiParameter);
public boolean isAvailable(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
PsiVariable variable = ObjectUtils.tryCast(startElement, PsiVariable.class);
return variable != null && variable.isValid() &&
BaseIntentionAction.canModify(variable) &&
!variable.hasInitializer() &&
!(variable instanceof PsiParameter);
}
@NotNull
@Override
public PsiElement getElementToMakeWritable(@NotNull PsiFile file) {
return myVariable;
return file;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
final LookupElement[] suggestedInitializers = suggestInitializer(myVariable);
public void invoke(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
PsiVariable variable = ObjectUtils.tryCast(startElement, PsiVariable.class);
if (variable == null) return;
final LookupElement[] suggestedInitializers = suggestInitializer(variable);
LOG.assertTrue(suggestedInitializers.length > 0);
LOG.assertTrue(suggestedInitializers[0] instanceof ExpressionLookupItem);
final PsiExpression initializer = (PsiExpression)suggestedInitializers[0].getObject();
myVariable.setInitializer(initializer);
variable.setInitializer(initializer);
Document document = Objects.requireNonNull(PsiDocumentManager.getInstance(project).getDocument(file));
PsiDocumentManager.getInstance(initializer.getProject()).doPostponedOperationsAndUnblockDocument(document);
runAssignmentTemplate(Collections.singletonList(myVariable.getInitializer()), suggestedInitializers, editor);
runAssignmentTemplate(Collections.singletonList(variable.getInitializer()), suggestedInitializers, editor);
}
static void runAssignmentTemplate(@NotNull final List<? extends PsiExpression> initializers,
@@ -81,6 +93,7 @@ public class AddVariableInitializerFix implements IntentionAction {
LOG.assertTrue(!initializers.isEmpty());
final PsiExpression initializer = ObjectUtils.notNull(ContainerUtil.getFirstItem(initializers));
PsiElement context = initializers.size() == 1 ? initializer : PsiTreeUtil.findCommonParent(initializers);
if (context == null) return;
final TemplateBuilderImpl builder = (TemplateBuilderImpl)TemplateBuilderFactory.getInstance().createTemplateBuilder(context);
for (PsiExpression e : initializers) {
builder.replaceElement(e, new ConstantNode(new PsiElementResult(suggestedInitializers[0].getPsiElement())).withLookupItems(suggestedInitializers));
@@ -219,7 +219,7 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction {
};
}
private static List<PsiMethod> filterConstructorsIfFieldAlreadyAssigned(PsiMethod[] constructors, PsiField field) {
static List<PsiMethod> filterConstructorsIfFieldAlreadyAssigned(PsiMethod[] constructors, PsiField field) {
final List<PsiMethod> result = new ArrayList<>(Arrays.asList(constructors));
for (PsiReference reference : ReferencesSearch.search(field, new LocalSearchScope(constructors))) {
final PsiElement element = reference.getElement();
@@ -296,10 +296,7 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction {
SuggestedNameInfo nameInfo = styleManager.suggestVariableName(VariableKind.PARAMETER, name, null, variable.getType());
String newName = nameInfo.names[0];
int n = 1;
while (true) {
if (isUnique(parameters, newName, usedNames)) {
break;
}
while (!isUnique(parameters, newName, usedNames)) {
newName = n < nameInfo.names.length &&
!JavaCodeStyleSettings.getInstance(variable.getContainingFile()).PREFER_LONGER_NAMES
? nameInfo.names[n++] : nameInfo.names[0] + n++;
@@ -327,8 +324,9 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction {
if (JavaHighlightUtil.getChainedConstructors(constructor).isEmpty()) {
final SmartPointerManager manager = SmartPointerManager.getInstance(project);
boolean created = false;
for (PsiField field : fields.keySet()) {
final String defaultParamName = fields.get(field);
for (Map.Entry<PsiField, String> entry : fields.entrySet()) {
PsiField field = entry.getKey();
final String defaultParamName = entry.getValue();
PsiParameter parameter = findParamByName(defaultParamName, field.getType(), newParameters, parameterInfos);
if (parameter == null) {
continue;
@@ -1,12 +1,11 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.generation.PsiMethodMember;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.ide.util.MemberChooser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -15,23 +14,17 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class InitializeFinalFieldInConstructorFix implements IntentionAction {
public class InitializeFinalFieldInConstructorFix extends LocalQuickFixAndIntentionActionOnPsiElement {
private static final Logger LOG = Logger.getInstance(InitializeFinalFieldInConstructorFix.class);
private final PsiField myField;
public InitializeFinalFieldInConstructorFix(@NotNull PsiField field) {
myField = field;
super(field);
}
@NotNull
@@ -47,35 +40,45 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction {
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (!myField.isValid() || myField.hasModifierProperty(PsiModifier.STATIC) || myField.hasInitializer()) {
public boolean isAvailable(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
PsiField field = ObjectUtils.tryCast(startElement, PsiField.class);
if (field == null) return false;
if (!field.isValid() || field.hasModifierProperty(PsiModifier.STATIC) || field.hasInitializer()) {
return false;
}
final PsiClass containingClass = myField.getContainingClass();
final PsiClass containingClass = field.getContainingClass();
if (containingClass == null || containingClass.getName() == null){
return false;
}
final PsiManager manager = myField.getManager();
return manager != null && BaseIntentionAction.canModify(myField);
final PsiManager manager = field.getManager();
return manager != null && BaseIntentionAction.canModify(field);
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
final PsiClass myClass = myField.getContainingClass();
if (myClass == null) {
return;
}
public void invoke(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
PsiField field = ObjectUtils.tryCast(startElement, PsiField.class);
if (field == null) return;
final PsiClass myClass = field.getContainingClass();
if (myClass == null) return;
if (myClass.getConstructors().length == 0) {
createDefaultConstructor(myClass, project, editor, file);
}
final List<PsiMethod> constructors = choose(filterIfFieldAlreadyAssigned(myField, myClass.getConstructors()), project);
PsiMethod[] ctors = CreateConstructorParameterFromFieldFix.filterConstructorsIfFieldAlreadyAssigned(myClass.getConstructors(), field)
.toArray(PsiMethod.EMPTY_ARRAY);
final List<PsiMethod> constructors = choose(ctors, project);
ApplicationManager.getApplication().runWriteAction(() -> addFieldInitialization(constructors, myField, project, editor));
ApplicationManager.getApplication().runWriteAction(() -> addFieldInitialization(constructors, field, project, editor));
}
private static void addFieldInitialization(@NotNull List<? extends PsiMethod> constructors,
@@ -180,16 +183,10 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction {
ApplicationManager.getApplication().runWriteAction(() -> defaultConstructorFix.invoke(project, editor, file));
}
@NotNull
private static PsiMethod[] filterIfFieldAlreadyAssigned(@NotNull PsiField field, @NotNull PsiMethod[] ctors) {
final List<PsiMethod> result = new ArrayList<>(Arrays.asList(ctors));
for (PsiReference reference : ReferencesSearch.search(field, new LocalSearchScope(ctors))) {
final PsiElement element = reference.getElement();
if (element instanceof PsiReferenceExpression && PsiUtil.isOnAssignmentLeftHand((PsiExpression)element)) {
result.remove(PsiTreeUtil.getParentOfType(element, PsiMethod.class));
}
}
return result.toArray(PsiMethod.EMPTY_ARRAY);
@Nullable
@Override
public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) {
return currentFile;
}
@Override
@@ -0,0 +1,78 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.nullable;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullabilityAnnotationInfo;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.daemon.impl.UnusedSymbolUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.AddVariableInitializerFix;
import com.intellij.codeInsight.daemon.impl.quickfix.DeleteElementFix;
import com.intellij.codeInsight.daemon.impl.quickfix.InitializeFinalFieldInConstructorFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class NotNullFieldNotInitializedInspection extends AbstractBaseJavaLocalInspectionTool {
private static final String IGNORE_IMPLICITLY_WRITTEN_FIELDS_NAME = "IGNORE_IMPLICITLY_WRITTEN_FIELDS";
public boolean IGNORE_IMPLICITLY_WRITTEN_FIELDS = true;
@Nullable
@Override
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel(InspectionsBundle.message("inspection.notnull.field.not.initialized.option"),
this, IGNORE_IMPLICITLY_WRITTEN_FIELDS_NAME);
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitField(PsiField field) {
NullableNotNullManager manager = NullableNotNullManager.getInstance(holder.getProject());
NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(field);
if (info == null ||
info.getNullability() != Nullability.NOT_NULL ||
HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field)) {
return;
}
boolean implicitWrite = UnusedSymbolUtil.isImplicitWrite(field);
if (IGNORE_IMPLICITLY_WRITTEN_FIELDS && implicitWrite) {
return;
}
boolean byDefault = info.isContainer();
PsiAnnotation annotation = info.getAnnotation();
PsiJavaCodeReferenceElement name = annotation.getNameReferenceElement();
boolean ownAnnotation = annotation.isPhysical() && !byDefault;
PsiElement anchor = ownAnnotation ? annotation : field.getNameIdentifier();
String message = (byDefault && name != null ? "@" + name.getReferenceName() : "Not-null") + " fields must be initialized";
List<LocalQuickFix> fixes = new ArrayList<>();
if (implicitWrite && isOnTheFly) {
fixes.add(new SetInspectionOptionFix(NotNullFieldNotInitializedInspection.this,
IGNORE_IMPLICITLY_WRITTEN_FIELDS_NAME,
InspectionsBundle.message("inspection.notnull.field.not.initialized.option"), true));
}
if (ownAnnotation) {
fixes.add(new DeleteElementFix(annotation, "Remove not-null annotation"));
}
if (isOnTheFly) {
fixes.add(new InitializeFinalFieldInConstructorFix(field));
fixes.add(new AddVariableInitializerFix(field));
}
holder.registerProblem(anchor, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
};
}
}
@@ -60,7 +60,6 @@ public class NullableStuffInspection extends NullableStuffInspectionBase {
private JButton myConfigureAnnotationsButton;
private JCheckBox myIgnoreExternalSuperNotNull;
private JCheckBox myNNParameterOverridesNA;
private JCheckBox myRequireNNFieldsInitialized;
private JBCheckBox myReportNullLiteralsPassedNotNullParameter;
private OptionsPanel() {
@@ -78,7 +77,6 @@ public class NullableStuffInspection extends NullableStuffInspectionBase {
myNNParameterOverridesNA.addActionListener(actionListener);
myReportNotAnnotatedGetter.addActionListener(actionListener);
myIgnoreExternalSuperNotNull.addActionListener(actionListener);
myRequireNNFieldsInitialized.addActionListener(actionListener);
myReportNullLiteralsPassedNotNullParameter.addActionListener(actionListener);
myConfigureAnnotationsButton.addActionListener(NullableNotNullDialog.createActionListener(this));
reset();
@@ -90,7 +88,6 @@ public class NullableStuffInspection extends NullableStuffInspectionBase {
myReportNotAnnotatedGetter.setSelected(REPORT_NOT_ANNOTATED_GETTER);
myIgnoreExternalSuperNotNull.setSelected(IGNORE_EXTERNAL_SUPER_NOTNULL);
myNNParameterOverridesNA.setSelected(REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED);
myRequireNNFieldsInitialized.setSelected(REQUIRE_NOTNULL_FIELDS_INITIALIZED);
myReportNullLiteralsPassedNotNullParameter.setSelected(REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER);
myIgnoreExternalSuperNotNull.setEnabled(myNAMethodOverridesNN.isSelected());
@@ -102,7 +99,6 @@ public class NullableStuffInspection extends NullableStuffInspectionBase {
REPORT_NOT_ANNOTATED_GETTER = myReportNotAnnotatedGetter.isSelected();
IGNORE_EXTERNAL_SUPER_NOTNULL = myIgnoreExternalSuperNotNull.isSelected();
REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED = myNNParameterOverridesNA.isSelected();
REQUIRE_NOTNULL_FIELDS_INITIALIZED = myRequireNNFieldsInitialized.isSelected();
REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER = myReportNullLiteralsPassedNotNullParameter.isSelected();
REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.codeInspection.nullable.NullableStuffInspection.OptionsPanel">
<grid id="cc1c9" binding="myPanel" layout-manager="GridLayoutManager" row-count="9" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="cc1c9" binding="myPanel" layout-manager="GridLayoutManager" row-count="8" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="69" y="57" width="634" height="277"/>
@@ -10,7 +10,7 @@
<children>
<vspacer id="c3eef">
<constraints>
<grid row="8" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="2f304" class="javax.swing.JCheckBox" binding="myReportNotAnnotatedGetter">
@@ -39,7 +39,7 @@
</component>
<component id="ef852" class="javax.swing.JButton" binding="myConfigureAnnotationsButton" default-binding="true">
<constraints>
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/InspectionsBundle" key="configure.annotations.option"/>
@@ -61,17 +61,9 @@
<text value="Report @NotNull &amp;parameters overriding non-annotated"/>
</properties>
</component>
<component id="f6e46" class="javax.swing.JCheckBox" binding="myRequireNNFieldsInitialized">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Require @NotNull fields to be initialized explicitly"/>
</properties>
</component>
<component id="9b307" class="com.intellij.ui.components.JBCheckBox" binding="myReportNullLiteralsPassedNotNullParameter" default-binding="true">
<constraints>
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Report @NotNull parameters with null-literal argument usages"/>
@@ -0,0 +1,23 @@
<html>
<body>
Reports fields annotated as not-null which are not initialized in constructor. Example:
<pre>public class MyClass {
private @NotNull String value;
public void setValue(@NotNull String value) {
this.value = value;
}
public @NotNull String getValue() {
return value;
}
}</pre>
<p>
Such fields may violate the not-null constraint. E.g. in the example above while <code>setValue</code> parameter is annotated as not-null
<code>getValue</code> may return null if setter was not called.
</p>
<!-- tooltip end -->
<p>Use the checkbox below to control whether warning should be issued if field could be initialized implicitly
(e.g. via dependency injection).</p>
</body>
</html>
@@ -0,0 +1,6 @@
import org.jetbrains.annotations.*;
class Test {
<warning descr="Not-null fields must be initialized">@NotNull</warning> String explicit;
@NotNull String implicit;
}
@@ -0,0 +1,6 @@
// "Initialize variable 'x'" "true"
import org.jetbrains.annotations.NotNull;
class X {
@NotNull String x = null;
}
@@ -0,0 +1,10 @@
// "Initialize in constructor" "true"
import org.jetbrains.annotations.NotNull;
class X {
@NotNull String x;
X() {
x = null;
}
}
@@ -0,0 +1,6 @@
// "Remove not-null annotation" "true"
import org.jetbrains.annotations.NotNull;
class X {
String x;
}
@@ -0,0 +1,6 @@
// "Initialize variable 'x'" "true"
import org.jetbrains.annotations.NotNull;
class X {
@<caret>NotNull String x;
}
@@ -0,0 +1,6 @@
// "Initialize in constructor" "true"
import org.jetbrains.annotations.NotNull;
class X {
@<caret>NotNull String x;
}
@@ -0,0 +1,6 @@
// "Remove not-null annotation" "true"
import org.jetbrains.annotations.NotNull;
class X {
@<caret>NotNull String x;
}
@@ -1,9 +0,0 @@
import org.jetbrains.annotations.*;
class Test {
@NotNull Object member;
private void accessMember() {
member = new Object();
}
}
@@ -0,0 +1,22 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInspection;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.nullable.NotNullFieldNotInitializedInspection;
import org.jetbrains.annotations.NotNull;
public class NotNullFieldNotInitializedFixesTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new NotNullFieldNotInitializedInspection()};
}
@Override
protected String getBasePath() {
return "/inspection/notNullField/quickFix";
}
}
@@ -0,0 +1,64 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInspection.nullable.NotNullFieldNotInitializedInspection;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class NotNullFieldNotInitializedInspectionTest extends LightJavaCodeInsightFixtureTestCase {
public void testNotNullFieldNotInitialized() { doTest(); }
public void testNotNullFieldInitializedInLambda() { doTest(); }
public void testNotNullFieldNotInitializedInOneConstructor() { doTest(); }
public void testTypeUseNotNullField() {
DataFlowInspection8Test.setupTypeUseAnnotations("typeUse", myFixture);
doTest();
}
public void testNotNullByDefaultFieldNotInitialized() {
DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture);
doTest();
}
public void testImplicit() {
PlatformTestUtil.registerExtension(Extensions.getRootArea(), ImplicitUsageProvider.EP_NAME, new ImplicitUsageProvider() {
@Override
public boolean isImplicitUsage(@NotNull PsiElement element) {
return false;
}
@Override
public boolean isImplicitRead(@NotNull PsiElement element) {
return false;
}
@Override
public boolean isImplicitWrite(@NotNull PsiElement element) {
return element instanceof PsiField && "implicit".equals(((PsiField)element).getName());
}
}, getTestRootDisposable());
doTest();
}
private void doTest() {
myFixture.enableInspections(new NotNullFieldNotInitializedInspection());
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return JAVA_8_ANNOTATED;
}
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/inspection/notNullField/";
}
}
@@ -73,19 +73,6 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest
public void testNotNullFieldNullableParam() { doTest(); }
public void testNotNullCustomException() { doTest(); }
public void testNotNullFieldNotInitialized() { doTest(); }
public void testNotNullFieldInitializedInLambda() { doTest(); }
public void testNotNullFieldNotInitializedInOneConstructor() { doTest(); }
public void testNotNullFieldNotInitializedSetting() {
myInspection.REQUIRE_NOTNULL_FIELDS_INITIALIZED = false;
doTest();
}
public void testNotNullByDefaultFieldNotInitialized() {
DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture);
doTest();
}
public void testNotNullAnnotationChecksInChildClassMethods() { doTest(); }
public void testGetterSetterProblems() { doTest(); }
@@ -275,11 +262,6 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest
doTest();
}
public void testTypeUseNotNullField() {
DataFlowInspection8Test.setupTypeUseAnnotations("typeUse", myFixture);
doTest();
}
public void testTypeUseNotNullOverriding() {
myInspection.REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED = true;
DataFlowInspection8Test.setupTypeUseAnnotations("typeUse", myFixture);
@@ -155,6 +155,9 @@ inspection.return.separated.from.computation.descriptor=Return separated from co
inspection.return.separated.from.computation.quickfix=Move ''return'' closer to computation of the value of ''{0}''
inspection.return.separated.from.computation.family.quickfix=Move 'return' closer to computation of the result
inspection.notnull.field.not.initialized.display.name=@NotNull field is not initialized
inspection.notnull.field.not.initialized.option=Ignore fields which could be initialized implicitly
inspection.nullable.problems.display.name=@NotNull/@Nullable problems
#check box options
inspection.nullable.problems.method.overrides.notnull.option=<html>Report @NotNull &parameters overriding @Nullable and <br>@Nullable methods overriding @NotNull</html>
@@ -566,6 +569,7 @@ edit.options.of.reporter.inspection.text=Edit inspection profile setting
group.names.abstraction.issues=Abstraction issues
group.names.assignment.issues=Assignment issues
group.names.probable.bugs=Probable bugs
group.names.nullability.problems=Nullability problems
group.names.bitwise.operation.issues=Bitwise operation issues
group.names.class.structure=Class structure
group.names.class.metrics=Class metrics
@@ -345,8 +345,8 @@
key="use.0index.in.jdbc.resultset.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.probable.bugs" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.bugs.ResultSetIndexZeroInspection"/>
<localInspection groupPath="Java" language="JAVA" suppressId="ReturnOfNull" shortName="ReturnNull" bundle="com.siyeh.InspectionGadgetsBundle"
key="return.of.null.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.probable.bugs"
<localInspection groupPath="Java,Probable bugs" language="JAVA" suppressId="ReturnOfNull" shortName="ReturnNull" bundle="com.siyeh.InspectionGadgetsBundle"
key="return.of.null.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.nullability.problems"
enabledByDefault="false" level="WARNING" implementationClass="com.siyeh.ig.bugs.ReturnNullInspection"/>
<localInspection groupPath="Java" language="JAVA" suppressId="StaticMethodReferencedViaSubclass" shortName="StaticCallOnSubclass"
bundle="com.siyeh.InspectionGadgetsBundle" key="static.method.via.subclass.display.name"