2 * Copyright 2000-2013 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.psi.impl.source.resolve.graphInference.constraints;
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;
25 import java.util.HashSet;
27 import java.util.stream.Collectors;
28 import java.util.stream.Stream;
30 public abstract class InputOutputConstraintFormula implements ConstraintFormula {
32 public abstract PsiExpression getExpression();
33 protected abstract PsiType getT();
34 protected abstract void setT(PsiType t);
35 protected abstract InputOutputConstraintFormula createSelfConstraint(PsiType type, PsiExpression expression);
36 protected abstract void collectReturnTypeVariables(InferenceSession session,
37 PsiExpression psiExpression,
39 Set<InferenceVariable> result);
41 public Set<InferenceVariable> getInputVariables(InferenceSession session) {
42 final PsiExpression psiExpression = getExpression();
43 final PsiType type = getT();
44 if (psiExpression instanceof PsiFunctionalExpression) {
45 final InferenceVariable inferenceVariable = session.getInferenceVariable(type);
46 if (inferenceVariable != null) {
47 final HashSet<InferenceVariable> result = new HashSet<>();
48 result.add(inferenceVariable);
51 if (LambdaUtil.isFunctionalType(type)) {
52 final PsiType functionType =
53 psiExpression instanceof PsiLambdaExpression
54 ? FunctionalInterfaceParameterizationUtil.getGroundTargetType(type, (PsiLambdaExpression)psiExpression, false)
56 final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionType);
57 final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
58 if (interfaceMethod != null) {
60 final Set<InferenceVariable> result = new HashSet<>();
61 final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, resolveResult);
62 if (psiExpression instanceof PsiLambdaExpression && !((PsiLambdaExpression)psiExpression).hasFormalParameterTypes() ||
63 psiExpression instanceof PsiMethodReferenceExpression && !((PsiMethodReferenceExpression)psiExpression).isExact()) {
64 for (PsiParameter parameter : interfaceMethod.getParameterList().getParameters()) {
65 session.collectDependencies(substitutor.substitute(parameter.getType()), result);
69 final PsiType returnType = interfaceMethod.getReturnType();
70 if (returnType != null) {
71 collectReturnTypeVariables(session, psiExpression, substitutor.substitute(returnType), result);
79 if (psiExpression instanceof PsiParenthesizedExpression) {
80 final PsiExpression expression = ((PsiParenthesizedExpression)psiExpression).getExpression();
81 return expression != null ? createSelfConstraint(type, expression).getInputVariables(session) : null;
84 if (psiExpression instanceof PsiConditionalExpression) {
85 final PsiExpression thenExpression = ((PsiConditionalExpression)psiExpression).getThenExpression();
86 final PsiExpression elseExpression = ((PsiConditionalExpression)psiExpression).getElseExpression();
87 final Set<InferenceVariable> thenResult = thenExpression != null ? createSelfConstraint(type, thenExpression).getInputVariables(session) : null;
88 final Set<InferenceVariable> elseResult = elseExpression != null ? createSelfConstraint(type, elseExpression).getInputVariables(session) : null;
89 if (thenResult == null) {
91 } else if (elseResult == null) {
94 thenResult.addAll(elseResult);
99 if (psiExpression instanceof PsiSwitchExpression) {
100 Set<InferenceVariable> variables =
101 PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)psiExpression).stream().flatMap(expression -> {
102 Set<InferenceVariable> inputVariables = createSelfConstraint(type, expression).getInputVariables(session);
103 return inputVariables != null ? inputVariables.stream() : Stream.empty();
104 }).collect(Collectors.toSet());
105 return variables.isEmpty() ? null : variables;
112 public Set<InferenceVariable> getOutputVariables(Set<InferenceVariable> inputVariables, InferenceSession session) {
113 final HashSet<InferenceVariable> mentionedVariables = new HashSet<>();
114 session.collectDependencies(getT(), mentionedVariables);
115 if (inputVariables != null) {
116 mentionedVariables.removeAll(inputVariables);
118 return mentionedVariables.isEmpty() ? null : mentionedVariables;
122 public void apply(PsiSubstitutor substitutor, boolean cache) {
123 setT(substitutor.substitute(getT()));
125 LambdaUtil.getFunctionalTypeMap().put(getExpression(), getT());
130 public String toString() {
131 return getExpression().getText() + " -> " + getT().getPresentableText();