class ContractChecker extends DataFlowRunner {
private final PsiMethod myMethod;
private final MethodContract myContract;
- private final boolean myOnTheFly;
private final Set<PsiElement> myViolations = ContainerUtil.newHashSet();
private final Set<PsiElement> myNonViolations = ContainerUtil.newHashSet();
private final Set<PsiElement> myFailures = ContainerUtil.newHashSet();
private ContractChecker(PsiMethod method, MethodContract contract, final boolean onTheFly) {
+ super(false, true, onTheFly);
myMethod = method;
myContract = contract;
- myOnTheFly = onTheFly;
}
static Map<PsiElement, String> checkContractClause(PsiMethod method,
return checker.getErrors();
}
- @Override
- protected boolean shouldCheckTimeLimit() {
- if (!myOnTheFly) return false;
- return super.shouldCheckTimeLimit();
- }
-
@NotNull
@Override
protected DfaInstructionState[] acceptInstruction(@NotNull InstructionVisitor visitor, @NotNull DfaInstructionState instructionState) {
public boolean TREAT_UNKNOWN_MEMBERS_AS_NULLABLE;
public boolean IGNORE_ASSERT_STATEMENTS;
public boolean REPORT_CONSTANT_REFERENCE_VALUES = true;
+ public boolean REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER = true;
@Override
public JComponent createOptionsPanel() {
@Override
public void visitMethod(PsiMethod method) {
analyzeCodeBlock(method.getBody(), holder, isOnTheFly);
+ analyzeNullLiteralMethodArguments(method, holder, isOnTheFly);
}
@Override
};
}
+ protected LocalQuickFix createNavigateToNullParameterUsagesFix(PsiParameter parameter) {
+ return null;
+ }
+
+ private void analyzeNullLiteralMethodArguments(PsiMethod method, ProblemsHolder holder, boolean isOnTheFly) {
+ if (REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER) {
+ for (PsiParameter parameter : NullParameterConstraintChecker.checkMethodParameters(method, isOnTheFly)) {
+ final String name = parameter.getName();
+ holder.registerProblem(parameter.getNameIdentifier(),
+ InspectionsBundle.message("dataflow.method.fails.with.null.argument", name),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ NullableStuffInspectionBase.getWrappedUiDependentQuickFix(this::createNavigateToNullParameterUsagesFix, parameter, isOnTheFly));
+ }
+ }
+ }
+
private void analyzeCodeBlock(@Nullable final PsiElement scope, ProblemsHolder holder, final boolean onTheFly) {
if (scope == null) return;
PsiClass containingClass = PsiTreeUtil.getParentOfType(scope, PsiClass.class);
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass) && !(containingClass instanceof PsiEnumConstantInitializer)) return;
- final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(TREAT_UNKNOWN_MEMBERS_AS_NULLABLE, !isInsideConstructorOrInitializer(
- scope)) {
- @Override
- protected boolean shouldCheckTimeLimit() {
- if (!onTheFly) return false;
- return super.shouldCheckTimeLimit();
- }
- };
+ final StandardDataFlowRunner dfaRunner =
+ new StandardDataFlowRunner(TREAT_UNKNOWN_MEMBERS_AS_NULLABLE,
+ !isInsideConstructorOrInitializer(scope), onTheFly);
analyzeDfaWithNestedClosures(scope, holder, dfaRunner, Collections.singletonList(dfaRunner.createMemoryState()), onTheFly);
}
private final MultiMap<PsiElement, DfaMemoryState> myNestedClosures = new MultiMap<>();
@NotNull
private final DfaValueFactory myValueFactory;
-
+ private final boolean myShouldCheckLimitTime;
// Maximum allowed attempts to process instruction. Fail as too complex to process if certain instruction
// is executed more than this limit times.
static final int MAX_STATES_PER_BRANCH = 300;
protected DataFlowRunner() {
- this(false, true);
+ this(false, true, false);
}
- protected DataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers) {
+ protected DataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers, boolean shouldCheckLimitTime) {
+ myShouldCheckLimitTime = shouldCheckLimitTime;
myValueFactory = new DfaValueFactory(honorFieldInitializers, unknownMembersAreNullable);
}
}
protected boolean shouldCheckTimeLimit() {
- return !ApplicationManager.getApplication().isUnitTestMode();
+ return myShouldCheckLimitTime && !ApplicationManager.getApplication().isUnitTestMode();
}
@NotNull
public Result<Set<PsiField>> compute() {
final PsiCodeBlock body = constructor.getBody();
final Map<PsiField, Boolean> map = ContainerUtil.newHashMap();
- final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(false, false) {
+ final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(false, false, false) {
private boolean isCallExposingNonInitializedFields(Instruction instruction) {
if (!(instruction instanceof MethodCallInstruction) ||
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInspection.dataFlow;
+
+import com.intellij.codeInsight.NullableNotNullManager;
+import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction;
+import com.intellij.codeInspection.dataFlow.instructions.Instruction;
+import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction;
+import com.intellij.codeInspection.dataFlow.value.DfaValue;
+import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
+import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.psi.impl.search.JavaNullMethodArgumentUtil;
+import com.intellij.util.SmartList;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * This checker uses following idea:
+ * On the checker's start mark all parameters with null-argument usages as violated (i.e. the method fails if parameter is null).
+ * A parameter can be amnestied (excluded from violated) when one of following statements is true:
+ * 1. If at least one successful method execution ({@link ReturnInstruction#isViaException()} == false)
+ * doesn't require a not-null value for the parameter ({@link DfaMemoryState#isNotNull(DfaValue) == false});
+ * OR
+ * 2. If the parameter has a reassignment while one of any method execution.
+ *
+ * All remaining violated parameters is required to be not-null for successful method execution.
+ */
+class NullParameterConstraintChecker extends DataFlowRunner {
+ private final Set<PsiParameter> myPossiblyViolatedParameters;
+
+ private NullParameterConstraintChecker(Collection<PsiParameter> parameters, boolean isOnTheFly) {
+ super(false, true, isOnTheFly);
+ myPossiblyViolatedParameters = new THashSet<>(parameters);
+ }
+
+ @NotNull
+ static PsiParameter[] checkMethodParameters(PsiMethod method, boolean isOnTheFly) {
+ if (method.getBody() == null) return PsiParameter.EMPTY_ARRAY;
+
+ final Collection<PsiParameter> nullableParameters = new SmartList<>();
+ final PsiParameter[] parameters = method.getParameterList().getParameters();
+ for (int index = 0; index < parameters.length; index++) {
+ PsiParameter parameter = parameters[index];
+ if (!(parameter.getType() instanceof PsiPrimitiveType) &&
+ !NullableNotNullManager.isNotNull(parameter) &&
+ JavaNullMethodArgumentUtil.hasNullArgument(method, index)) {
+ nullableParameters.add(parameter);
+ }
+ }
+ if (nullableParameters.isEmpty()) return PsiParameter.EMPTY_ARRAY;
+
+ final NullParameterConstraintChecker checker = new NullParameterConstraintChecker(nullableParameters, isOnTheFly);
+ checker.analyzeMethod(method.getBody(), new StandardInstructionVisitor());
+
+ return checker.myPossiblyViolatedParameters.toArray(new PsiParameter[checker.myPossiblyViolatedParameters.size()]);
+ }
+
+ @NotNull
+ @Override
+ protected DfaInstructionState[] acceptInstruction(@NotNull InstructionVisitor visitor, @NotNull DfaInstructionState instructionState) {
+ DfaMemoryState memState = instructionState.getMemoryState();
+ if (memState.isEphemeral()) {
+ return DfaInstructionState.EMPTY_ARRAY;
+ }
+ Instruction instruction = instructionState.getInstruction();
+
+ if (instruction instanceof AssignInstruction) {
+ final DfaValue value = ((AssignInstruction)instruction).getAssignedValue();
+ if (value instanceof DfaVariableValue) {
+ final PsiModifierListOwner psiVariable = ((DfaVariableValue)value).getPsiVariable();
+ if (psiVariable instanceof PsiParameter) {
+ myPossiblyViolatedParameters.remove(psiVariable);
+ }
+ }
+ }
+
+ if (instruction instanceof ReturnInstruction && !((ReturnInstruction)instruction).isViaException()) {
+ for (PsiParameter parameter : myPossiblyViolatedParameters.toArray(new PsiParameter[myPossiblyViolatedParameters.size()])) {
+ final DfaVariableValue dfaVar = getFactory().getVarFactory().createVariableValue(parameter, false);
+ if (!memState.isNotNull(dfaVar)) {
+ myPossiblyViolatedParameters.remove(parameter);
+ }
+ }
+ }
+
+ return super.acceptInstruction(visitor, instructionState);
+ }
+
+ @NotNull
+ @Override
+ protected DfaMemoryState createMemoryState() {
+ return new MyDfaMemoryState(getFactory());
+ }
+
+ private class MyDfaMemoryState extends DfaMemoryStateImpl {
+
+ protected MyDfaMemoryState(DfaValueFactory factory) {
+ super(factory);
+ }
+
+ protected MyDfaMemoryState(MyDfaMemoryState toCopy) {
+ super(toCopy);
+ }
+
+ @Override
+ public void flushVariable(@NotNull DfaVariableValue variable) {
+ final PsiModifierListOwner psi = variable.getPsiVariable();
+ if (psi instanceof PsiParameter && myPossiblyViolatedParameters.contains(psi)) return;
+ super.flushVariable(variable);
+ }
+
+ @NotNull
+ @Override
+ public DfaMemoryStateImpl createCopy() {
+ return new MyDfaMemoryState(this);
+ }
+ }
+}
public class StandardDataFlowRunner extends DataFlowRunner {
public StandardDataFlowRunner() {
- this(false, true);
+ this(false, true, false);
}
- public StandardDataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers) {
- super(unknownMembersAreNullable, honorFieldInitializers);
+ public StandardDataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers, boolean shouldCheckLimitTime) {
+ super(unknownMembersAreNullable, honorFieldInitializers, shouldCheckLimitTime);
}
}
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
+import com.intellij.psi.impl.search.JavaNullMethodArgumentUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
-import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionTool {
// deprecated fields remain to minimize changes to users inspection profiles (which are often located in version control).
@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
@SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD = true;
+ public boolean REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER = true;
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.nullable.NullableStuffInspectionBase");
return new JavaElementVisitor() {
@Override
public void visitMethod(PsiMethod method) {
- checkNullableStuffForMethod(method, holder);
+ checkNullableStuffForMethod(method, holder, isOnTheFly);
}
@Override
};
}
+ protected LocalQuickFix createNavigateToNullParameterUsagesFix(PsiParameter parameter) {
+ return null;
+ }
+
private static boolean nullabilityAnnotationsNotAvailable(final PsiFile file) {
final Project project = file.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
return "NullableProblems";
}
- private void checkNullableStuffForMethod(PsiMethod method, final ProblemsHolder holder) {
+ private void checkNullableStuffForMethod(PsiMethod method, final ProblemsHolder holder, boolean isOnFly) {
Annotated annotated = check(method, holder, method.getReturnType());
List<PsiMethod> superMethods = ContainerUtil.map(
final NullableNotNullManager nullableManager = NullableNotNullManager.getInstance(holder.getProject());
checkSupers(method, holder, annotated, superMethods, nullableManager);
- checkParameters(method, holder, superMethods, nullableManager);
+ checkParameters(method, holder, superMethods, nullableManager, isOnFly);
checkOverriders(method, holder, annotated, nullableManager);
}
holder.registerProblem(method.getNameIdentifier(),
InspectionsBundle.message("inspection.nullable.problems.method.overrides.NotNull", getPresentableAnnoName(superMethod)),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
- wrapFix(fix));
+ fix);
break;
}
}
}
}
- private void checkParameters(PsiMethod method, ProblemsHolder holder, List<PsiMethod> superMethods, NullableNotNullManager nullableManager) {
+ private void checkParameters(PsiMethod method,
+ ProblemsHolder holder,
+ List<PsiMethod> superMethods,
+ NullableNotNullManager nullableManager,
+ boolean isOnFly) {
PsiParameter[] parameters = method.getParameterList().getParameters();
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
holder.registerProblem(parameter.getNameIdentifier(),
InspectionsBundle.message("inspection.nullable.problems.parameter.overrides.NotNull", getPresentableAnnoName(superParameter)),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
- wrapFix(fix));
+ fix);
break;
}
}
holder.registerProblem(physical ? notNullAnnotation : parameter.getNameIdentifier(),
InspectionsBundle.message("inspection.nullable.problems.NotNull.parameter.overrides.not.annotated", getPresentableAnnoName(parameter)),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
- wrapFix(fix));
+ fix);
break;
}
}
}
+
+ checkNullLiteralArgumentOfNotNullParameterUsages(method, holder, nullableManager, isOnFly, i, parameter);
+ }
+ }
+
+ private void checkNullLiteralArgumentOfNotNullParameterUsages(PsiMethod method,
+ ProblemsHolder holder,
+ NullableNotNullManager nullableManager,
+ boolean isOnFly,
+ int parameterIdx,
+ PsiParameter parameter) {
+ if (REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER && isNotNullNotInferred(parameter, false, false)) {
+ PsiAnnotation notNullAnnotation = nullableManager.getNotNullAnnotation(parameter, false);
+ if (JavaNullMethodArgumentUtil.hasNullArgument(method, parameterIdx)) {
+ LocalQuickFix[] fixes = getWrappedUiDependentQuickFix(this::createNavigateToNullParameterUsagesFix, parameter, isOnFly);
+ boolean physical = PsiTreeUtil.isAncestor(parameter, notNullAnnotation, true);
+ holder.registerProblem(physical ? notNullAnnotation : parameter.getNameIdentifier(),
+ InspectionsBundle.message("inspection.nullable.problems.NotNull.parameter.receives.null.literal", getPresentableAnnoName(parameter)),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ fixes);
+ }
}
}
}
holder.registerProblem(psiElement, InspectionsBundle.message("nullable.stuff.problems.overridden.methods.are.not.annotated"),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
- wrapFix(fix));
+ fix);
methodQuickFixSuggested = true;
}
if (hasAnnotatedParameter) {
holder.registerProblem(psiElement,
InspectionsBundle.message("nullable.stuff.problems.overridden.method.parameters.are.not.annotated"),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
- wrapFix(!applicable
+ !applicable
? createChangeDefaultNotNullFix(nullableManager, parameters[i])
: new AnnotateOverriddenMethodParameterFix(defaultNotNull,
- nullableManager.getDefaultNullable())));
+ nullableManager.getDefaultNullable()));
parameterQuickFixSuggested[i] = true;
}
}
}
}
+ @NotNull
+ public static LocalQuickFix[] getWrappedUiDependentQuickFix(Function<PsiParameter, LocalQuickFix> fixSupplier, PsiParameter parameter, boolean isOnTheFly) {
+ if (!isOnTheFly) return LocalQuickFix.EMPTY_ARRAY;
+ final LocalQuickFix fix = fixSupplier.apply(parameter);
+ return fix == null ? LocalQuickFix.EMPTY_ARRAY : new LocalQuickFix[]{fix};
+ }
+
private static boolean isNotNullNotInferred(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean skipExternal) {
Project project = owner.getProject();
NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
return !(anno != null && AnnotationUtil.isInferredAnnotation(anno));
}
- @NotNull
- private static LocalQuickFix[] wrapFix(LocalQuickFix fix) {
- if (fix == null) return LocalQuickFix.EMPTY_ARRAY;
- return new LocalQuickFix[]{fix};
- }
-
private static LocalQuickFix createChangeDefaultNotNullFix(NullableNotNullManager nullableManager, PsiModifierListOwner modifierListOwner) {
final PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierListOwner, nullableManager.getNotNulls());
if (annotation != null) {
import com.intellij.codeInsight.NullableNotNullDialog;
import com.intellij.codeInspection.*;
+import com.intellij.codeInspection.nullable.NullableStuffInspection;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.project.Project;
};
}
+ @Override
+ protected LocalQuickFix createNavigateToNullParameterUsagesFix(PsiParameter parameter) {
+ return new NullableStuffInspection.NavigateToNullLiteralArguments(parameter);
+ }
+
private class OptionsPanel extends JPanel {
private final JCheckBox myIgnoreAssertions;
private final JCheckBox myReportConstantReferences;
private final JCheckBox mySuggestNullables;
private final JCheckBox myDontReportTrueAsserts;
private final JCheckBox myTreatUnknownMembersAsNullable;
+ private final JCheckBox myReportNullArguments;
private OptionsPanel() {
super(new GridBagLayout());
}
});
+ myReportNullArguments = new JCheckBox("Report 'null' literals passed to not-null required parameter");
+ myTreatUnknownMembersAsNullable.setSelected(REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER);
+ myTreatUnknownMembersAsNullable.getModel().addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER = myTreatUnknownMembersAsNullable.isSelected();
+ }
+ });
+
gc.insets = JBUI.emptyInsets();
gc.gridy = 0;
add(mySuggestNullables, gc);
gc.gridy++;
add(myTreatUnknownMembersAsNullable, gc);
+
+ gc.gridy++;
+ add(myReportNullArguments, gc);
}
}
package com.intellij.codeInspection.nullable;
import com.intellij.codeInsight.NullableNotNullDialog;
+import com.intellij.codeInspection.InspectionsBundle;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
+import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.impl.search.JavaNullMethodArgumentUtil;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.ui.components.JBCheckBox;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.usages.*;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
public class NullableStuffInspection extends NullableStuffInspectionBase {
+ @Override
+ protected LocalQuickFix createNavigateToNullParameterUsagesFix(PsiParameter parameter) {
+ return new NavigateToNullLiteralArguments(parameter);
+ }
+
@Override
public JComponent createOptionsPanel() {
return new OptionsPanel();
private JCheckBox myIgnoreExternalSuperNotNull;
private JCheckBox myNNParameterOverridesNA;
private JCheckBox myRequireNNFieldsInitialized;
+ private JBCheckBox myReportNullLiteralsPassedNotNullParameter;
private OptionsPanel() {
super(new BorderLayout());
myReportNotAnnotatedGetter.addActionListener(actionListener);
myIgnoreExternalSuperNotNull.addActionListener(actionListener);
myRequireNNFieldsInitialized.addActionListener(actionListener);
+ myReportNullLiteralsPassedNotNullParameter.addActionListener(actionListener);
myConfigureAnnotationsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
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());
}
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;
myIgnoreExternalSuperNotNull.setEnabled(myNAMethodOverridesNN.isSelected());
}
}
+
+ public static class NavigateToNullLiteralArguments extends LocalQuickFixOnPsiElement {
+ public NavigateToNullLiteralArguments(@NotNull PsiParameter element) {
+ super(element);
+ }
+
+ @NotNull
+ @Override
+ public String getText() {
+ return getFamilyName();
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getFamilyName() {
+ return InspectionsBundle.message("nullable.stuff.inspection.navigate.null.argument.usages.fix.family.name");
+ }
+
+ @Override
+ public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
+ PsiParameter p = (PsiParameter)startElement;
+ final PsiMethod method = PsiTreeUtil.getParentOfType(p, PsiMethod.class);
+ if (method == null) return;
+ final int parameterIdx = ArrayUtil.find(method.getParameterList().getParameters(), p);
+ if (parameterIdx < 0) return;
+
+ UsageViewPresentation presentation = new UsageViewPresentation();
+ String title = InspectionsBundle.message("nullable.stuff.inspection.navigate.null.argument.usages.view.name", p.getName());
+ presentation.setUsagesString(title);
+ presentation.setTabName(title);
+ presentation.setTabText(title);
+ UsageViewManager.getInstance(project).searchAndShowUsages(
+ new UsageTarget[]{new PsiElement2UsageTargetAdapter(method.getParameterList().getParameters()[parameterIdx])},
+ () -> new UsageSearcher() {
+ @Override
+ public void generate(@NotNull final Processor<Usage> processor) {
+ ReadAction.run(() -> JavaNullMethodArgumentUtil.searchNullArgument(method, parameterIdx, (arg) -> processor.process(new UsageInfo2UsageAdapter(new UsageInfo(arg)))));
+ }
+ }, false, false, presentation, null);
+ }
+ };
}
<?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="8" 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="9" 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="239"/>
+ <xy x="69" y="57" width="634" height="277"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<vspacer id="c3eef">
<constraints>
- <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"/>
+ <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"/>
</constraints>
</vspacer>
<component id="2f304" class="javax.swing.JCheckBox" binding="myReportNotAnnotatedGetter">
</component>
<component id="ef852" class="javax.swing.JButton" binding="myConfigureAnnotationsButton" default-binding="true">
<constraints>
- <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"/>
+ <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"/>
</constraints>
<properties>
<text resource-bundle="messages/InspectionsBundle" key="configure.annotations.option"/>
<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"/>
+ </constraints>
+ <properties>
+ <text value="Report 'null' literals passed to @NotNull method parameters"/>
+ </properties>
+ </component>
</children>
</grid>
</form>
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.psi.impl.search;
+
+import com.intellij.ide.highlighter.JavaFileType;
+import com.intellij.lang.LighterAST;
+import com.intellij.lang.LighterASTNode;
+import com.intellij.lang.LighterASTTokenNode;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiKeyword;
+import com.intellij.psi.impl.cache.RecordUtil;
+import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
+import com.intellij.psi.impl.source.tree.ElementType;
+import com.intellij.psi.impl.source.tree.JavaElementType;
+import com.intellij.psi.impl.source.tree.LightTreeUtil;
+import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.util.containers.IntArrayList;
+import com.intellij.util.indexing.*;
+import com.intellij.util.io.DataInputOutputUtil;
+import com.intellij.util.io.EnumeratorStringDescriptor;
+import com.intellij.util.io.KeyDescriptor;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class JavaNullMethodArgumentIndex extends ScalarIndexExtension<JavaNullMethodArgumentIndex.MethodCallData> implements PsiDependentIndex {
+ private static final Logger LOG = Logger.getInstance(JavaNullMethodArgumentIndex.class);
+
+ public static final ID<MethodCallData, Void> INDEX_ID = ID.create("java.null.method.argument");
+
+ @NotNull
+ @Override
+ public ID<MethodCallData, Void> getName() {
+ return INDEX_ID;
+ }
+
+ @NotNull
+ @Override
+ public DataIndexer<MethodCallData, Void, FileContent> getIndexer() {
+ return inputData -> {
+ final CharSequence contentAsText = inputData.getContentAsText();
+ if (!JavaStubElementTypes.JAVA_FILE.shouldBuildStubFor(inputData.getFile())) {
+ return Collections.emptyMap();
+ }
+ if (!StringUtil.contains(contentAsText, PsiKeyword.NULL)) {
+ return Collections.emptyMap();
+ }
+
+ Map<MethodCallData, Void> result = new THashMap<>();
+ final LighterAST lighterAst = ((FileContentImpl)inputData).getLighterASTForPsiDependentIndex();
+
+ new RecursiveLighterASTNodeWalkingVisitor(lighterAst) {
+ @Override
+ public void visitNode(@NotNull LighterASTNode element) {
+ if (element.getTokenType() == JavaElementType.METHOD_CALL_EXPRESSION ||
+ element.getTokenType() == JavaElementType.NEW_EXPRESSION ||
+ element.getTokenType() == JavaElementType.ANONYMOUS_CLASS) {
+ final IntArrayList indices = getNullParameterIndices(element);
+ if (indices != null) {
+ final String name = getMethodName(element, element.getTokenType());
+ if (name != null) {
+ for (int i = 0; i < indices.size(); i++) {
+ final int nullParameterIndex = indices.get(i);
+ result.put(new MethodCallData(name, nullParameterIndex), null);
+ }
+ }
+ }
+ }
+ super.visitNode(element);
+ }
+
+ @Nullable
+ private IntArrayList getNullParameterIndices(@NotNull LighterASTNode methodCall) {
+ final LighterASTNode node = LightTreeUtil.firstChildOfType(lighterAst, methodCall, JavaElementType.EXPRESSION_LIST);
+ if (node == null) return null;
+ final List<LighterASTNode> parameters = LightTreeUtil.getChildrenOfType(lighterAst, node, ElementType.EXPRESSION_BIT_SET);
+ IntArrayList indices = null;
+ for (int idx = 0; idx < parameters.size(); idx++) {
+ LighterASTNode parameter = parameters.get(idx);
+ if (parameter.getTokenType() == JavaElementType.LITERAL_EXPRESSION) {
+ final CharSequence literal = ((LighterASTTokenNode) lighterAst.getChildren(parameter).get(0)).getText();
+ if (StringUtil.equals(literal, PsiKeyword.NULL)) {
+ if (indices == null) {
+ indices = new IntArrayList(1);
+ }
+ indices.add(idx);
+ }
+ }
+ }
+ return indices;
+ }
+
+ @Nullable
+ private String getMethodName(@NotNull LighterASTNode call, IElementType elementType) {
+ if (elementType == JavaElementType.NEW_EXPRESSION || elementType == JavaElementType.ANONYMOUS_CLASS) {
+ final List<LighterASTNode> refs = LightTreeUtil.getChildrenOfType(lighterAst, call, JavaElementType.JAVA_CODE_REFERENCE);
+ if (refs.isEmpty()) return null;
+ final LighterASTNode lastRef = refs.get(refs.size() - 1);
+ return getLastIdentifierText(lastRef);
+ } else {
+ LOG.assertTrue(elementType == JavaElementType.METHOD_CALL_EXPRESSION);
+ final LighterASTNode methodReference = lighterAst.getChildren(call).get(0);
+ if (methodReference.getTokenType() == JavaElementType.REFERENCE_EXPRESSION) {
+ return getLastIdentifierText(methodReference);
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private String getLastIdentifierText(LighterASTNode lastRef) {
+ final List<LighterASTNode> identifiers = LightTreeUtil.getChildrenOfType(lighterAst, lastRef, JavaTokenType.IDENTIFIER);
+ if (identifiers.isEmpty()) return null;
+ final LighterASTNode methodNameIdentifier = identifiers.get(identifiers.size() - 1);
+ return RecordUtil.intern(lighterAst.getCharTable(), methodNameIdentifier);
+ }
+ }.visitNode(lighterAst.getRoot());
+
+ return result;
+ };
+ }
+
+ @NotNull
+ @Override
+ public KeyDescriptor<MethodCallData> getKeyDescriptor() {
+ return new KeyDescriptor<MethodCallData>() {
+ @Override
+ public int getHashCode(MethodCallData value) {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean isEqual(MethodCallData val1, MethodCallData val2) {
+ return val1.equals(val2);
+ }
+
+ @Override
+ public void save(@NotNull DataOutput out, MethodCallData value) throws IOException {
+ EnumeratorStringDescriptor.INSTANCE.save(out, value.getMethodName());
+ DataInputOutputUtil.writeINT(out, value.getNullParameterIndex());
+ }
+
+ @Override
+ public MethodCallData read(@NotNull DataInput in) throws IOException {
+ return new MethodCallData(EnumeratorStringDescriptor.INSTANCE.read(in),
+ DataInputOutputUtil.readINT(in));
+ }
+ };
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @NotNull
+ @Override
+ public FileBasedIndex.InputFilter getInputFilter() {
+ return new DefaultFileTypeSpecificInputFilter(JavaFileType.INSTANCE);
+ }
+
+ @Override
+ public boolean dependsOnFileContent() {
+ return true;
+ }
+
+ public static final class MethodCallData {
+ @NotNull
+ private final String myMethodName;
+ private final int myNullParameterIndex;
+
+ public MethodCallData(@NotNull String name, int index) {
+ myMethodName = name;
+ myNullParameterIndex = index;
+ }
+
+ @NotNull
+ public String getMethodName() {
+ return myMethodName;
+ }
+
+ public int getNullParameterIndex() {
+ return myNullParameterIndex;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodCallData data = (MethodCallData)o;
+
+ if (myNullParameterIndex != data.myNullParameterIndex) return false;
+ if (!myMethodName.equals(data.myMethodName)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = myMethodName.hashCode();
+ result = 31 * result + myNullParameterIndex;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "MethodCallData{" +
+ "myMethodName='" + myMethodName + '\'' +
+ ", myNullParameterIndex=" + myNullParameterIndex +
+ '}';
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.psi.impl.search;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.GlobalSearchScopeUtil;
+import com.intellij.psi.search.searches.MethodReferencesSearch;
+import com.intellij.util.CommonProcessors;
+import com.intellij.util.Processor;
+import com.intellij.util.indexing.FileBasedIndex;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+public class JavaNullMethodArgumentUtil {
+
+ public static boolean hasNullArgument(@NotNull PsiMethod method, final int argumentIdx) {
+ final boolean[] result = {false};
+ searchNullArgument(method, argumentIdx, expression -> {
+ result[0] = true;
+ return false;
+ });
+ return result[0];
+ }
+
+ public static void searchNullArgument(@NotNull PsiMethod method, final int argumentIdx, @NotNull Processor<PsiExpression> nullArgumentProcessor) {
+ final GlobalSearchScope scope = findScopeWhereNullArgumentCanPass(method, argumentIdx);
+ if (scope == null) return;
+ MethodReferencesSearch.search(method, scope, true).forEach(ref -> {
+ final PsiElement psi = ref.getElement();
+ if (psi != null) {
+ final PsiElement parent = psi.getParent();
+ PsiExpressionList argumentList = null;
+ if (parent instanceof PsiCallExpression) {
+ argumentList = ((PsiCallExpression)parent).getArgumentList();
+ }
+ else if (parent instanceof PsiAnonymousClass) {
+ argumentList = ((PsiAnonymousClass)parent).getArgumentList();
+ }
+ if (argumentList != null) {
+ final PsiExpression[] arguments = argumentList.getExpressions();
+ final PsiExpression argument = arguments[argumentIdx];
+ if (argument instanceof PsiLiteralExpression && PsiKeyword.NULL.equals(argument.getText())) {
+ return nullArgumentProcessor.process(argument);
+ }
+ }
+ }
+ return true;
+ });
+ }
+
+ @Nullable
+ private static GlobalSearchScope findScopeWhereNullArgumentCanPass(@NotNull PsiMethod method, int parameterIndex) {
+ final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
+ final CommonProcessors.CollectProcessor<VirtualFile> collector = new CommonProcessors.CollectProcessor<>(new ArrayList<>());
+ fileBasedIndex.getFilesWithKey(JavaNullMethodArgumentIndex.INDEX_ID,
+ Collections.singleton(new JavaNullMethodArgumentIndex.MethodCallData(method.getName(), parameterIndex)),
+ collector,
+ GlobalSearchScopeUtil.toGlobalSearchScope(method.getUseScope(), method.getProject()));
+ final Collection<VirtualFile> candidateFiles = collector.getResults();
+ return candidateFiles.isEmpty() ? null : GlobalSearchScope.filesScope(method.getProject(), candidateFiles);
+ }
+
+}
}
@NotNull
- private static <T> T notNull(@Nullable T value) {
+ private static <T> T notNull(@Nullable T <warning descr="Method fails when parameter 'value' is 'null'">value</warning>) {
if (value == null) {
throw new RuntimeException("null");
--- /dev/null
+import java.util.Objects;
+
+class Test {
+
+ void dangerousMethod(String reassginedParameter) {
+ if (reassginedParameter == null) {
+ reassginedParameter = "default value";
+ }
+ Objects.requireNonNull(reassginedParameter);
+ System.out.println(reassginedParameter);
+ }
+
+ void usage() {
+ dangerousMethod(<warning descr="Passing 'null' argument to non annotated parameter">null</warning>);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+import java.util.Objects;
+
+class Test {
+ private static void testMethod(Object <warning descr="Method fails when parameter 'o' is 'null'">o</warning>, Object o2, Object <warning descr="Method fails when parameter 'o3' is 'null'">o3</warning>, Object o4, int i) {
+ Objects.requireNonNull(o, "o is not null");
+ if (o3 != null) {
+ System.out.println(o3.hashCode());
+ } else {
+ throw new NullPointerException();
+ }
+
+ System.out.println(o);
+ for (int j = 0; j < i; j++) {
+ System.out.println(j);
+ Objects.requireNonNull(o4);
+ }
+ Objects.requireNonNull(o3);
+
+ if (o4 == null) {
+ System.out.println("o4 is null");
+ } else {
+ Objects.requireNonNull(o4);
+ }
+ }
+
+ public static void main(String[] args) {
+ testMethod(<warning descr="Passing 'null' argument to non annotated parameter">null</warning>, <warning descr="Passing 'null' argument to non annotated parameter">null</warning>, <warning descr="Passing 'null' argument to non annotated parameter">null</warning>, <warning descr="Passing 'null' argument to non annotated parameter">null</warning>, 10);
+ }
+}
\ No newline at end of file
--- /dev/null
+import java.util.Objects;
+
+class Test {
+ private static void testMethod(Object o, Object o2, Object o3, Object o4, int i) {
+ Objects.requireNonNull(o2, "no usages with null literal argument");
+ if (i > 2016) {
+ Objects.requireNonNull(o, "o is not null");
+ }
+
+ System.out.println(o);
+ for (int j = 0; j < i; j++) {
+ System.out.println(j);
+ Objects.requireNonNull(o4);
+ }
+
+ if (o4 == null) {
+ System.out.println("o4 is null");
+ } else {
+ Objects.requireNonNull(o4);
+ }
+ }
+
+ public static void main(String[] args) {
+ testMethod(<warning descr="Passing 'null' argument to non annotated parameter">null</warning>, "I am not null", <warning descr="Passing 'null' argument to non annotated parameter">null</warning>, <warning descr="Passing 'null' argument to non annotated parameter">null</warning>, 10);
+ }
+}
--- /dev/null
+import org.jetbrains.annotations.NotNull;
+
+class Main111 {
+
+ Main111(<warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object o) {
+
+ }
+
+ static class SubClass {
+ SubClass(<warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object o) {
+
+ }
+ }
+
+ static class SubClass2 {
+ SubClass2(<warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object o) {
+
+ }
+ }
+
+ static void main() {
+ new Main111(null);
+ new Main111.SubClass(null);
+ new SubClass2(null);
+
+ new ParamerizedRunnable(null) {
+ @Override
+ void run() {
+
+ }
+ };
+ }
+
+ abstract static class ParamerizedRunnable {
+ private Object parameter;
+
+ public ParamerizedRunnable(<warning descr="Parameter annotated @NotNull should not receive null as an argument"><warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning></warning> Object parameter) {
+ this.parameter = parameter;
+ }
+
+ abstract void run();
+ }
+}
\ No newline at end of file
--- /dev/null
+import org.jetbrains.annotations.NotNull;
+
+class Test {
+ void someMethod(<warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object warn, <warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object o2, <warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object warn1) {
+
+ }
+
+ static void someStaticMethod(Object notAnnotated, <warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object o2, <warning descr="Parameter annotated @NotNull should not receive null as an argument">@NotNull</warning> Object warn2) {
+
+ }
+
+ public static void main(String[] args) {
+ someStaticMethod(null, "", "");
+
+ Test.someStaticMethod("", null, null);
+
+ new Test().someMethod("", null, null);
+
+ Test m = new Test();
+
+ m.someMethod(null, "", "");
+ m.someMethod(null, "", "");
+ }
+}
public void testPrimitiveInVoidLambda() { doTest(); }
public void testNotNullLambdaParameter() { doTest(); }
+ public void testNullArgumentIsFailingMethodCall() {
+ doTest();
+ }
+
+ public void testNullArgumentIsNotFailingMethodCall() {
+ doTest();
+ }
+
+ public void testNullArgumentButParameterIsReassigned() {
+ doTest();
+ }
+
public void testNullableArrayComponent() {
setupCustomAnnotations();
DataFlowInspection inspection = new DataFlowInspection();
myFixture.checkHighlighting(true, false, true);
}
+ public void testNullPassedToNotNullParameter() {
+ doTest();
+ }
+
+ public void testNullPassedToNotNullConstructorParameter() {
+ doTest();
+ }
+
public void testHonorParameterDefaultInSetters() {
DataFlowInspectionTest.addJavaxNullabilityAnnotations(myFixture);
DataFlowInspectionTest.addJavaxDefaultNullabilityAnnotations(myFixture);
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.psi.impl.search
+
+import com.intellij.openapi.fileTypes.StdFileTypes
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase
+import com.intellij.util.indexing.FileContentImpl
+import com.intellij.util.indexing.IndexingDataKeys
+import org.intellij.lang.annotations.Language
+
+class JavaNullMethodArgumentIndexTest : LightPlatformCodeInsightFixtureTestCase() {
+
+ fun testIndex() {
+ @Language("JAVA")
+ val file = myFixture.configureByText(StdFileTypes.JAVA, """
+ package org.some;
+
+ class Main111 {
+
+ Main111(Object o) {
+
+ }
+
+ void someMethod(Object o, Object o2, Object o3) {
+ }
+
+ static void staticMethod(Object o, Object o2, Object o3) {
+ }
+
+ public static void main(String[] args) {
+ staticMethod(null, "", "");
+ org.some.Main111.staticMethod("", "", null);
+ new Main111(null).someMethod("", "", null);
+ Main111 m = new Main111(null);
+ m.someMethod(null, "", "");
+ }
+
+ static class SubClass {
+ SubClass(Object o) {
+
+ }
+ }
+
+ static class SubClass2 {
+ SubClass2(Object o) {
+
+ }
+ }
+
+ static void main() {
+ new org.some.Main111(null);
+ new org.some.Main111.SubClass(null);
+ new SubClass2(null);
+
+
+ new ParametrizedRunnable(null) {
+ @Override
+ void run() {
+
+ }};
+ }
+
+ abstract class ParametrizedRunnable {
+ Object parameter;
+
+ ParametrizedRunnable(Object parameter){
+ this.parameter = parameter;
+ }
+
+ abstract void run();
+ }
+ }
+ """).virtualFile
+ val content = FileContentImpl.createByFile(file)
+ content.putUserData(IndexingDataKeys.PROJECT, project)
+ val data = JavaNullMethodArgumentIndex().indexer.map(content).keys
+
+ assertSize(8, data)
+ assertContainsElements(data,
+ JavaNullMethodArgumentIndex.MethodCallData("staticMethod", 0),
+ JavaNullMethodArgumentIndex.MethodCallData("staticMethod", 2),
+ JavaNullMethodArgumentIndex.MethodCallData("someMethod", 0),
+ JavaNullMethodArgumentIndex.MethodCallData("someMethod", 2),
+ JavaNullMethodArgumentIndex.MethodCallData("Main111", 0),
+ JavaNullMethodArgumentIndex.MethodCallData("SubClass", 0),
+ JavaNullMethodArgumentIndex.MethodCallData("SubClass2", 0),
+ JavaNullMethodArgumentIndex.MethodCallData("ParametrizedRunnable", 0))
+
+ }
+}
\ No newline at end of file
dataflow.message.unboxing=Unboxing of <code>#ref</code> #loc may produce <code>java.lang.NullPointerException</code>
dataflow.message.unboxing.method.reference=Use of <code>#ref</code> #loc would need unboxing which may produce <code>java.lang.NullPointerException</code>
dataflow.too.complex=Method <code>#ref</code> is too complex to analyze by data flow algorithm
+dataflow.method.fails.with.null.argument=Method fails when parameter ''{0}'' is <code>null<code>
#deprecated
inspection.deprecated.display.name=Deprecated API usage
inspection.nullable.problems.NotNull.parameter.overrides.not.annotated=Parameter annotated @{0} should not override non-annotated parameter
inspection.nullable.problems.parameter.overrides.NotNull=Not annotated parameter overrides @{0} parameter
inspection.nullable.problems.primitive.type.annotation=Primitive type members cannot be annotated
+inspection.nullable.problems.NotNull.parameter.receives.null.literal=Parameter annotated @{0} should not receive 'null' as an argument
inspection.test.only.problems.display.name=Test-only class or method call in production code
inspection.test.only.problems.test.only.method.call=Test-only method is called in production code
inspection.tool.window.dialog.title=Inspection Tool Window
inspection.tool.window.dialog.no.options=Inspection ''{0}'' has no configurable options
inspection.tool.window.inspection.dialog.title=Inspection ''{0}'' options
+nullable.stuff.inspection.navigate.null.argument.usages.fix.family.name=Navigate to 'null' argument usages
+nullable.stuff.inspection.navigate.null.argument.usages.view.name=''null'' argument usages for parameter {0}
\ No newline at end of file
implementationClass="com.intellij.refactoring.introduceparameterobject.JavaIntroduceParameterObjectDelegate"/>
<refactoring.pushDown language="JAVA" implementationClass="com.intellij.refactoring.memberPushDown.JavaPushDownDelegate" id="java"/>
<library.javaSourceRootDetector implementation="com.intellij.openapi.roots.ui.configuration.LibraryJavaSourceRootDetector"/>
+
+ <fileBasedIndex implementation="com.intellij.psi.impl.search.JavaNullMethodArgumentIndex"/>
</extensions>
<actions>