get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / PsiDiamondTypeImpl.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.psi;
17
18 import com.intellij.lang.java.JavaLanguage;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.util.Comparing;
22 import com.intellij.openapi.util.Ref;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
25 import com.intellij.psi.infos.CandidateInfo;
26 import com.intellij.psi.infos.MethodCandidateInfo;
27 import com.intellij.psi.scope.PsiConflictResolver;
28 import com.intellij.psi.scope.conflictResolvers.JavaMethodsConflictResolver;
29 import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.util.*;
32 import com.intellij.util.Function;
33 import com.intellij.util.IncorrectOperationException;
34 import com.intellij.util.VisibilityUtil;
35 import com.intellij.util.text.UniqueNameGenerator;
36 import org.jetbrains.annotations.NonNls;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39
40 import java.util.*;
41
42 /**
43  * @author anna
44  */
45 public class PsiDiamondTypeImpl extends PsiDiamondType {
46   private static final Logger LOG = Logger.getInstance(PsiDiamondTypeImpl.class);
47
48   private final PsiManager myManager;
49   private final PsiTypeElement myTypeElement;
50
51   public PsiDiamondTypeImpl(PsiManager manager, PsiTypeElement psiTypeElement) {
52     myManager = manager;
53     myTypeElement = psiTypeElement;
54   }
55
56   @NotNull
57   @Override
58   public String getPresentableText() {
59     return "";
60   }
61
62   @NotNull
63   @Override
64   public String getCanonicalText() {
65     return "";
66   }
67
68   @NotNull
69   @Override
70   public String getInternalCanonicalText() {
71     return "Diamond Type";
72   }
73
74   @Override
75   public boolean isValid() {
76     return false;
77   }
78
79   @Override
80   public boolean equalsToText(@NotNull @NonNls String text) {
81     return text.isEmpty();
82   }
83
84   @Override
85   public <A> A accept(@NotNull PsiTypeVisitor<A> visitor) {
86     return visitor.visitDiamondType(this);
87   }
88
89   @NotNull
90   @Override
91   public GlobalSearchScope getResolveScope() {
92     return GlobalSearchScope.allScope(myManager.getProject());
93   }
94
95   @NotNull
96   @Override
97   public PsiType[] getSuperTypes() {
98     return new PsiType[]{getJavaLangObject(myManager, getResolveScope())};
99   }
100
101   @Override
102   public DiamondInferenceResult resolveInferredTypes() {
103     final PsiNewExpression newExpression = getNewExpression();
104     if (newExpression == null) {
105       return PsiDiamondTypeImpl.DiamondInferenceResult.NULL_RESULT;
106     }
107
108     return resolveInferredTypes(newExpression);
109   }
110
111   private PsiNewExpression getNewExpression() {
112     PsiElement typeElementWithDiamondTypeArgument = myTypeElement.getParent();
113     return PsiTreeUtil.getParentOfType(typeElementWithDiamondTypeArgument, PsiNewExpression.class, true, PsiTypeElement.class);
114   }
115
116   @Nullable
117   @Override
118   public JavaResolveResult getStaticFactory() {
119     final PsiNewExpression newExpression = getNewExpression();
120     return newExpression != null ? getStaticFactory(newExpression, newExpression) : null;
121   }
122
123   public static DiamondInferenceResult resolveInferredTypes(PsiNewExpression newExpression) {
124     return resolveInferredTypes(newExpression, newExpression);
125   }
126
127   public static DiamondInferenceResult resolveInferredTypes(PsiNewExpression newExpression,
128                                                             PsiElement context) {
129     final PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass();
130     if (anonymousClass != null && !PsiUtil.isLanguageLevel9OrHigher(newExpression)) {
131       final PsiElement resolve = anonymousClass.getBaseClassReference().resolve();
132       if (resolve instanceof PsiClass) {
133         return PsiDiamondTypeImpl.DiamondInferenceResult.ANONYMOUS_INNER_RESULT;
134       }
135     }
136
137     final PsiReferenceParameterList referenceParameterList = PsiTreeUtil.getChildOfType(newExpression, PsiReferenceParameterList.class);
138     if (referenceParameterList != null && referenceParameterList.getTypeParameterElements().length > 0) {
139       return DiamondInferenceResult.EXPLICIT_CONSTRUCTOR_TYPE_ARGS;
140     }
141
142     final DiamondInferenceResult inferenceResult = resolveInferredTypesNoCheck(newExpression, context);
143     if (anonymousClass != null && PsiUtil.isLanguageLevel9OrHigher(newExpression)) {
144       final InferredAnonymousTypeVisitor anonymousTypeVisitor = new InferredAnonymousTypeVisitor(context);
145       for (PsiType type : inferenceResult.getInferredTypes()) {
146         final Boolean accepted = type.accept(anonymousTypeVisitor);
147         if (accepted != null && !accepted.booleanValue()) {
148           return PsiDiamondTypeImpl.DiamondInferenceResult.ANONYMOUS_INNER_RESULT;
149         } 
150       }
151     }
152     return inferenceResult;
153   }
154
155   private static JavaResolveResult getStaticFactory(final PsiNewExpression newExpression, final PsiElement context) {
156     return context == newExpression && !MethodCandidateInfo.isOverloadCheck(newExpression.getArgumentList())
157            ? CachedValuesManager.getCachedValue(newExpression,
158                                                 () -> new CachedValueProvider.Result<>(
159                                                   getStaticFactoryCandidateInfo(newExpression, newExpression),
160                                                   PsiModificationTracker.MODIFICATION_COUNT))
161            : getStaticFactoryCandidateInfo(newExpression, context);
162   }
163
164   public static DiamondInferenceResult resolveInferredTypesNoCheck(final PsiNewExpression newExpression, final PsiElement context) {
165     final JavaResolveResult staticFactoryCandidateInfo = getStaticFactory(newExpression, context);
166     if (staticFactoryCandidateInfo == null) {
167       return DiamondInferenceResult.NULL_RESULT;
168     }
169     final Ref<String> refError = new Ref<>();
170     final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(context, false, () -> {
171       PsiSubstitutor substitutor = staticFactoryCandidateInfo.getSubstitutor();
172       if (staticFactoryCandidateInfo instanceof MethodCandidateInfo) {
173         refError.set(((MethodCandidateInfo)staticFactoryCandidateInfo).getInferenceErrorMessageAssumeAlreadyComputed());
174       }
175       return substitutor;
176     });
177     if (inferredSubstitutor == null) {
178       return DiamondInferenceResult.NULL_RESULT;
179     }
180
181     if (!(staticFactoryCandidateInfo instanceof MethodCandidateInfo)) {
182       return DiamondInferenceResult.UNRESOLVED_CONSTRUCTOR;
183     }
184
185     final String errorMessage = refError.get();
186
187     //15.9.3 Choosing the Constructor and its Arguments
188     //The return type and throws clause of cj are the same as the return type and throws clause determined for mj (p15.12.2.6)
189     if (errorMessage == null && ((MethodCandidateInfo)staticFactoryCandidateInfo).isErased()) {
190       return DiamondInferenceResult.RAW_RESULT;
191     }
192
193     final PsiMethod staticFactory = ((MethodCandidateInfo)staticFactoryCandidateInfo).getElement();
194     final PsiTypeParameter[] parameters = staticFactory.getTypeParameters();
195     final PsiElement staticFactoryContext = staticFactory.getContext();
196     final PsiClass psiClass = PsiTreeUtil.getContextOfType(staticFactoryContext, PsiClass.class, false);
197     if (psiClass == null) {
198       LOG.error("failed for expression:" + newExpression);
199       return DiamondInferenceResult.NULL_RESULT;
200     }
201     final PsiTypeParameter[] classParameters = psiClass.getTypeParameters();
202     final PsiJavaCodeReferenceElement classOrAnonymousClassReference = newExpression.getClassOrAnonymousClassReference();
203     LOG.assertTrue(classOrAnonymousClassReference != null);
204     final DiamondInferenceResult result = new DiamondInferenceResult(classOrAnonymousClassReference.getReferenceName() + "<>") {
205       @Override
206       public String getErrorMessage() {
207         return errorMessage != null ? DiamondInferenceResult.NULL_RESULT.getErrorMessage() : super.getErrorMessage();
208       }
209     };
210
211     if (errorMessage == null && PsiUtil.isRawSubstitutor(staticFactory, inferredSubstitutor)) {
212       //http://www.oracle.com/technetwork/java/javase/8-compatibility-guide-2156366.html#A999198 REF 7144506
213       if (!PsiUtil.isLanguageLevel8OrHigher(newExpression) && PsiUtil.skipParenthesizedExprUp(newExpression.getParent()) instanceof PsiExpressionList) {
214         for (PsiTypeParameter ignored : parameters) {
215           result.addInferredType(PsiType.getJavaLangObject(newExpression.getManager(), GlobalSearchScope.allScope(newExpression.getProject())));
216         }
217       }
218       return result;
219     }
220
221     for (PsiTypeParameter parameter : parameters) {
222       for (PsiTypeParameter classParameter : classParameters) {
223         if (Comparing.strEqual(classParameter.getName(), parameter.getName())) {
224           result.addInferredType(inferredSubstitutor.substitute(parameter));
225           break;
226         }
227       }
228     }
229     return result;
230   }
231
232   private static JavaResolveResult getStaticFactoryCandidateInfo(final PsiNewExpression newExpression,
233                                                                  final PsiElement context) {
234     return ourDiamondGuard.doPreventingRecursion(context, false, () -> {
235
236       final PsiExpressionList argumentList = newExpression.getArgumentList();
237       if (argumentList == null) {
238         //token expected diagnostic is provided by parser
239         return null;
240       }
241
242       final JavaMethodsConflictResolver resolver = new JavaMethodsConflictResolver(argumentList, PsiUtil.getLanguageLevel(newExpression));
243       final List<CandidateInfo> results = collectStaticFactories(newExpression);
244       CandidateInfo result = results != null ? resolver.resolveConflict(new ArrayList<>(results)) : null;
245       final PsiMethod staticFactory = result != null ? (PsiMethod)result.getElement() : null;
246       if (staticFactory == null) {
247         //additional diagnostics: inference fails due to unresolved constructor
248         return JavaResolveResult.EMPTY;
249       }
250
251       final MethodCandidateInfo staticFactoryCandidateInfo = createMethodCandidate((MethodCandidateInfo)result, context, false, argumentList);
252       if (!staticFactory.isVarArgs()) {
253         return staticFactoryCandidateInfo;
254       }
255
256       final ArrayList<CandidateInfo> conflicts = new ArrayList<>();
257       conflicts.add(staticFactoryCandidateInfo);
258       conflicts.add(createMethodCandidate((MethodCandidateInfo)result, context, true, argumentList));
259       return resolver.resolveConflict(conflicts);
260     });
261   }
262
263   @Nullable
264   public static List<CandidateInfo> collectStaticFactories(PsiNewExpression newExpression) {
265     return CachedValuesManager.getCachedValue(newExpression,
266                                               () -> new CachedValueProvider.Result<>(collectStaticFactoriesInner(newExpression),
267                                                                                      PsiModificationTracker.MODIFICATION_COUNT));
268   }
269
270   private static List<CandidateInfo> collectStaticFactoriesInner(PsiNewExpression newExpression) {
271     PsiExpressionList argumentList = newExpression.getArgumentList();
272     if (argumentList == null) {
273       return null;
274     }
275
276     final PsiClass psiClass = findClass(newExpression);
277     if (psiClass == null) {
278       //should not happens: unresolved class reference would be first and inference won't start
279       return null;
280     }
281
282     final List<CandidateInfo> candidates = new ArrayList<>();
283     PsiMethod[] constructors = psiClass.getConstructors();
284     if (constructors.length == 0) {
285       //default constructor
286       constructors = new PsiMethod[] {null};
287     }
288
289     final MethodCandidatesProcessor
290       processor = new MethodCandidatesProcessor(argumentList, argumentList.getContainingFile(), new PsiConflictResolver[0], candidates) {
291       @Override
292       protected boolean isAccepted(@NotNull PsiMethod candidate) {
293         return true;
294       }
295
296       @Override
297       protected PsiClass getContainingClass(@NotNull PsiMethod method) {
298         return psiClass;
299       }
300
301       @Override
302       protected boolean acceptVarargs() {
303         return true;
304       }
305     };
306     processor.setArgumentList(argumentList);
307
308     for (PsiMethod constructor : constructors) {
309       final PsiTypeParameter[] params = getAllTypeParams(constructor, psiClass);
310       final PsiMethod staticFactory = generateStaticFactory(constructor, psiClass, params, newExpression.getClassReference());
311       if (staticFactory != null) {
312         processor.add(staticFactory, PsiSubstitutor.EMPTY);
313       }
314     }
315
316     return processor.getResults();
317   }
318
319   @Nullable
320   private static PsiClass findClass(PsiNewExpression newExpression) {
321     final PsiJavaCodeReferenceElement classReference = newExpression.getClassOrAnonymousClassReference();
322     if (classReference != null) {
323       final String text = classReference.getReferenceName();
324       if (text != null) {
325         final Project project = newExpression.getProject();
326         final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
327         final PsiResolveHelper resolveHelper = facade.getResolveHelper();
328         final PsiExpression newExpressionQualifier = newExpression.getQualifier();
329         final PsiElement qualifierElement = classReference.getQualifier();
330         final String qualifier = qualifierElement != null ? qualifierElement.getText() : "";
331         final String qualifiedName = StringUtil.getQualifiedName(qualifier, text);
332         if (newExpressionQualifier != null) {
333           final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(newExpressionQualifier.getType());
334           if (aClass != null) {
335             return aClass.findInnerClassByName(qualifiedName, false);
336           }
337         }
338         return resolveHelper.resolveReferencedClass(qualifiedName, newExpression);
339       } else {
340         return null;
341       }
342     }
343     return null;
344   }
345
346   @Nullable
347   private static PsiMethod generateStaticFactory(@Nullable PsiMethod constructor,
348                                                  PsiClass containingClass,
349                                                  PsiTypeParameter[] params,
350                                                  PsiJavaCodeReferenceElement reference) {
351     final StringBuilder buf = new StringBuilder();
352     final String modifier = VisibilityUtil.getVisibilityModifier(constructor != null ? constructor.getModifierList() : containingClass.getModifierList());
353     if (!PsiModifier.PACKAGE_LOCAL.equals(modifier)) {
354       buf.append(modifier);
355       buf.append(" ");
356     }
357     buf.append("static ");
358     buf.append("<");
359     //it's possible that constructor type parameters and class type parameters are same named:
360     //it's important that class type parameters names are preserved(they are first in the list),
361     //though constructor parameters would be renamed in case of conflicts
362     final UniqueNameGenerator generator = new UniqueNameGenerator();
363     buf.append(StringUtil.join(params, psiTypeParameter -> {
364       String extendsList = "";
365       if (psiTypeParameter.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
366         final PsiClassType[] extendsListTypes = psiTypeParameter.getExtendsListTypes();
367         if (extendsListTypes.length > 0) {
368           final Function<PsiClassType, String> canonicalTypePresentationFun = type -> type.getCanonicalText();
369           extendsList = " extends " + StringUtil.join(extendsListTypes, canonicalTypePresentationFun, "&");
370         }
371       }
372       return generator.generateUniqueName(psiTypeParameter.getName()) + extendsList;
373     }, ", "));
374     buf.append(">");
375
376     final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(containingClass.getProject());
377
378     String qualifiedName = containingClass.getQualifiedName();
379
380     PsiElement qualifier = reference != null ? reference.getQualifier() : null;
381     if (qualifier instanceof PsiJavaCodeReferenceElement) {
382       final JavaResolveResult resolveResult = ((PsiJavaCodeReferenceElement)qualifier).advancedResolve(false);
383       final PsiElement element = resolveResult.getElement();
384       if (element instanceof PsiClass) {
385         final String outerClassSubstitutedQName =
386           elementFactory.createType((PsiClass)element, resolveResult.getSubstitutor()).getInternalCanonicalText();
387         qualifiedName = outerClassSubstitutedQName + "." + containingClass.getName();
388       }
389     } else if (reference != null && qualifier == null && containingClass.getContainingClass() != null) {
390       qualifiedName = null;
391     }
392
393     buf.append(qualifiedName != null ? qualifiedName : containingClass.getName());
394     final PsiTypeParameter[] parameters = containingClass.getTypeParameters();
395     buf.append("<");
396     buf.append(StringUtil.join(parameters, psiTypeParameter -> psiTypeParameter.getName(), ", "));
397     buf.append("> ");
398
399     String staticFactoryName = "staticFactory";
400     final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(containingClass.getProject());
401     staticFactoryName = styleManager.suggestUniqueVariableName(staticFactoryName, containingClass, false);
402     buf.append(staticFactoryName);
403     if (constructor == null) {
404       buf.append("()");
405     }
406     else {
407       buf.append("(").append(StringUtil.join(constructor.getParameterList().getParameters(), new Function<PsiParameter, String>() {
408         int myIdx;
409         @Override
410         public String fun(PsiParameter psiParameter) {
411           return psiParameter.getType().getCanonicalText() + " p" + myIdx++;
412         }
413       }, ",")).append(")");
414       PsiClassType[] types = constructor.getThrowsList().getReferencedTypes();
415       if (types.length > 0) {
416         buf.append("throws ").append(StringUtil.join(types, type -> type.getCanonicalText(), ", "));
417       }
418     }
419     buf.append("{}");
420
421     try {
422       return elementFactory.createMethodFromText(buf.toString(), constructor != null ? constructor : containingClass);
423     }
424     catch (IncorrectOperationException e) {
425       return null;
426     }
427   }
428
429   @NotNull
430   private static PsiTypeParameter[] getAllTypeParams(PsiTypeParameterListOwner listOwner, PsiClass containingClass) {
431     Set<PsiTypeParameter> params = new LinkedHashSet<>();
432     Collections.addAll(params, containingClass.getTypeParameters());
433     if (listOwner != null) {
434       Collections.addAll(params, listOwner.getTypeParameters());
435     }
436     return params.toArray(PsiTypeParameter.EMPTY_ARRAY);
437   }
438
439
440   private static MethodCandidateInfo createMethodCandidate(@NotNull final MethodCandidateInfo staticFactoryMethod,
441                                                            final PsiElement parent,
442                                                            final boolean varargs,
443                                                            final PsiExpressionList argumentList) {
444     return new MethodCandidateInfo(staticFactoryMethod.getElement(), PsiSubstitutor.EMPTY, !staticFactoryMethod.isAccessible(), false, argumentList, parent, null, null) {
445       private PsiType[] myExpressionTypes;
446
447       @Override
448       public boolean isVarargs() {
449         return varargs;
450       }
451
452       @Override
453       protected PsiElement getParent() {
454         return parent;
455       }
456
457       @Override
458       public PsiType[] getArgumentTypes() {
459         if (myExpressionTypes == null) {
460           final PsiType[] expressionTypes = argumentList.getExpressionTypes();
461           if (MethodCandidateInfo.isOverloadCheck()) {
462             return expressionTypes;
463           }
464           myExpressionTypes = expressionTypes;
465         }
466         return myExpressionTypes;
467       }
468     };
469   }
470
471   public static boolean hasDefaultConstructor(@NotNull final PsiClass psiClass) {
472     final PsiMethod[] constructors = psiClass.getConstructors();
473     for (PsiMethod method : constructors) {
474       if (method.getParameterList().isEmpty()) return true;
475     }
476     return constructors.length == 0;
477   }
478
479   public static boolean haveConstructorsGenericsParameters(@NotNull final PsiClass psiClass) {
480     for (final PsiMethod method : psiClass.getConstructors()) {
481       for (PsiParameter parameter : method.getParameterList().getParameters()) {
482         final PsiType type = parameter.getType();
483         final Boolean accept = type.accept(new PsiTypeVisitor<Boolean>() {
484           @Override
485           public Boolean visitArrayType(PsiArrayType arrayType) {
486             return arrayType.getComponentType().accept(this);
487           }
488
489           @Override
490           public Boolean visitClassType(PsiClassType classType) {
491             for (PsiType psiType : classType.getParameters()) {
492               if (psiType != null) {
493                 final Boolean typeParamFound = psiType.accept(this);
494                 if (typeParamFound != null && typeParamFound) return true;
495               }
496             }
497             final PsiClass aClass = PsiUtil.resolveClassInType(classType);
498             return aClass instanceof PsiTypeParameter && ((PsiTypeParameter)aClass).getOwner() == method;
499           }
500
501           @Override
502           public Boolean visitWildcardType(PsiWildcardType wildcardType) {
503             final PsiType bound = wildcardType.getBound();
504             if (bound == null) return false;
505             return bound.accept(this);
506           }
507         });
508         if (accept != null && accept.booleanValue()) return true;
509       }
510     }
511     return false;
512   }
513
514   /**
515    * from JDK-8062373 Allow diamond to be used with anonymous classes
516    * It is a compile-time error if the superclass or superinterface type of the anonymous class, T, or any subexpression of T, has one of the following forms:
517    *  - A type variable (4.4) that was not declared as a type parameter (such as a type variable produced by capture conversion (5.1.10)) 
518    *  - An intersection type (4.9) 
519    *  - A class or interface type, where the class or interface declaration is not accessible from the class or interface in which the expression appears.
520    * 
521    * The term "subexpression" includes type arguments of parameterized types (4.5), bounds of wildcards (4.5.1), and element types of array types (10.1).
522    * It excludes bounds of type variables.
523    */
524   private static class InferredAnonymousTypeVisitor extends PsiTypeVisitor<Boolean> {
525     private final PsiElement myExpression;
526
527     InferredAnonymousTypeVisitor(PsiElement expression) {
528       myExpression = expression;
529     }
530
531     @Nullable
532     @Override
533     public Boolean visitType(PsiType type) {
534       return true;
535     }
536
537     @Nullable
538     @Override
539     public Boolean visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
540       return false;
541     }
542
543     @Nullable
544     @Override
545     public Boolean visitIntersectionType(PsiIntersectionType intersectionType) {
546       return false;
547     }
548
549     @Nullable
550     @Override
551     public Boolean visitClassType(PsiClassType classType) {
552       final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics();
553       final PsiClass psiClass = resolveResult.getElement();
554       if (psiClass != null) {
555         if (psiClass instanceof PsiTypeParameter && TypeConversionUtil.isFreshVariable((PsiTypeParameter)psiClass)) {
556           return false;
557         }
558         
559         if (!PsiUtil.isAccessible(psiClass, myExpression, null)) {
560           return false;
561         }
562         
563         for (PsiType psiType : resolveResult.getSubstitutor().getSubstitutionMap().values()) {
564           final Boolean accepted = psiType != null ? psiType.accept(this) : null;
565           if (accepted != null && !accepted.booleanValue()) {
566             return false;
567           }
568         }
569       }
570       return true;
571     }
572   }
573 }