java: prohibit caching when using thread-local types imposed on expressions and decla...
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / source / resolve / graphInference / constraints / InputOutputConstraintFormula.java
1 /*
2  * Copyright 2000-2013 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.psi.impl.source.resolve.graphInference.constraints;
17
18 import com.intellij.psi.*;
19 import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
20 import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
21 import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
22 import com.intellij.psi.util.PsiUtil;
23 import org.jetbrains.annotations.Nullable;
24
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.stream.Collectors;
28 import java.util.stream.Stream;
29
30 public abstract class InputOutputConstraintFormula implements ConstraintFormula {
31   private PsiType myT;
32
33   protected InputOutputConstraintFormula(PsiType t) {
34     myT = t;
35   }
36
37   public abstract PsiExpression getExpression();
38   protected abstract InputOutputConstraintFormula createSelfConstraint(PsiType type, PsiExpression expression);
39   protected abstract void collectReturnTypeVariables(InferenceSession session,
40                                                      PsiExpression psiExpression,
41                                                      PsiType returnType, 
42                                                      Set<InferenceVariable> result);
43
44   public Set<InferenceVariable> getInputVariables(InferenceSession session) {
45     final PsiExpression psiExpression = getExpression();
46     final PsiType type = myT;
47     if (psiExpression instanceof PsiFunctionalExpression) {
48       final InferenceVariable inferenceVariable = session.getInferenceVariable(type);
49       if (inferenceVariable != null) {
50         final HashSet<InferenceVariable> result = new HashSet<>();
51         result.add(inferenceVariable);
52         return result;
53       }
54       if (LambdaUtil.isFunctionalType(type)) {
55         final PsiType functionType =
56           psiExpression instanceof PsiLambdaExpression
57           ? FunctionalInterfaceParameterizationUtil.getGroundTargetType(type, (PsiLambdaExpression)psiExpression, false)
58           : type;
59         final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionType);
60         final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
61         if (interfaceMethod != null) {
62
63           final Set<InferenceVariable> result = new HashSet<>();
64           final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, resolveResult);
65           if (psiExpression instanceof PsiLambdaExpression && !((PsiLambdaExpression)psiExpression).hasFormalParameterTypes() || 
66               psiExpression instanceof PsiMethodReferenceExpression && !((PsiMethodReferenceExpression)psiExpression).isExact()) {
67             for (PsiParameter parameter : interfaceMethod.getParameterList().getParameters()) {
68               session.collectDependencies(substitutor.substitute(parameter.getType()), result);
69             }
70           }
71
72           final PsiType returnType = interfaceMethod.getReturnType();
73           if (returnType != null) {
74             collectReturnTypeVariables(session, psiExpression, substitutor.substitute(returnType), result);
75           }
76
77           return result;
78         }
79       }
80     }
81
82     if (psiExpression instanceof PsiParenthesizedExpression) {
83       final PsiExpression expression = ((PsiParenthesizedExpression)psiExpression).getExpression();
84       return expression != null ? createSelfConstraint(type, expression).getInputVariables(session) : null;
85     }
86
87     if (psiExpression instanceof PsiConditionalExpression) {
88       final PsiExpression thenExpression = ((PsiConditionalExpression)psiExpression).getThenExpression();
89       final PsiExpression elseExpression = ((PsiConditionalExpression)psiExpression).getElseExpression();
90       final Set<InferenceVariable> thenResult = thenExpression != null ? createSelfConstraint(type, thenExpression).getInputVariables(session) : null;
91       final Set<InferenceVariable> elseResult = elseExpression != null ? createSelfConstraint(type, elseExpression).getInputVariables(session) : null;
92       if (thenResult == null) {
93         return elseResult;
94       } else if (elseResult == null) {
95         return thenResult;
96       } else {
97         thenResult.addAll(elseResult);
98         return thenResult;
99       }
100     }
101
102     if (psiExpression instanceof PsiSwitchExpression) {
103       Set<InferenceVariable> variables =
104         PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)psiExpression).stream().flatMap(expression -> {
105           Set<InferenceVariable> inputVariables = createSelfConstraint(type, expression).getInputVariables(session);
106           return inputVariables != null ? inputVariables.stream() : Stream.empty();
107         }).collect(Collectors.toSet());
108       return variables.isEmpty() ? null : variables;
109     }
110     return null;
111   }
112
113
114   @Nullable
115   public Set<InferenceVariable> getOutputVariables(Set<InferenceVariable> inputVariables, InferenceSession session) {
116     final HashSet<InferenceVariable> mentionedVariables = new HashSet<>();
117     session.collectDependencies(myT, mentionedVariables);
118     if (inputVariables != null) {
119       mentionedVariables.removeAll(inputVariables);
120     }
121     return mentionedVariables.isEmpty() ? null : mentionedVariables;
122   }
123
124   @Override
125   public void apply(PsiSubstitutor substitutor, boolean cache) {
126     myT = substitutor.substitute(myT);
127   }
128
129   public PsiType getCurrentType() {
130     return myT;
131   }
132
133   @Override
134   public String toString() {
135     return getExpression().getText() + " -> " + myT.getPresentableText();
136   }
137 }