2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.codeInspection.dataFlow;
18 import com.intellij.codeInsight.NullableNotNullManager;
19 import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction;
20 import com.intellij.codeInspection.dataFlow.instructions.Instruction;
21 import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction;
22 import com.intellij.codeInspection.dataFlow.value.DfaValue;
23 import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
24 import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
25 import com.intellij.psi.PsiMethod;
26 import com.intellij.psi.PsiModifierListOwner;
27 import com.intellij.psi.PsiParameter;
28 import com.intellij.psi.PsiPrimitiveType;
29 import com.intellij.psi.impl.search.JavaNullMethodArgumentUtil;
30 import com.intellij.util.SmartList;
31 import gnu.trove.THashSet;
32 import org.jetbrains.annotations.NotNull;
34 import java.util.Collection;
38 * This checker uses following idea:
39 * On the checker's start mark all parameters with null-argument usages as violated (i.e. the method fails if parameter is null).
40 * A parameter can be amnestied (excluded from violated) when one of following statements is true:
41 * 1. If at least one successful method execution ({@link ReturnInstruction#isViaException()} == false)
42 * doesn't require a not-null value for the parameter ({@link DfaMemoryState#isNotNull(DfaValue) == false});
44 * 2. If the parameter has a reassignment while one of any method execution.
46 * All remaining violated parameters is required to be not-null for successful method execution.
48 class NullParameterConstraintChecker extends DataFlowRunner {
49 private final Set<PsiParameter> myPossiblyViolatedParameters;
51 private NullParameterConstraintChecker(Collection<PsiParameter> parameters, boolean isOnTheFly) {
52 super(false, true, isOnTheFly);
53 myPossiblyViolatedParameters = new THashSet<>(parameters);
57 static PsiParameter[] checkMethodParameters(PsiMethod method) {
58 if (method.getBody() == null) return PsiParameter.EMPTY_ARRAY;
60 final Collection<PsiParameter> nullableParameters = new SmartList<>();
61 final PsiParameter[] parameters = method.getParameterList().getParameters();
62 for (int index = 0; index < parameters.length; index++) {
63 PsiParameter parameter = parameters[index];
64 if (!(parameter.getType() instanceof PsiPrimitiveType) &&
65 !NullableNotNullManager.isNotNull(parameter) &&
66 !NullableNotNullManager.isNullable(parameter) &&
67 JavaNullMethodArgumentUtil.hasNullArgument(method, index)) {
68 nullableParameters.add(parameter);
71 if (nullableParameters.isEmpty()) return PsiParameter.EMPTY_ARRAY;
73 final NullParameterConstraintChecker checker = new NullParameterConstraintChecker(nullableParameters, true);
74 checker.analyzeMethod(method.getBody(), new StandardInstructionVisitor());
76 return checker.myPossiblyViolatedParameters.toArray(new PsiParameter[checker.myPossiblyViolatedParameters.size()]);
81 protected DfaInstructionState[] acceptInstruction(@NotNull InstructionVisitor visitor, @NotNull DfaInstructionState instructionState) {
82 Instruction instruction = instructionState.getInstruction();
84 if (instruction instanceof AssignInstruction) {
85 final DfaValue value = ((AssignInstruction)instruction).getAssignedValue();
86 if (value instanceof DfaVariableValue) {
87 final PsiModifierListOwner psiVariable = ((DfaVariableValue)value).getPsiVariable();
88 if (psiVariable instanceof PsiParameter) {
89 myPossiblyViolatedParameters.remove(psiVariable);
94 if (instruction instanceof ReturnInstruction && !((ReturnInstruction)instruction).isViaException()) {
95 DfaMemoryState memState = instructionState.getMemoryState();
96 for (PsiParameter parameter : myPossiblyViolatedParameters.toArray(new PsiParameter[myPossiblyViolatedParameters.size()])) {
97 final DfaVariableValue dfaVar = getFactory().getVarFactory().createVariableValue(parameter, false);
98 if (!memState.isNotNull(dfaVar)) {
99 myPossiblyViolatedParameters.remove(parameter);
104 return super.acceptInstruction(visitor, instructionState);
109 protected DfaMemoryState createMemoryState() {
110 return new MyDfaMemoryState(getFactory());
113 private class MyDfaMemoryState extends DfaMemoryStateImpl {
115 protected MyDfaMemoryState(DfaValueFactory factory) {
119 protected MyDfaMemoryState(MyDfaMemoryState toCopy) {
124 public void flushVariable(@NotNull DfaVariableValue variable) {
125 final PsiModifierListOwner psi = variable.getPsiVariable();
126 if (psi instanceof PsiParameter && myPossiblyViolatedParameters.contains(psi)) return;
127 super.flushVariable(variable);
132 public DfaMemoryStateImpl createCopy() {
133 return new MyDfaMemoryState(this);