Merge branch 'db/javac-ast'
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInspection / dataFlow / NullParameterConstraintChecker.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.codeInspection.dataFlow;
17
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;
33
34 import java.util.Collection;
35 import java.util.Set;
36
37 /**
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});
43  * OR
44  * 2. If the parameter has a reassignment while one of any method execution.
45  *
46  * All remaining violated parameters is required to be not-null for successful method execution.
47  */
48 class NullParameterConstraintChecker extends DataFlowRunner {
49   private final Set<PsiParameter> myPossiblyViolatedParameters;
50
51   private NullParameterConstraintChecker(Collection<PsiParameter> parameters, boolean isOnTheFly) {
52     super(false, true, isOnTheFly);
53     myPossiblyViolatedParameters = new THashSet<>(parameters);
54   }
55
56   @NotNull
57   static PsiParameter[] checkMethodParameters(PsiMethod method) {
58     if (method.getBody() == null) return PsiParameter.EMPTY_ARRAY;
59
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);
69       }
70     }
71     if (nullableParameters.isEmpty()) return PsiParameter.EMPTY_ARRAY;
72
73     final NullParameterConstraintChecker checker = new NullParameterConstraintChecker(nullableParameters, true);
74     checker.analyzeMethod(method.getBody(), new StandardInstructionVisitor());
75
76     return checker.myPossiblyViolatedParameters.toArray(new PsiParameter[checker.myPossiblyViolatedParameters.size()]);
77   }
78
79   @NotNull
80   @Override
81   protected DfaInstructionState[] acceptInstruction(@NotNull InstructionVisitor visitor, @NotNull DfaInstructionState instructionState) {
82     Instruction instruction = instructionState.getInstruction();
83
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);
90         }
91       }
92     }
93
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);
100         }
101       }
102     }
103
104     return super.acceptInstruction(visitor, instructionState);
105   }
106
107   @NotNull
108   @Override
109   protected DfaMemoryState createMemoryState() {
110     return new MyDfaMemoryState(getFactory());
111   }
112
113   private class MyDfaMemoryState extends DfaMemoryStateImpl {
114
115     protected MyDfaMemoryState(DfaValueFactory factory) {
116       super(factory);
117     }
118
119     protected MyDfaMemoryState(MyDfaMemoryState toCopy) {
120       super(toCopy);
121     }
122
123     @Override
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);
128     }
129
130     @NotNull
131     @Override
132     public DfaMemoryStateImpl createCopy() {
133       return new MyDfaMemoryState(this);
134     }
135   }
136 }