constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / source / JavaVarTypeUtil.java
1 /*
2  * Copyright 2000-2017 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;
17
18 import com.intellij.openapi.util.RecursionManager;
19 import com.intellij.psi.*;
20 import com.intellij.psi.util.PsiTypesUtil;
21 import com.intellij.psi.util.PsiUtil;
22 import com.intellij.util.ArrayUtil;
23 import com.intellij.util.containers.ContainerUtil;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
26
27 import java.util.Arrays;
28
29 public class JavaVarTypeUtil {
30   public static PsiType getUpwardProjection(@NotNull PsiType t) {
31     return t.accept(new UpwardProjectionTypeVisitor());
32   }
33
34   public static PsiType getDownwardProjection(@NotNull PsiType type) {
35     return type.accept(new DownwardProjectionTypeVisitor());
36   }
37   
38   private static boolean mentionsRestrictedTypeVariables(PsiType type) {
39     return type.accept(new PsiTypeVisitor<Boolean>() {
40       @Override
41       public Boolean visitType(PsiType type) {
42         return false;
43       }
44
45       @Override
46       public Boolean visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
47         return true;
48       }
49     });
50   }
51
52   private static class UpwardProjectionTypeVisitor extends PsiTypeVisitorEx<PsiType> {
53     @Override
54     public PsiType visitType(PsiType type) {
55       return type;
56     }
57
58     @Nullable
59     @Override
60     public PsiType visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
61       return capturedWildcardType.getUpperBound().accept(this);
62     }
63
64     @Override
65     public PsiType visitArrayType(PsiArrayType arrayType) {
66       PsiType componentType = arrayType.getComponentType();
67       return componentType.accept(this).createArrayType();
68     }
69
70     @Nullable
71     @Override
72     public PsiType visitLambdaExpressionType(PsiLambdaExpressionType lambdaExpressionType) {
73       return lambdaExpressionType;
74     }
75
76     @Override
77     public PsiType visitMethodReferenceType(PsiMethodReferenceType methodReferenceType) {
78       return methodReferenceType;
79     }
80
81     @Override
82     public PsiType visitIntersectionType(PsiIntersectionType intersectionType) {
83       return PsiIntersectionType.createIntersection(Arrays.stream(intersectionType.getConjuncts())
84                                                       .map(conjunct -> conjunct.accept(this))
85                                                       .toArray(PsiType[]::new));
86     }
87
88     @Override
89     public PsiType visitClassType(PsiClassType classType) {
90       PsiClassType.ClassResolveResult result = classType.resolveGenerics();
91       PsiClass aClass = result.getElement();
92       if (aClass != null) {
93         PsiManager manager = aClass.getManager();
94         PsiSubstitutor targetSubstitutor = PsiSubstitutor.EMPTY;
95         PsiSubstitutor substitutor = result.getSubstitutor();
96         for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(aClass)) {
97           PsiType ai = substitutor.substitute(parameter);
98           targetSubstitutor = targetSubstitutor.put(parameter, ai);
99           if (ai != null && mentionsRestrictedTypeVariables(ai)) {
100             if (ai instanceof PsiWildcardType) {
101
102               if (((PsiWildcardType)ai).isExtends()) {
103                 targetSubstitutor = targetSubstitutor.put(parameter, 
104                                                           PsiWildcardType.createExtends(manager, ((PsiWildcardType)ai).getExtendsBound().accept(this)));
105               }
106
107               if (((PsiWildcardType)ai).isSuper()) {
108                 targetSubstitutor = targetSubstitutor.put(parameter, createDownwardProjection(manager, ((PsiWildcardType)ai).getSuperBound()));
109               }
110
111             }
112             else {
113               PsiType U = RecursionManager.doPreventingRecursion(ai, true, () -> ai.accept(this));
114               if (U == null) {
115                 targetSubstitutor = targetSubstitutor.put(parameter, PsiWildcardType.createUnbounded(manager));
116               }
117               else if (!U.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && tryUpperBound(aClass, parameter, U)) {
118                 targetSubstitutor = targetSubstitutor.put(parameter, PsiWildcardType.createExtends(manager, U));
119               }
120               else {
121                 targetSubstitutor = targetSubstitutor.put(parameter, createDownwardProjection(manager, ai));
122               }
123             }
124           }
125         }
126         return JavaPsiFacade.getElementFactory(aClass.getProject()).createType(aClass, targetSubstitutor);
127       }
128       return classType;
129     }
130
131     private static PsiWildcardType createDownwardProjection(PsiManager manager, PsiType bound) {
132       PsiType downwardProjection = getDownwardProjection(bound);
133       return downwardProjection != PsiType.NULL ? PsiWildcardType.createSuper(manager, downwardProjection)
134                                                 : PsiWildcardType.createUnbounded(manager);
135     }
136
137     private static boolean tryUpperBound(PsiClass aClass, PsiTypeParameter parameter, PsiType U) {
138       PsiClassType[] extendsListTypes = parameter.getExtendsListTypes();
139       if (extendsListTypes.length == 0) return true;
140       PsiType bi = PsiIntersectionType.createIntersection(extendsListTypes);
141       return PsiTypesUtil.mentionsTypeParameters(bi, ContainerUtil.newHashSet(aClass.getTypeParameters())) ||
142              !U.isAssignableFrom(bi);
143     }
144   }
145
146   private static class DownwardProjectionTypeVisitor extends PsiTypeVisitor<PsiType> {
147     @Override
148     public PsiType visitType(PsiType type) {
149       return type;
150     }
151
152     @Override
153     public PsiType visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
154       return capturedWildcardType.getLowerBound().accept(this);
155     }
156
157     @Override
158     public PsiType visitArrayType(PsiArrayType arrayType) {
159       PsiType projection = arrayType.getComponentType().accept(this);
160       if (projection == PsiType.NULL) return PsiType.NULL;
161       return projection.createArrayType();
162     }
163
164     @Override
165     public PsiType visitIntersectionType(PsiIntersectionType intersectionType) {
166       PsiType[] conjuncts = Arrays.stream(intersectionType.getConjuncts()).map(conjunct -> conjunct.accept(this)).toArray(PsiType[]::new);
167       if (ArrayUtil.find(conjuncts, PsiType.NULL) > -1) return PsiType.NULL;
168       return PsiIntersectionType.createIntersection(conjuncts);
169     }
170
171     @Override
172     public PsiType visitLambdaExpressionType(PsiLambdaExpressionType lambdaExpressionType) {
173       return lambdaExpressionType;
174     }
175
176     @Override
177     public PsiType visitMethodReferenceType(PsiMethodReferenceType methodReferenceType) {
178       return methodReferenceType;
179     }
180
181     @Override
182     public PsiType visitClassType(PsiClassType classType) {
183       PsiClassType.ClassResolveResult result = classType.resolveGenerics();
184       PsiClass aClass = result.getElement();
185       if (aClass != null) {
186         PsiSubstitutor substitutor = result.getSubstitutor();
187         PsiSubstitutor targetSubstitutor = PsiSubstitutor.EMPTY;
188         for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(aClass)) {
189           PsiType ai = substitutor.substitute(parameter);
190           if (ai == null) return PsiType.NULL;
191           if (!mentionsRestrictedTypeVariables(ai)) {
192             targetSubstitutor = targetSubstitutor.put(parameter, ai);
193           }
194           else if (ai instanceof PsiWildcardType) {
195             if (((PsiWildcardType)ai).isExtends()) {
196               PsiType extendsBound = ((PsiWildcardType)ai).getExtendsBound();
197               PsiType projection = extendsBound.accept(this);
198               if (projection == PsiType.NULL) return PsiType.NULL;
199               targetSubstitutor = targetSubstitutor.put(parameter, PsiWildcardType.createExtends(parameter.getManager(), projection));
200             }
201             else if (((PsiWildcardType)ai).isSuper()) {
202               PsiType superBound = ((PsiWildcardType)ai).getSuperBound();
203               targetSubstitutor = targetSubstitutor.put(parameter, getUpwardProjection(superBound));
204             }
205             else {
206               return PsiType.NULL;
207             }
208           }
209           else {
210             return PsiType.NULL;
211           }
212         }
213         return JavaPsiFacade.getElementFactory(aClass.getProject()).createType(aClass, targetSubstitutor);
214       }
215       return PsiType.NULL;
216     }
217   }
218 }