constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInspection / dataFlow / value / DfaValueFactory.java
1 // 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.
2
3 package com.intellij.codeInspection.dataFlow.value;
4
5 import com.intellij.codeInsight.AnnotationUtil;
6 import com.intellij.codeInsight.Nullability;
7 import com.intellij.codeInspection.dataFlow.*;
8 import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
9 import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
10 import com.intellij.lang.java.JavaLanguage;
11 import com.intellij.openapi.util.Pair;
12 import com.intellij.patterns.ElementPattern;
13 import com.intellij.psi.*;
14 import com.intellij.psi.util.*;
15 import com.intellij.util.containers.FList;
16 import com.intellij.util.containers.FactoryMap;
17 import one.util.streamex.StreamEx;
18 import org.jetbrains.annotations.Contract;
19 import org.jetbrains.annotations.NotNull;
20 import org.jetbrains.annotations.Nullable;
21
22 import java.util.*;
23
24 import static com.intellij.patterns.PsiJavaPatterns.psiMember;
25 import static com.intellij.patterns.PsiJavaPatterns.psiParameter;
26 import static com.intellij.patterns.StandardPatterns.or;
27
28 public class DfaValueFactory {
29   private final List<DfaValue> myValues = new ArrayList<>();
30   final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myAssignableCache = new HashMap<>();
31   final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache = new HashMap<>();
32   private final Map<PsiType, DfaPsiType> myDfaTypes = new HashMap<>();
33   private final boolean myUnknownMembersAreNullable;
34   private final FieldChecker myFieldChecker;
35
36   /**
37    * @param context                   an item to analyze (code-block, expression, class)
38    * @param unknownMembersAreNullable
39    */
40   public DfaValueFactory(@Nullable PsiElement context, boolean unknownMembersAreNullable) {
41     myFieldChecker = new FieldChecker(context);
42     myUnknownMembersAreNullable = unknownMembersAreNullable;
43     myValues.add(null);
44     myVarFactory = new DfaVariableValue.Factory(this);
45     myConstFactory = new DfaConstValue.Factory(this);
46     myBoxedFactory = new DfaBoxedValue.Factory(this);
47     myRelationFactory = new DfaRelationValue.Factory(this);
48     myExpressionFactory = new DfaExpressionFactory(this);
49     myFactFactory = new DfaFactMapValue.Factory(this);
50     myBinOpFactory = new DfaBinOpValue.Factory(this);
51   }
52
53   public boolean canTrustFieldInitializer(PsiField field) {
54     return myFieldChecker.canTrustFieldInitializer(field);
55   }
56
57   private static final ElementPattern<? extends PsiModifierListOwner> MEMBER_OR_METHOD_PARAMETER =
58     or(psiMember(), psiParameter().withSuperParent(2, psiMember()));
59
60
61   @NotNull
62   public Nullability suggestNullabilityForNonAnnotatedMember(@NotNull PsiModifierListOwner member) {
63     if (myUnknownMembersAreNullable &&
64         MEMBER_OR_METHOD_PARAMETER.accepts(member) &&
65         AnnotationUtil.getSuperAnnotationOwners(member).isEmpty()) {
66       return Nullability.NULLABLE;
67     }
68
69     return Nullability.UNKNOWN;
70   }
71
72   @NotNull
73   public DfaValue createTypeValue(@Nullable PsiType type, @NotNull Nullability nullability) {
74     if (type == null) return DfaUnknownValue.getInstance();
75     if (type instanceof PsiPrimitiveType) {
76       LongRangeSet range = LongRangeSet.fromType(type);
77       if (range != null) {
78         return getFactFactory().createValue(DfaFactType.RANGE, range);
79       }
80     }
81     DfaFactMap facts = DfaFactMap.EMPTY.with(DfaFactType.TYPE_CONSTRAINT, createDfaType(type).asConstraint())
82       .with(DfaFactType.NULLABILITY, DfaNullability.fromNullability(nullability));
83     return getFactFactory().createValue(facts);
84   }
85
86   @NotNull
87   public DfaValue createExactTypeValue(@Nullable PsiType type) {
88     if (type == null) return DfaUnknownValue.getInstance();
89     DfaFactMap facts = DfaFactMap.EMPTY.with(DfaFactType.TYPE_CONSTRAINT, TypeConstraint.exact(createDfaType(type)))
90       .with(DfaFactType.NULLABILITY, DfaNullability.NOT_NULL);
91     return getFactFactory().createValue(facts);
92   }
93
94   @NotNull
95   public <T> DfaValue withFact(@NotNull DfaValue value, @NotNull DfaFactType<T> factType, @Nullable T factValue) {
96     if(value instanceof DfaUnknownValue) {
97       return getFactFactory().createValue(DfaFactMap.EMPTY.with(factType, factValue));
98     }
99     if(value instanceof DfaFactMapValue) {
100       return ((DfaFactMapValue)value).withFact(factType, factValue);
101     }
102     return DfaUnknownValue.getInstance();
103   }
104
105   @NotNull
106   public DfaPsiType createDfaType(@NotNull PsiType psiType) {
107     psiType = DfaPsiType.normalizeType(psiType);
108     DfaPsiType dfaType = myDfaTypes.get(psiType);
109     if (dfaType == null) {
110       myDfaTypes.put(psiType, dfaType = new DfaPsiType(myDfaTypes.size() + 1, psiType, this));
111     }
112     return dfaType;
113   }
114
115   int registerValue(DfaValue value) {
116     myValues.add(value);
117     return myValues.size() - 1;
118   }
119
120   public DfaValue getValue(int id) {
121     return myValues.get(id);
122   }
123
124   @NotNull
125   public DfaPsiType getType(int id) {
126     return StreamEx.ofValues(myDfaTypes).findFirst(t -> t.getID() == id).orElseThrow(IllegalArgumentException::new);
127   }
128
129   @Nullable
130   @Contract("null -> null")
131   public DfaValue createValue(PsiExpression psiExpression) {
132     return myExpressionFactory.getExpressionDfaValue(psiExpression);
133   }
134
135   @NotNull
136   public DfaConstValue getInt(int value) {
137     return getConstFactory().createFromValue(value, PsiType.INT);
138   }
139
140   @Nullable
141   public DfaValue createLiteralValue(PsiLiteralExpression literal) {
142     return getConstFactory().create(literal);
143   }
144
145   /**
146    * Create condition (suitable to pass into {@link DfaMemoryState#applyCondition(DfaValue)}),
147    * evaluating it statically if possible.
148    *
149    * @param dfaLeft      left operand
150    * @param relationType relation
151    * @param dfaRight     right operand
152    * @return resulting condition: either {@link DfaRelationValue} or {@link DfaConstValue} (true or false) or {@link DfaUnknownValue}.
153    */
154   @NotNull
155   public DfaValue createCondition(@NotNull DfaValue dfaLeft, @NotNull RelationType relationType, @NotNull DfaValue dfaRight) {
156     DfaConstValue value = tryEvaluate(dfaLeft, relationType, dfaRight);
157     if (value != null) return value;
158     DfaRelationValue relation = getRelationFactory().createRelation(dfaLeft, relationType, dfaRight);
159     if (relation != null) return relation;
160     return DfaUnknownValue.getInstance();
161   }
162
163   @Nullable
164   private DfaConstValue tryEvaluate(DfaValue dfaLeft, RelationType relationType, DfaValue dfaRight) {
165     DfaConstValue sentinel = getConstFactory().getSentinel();
166     if ((dfaLeft == sentinel) != (dfaRight == sentinel)) {
167       return getBoolean(relationType == RelationType.NE);
168     }
169     if (dfaRight instanceof DfaFactMapValue && dfaLeft == getConstFactory().getNull()) {
170       return tryEvaluate(dfaRight, relationType, dfaLeft);
171     }
172     if (dfaLeft instanceof DfaFactMapValue &&
173         dfaRight == getConstFactory().getNull() &&
174         DfaNullability.isNotNull(((DfaFactMapValue)dfaLeft).getFacts())) {
175       if (relationType == RelationType.EQ) {
176         return getConstFactory().getFalse();
177       }
178       if (relationType == RelationType.NE) {
179         return getConstFactory().getTrue();
180       }
181     }
182
183     if(dfaLeft instanceof DfaFactMapValue && dfaRight instanceof DfaFactMapValue) {
184       if(relationType == RelationType.IS || relationType == RelationType.IS_NOT) {
185         DfaFactMap leftFacts = ((DfaFactMapValue)dfaLeft).getFacts();
186         DfaFactMap rightFacts = ((DfaFactMapValue)dfaRight).getFacts();
187         boolean isSuperState = rightFacts.isSuperStateOf(leftFacts);
188         if (isSuperState) {
189           return getBoolean(relationType == RelationType.IS);
190         }
191         boolean isDistinct = rightFacts.intersect(leftFacts) == null;
192         if (isDistinct) {
193           return getBoolean(relationType == RelationType.IS_NOT);
194         }
195       }
196     }
197
198     LongRangeSet leftRange = LongRangeSet.fromDfaValue(dfaLeft);
199     LongRangeSet rightRange = LongRangeSet.fromDfaValue(dfaRight);
200     if (leftRange != null && rightRange != null) {
201       LongRangeSet constraint = rightRange.fromRelation(relationType);
202       if (constraint != null && !constraint.intersects(leftRange)) {
203         return getConstFactory().getFalse();
204       }
205       LongRangeSet revConstraint = rightRange.fromRelation(relationType.getNegated());
206       if (revConstraint != null && !revConstraint.intersects(leftRange)) {
207         return getConstFactory().getTrue();
208       }
209     }
210
211     if(dfaLeft instanceof DfaConstValue && dfaRight instanceof DfaConstValue &&
212        (relationType == RelationType.EQ || relationType == RelationType.NE)) {
213       return getBoolean(dfaLeft == dfaRight ^
214                         !DfaUtil.isNaN(((DfaConstValue)dfaLeft).getValue()) ^
215                         relationType == RelationType.EQ);
216     }
217
218     return null;
219   }
220
221   public DfaConstValue getBoolean(boolean value) {
222     return value ? getConstFactory().getTrue() : getConstFactory().getFalse();
223   }
224
225   public <T> DfaValue getFactValue(@NotNull DfaFactType<T> factType, @Nullable T value) {
226     return getFactFactory().createValue(factType, value);
227   }
228
229   public Collection<DfaValue> getValues() {
230     return Collections.unmodifiableCollection(myValues);
231   }
232
233   @NotNull
234   public DfaControlTransferValue controlTransfer(TransferTarget kind, FList<Trap> traps) {
235     return myControlTransfers.get(Pair.create(kind, traps));
236   }
237
238   private final Map<Pair<TransferTarget, FList<Trap>>, DfaControlTransferValue> myControlTransfers =
239     FactoryMap.create(p -> new DfaControlTransferValue(this, p.first, p.second));
240
241   private final DfaVariableValue.Factory myVarFactory;
242   private final DfaConstValue.Factory myConstFactory;
243   private final DfaBoxedValue.Factory myBoxedFactory;
244   private final DfaBinOpValue.Factory myBinOpFactory;
245   private final DfaRelationValue.Factory myRelationFactory;
246   private final DfaExpressionFactory myExpressionFactory;
247   private final DfaFactMapValue.Factory myFactFactory;
248
249   @NotNull
250   public DfaVariableValue.Factory getVarFactory() {
251     return myVarFactory;
252   }
253
254   @NotNull
255   public DfaConstValue.Factory getConstFactory() {
256     return myConstFactory;
257   }
258   @NotNull
259   public DfaBoxedValue.Factory getBoxedFactory() {
260     return myBoxedFactory;
261   }
262
263   @NotNull
264   public DfaRelationValue.Factory getRelationFactory() {
265     return myRelationFactory;
266   }
267
268   @NotNull
269   public DfaFactMapValue.Factory getFactFactory() {
270     return myFactFactory;
271   }
272
273   @NotNull
274   public DfaExpressionFactory getExpressionFactory() { return myExpressionFactory;}
275
276   @NotNull
277   public DfaBinOpValue.Factory getBinOpFactory() {
278     return myBinOpFactory;
279   }
280
281   @NotNull
282   public DfaValue createCommonValue(@NotNull PsiExpression[] expressions, PsiType targetType) {
283     DfaValue loopElement = null;
284     for (PsiExpression expression : expressions) {
285       DfaValue expressionValue = createValue(expression);
286       if (expressionValue == null) {
287         expressionValue = createTypeValue(expression.getType(), NullabilityUtil.getExpressionNullability(expression));
288       }
289       loopElement = loopElement == null ? expressionValue : loopElement.unite(expressionValue);
290       if (loopElement == DfaUnknownValue.getInstance()) break;
291     }
292     return loopElement == null ? DfaUnknownValue.getInstance() : DfaUtil.boxUnbox(loopElement, targetType);
293   }
294
295   private static class ClassInitializationInfo {
296     final boolean myCanInstantiateItself;
297     final boolean myCtorsCallMethods;
298     final boolean mySuperCtorsCallMethods;
299
300     ClassInitializationInfo(@NotNull PsiClass psiClass) {
301       // Indirect instantiation via other class is still possible, but hopefully unlikely
302       myCanInstantiateItself = StreamEx.of(psiClass.getChildren())
303                                        .select(PsiMember.class)
304                                        .filter(member -> member.hasModifierProperty(PsiModifier.STATIC))
305                                        .flatMap(member -> StreamEx.<PsiElement>ofTree(member, e -> StreamEx.of(e.getChildren())))
306                                        .select(PsiNewExpression.class).map(PsiNewExpression::getClassReference).nonNull()
307                                        .anyMatch(classRef -> classRef.isReferenceTo(psiClass));
308       mySuperCtorsCallMethods =
309         !InheritanceUtil.processSupers(psiClass, false, superClass -> !canCallMethodsInConstructors(superClass, true));
310       myCtorsCallMethods = canCallMethodsInConstructors(psiClass, false);
311     }
312
313     private static boolean canCallMethodsInConstructors(@NotNull PsiClass aClass, boolean virtual) {
314       for (PsiMethod constructor : aClass.getConstructors()) {
315         if (!constructor.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return true;
316
317         PsiCodeBlock body = constructor.getBody();
318         if (body == null) continue;
319
320         for (PsiMethodCallExpression call : SyntaxTraverser.psiTraverser().withRoot(body).filter(PsiMethodCallExpression.class)) {
321           PsiReferenceExpression methodExpression = call.getMethodExpression();
322           if (methodExpression.textMatches(PsiKeyword.THIS) || methodExpression.textMatches(PsiKeyword.SUPER)) continue;
323           if (!virtual) return true;
324
325           PsiMethod target = call.resolveMethod();
326           if (target != null && PsiUtil.canBeOverridden(target)) return true;
327         }
328       }
329
330       return false;
331     }
332   }
333
334   private static class FieldChecker {
335     private final boolean myTrustDirectFieldInitializers;
336     private final boolean myTrustFieldInitializersInConstructors;
337     private final boolean myCanInstantiateItself;
338     private final PsiClass myClass;
339
340     FieldChecker(PsiElement context) {
341       PsiMethod method = context instanceof PsiClass ? null : PsiTreeUtil.getParentOfType(context, PsiMethod.class);
342       PsiClass contextClass = method != null ? method.getContainingClass() : context instanceof PsiClass ? (PsiClass)context : null;
343       myClass = contextClass;
344       if (method == null || myClass == null) {
345         myTrustDirectFieldInitializers = myTrustFieldInitializersInConstructors = myCanInstantiateItself = false;
346         return;
347       }
348       // Indirect instantiation via other class is still possible, but hopefully unlikely
349       ClassInitializationInfo info = CachedValuesManager.getCachedValue(contextClass, () -> CachedValueProvider.Result
350         .create(new ClassInitializationInfo(contextClass), PsiModificationTracker.MODIFICATION_COUNT));
351       myCanInstantiateItself = info.myCanInstantiateItself;
352       if (method.hasModifierProperty(PsiModifier.STATIC) || method.isConstructor()) {
353         myTrustDirectFieldInitializers = true;
354         myTrustFieldInitializersInConstructors = false;
355         return;
356       }
357       myTrustFieldInitializersInConstructors = !info.mySuperCtorsCallMethods && !info.myCtorsCallMethods;
358       myTrustDirectFieldInitializers = !info.mySuperCtorsCallMethods;
359     }
360
361     boolean canTrustFieldInitializer(PsiField field) {
362       if (field.hasInitializer()) {
363         boolean staticField = field.hasModifierProperty(PsiModifier.STATIC);
364         if (staticField && myClass != null && field.getContainingClass() != myClass) return true;
365         return myTrustDirectFieldInitializers && (!myCanInstantiateItself || !staticField);
366       }
367       return myTrustFieldInitializersInConstructors;
368     }
369   }
370 }