get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / ExpectedTypesProvider.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 package com.intellij.codeInsight;
3
4 import com.intellij.codeInsight.completion.CompletionMemory;
5 import com.intellij.codeInsight.completion.CompletionUtil;
6 import com.intellij.codeInsight.completion.InsertionContext;
7 import com.intellij.codeInsight.completion.JavaMethodCallElement;
8 import com.intellij.codeInsight.daemon.impl.analysis.LambdaHighlightingUtil;
9 import com.intellij.codeInsight.hints.ParameterHintsPass;
10 import com.intellij.openapi.components.ServiceManager;
11 import com.intellij.openapi.diagnostic.Logger;
12 import com.intellij.openapi.editor.Editor;
13 import com.intellij.openapi.project.Project;
14 import com.intellij.openapi.util.NullableComputable;
15 import com.intellij.pom.java.LanguageLevel;
16 import com.intellij.psi.*;
17 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
18 import com.intellij.psi.codeStyle.VariableKind;
19 import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy;
20 import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
21 import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
22 import com.intellij.psi.infos.CandidateInfo;
23 import com.intellij.psi.infos.MethodCandidateInfo;
24 import com.intellij.psi.search.GlobalSearchScope;
25 import com.intellij.psi.search.PsiShortNamesCache;
26 import com.intellij.psi.tree.IElementType;
27 import com.intellij.psi.util.*;
28 import com.intellij.util.ArrayUtil;
29 import com.intellij.util.JavaPsiConstructorUtil;
30 import com.intellij.util.ObjectUtils;
31 import com.intellij.util.containers.ContainerUtil;
32 import com.intellij.util.containers.Stack;
33 import com.siyeh.ig.psiutils.TypeUtils;
34 import gnu.trove.THashSet;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38
39 import java.util.*;
40
41 /**
42  * @author ven
43  */
44 public class ExpectedTypesProvider {
45   private static final ExpectedTypeInfo VOID_EXPECTED = createInfoImpl(PsiType.VOID, ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.VOID, TailType.SEMICOLON);
46
47   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ExpectedTypesProvider");
48
49   public static ExpectedTypesProvider getInstance(@NotNull Project project) {
50     return ServiceManager.getService(project, ExpectedTypesProvider.class);
51   }
52
53   private static final int MAX_COUNT = 50;
54   private static final ExpectedClassProvider ourGlobalScopeClassProvider = new ExpectedClassProvider() {
55     @Override
56     @NotNull
57     public PsiField[] findDeclaredFields(@NotNull final PsiManager manager, @NotNull String name) {
58       final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(manager.getProject());
59       GlobalSearchScope scope = GlobalSearchScope.allScope(manager.getProject());
60       return cache.getFieldsByName(name, scope);
61     }
62
63     @Override
64     @NotNull
65     public PsiMethod[] findDeclaredMethods(@NotNull final PsiManager manager, @NotNull String name) {
66       Project project = manager.getProject();
67       final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
68       GlobalSearchScope sources = GlobalSearchScope.projectScope(project);
69       GlobalSearchScope libraries = GlobalSearchScope.notScope(sources);
70       PsiMethod[] sourceMethods = cache.getMethodsByNameIfNotMoreThan(name, sources, MAX_COUNT);
71       if (sourceMethods.length >= MAX_COUNT) return sourceMethods;
72       PsiMethod[] libraryMethods = cache.getMethodsByNameIfNotMoreThan(name, libraries, MAX_COUNT-sourceMethods.length);
73       return ArrayUtil.mergeArrays(sourceMethods, libraryMethods);
74     }
75   };
76   private static final PsiType[] PRIMITIVE_TYPES = {PsiType.BYTE, PsiType.CHAR, PsiType.SHORT, PsiType.INT, PsiType.LONG, PsiType.FLOAT, PsiType.DOUBLE};
77
78   @NotNull
79   public static ExpectedTypeInfo createInfo(@NotNull  PsiType type, @ExpectedTypeInfo.Type int kind, PsiType defaultType, @NotNull TailType tailType) {
80     return createInfoImpl(type, kind, defaultType, tailType);
81   }
82
83   @NotNull
84   private static ExpectedTypeInfoImpl createInfoImpl(@NotNull PsiType type, PsiType defaultType) {
85     return createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, defaultType, TailType.NONE);
86   }
87
88   @NotNull
89   private static ExpectedTypeInfoImpl createInfoImpl(@NotNull PsiType type, @ExpectedTypeInfo.Type int kind, PsiType defaultType, @NotNull TailType tailType) {
90     return new ExpectedTypeInfoImpl(type, kind, defaultType, tailType, null, ExpectedTypeInfoImpl.NULL);
91   }
92   @NotNull
93   private static ExpectedTypeInfoImpl createInfoImpl(@NotNull PsiType type,
94                                                      int kind,
95                                                      PsiType defaultType,
96                                                      @NotNull TailType tailType,
97                                                      PsiMethod calledMethod,
98                                                      NullableComputable<String> expectedName) {
99     return new ExpectedTypeInfoImpl(type, kind, defaultType, tailType, calledMethod, expectedName);
100   }
101
102   @Nullable
103   public static ExpectedTypeInfo getSingleExpectedTypeForCompletion(@Nullable PsiExpression expr) {
104     ExpectedTypeInfo[] expectedTypes = getExpectedTypes(expr, true, ourGlobalScopeClassProvider, false, false, 1);
105     if (expectedTypes.length > 0) {
106       return expectedTypes[0];
107     }
108     return null;
109   }
110
111   @NotNull
112   public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion) {
113     return getExpectedTypes(expr, forCompletion, false, false);
114   }
115
116   @NotNull
117   public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion, final boolean voidable, boolean usedAfter) {
118     return getExpectedTypes(expr, forCompletion, ourGlobalScopeClassProvider, voidable, usedAfter);
119   }
120
121   @NotNull
122   public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr,
123                                                     boolean forCompletion,
124                                                     ExpectedClassProvider classProvider, boolean usedAfter) {
125     return getExpectedTypes(expr, forCompletion, classProvider, false, usedAfter);
126   }
127
128   @NotNull
129   public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion, ExpectedClassProvider classProvider,
130                                                     final boolean voidable, boolean usedAfter) {
131     return getExpectedTypes(expr, forCompletion, classProvider, voidable, usedAfter, Integer.MAX_VALUE);
132   }
133
134   @NotNull
135   private static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr,
136                                                      boolean forCompletion,
137                                                      ExpectedClassProvider classProvider,
138                                                      boolean voidable,
139                                                      boolean usedAfter, int maxCandidates) {
140     if (expr == null) return ExpectedTypeInfo.EMPTY_ARRAY;
141     PsiElement parent = expr.getParent();
142     PsiFunctionalExpression functionalExpression = extractFunctionalExpression(expr);
143     if (functionalExpression != null) {
144       final Collection<? extends PsiType> types = FunctionalInterfaceSuggester.suggestFunctionalInterfaces(functionalExpression);
145       if (types.isEmpty()) {
146         return ExpectedTypeInfo.EMPTY_ARRAY;
147       }
148       else {
149         final ExpectedTypeInfo[] result = new ExpectedTypeInfo[types.size()];
150         int i = 0;
151         for (PsiType type : types) {
152           result[i++] = new ExpectedTypeInfoImpl(type, ExpectedTypeInfo.TYPE_SAME_SHAPED, type, TailType.NONE, null, ExpectedTypeInfoImpl.NULL);
153         }
154         return result;
155       }
156     }
157     MyParentVisitor visitor = new MyParentVisitor(expr, forCompletion, classProvider, voidable, usedAfter, maxCandidates);
158     if (parent != null) {
159       parent.accept(visitor);
160     }
161     return visitor.getResult();
162   }
163
164   private static PsiFunctionalExpression extractFunctionalExpression(PsiExpression expr) {
165     PsiElement parent = expr.getParent();
166     if (expr instanceof PsiFunctionalExpression && parent instanceof PsiExpressionStatement && !(parent.getParent() instanceof PsiSwitchLabeledRuleStatement)) {
167       return (PsiFunctionalExpression)expr;
168     }
169     parent = PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class);
170     if (parent instanceof PsiAssignmentExpression &&
171         parent.getParent() instanceof PsiExpressionStatement &&
172         PsiTreeUtil.isAncestor(((PsiAssignmentExpression)parent).getLExpression(), expr, false)) {
173       return ObjectUtils.tryCast(((PsiAssignmentExpression)parent).getRExpression(), PsiFunctionalExpression.class);
174     }
175     return null;
176   }
177
178   @NotNull
179   public static PsiType[] processExpectedTypes(@NotNull ExpectedTypeInfo[] infos,
180                                                @NotNull PsiTypeVisitor<? extends PsiType> visitor, @NotNull Project project) {
181     LinkedHashSet<PsiType> set = new LinkedHashSet<>();
182     for (ExpectedTypeInfo info : infos) {
183       ExpectedTypeInfoImpl infoImpl = (ExpectedTypeInfoImpl)info;
184
185       if (infoImpl.getDefaultType() instanceof PsiClassType) {
186         JavaResolveResult result = ((PsiClassType)infoImpl.getDefaultType()).resolveGenerics();
187         PsiClass aClass = (PsiClass)result.getElement();
188         if (aClass instanceof PsiAnonymousClass) {
189           processType(((PsiAnonymousClass)aClass).getBaseClassType(), visitor, set);
190           ((PsiAnonymousClass)aClass).getBaseClassType().accept(visitor);
191         }
192         else {
193           processType(infoImpl.getDefaultType(), visitor, set);
194         }
195       }
196       else {
197         processType(infoImpl.getDefaultType(), visitor, set);
198       }
199
200       if (infoImpl.getKind() == ExpectedTypeInfo.TYPE_OR_SUPERTYPE) {
201         processAllSuperTypes(infoImpl.getType(), visitor, project, set, new HashSet<>());
202       }
203       else if (infoImpl.getKind() == ExpectedTypeInfo.TYPE_OR_SUBTYPE) {
204         if (infoImpl.getType() instanceof PsiPrimitiveType) {
205           processPrimitiveTypeAndSubtypes((PsiPrimitiveType)infoImpl.getType(), visitor, set);
206         }
207         //else too expensive to search
208       }
209     }
210
211     return set.toArray(PsiType.createArray(set.size()));
212   }
213
214   private static void processType(@NotNull PsiType type, @NotNull PsiTypeVisitor<? extends PsiType> visitor, @NotNull Set<? super PsiType> typeSet) {
215     PsiType accepted = type.accept(visitor);
216     if (accepted != null) typeSet.add(accepted);
217   }
218
219   private static void processPrimitiveTypeAndSubtypes(@NotNull PsiPrimitiveType type,
220                                                       @NotNull PsiTypeVisitor<? extends PsiType> visitor,
221                                                       @NotNull Set<? super PsiType> set) {
222     if (type.equals(PsiType.BOOLEAN) || type.equals(PsiType.VOID) || type.equals(PsiType.NULL)) return;
223
224     for (int i = 0; ; i++) {
225       final PsiType primitive = PRIMITIVE_TYPES[i];
226       processType(primitive, visitor, set);
227       if (primitive.equals(type)) return;
228     }
229   }
230
231   public static void processAllSuperTypes(@NotNull PsiType type, @NotNull PsiTypeVisitor<? extends PsiType> visitor, @NotNull Project project, @NotNull Set<? super PsiType> set, @NotNull Set<? super PsiType> visited) {
232     if (!visited.add(type)) return;
233
234     if (type instanceof PsiPrimitiveType) {
235       if (type.equals(PsiType.BOOLEAN) || type.equals(PsiType.VOID) || type.equals(PsiType.NULL)) return;
236
237       Stack<PsiType> stack = new Stack<>();
238       for (int i = PRIMITIVE_TYPES.length - 1; !PRIMITIVE_TYPES[i].equals(type); i--) {
239         stack.push(PRIMITIVE_TYPES[i]);
240       }
241       while(!stack.empty()) {
242         processType(stack.pop(), visitor, set);
243       }
244     }
245     else{
246       PsiManager manager = PsiManager.getInstance(project);
247       GlobalSearchScope resolveScope = type.getResolveScope();
248       if (resolveScope == null) resolveScope = GlobalSearchScope.allScope(project);
249       PsiClassType objectType = PsiType.getJavaLangObject(manager, resolveScope);
250       processType(objectType, visitor, set);
251
252       if (type instanceof PsiClassType) {
253         for (PsiType superType : type.getSuperTypes()) {
254           processType(superType, visitor, set);
255           processAllSuperTypes(superType, visitor, project, set, visited);
256         }
257       }
258     }
259   }
260
261   private static class MyParentVisitor extends JavaElementVisitor {
262     private PsiExpression myExpr;
263     private final boolean myForCompletion;
264     private final boolean myUsedAfter;
265     private final int myMaxCandidates;
266     private final ExpectedClassProvider myClassProvider;
267     private final boolean myVoidable;
268     final List<ExpectedTypeInfo> myResult = new ArrayList<>();
269     @NonNls private static final String LENGTH_SYNTHETIC_ARRAY_FIELD = "length";
270
271     private MyParentVisitor(PsiExpression expr,
272                             boolean forCompletion,
273                             ExpectedClassProvider classProvider,
274                             boolean voidable,
275                             boolean usedAfter,
276                             int maxCandidates) {
277       myExpr = expr;
278       myForCompletion = forCompletion;
279       myClassProvider = classProvider;
280       myVoidable = voidable;
281       myUsedAfter = usedAfter;
282       myMaxCandidates = maxCandidates;
283     }
284
285     @NotNull
286     public ExpectedTypeInfo[] getResult() {
287       if (myResult.size() > myMaxCandidates) {
288         return myResult.subList(0, myMaxCandidates).toArray(ExpectedTypeInfo.EMPTY_ARRAY);
289       }
290       return myResult.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
291     }
292
293     @Override
294     public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
295       PsiElement parent = expression.getParent();
296       if (parent != null) {
297         final MyParentVisitor visitor = new MyParentVisitor(expression, myForCompletion, myClassProvider, myVoidable, myUsedAfter,
298                                                             myMaxCandidates);
299         parent.accept(visitor);
300         for (final ExpectedTypeInfo info : visitor.myResult) {
301           myResult.add(createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailTypes.RPARENTH, info.getCalledMethod(),
302                                       ((ExpectedTypeInfoImpl)info)::getExpectedName));
303         }
304       }
305     }
306
307     @Override
308     public void visitAnnotationMethod(@NotNull final PsiAnnotationMethod method) {
309       if (myExpr == method.getDefaultValue()) {
310         final PsiType type = method.getReturnType();
311         if (type != null) {
312           myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.SEMICOLON));
313         }
314       }
315     }
316
317     @Override
318     public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
319       if (myForCompletion) {
320         final MyParentVisitor visitor = new MyParentVisitor(expression, true, myClassProvider, myVoidable, myUsedAfter, myMaxCandidates);
321         expression.getParent().accept(visitor);
322         myResult.addAll(visitor.myResult);
323         return;
324       }
325
326       String referenceName = expression.getReferenceName();
327       if (referenceName != null) {
328         final PsiElement parent = expression.getParent();
329         if (parent instanceof PsiMethodCallExpression) {
330           Collections.addAll(myResult, findClassesWithDeclaredMethod((PsiMethodCallExpression)parent));
331         }
332         else if (parent instanceof PsiVariable ||
333                  parent instanceof PsiExpression) {
334           if (LENGTH_SYNTHETIC_ARRAY_FIELD.equals(referenceName)) {
335             myResult.addAll(anyArrayType());
336           }
337           else {
338             Collections.addAll(myResult, findClassesWithDeclaredField(expression));
339           }
340         }
341       }
342     }
343
344     @Override
345     public void visitExpressionStatement(PsiExpressionStatement statement) {
346       if (statement.getParent() instanceof PsiSwitchLabeledRuleStatement) {
347         PsiSwitchBlock block = ((PsiSwitchLabeledRuleStatement)statement.getParent()).getEnclosingSwitchBlock();
348         if (block instanceof PsiSwitchExpression) {
349           Collections.addAll(myResult, getExpectedTypes((PsiExpression)block, myForCompletion));
350           return;
351         }
352       }
353       if (myVoidable) {
354         myResult.add(VOID_EXPECTED);
355       }
356     }
357
358     @Override
359     public void visitBreakStatement(PsiBreakStatement statement) {
360       PsiElement exitedElement = statement.findExitedElement();
361       if (exitedElement instanceof PsiSwitchExpression) {
362         Collections.addAll(myResult, getExpectedTypes((PsiSwitchExpression)exitedElement, myForCompletion));
363       }
364     }
365
366     @Override
367     public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
368       myExpr = (PsiExpression)myExpr.getParent();
369       expression.getParent().accept(this);
370     }
371
372     @Override
373     public void visitAnnotationArrayInitializer(@NotNull PsiArrayInitializerMemberValue initializer) {
374       PsiElement parent = initializer.getParent();
375       while (parent instanceof PsiArrayInitializerMemberValue) {
376         parent = parent.getParent();
377       }
378       final PsiType type;
379       if (parent instanceof PsiNameValuePair) {
380         type = getAnnotationMethodType((PsiNameValuePair)parent);
381       }
382       else {
383         type = ((PsiAnnotationMethod)parent).getReturnType();
384       }
385       if (type instanceof PsiArrayType) {
386         final PsiType componentType = ((PsiArrayType)type).getComponentType();
387         myResult.add(createInfoImpl(componentType, componentType));
388       }
389     }
390
391     @Override
392     public void visitNameValuePair(@NotNull PsiNameValuePair pair) {
393       final PsiType type = getAnnotationMethodType(pair);
394       if (type == null) return;
395       if (type instanceof PsiArrayType) {
396         PsiType componentType = ((PsiArrayType)type).getComponentType();
397         myResult.add(createInfoImpl(componentType, componentType));
398       }
399       else {
400         myResult.add(createInfoImpl(type, type));
401       }
402     }
403
404     @Nullable
405     private static PsiType getAnnotationMethodType(@NotNull final PsiNameValuePair pair) {
406       final PsiReference reference = pair.getReference();
407       if (reference != null) {
408         final PsiElement method = reference.resolve();
409         if (method instanceof PsiMethod) {
410           return ((PsiMethod)method).getReturnType();
411         }
412       }
413       return null;
414     }
415
416     @Override
417     public void visitLambdaExpression(PsiLambdaExpression lambdaExpression) {
418       super.visitLambdaExpression(lambdaExpression);
419       final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
420       final PsiMethod scopeMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
421       if (scopeMethod != null) {
422         visitMethodReturnType(scopeMethod, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType), LambdaHighlightingUtil
423           .insertSemicolonAfter(lambdaExpression));
424       }
425     }
426
427     @Override
428     public void visitReturnStatement(PsiReturnStatement statement) {
429       final PsiMethod method;
430       final PsiType type;
431       final boolean tailTypeSemicolon;
432       final NavigatablePsiElement psiElement = PsiTreeUtil.getParentOfType(statement, PsiLambdaExpression.class, PsiMethod.class);
433       if (psiElement instanceof PsiLambdaExpression) {
434         final PsiType functionalInterfaceType = ((PsiLambdaExpression)psiElement).getFunctionalInterfaceType();
435         method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
436         type = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
437         tailTypeSemicolon = LambdaHighlightingUtil.insertSemicolonAfter((PsiLambdaExpression)psiElement);
438       }
439       else if (psiElement instanceof PsiMethod) {
440         method = (PsiMethod)psiElement;
441         type = method.getReturnType();
442         tailTypeSemicolon = true;
443       } else {
444         method = null;
445         type = null;
446         tailTypeSemicolon = true;
447       }
448       if (method != null) {
449         visitMethodReturnType(method, type, tailTypeSemicolon);
450       }
451
452     }
453
454     private void visitMethodReturnType(final PsiMethod scopeMethod, PsiType type, boolean tailTypeSemicolon) {
455       if (type != null) {
456         NullableComputable<String> expectedName;
457         if (PropertyUtilBase.isSimplePropertyAccessor(scopeMethod)) {
458           expectedName = () -> PropertyUtilBase.getPropertyName(scopeMethod);
459         }
460         else {
461           expectedName = ExpectedTypeInfoImpl.NULL;
462         }
463
464         myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type,
465                                                    tailTypeSemicolon ? TailType.SEMICOLON : TailType.NONE, null, expectedName));
466       }
467     }
468
469     @Override
470     public void visitIfStatement(PsiIfStatement statement) {
471       myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailTypes.IF_RPARENTH));
472     }
473
474     @Override
475     public void visitWhileStatement(PsiWhileStatement statement) {
476       myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailTypes.WHILE_RPARENTH));
477     }
478
479     @Override
480     public void visitDoWhileStatement(PsiDoWhileStatement statement) {
481       myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailTypes.WHILE_RPARENTH));
482     }
483
484     @Override
485     public void visitForStatement(@NotNull PsiForStatement statement) {
486       if (myExpr.equals(statement.getCondition())) {
487         myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.SEMICOLON));
488       }
489     }
490
491     @Override
492     public void visitAssertStatement(@NotNull PsiAssertStatement statement) {
493       if (statement.getAssertDescription() == myExpr) {
494         final PsiClassType stringType = PsiType.getJavaLangString(myExpr.getManager(), myExpr.getResolveScope());
495         myResult.add(createInfoImpl(stringType, ExpectedTypeInfo.TYPE_STRICTLY, stringType, TailType.SEMICOLON));
496       }
497       else {
498         myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.SEMICOLON));
499       }
500     }
501
502     @Override
503     public void visitForeachStatement(@NotNull PsiForeachStatement statement) {
504       if (myExpr.equals(statement.getIteratedValue())) {
505         PsiType type = statement.getIterationParameter().getType();
506
507         if (PsiType.NULL.equals(type)) return;
508
509         PsiType arrayType = type.createArrayType();
510         myResult.add(createInfoImpl(arrayType, arrayType));
511
512         PsiManager manager = statement.getManager();
513         PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
514         PsiClass iterableClass =
515           JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Iterable", statement.getResolveScope());
516         if (iterableClass != null && iterableClass.getTypeParameters().length == 1) {
517           Map<PsiTypeParameter, PsiType> map = new HashMap<>();
518           map.put(iterableClass.getTypeParameters()[0], PsiWildcardType.createExtends(manager, type));
519           PsiType iterableType = factory.createType(iterableClass, factory.createSubstitutor(map));
520           myResult.add(createInfoImpl(iterableType, iterableType));
521         }
522       }
523     }
524
525     @Override
526     public void visitSwitchStatement(@NotNull PsiSwitchStatement statement) {
527       processSwitchBlock(statement);
528     }
529
530     @Override
531     public void visitSwitchExpression(@NotNull PsiSwitchExpression expression) {
532       processSwitchBlock(expression);
533     }
534
535     public void processSwitchBlock(@NotNull PsiSwitchBlock statement) {
536       myResult.add(createInfoImpl(PsiType.LONG, PsiType.INT));
537       LanguageLevel level = PsiUtil.getLanguageLevel(statement);
538       if (level.isAtLeast(LanguageLevel.JDK_1_5)) {
539         PsiClassType enumType = TypeUtils.getType(CommonClassNames.JAVA_LANG_ENUM, statement);
540         myResult.add(createInfoImpl(enumType, enumType));
541
542         if (level.isAtLeast(LanguageLevel.JDK_1_7)) {
543           PsiClassType stringType = TypeUtils.getStringType(statement);
544           myResult.add(createInfoImpl(stringType, stringType));
545         }
546       }
547     }
548
549     @Override
550     public void visitSynchronizedStatement(@NotNull PsiSynchronizedStatement statement) {
551       PsiElementFactory factory = JavaPsiFacade.getElementFactory(statement.getProject());
552       PsiType objectType = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, myExpr.getResolveScope());
553       myResult.add(createInfoImpl(objectType, objectType));
554     }
555
556     @Override
557     public void visitVariable(@NotNull PsiVariable variable) {
558       PsiType type = variable.getType();
559       TailType tail = variable instanceof PsiResourceVariable ? TailType.NONE : TailType.SEMICOLON;
560       myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, tail, null, getPropertyName(variable)));
561     }
562
563     @Override
564     public void visitAssignmentExpression(@NotNull PsiAssignmentExpression assignment) {
565       if (myExpr.equals(assignment.getRExpression())) {
566         PsiExpression lExpr = assignment.getLExpression();
567         PsiType type = lExpr.getType();
568         if (type != null) {
569           TailType tailType = getAssignmentRValueTailType(assignment);
570           NullableComputable<String> expectedName = ExpectedTypeInfoImpl.NULL;
571           if (lExpr instanceof PsiReferenceExpression) {
572             PsiElement refElement = ((PsiReferenceExpression)lExpr).resolve();
573             if (refElement instanceof PsiVariable) {
574               expectedName = getPropertyName((PsiVariable)refElement);
575             }
576           }
577           myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, tailType, null, expectedName));
578         }
579       }
580       else {
581         if (myForCompletion) {
582           myExpr = (PsiExpression)myExpr.getParent();
583           assignment.getParent().accept(this);
584           return;
585         }
586
587         PsiExpression rExpr = assignment.getRExpression();
588         if (rExpr != null) {
589           PsiType type = rExpr.getType();
590           if (type != null && type != PsiType.NULL) {
591             if (type instanceof PsiClassType) {
592               final PsiClass resolved = ((PsiClassType)type).resolve();
593               if (resolved instanceof PsiAnonymousClass) {
594                 type = ((PsiAnonymousClass)resolved).getBaseClassType();
595               }
596             }
597             final int kind = assignment.getOperationTokenType() != JavaTokenType.EQ
598                              ? ExpectedTypeInfo.TYPE_STRICTLY
599                              : ExpectedTypeInfo.TYPE_OR_SUPERTYPE;
600             myResult.add(createInfoImpl(type, kind, type, TailType.NONE));
601           }
602         }
603       }
604     }
605
606     @NotNull
607     private static TailType getAssignmentRValueTailType(@NotNull PsiAssignmentExpression assignment) {
608       if (assignment.getParent() instanceof PsiExpressionStatement) {
609         if (!(assignment.getParent().getParent() instanceof PsiForStatement)) {
610           return TailType.SEMICOLON;
611         }
612
613         PsiForStatement forStatement = (PsiForStatement)assignment.getParent().getParent();
614         if (!assignment.getParent().equals(forStatement.getUpdate())) {
615           return TailType.SEMICOLON;
616         }
617       }
618       return TailType.NONE;
619     }
620
621     @Override
622     public void visitExpressionList(@NotNull PsiExpressionList list) {
623       PsiResolveHelper helper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper();
624       PsiElement parent = list.getParent();
625       if (parent instanceof PsiMethodCallExpression) {
626         PsiMethodCallExpression methodCall = (PsiMethodCallExpression)parent;
627         CandidateInfo[] candidates = helper.getReferencedMethodCandidates(methodCall, false, true);
628         Collections.addAll(myResult, getExpectedArgumentTypesForMethodCall(candidates, list, myExpr, myForCompletion));
629       }
630       else if (parent instanceof PsiEnumConstant) {
631         getExpectedArgumentsTypesForEnumConstant((PsiEnumConstant)parent, list);
632       }
633       else if (parent instanceof PsiNewExpression) {
634         getExpectedArgumentsTypesForNewExpression((PsiNewExpression)parent, list);
635       }
636       else if (parent instanceof PsiAnonymousClass) {
637         getExpectedArgumentsTypesForNewExpression((PsiNewExpression)parent.getParent(), list);
638       }
639       else if (parent instanceof PsiSwitchLabelStatementBase) {
640         PsiSwitchBlock switchBlock = ((PsiSwitchLabelStatementBase)parent).getEnclosingSwitchBlock();
641         if (switchBlock != null) {
642           PsiExpression expression = switchBlock.getExpression();
643           if (expression != null) {
644             PsiType type = expression.getType();
645             if (type != null) {
646               myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailTypes.forSwitchLabel(switchBlock)));
647             }
648           }
649         }
650       }
651     }
652
653     private void getExpectedArgumentsTypesForEnumConstant(@NotNull final PsiEnumConstant enumConstant,
654                                                           @NotNull final PsiExpressionList list) {
655       final PsiClass aClass = enumConstant.getContainingClass();
656       if (aClass != null) {
657         LOG.assertTrue(aClass.isEnum());
658         getExpectedTypesForConstructorCall(aClass, list, PsiSubstitutor.EMPTY);
659       }
660     }
661
662     private void getExpectedArgumentsTypesForNewExpression(@NotNull final PsiNewExpression newExpr,
663                                                            @NotNull final PsiExpressionList list) {
664       if (PsiDiamondType.hasDiamond(newExpr)) {
665         final List<CandidateInfo> candidates = PsiDiamondTypeImpl.collectStaticFactories(newExpr);
666         if (candidates != null) {
667           final PsiExpressionList argumentList = Objects.requireNonNull(newExpr.getArgumentList());
668           Collections.addAll(myResult, getExpectedArgumentTypesForMethodCall(candidates.toArray(CandidateInfo.EMPTY_ARRAY), argumentList, myExpr, myForCompletion));
669         }
670         return;
671       }
672       PsiType newType = newExpr.getType();
673       if (newType instanceof PsiClassType) {
674         JavaResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(newType);
675         PsiClass newClass = (PsiClass)resolveResult.getElement();
676         final PsiSubstitutor substitutor;
677         if (newClass instanceof PsiAnonymousClass) {
678           final PsiAnonymousClass anonymous = (PsiAnonymousClass)newClass;
679           newClass = anonymous.getBaseClassType().resolve();
680           if (newClass == null) return;
681
682           substitutor = TypeConversionUtil.getSuperClassSubstitutor(newClass, anonymous, PsiSubstitutor.EMPTY);
683         } else if (newClass != null) {
684           substitutor = resolveResult.getSubstitutor();
685         }
686         else {
687           return;
688         }
689         getExpectedTypesForConstructorCall(newClass, list, substitutor);
690       }
691     }
692
693     private void getExpectedTypesForConstructorCall(@NotNull PsiClass referencedClass,
694                                                     @NotNull PsiExpressionList argumentList,
695                                                     @NotNull PsiSubstitutor substitutor) {
696       List<CandidateInfo> array = new ArrayList<>();
697       for (PsiMethod constructor : referencedClass.getConstructors()) {
698         array.add(new MethodCandidateInfo(constructor, substitutor, false, false, argumentList, null, argumentList.getExpressionTypes(), null));
699       }
700       CandidateInfo[] candidates = array.toArray(CandidateInfo.EMPTY_ARRAY);
701       Collections.addAll(myResult, getExpectedArgumentTypesForMethodCall(candidates, argumentList, myExpr, myForCompletion));
702     }
703
704     @Override
705     public void visitPolyadicExpression(@NotNull PsiPolyadicExpression expr) {
706       PsiExpression[] operands = expr.getOperands();
707       final int index = Arrays.asList(operands).indexOf(myExpr);
708       if (index < 0) return; // broken syntax
709
710       IElementType op = expr.getOperationTokenType();
711       PsiExpression anotherExpr = index > 0 ? operands[0] : 1 < operands.length ? operands[1] : null;
712
713       if (myForCompletion && index == 0) {
714         if (op == JavaTokenType.EQEQ || op == JavaTokenType.NE) {
715           ContainerUtil.addIfNotNull(myResult, getEqualsType(anotherExpr));
716         }
717         final MyParentVisitor visitor = new MyParentVisitor(expr, true, myClassProvider, myVoidable, myUsedAfter, myMaxCandidates);
718         myExpr = (PsiExpression)myExpr.getParent();
719         expr.getParent().accept(visitor);
720         myResult.addAll(visitor.myResult);
721         if (!(expr.getParent() instanceof PsiExpressionList)) {
722           for (int i = 0; i < myResult.size(); i++) {
723             final ExpectedTypeInfo info = myResult.get(i);
724             myResult.set(i, createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailType.NONE, info.getCalledMethod(),
725                                            ((ExpectedTypeInfoImpl)info)::getExpectedName
726             ));
727           }
728         }
729         return;
730       }
731
732       PsiType anotherType = anotherExpr != null ? anotherExpr.getType() : null;
733
734       if (op == JavaTokenType.MINUS ||
735           op == JavaTokenType.ASTERISK ||
736           op == JavaTokenType.DIV ||
737           op == JavaTokenType.PERC ||
738           op == JavaTokenType.LT ||
739           op == JavaTokenType.GT ||
740           op == JavaTokenType.LE ||
741           op == JavaTokenType.GE) {
742         if (anotherType != null) {
743           myResult.add(createInfoImpl(PsiType.DOUBLE, anotherType));
744         }
745       }
746       else if (op == JavaTokenType.PLUS) {
747         if (anotherType == null || anotherType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
748           PsiClassType objectType = PsiType.getJavaLangObject(expr.getManager(), expr.getResolveScope());
749           myResult.add(createInfoImpl(objectType, anotherType != null ? anotherType : objectType));
750         }
751         else if (PsiType.DOUBLE.isAssignableFrom(anotherType)) {
752           myResult.add(createInfoImpl(PsiType.DOUBLE, anotherType));
753         }
754       }
755       else if (op == JavaTokenType.EQEQ || op == JavaTokenType.NE) {
756         ContainerUtil.addIfNotNull(myResult, getEqualsType(anotherExpr));
757       }
758       else if (op == JavaTokenType.LTLT || op == JavaTokenType.GTGT || op == JavaTokenType.GTGTGT) {
759         if (anotherType != null) {
760           myResult.add(createInfoImpl(PsiType.LONG, ExpectedTypeInfo.TYPE_BETWEEN, PsiType.SHORT, TailType.NONE));
761         }
762       }
763       else if (op == JavaTokenType.OROR || op == JavaTokenType.ANDAND) {
764         myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.NONE));
765       }
766       else if (op == JavaTokenType.OR || op == JavaTokenType.XOR || op == JavaTokenType.AND) {
767         if (anotherType != null) {
768           ExpectedTypeInfoImpl info;
769           if (PsiType.BOOLEAN.equals(anotherType)) {
770             info = createInfoImpl(anotherType, ExpectedTypeInfo.TYPE_STRICTLY, anotherType, TailType.NONE);
771           }
772           else {
773             info = createInfoImpl(PsiType.LONG, anotherType);
774           }
775           myResult.add(info);
776         }
777       }
778     }
779
780     @Nullable
781     private static ExpectedTypeInfo getEqualsType(@Nullable PsiExpression anotherExpr) {
782       PsiType anotherType = anotherExpr != null ? anotherExpr.getType() : null;
783       if (anotherType == null) {
784         return null;
785       }
786
787       NullableComputable<String> expectedName = ExpectedTypeInfoImpl.NULL;
788       if (anotherExpr instanceof PsiReferenceExpression) {
789         PsiElement refElement = ((PsiReferenceExpression)anotherExpr).resolve();
790         if (refElement instanceof PsiVariable) {
791           expectedName = getPropertyName((PsiVariable)refElement);
792         }
793       }
794       ExpectedTypeInfoImpl info;
795       if (anotherType instanceof PsiPrimitiveType) {
796         if (PsiType.BOOLEAN.equals(anotherType)) {
797           info = createInfoImpl(anotherType, ExpectedTypeInfo.TYPE_STRICTLY, anotherType, TailType.NONE, null, expectedName);
798         }
799         else if (PsiType.NULL.equals(anotherType)) {
800           PsiType objectType = PsiType.getJavaLangObject(anotherExpr.getManager(), anotherExpr.getResolveScope());
801           info = createInfoImpl(objectType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, objectType, TailType.NONE, null, expectedName);
802         }
803         else {
804           info = createInfoImpl(PsiType.DOUBLE, ExpectedTypeInfo.TYPE_OR_SUBTYPE, anotherType, TailType.NONE, null, expectedName);
805         }
806       }
807       else {
808         info = createInfoImpl(anotherType, ExpectedTypeInfo.TYPE_STRICTLY, anotherType, TailType.NONE, null, expectedName);
809       }
810
811       return info;
812     }
813
814     @Override
815     public void visitPrefixExpression(@NotNull PsiPrefixExpression expr) {
816       IElementType i = expr.getOperationTokenType();
817       final PsiType type = expr.getType();
818       final PsiElement parent = expr.getParent();
819       final TailType tailType = parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getRExpression() == expr ?
820                                 getAssignmentRValueTailType((PsiAssignmentExpression)parent) :
821                                 TailType.NONE;
822       if (i == JavaTokenType.PLUSPLUS || i == JavaTokenType.MINUSMINUS || i == JavaTokenType.TILDE) {
823         ExpectedTypeInfoImpl info;
824         if (myUsedAfter && type != null) {
825           info = createInfoImpl(type, ExpectedTypeInfo.TYPE_STRICTLY, type, tailType);
826         }
827         else {
828           if (type != null) {
829             info = createInfoImpl(type, type instanceof PsiPrimitiveType ? ExpectedTypeInfo.TYPE_OR_SUPERTYPE : ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.INT, tailType);
830           }
831           else {
832             info = createInfoImpl(PsiType.LONG, ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.INT, tailType);
833           }
834         }
835         myResult.add(info);
836       }
837       else if (i == JavaTokenType.PLUS || i == JavaTokenType.MINUS) {
838         if (parent instanceof PsiStatement) {
839           myResult.add(createInfoImpl(PsiType.DOUBLE, ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.INT, tailType));
840         }
841         else {
842           myExpr = (PsiExpression)myExpr.getParent();
843           parent.accept(this);
844         }
845       }
846       else if (i == JavaTokenType.EXCL) {
847         myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, tailType));
848       }
849     }
850
851     @Override
852     public void visitPostfixExpression(@NotNull PsiPostfixExpression expr) {
853       if (myForCompletion) return;
854       PsiType type = expr.getType();
855       ExpectedTypeInfoImpl info;
856       if (myUsedAfter && type != null) {
857         info = createInfoImpl(type, ExpectedTypeInfo.TYPE_STRICTLY, type, TailType.NONE);
858       }
859       else {
860         if (type != null) {
861           info = createInfoImpl(type, type instanceof PsiPrimitiveType ? ExpectedTypeInfo.TYPE_OR_SUPERTYPE : ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.INT, TailType.NONE);
862         }
863         else {
864           info = createInfoImpl(PsiType.LONG, PsiType.INT);
865         }
866       }
867       myResult.add(info);
868     }
869
870     @Override
871     public void visitArrayInitializerExpression(@NotNull PsiArrayInitializerExpression expr) {
872       PsiElement pParent = expr.getParent();
873       PsiType arrayType = null;
874       if (pParent instanceof PsiVariable) {
875         arrayType = ((PsiVariable)pParent).getType();
876       }
877       else if (pParent instanceof PsiNewExpression) {
878         arrayType = ((PsiNewExpression)pParent).getType();
879       }
880       else if (pParent instanceof PsiArrayInitializerExpression) {
881         PsiType type = ((PsiArrayInitializerExpression)pParent).getType();
882         if (type instanceof PsiArrayType) {
883           arrayType = ((PsiArrayType)type).getComponentType();
884         }
885       }
886
887       if (arrayType instanceof PsiArrayType) {
888         PsiType componentType = ((PsiArrayType)arrayType).getComponentType();
889         myResult.add(createInfoImpl(componentType, componentType));
890       }
891     }
892
893     @Override
894     public void visitNewExpression(@NotNull PsiNewExpression expression) {
895       PsiExpression[] arrayDimensions = expression.getArrayDimensions();
896       for (PsiExpression dimension : arrayDimensions) {
897         if (myExpr.equals(dimension)) {
898           myResult.add(createInfoImpl(PsiType.INT, PsiType.INT));
899           return;
900         }
901       }
902     }
903
904     @Override
905     public void visitArrayAccessExpression(@NotNull PsiArrayAccessExpression expr) {
906       if (myExpr.equals(expr.getIndexExpression())) {
907         myResult.add(createInfoImpl(PsiType.INT, PsiType.INT));
908       }
909       else if (myExpr.equals(expr.getArrayExpression())) {
910         if (myForCompletion) {
911           myExpr = (PsiExpression)myExpr.getParent();
912           expr.getParent().accept(this);
913           return;
914         }
915
916         PsiElement parent = expr.getParent();
917         MyParentVisitor visitor = new MyParentVisitor(expr, false, myClassProvider, myVoidable, myUsedAfter, myMaxCandidates);
918         myExpr = (PsiExpression)myExpr.getParent();
919         parent.accept(visitor);
920         ExpectedTypeInfo[] componentTypeInfo = visitor.getResult();
921         if (componentTypeInfo.length == 0) {
922           myResult.addAll(anyArrayType());
923         }
924         else {
925           for (ExpectedTypeInfo compInfo : componentTypeInfo) {
926             PsiType expectedArrayType = compInfo.getType().createArrayType();
927             myResult.add(createInfoImpl(expectedArrayType, expectedArrayType));
928           }
929         }
930       }
931     }
932
933     @Override
934     public void visitConditionalExpression(@NotNull PsiConditionalExpression expr) {
935       if (myExpr.equals(expr.getCondition())) {
936         if (myForCompletion) {
937           myExpr = expr;
938           myExpr.getParent().accept(this);
939           return;
940         }
941
942         myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.NONE));
943       }
944       else if (myExpr.equals(expr.getThenExpression())) {
945         ExpectedTypeInfo[] types = getExpectedTypes(expr, myForCompletion, ourGlobalScopeClassProvider, false, false, myMaxCandidates);
946         for (int i = 0; i < types.length; i++) {
947           final ExpectedTypeInfo info = types[i];
948           types[i] = createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailType.COND_EXPR_COLON, info.getCalledMethod(),
949                                     ((ExpectedTypeInfoImpl)info)::getExpectedName);
950         }
951         Collections.addAll(myResult, types);
952       }
953       else {
954         if (!myExpr.equals(expr.getElseExpression())) {
955           LOG.error(Arrays.asList(expr.getChildren()) + "; " + myExpr);
956         }
957         Collections.addAll(myResult, getExpectedTypes(expr, myForCompletion, ourGlobalScopeClassProvider, false, false, myMaxCandidates));
958       }
959     }
960
961     @Override
962     public void visitThrowStatement(@NotNull PsiThrowStatement statement) {
963       if (statement.getException() == myExpr) {
964         PsiManager manager = statement.getManager();
965         PsiType throwableType = JavaPsiFacade.getElementFactory(manager.getProject()).createTypeByFQClassName("java.lang.Throwable", myExpr.getResolveScope());
966         PsiElement container = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, PsiLambdaExpression.class, PsiClass.class);
967         PsiType[] throwsTypes = PsiType.EMPTY_ARRAY;
968         if (container instanceof PsiMethod) {
969           throwsTypes = ((PsiMethod)container).getThrowsList().getReferencedTypes();
970         }
971         else if (container instanceof PsiLambdaExpression) {
972           final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(container);
973           if (method != null) {
974             throwsTypes = method.getThrowsList().getReferencedTypes();
975           }
976         }
977
978         if (throwsTypes.length == 0) {
979           final PsiClassType exceptionType = JavaPsiFacade.getElementFactory(manager.getProject()).createTypeByFQClassName("java.lang.Exception", myExpr.getResolveScope());
980           throwsTypes = new PsiClassType[]{exceptionType};
981         }
982
983         for (PsiType throwsType : throwsTypes) {
984           myResult.add(createInfoImpl(
985             myExpr instanceof PsiTypeCastExpression && myForCompletion ? throwsType : throwableType,
986             ExpectedTypeInfo.TYPE_OR_SUBTYPE,
987             throwsType,
988             TailType.SEMICOLON
989           ));
990         }
991       }
992     }
993
994     @Override
995     public void visitCodeFragment(@NotNull JavaCodeFragment codeFragment) {
996       if (codeFragment instanceof PsiExpressionCodeFragment) {
997         final PsiType type = ((PsiExpressionCodeFragment)codeFragment).getExpectedType();
998         if (type != null) {
999           myResult.add(createInfoImpl(type, type));
1000         }
1001       }
1002     }
1003
1004     @NotNull
1005     private ExpectedTypeInfo[] getExpectedArgumentTypesForMethodCall(@NotNull CandidateInfo[] allCandidates,
1006                                                                      @NotNull PsiExpressionList argumentList,
1007                                                                      @NotNull PsiExpression argument,
1008                                                                      boolean forCompletion) {
1009       if (allCandidates.length == 0) {
1010         return ExpectedTypeInfo.EMPTY_ARRAY;
1011       }
1012
1013       if (CodeInsightSettings.getInstance().SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION) {
1014         allCandidates = selectCandidateChosenOnCompletion(argumentList.getParent(), allCandidates);
1015       }
1016
1017       PsiMethod toExclude = JavaPsiConstructorUtil.isConstructorCall(argumentList.getParent())
1018                             ? PsiTreeUtil.getParentOfType(argument, PsiMethod.class) : null;
1019
1020       PsiResolveHelper helper = JavaPsiFacade.getInstance(myExpr.getProject()).getResolveHelper();
1021       List<CandidateInfo> methodCandidates = new ArrayList<>();
1022       for (CandidateInfo candidate : allCandidates) {
1023         PsiElement element = candidate.getElement();
1024         if (element instanceof PsiMethod && helper.isAccessible((PsiMember)element, argumentList, null) && element != toExclude) {
1025           methodCandidates.add(candidate);
1026         }
1027       }
1028       if (methodCandidates.isEmpty()) {
1029         Collections.addAll(methodCandidates, allCandidates);
1030       }
1031
1032       final PsiExpression[] args = argumentList.getExpressions().clone();
1033       final int index = ArrayUtil.indexOf(args, argument);
1034       LOG.assertTrue(index >= 0);
1035
1036       final PsiExpression[] leftArgs;
1037       if (index <= args.length - 1) {
1038         leftArgs = new PsiExpression[index];
1039         System.arraycopy(args, 0, leftArgs, 0, index);
1040         if (forCompletion) {
1041           args[index] = null;
1042         }
1043       }
1044       else {
1045         leftArgs = null;
1046       }
1047
1048       ParameterTypeInferencePolicy policy = forCompletion ? CompletionParameterTypeInferencePolicy.INSTANCE : DefaultParameterTypeInferencePolicy.INSTANCE;
1049
1050       Set<ExpectedTypeInfo> set = new LinkedHashSet<>();
1051       for (CandidateInfo candidateInfo : methodCandidates) {
1052         PsiMethod method = (PsiMethod)candidateInfo.getElement();
1053         PsiSubstitutor substitutor;
1054         if (candidateInfo instanceof MethodCandidateInfo) {
1055           MethodCandidateInfo info = (MethodCandidateInfo)candidateInfo;
1056           substitutor = info.inferSubstitutorFromArgs(policy, args);
1057           if (!info.isStaticsScopeCorrect() && !method.hasModifierProperty(PsiModifier.STATIC) || info.getInferenceErrorMessage() != null) continue;
1058         }
1059         else {
1060           substitutor = candidateInfo.getSubstitutor();
1061         }
1062         if (substitutor == null) {
1063           return ExpectedTypeInfo.EMPTY_ARRAY;
1064         }
1065         inferMethodCallArgumentTypes(argument, forCompletion, args, index, method, substitutor, set);
1066         if (set.size() >= myMaxCandidates) break;
1067
1068         if (leftArgs != null && candidateInfo instanceof MethodCandidateInfo) {
1069           substitutor = ((MethodCandidateInfo)candidateInfo).inferSubstitutorFromArgs(policy, leftArgs);
1070           if (substitutor != null) {
1071             inferMethodCallArgumentTypes(argument, forCompletion, leftArgs, index, method, substitutor, set);
1072             if (set.size() >= myMaxCandidates) break;
1073           }
1074         }
1075       }
1076
1077       // try to find some variants without considering previous argument PRIMITIVE_TYPES
1078       if (forCompletion && set.isEmpty()) {
1079         for (CandidateInfo candidate : methodCandidates) {
1080           PsiMethod method = (PsiMethod)candidate.getElement();
1081           PsiSubstitutor substitutor = candidate.getSubstitutor();
1082           PsiParameter[] params = method.getParameterList().getParameters();
1083           if (params.length <= index) continue;
1084           PsiParameter param = params[index];
1085           PsiType paramType = getParameterType(param, substitutor);
1086           TailType tailType = getMethodArgumentTailType(argument, index, method, substitutor, params);
1087           ExpectedTypeInfoImpl info = createInfoImpl(paramType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, paramType,
1088                                                      tailType, method, getPropertyName(param));
1089           set.add(info);
1090           if (set.size() >= myMaxCandidates) break;
1091         }
1092       }
1093
1094       return set.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
1095     }
1096
1097     @NotNull
1098     private static CandidateInfo[] selectCandidateChosenOnCompletion(@Nullable PsiElement call, @NotNull CandidateInfo[] candidates) {
1099       if (call instanceof PsiCall) {
1100         PsiCall originalCall = CompletionUtil.getOriginalElement((PsiCall)call);
1101         if (originalCall != null) {
1102           PsiMethod method = CompletionMemory.getChosenMethod(originalCall);
1103           if (method != null) {
1104             for (CandidateInfo candidate : candidates) {
1105               if (CompletionUtil.getOriginalOrSelf(candidate.getElement()) == method) return new CandidateInfo[]{candidate};
1106             }
1107           }
1108         }
1109       }
1110       return candidates;
1111     }
1112
1113     @NotNull
1114     private static TailType getMethodArgumentTailType(@NotNull final PsiExpression argument,
1115                                                       final int index,
1116                                                       @NotNull final PsiMethod method,
1117                                                       @NotNull final PsiSubstitutor substitutor,
1118                                                       @NotNull final PsiParameter[] params) {
1119       if (index >= params.length || index == params.length - 2 && params[index + 1].isVarArgs()) {
1120         return TailType.NONE;
1121       }
1122       if (index == params.length - 1) {
1123         final PsiElement call = argument.getParent().getParent();
1124         // ignore JspMethodCall
1125         if (call instanceof SyntheticElement) return TailType.NONE;
1126
1127         PsiType returnType = method.getReturnType();
1128         if (returnType != null) returnType = substitutor.substitute(returnType);
1129         return getFinalCallParameterTailType(call, returnType, method);
1130       }
1131       if (CodeInsightSettings.getInstance().SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION) {
1132         PsiCall completedOuterCall = getCompletedOuterCall(argument);
1133         if (completedOuterCall != null) return new CommaTailTypeWithSyncHintUpdate(completedOuterCall);
1134       }
1135       return TailType.COMMA;
1136     }
1137
1138     @Nullable
1139     private static PsiCall getCompletedOuterCall(@NotNull PsiExpression argument) {
1140       PsiElement expressionList = argument.getParent();
1141       if (expressionList instanceof PsiExpressionList) {
1142         PsiElement call = expressionList.getParent();
1143         if (call instanceof PsiCall) {
1144           PsiCall originalCall = CompletionUtil.getOriginalElement((PsiCall)call);
1145           if (originalCall != null && JavaMethodCallElement.isCompletionMode(originalCall)) {
1146             return originalCall;
1147           }
1148         }
1149       }
1150       return null;
1151     }
1152
1153     private static void inferMethodCallArgumentTypes(@NotNull final PsiExpression argument,
1154                                                      final boolean forCompletion,
1155                                                      @NotNull final PsiExpression[] args,
1156                                                      final int index,
1157                                                      @NotNull final PsiMethod method,
1158                                                      @NotNull final PsiSubstitutor substitutor,
1159                                                      @NotNull final Set<? super ExpectedTypeInfo> array) {
1160       LOG.assertTrue(substitutor.isValid());
1161       PsiParameter[] parameters = method.getParameterList().getParameters();
1162       if (!forCompletion && parameters.length != args.length && !method.isVarArgs()) return;
1163       if (parameters.length <= index && !method.isVarArgs()) return;
1164
1165       for (int j = 0; j < index; j++) {
1166         PsiType paramType = getParameterType(parameters[Math.min(parameters.length - 1, j)],
1167                                              substitutor);
1168         PsiType argType = args[j].getType();
1169         if (argType != null && !paramType.isAssignableFrom(argType)) return;
1170       }
1171       PsiParameter parameter = parameters[Math.min(parameters.length - 1, index)];
1172       PsiType parameterType = getParameterType(parameter, substitutor);
1173
1174       TailType tailType = getMethodArgumentTailType(argument, index, method, substitutor, parameters);
1175       PsiType defaultType = getDefaultType(method, substitutor, parameterType, argument, args, index);
1176
1177       NullableComputable<String> propertyName = getPropertyName(parameter);
1178       ExpectedTypeInfoImpl info = createInfoImpl(parameterType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, defaultType, tailType, method,
1179                                                  propertyName);
1180       array.add(info);
1181
1182       if (index == parameters.length - 1 && parameter.isVarArgs()) {
1183         //Then we may still want to call with array argument
1184         final PsiArrayType arrayType = parameterType.createArrayType();
1185         ExpectedTypeInfoImpl info1 = createInfoImpl(arrayType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, arrayType, tailType, method, propertyName);
1186         array.add(info1);
1187       }
1188     }
1189
1190     @Nullable
1191     private static PsiType getDefaultType(@NotNull final PsiMethod method, final PsiSubstitutor substitutor, @NotNull final PsiType parameterType,
1192                                           @NotNull final PsiExpression argument, @NotNull PsiExpression[] args, int index) {
1193       final PsiClass containingClass = method.getContainingClass();
1194       if (containingClass == null) return parameterType;
1195
1196       PsiType hardcoded = HardcodedDefaultTypesKt.getDefaultType(method, substitutor, index, argument);
1197       if (hardcoded != null) return hardcoded;
1198
1199       @NonNls final String name = method.getName();
1200       final PsiElementFactory factory = JavaPsiFacade.getElementFactory(containingClass.getProject());
1201       int argCount = Math.max(index + 1, args.length);
1202       if ("assertEquals".equals(name) || "assertSame".equals(name) && method.getParameterList().getParametersCount() == argCount) {
1203         if (argCount == 2 ||
1204             argCount == 3 && method.getParameterList().getParameters()[0].getType().equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
1205           int other = index == argCount - 1 ? index - 1 : index + 1;
1206           if (args.length > other) {
1207             ExpectedTypeInfo info = getEqualsType(args[other]);
1208             if (info != null && parameterType.isAssignableFrom(info.getDefaultType())) {
1209               return info.getDefaultType();
1210             }
1211           }
1212         }
1213       }
1214       String className = containingClass.getName();
1215       if (className != null && className.startsWith("Log")) {
1216         if (parameterType instanceof PsiClassType) {
1217           PsiType typeArg = PsiUtil.substituteTypeParameter(parameterType, CommonClassNames.JAVA_LANG_CLASS, 0, true);
1218           if (typeArg instanceof PsiWildcardType && !((PsiWildcardType)typeArg).isBounded() ||
1219               typeArg != null && TypeConversionUtil.erasure(typeArg).equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
1220             PsiClass placeClass = PsiTreeUtil.getContextOfType(argument, PsiClass.class);
1221             PsiClass classClass = ((PsiClassType)parameterType).resolve();
1222             if (placeClass != null && classClass != null && classClass.getTypeParameters().length == 1) {
1223               return factory.createType(classClass, factory.createType(placeClass));
1224             }
1225           }
1226         }
1227       }
1228       return parameterType;
1229     }
1230
1231     private static PsiType getParameterType(@NotNull PsiParameter parameter, @NotNull PsiSubstitutor substitutor) {
1232       PsiType type = parameter.getType();
1233       LOG.assertTrue(type.isValid());
1234       if (parameter.isVarArgs()) {
1235         if (type instanceof PsiArrayType) {
1236           type = ((PsiArrayType)type).getComponentType();
1237         }
1238         else {
1239           LOG.error("Vararg parameter with non-array type. Class=" + parameter.getClass() + "; type=" + parameter.getType());
1240         }
1241       }
1242       PsiType parameterType = substitutor.substitute(type);
1243       if (parameterType instanceof PsiCapturedWildcardType) {
1244         parameterType = ((PsiCapturedWildcardType)parameterType).getWildcard();
1245       }
1246       if (parameterType instanceof PsiWildcardType) {
1247         final PsiType bound = ((PsiWildcardType)parameterType).getBound();
1248         return bound != null ? bound : PsiType.getJavaLangObject(parameter.getManager(), GlobalSearchScope.allScope(parameter.getProject()));
1249       }
1250       return parameterType;
1251     }
1252
1253     @NotNull
1254     private static NullableComputable<String> getPropertyName(@NotNull final PsiVariable variable) {
1255       return () -> {
1256         final String name = variable.getName();
1257         if (name == null) return null;
1258         JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(variable.getProject());
1259         VariableKind variableKind = codeStyleManager.getVariableKind(variable);
1260         return codeStyleManager.variableNameToPropertyName(name, variableKind);
1261       };
1262     }
1263
1264     @NotNull
1265     private List<ExpectedTypeInfo> anyArrayType() {
1266       PsiType objType = PsiType.getJavaLangObject(myExpr.getManager(), myExpr.getResolveScope()).createArrayType();
1267       ExpectedTypeInfo info = createInfoImpl(objType, objType);
1268       ExpectedTypeInfo info1 = createInfoImpl(PsiType.DOUBLE.createArrayType(), PsiType.INT.createArrayType());
1269       PsiType booleanType = PsiType.BOOLEAN.createArrayType();
1270       ExpectedTypeInfo info2 = createInfoImpl(booleanType, ExpectedTypeInfo.TYPE_STRICTLY, booleanType, TailType.NONE);
1271       return Arrays.asList(info, info1, info2);
1272     }
1273
1274     @NotNull
1275     private ExpectedTypeInfo[] findClassesWithDeclaredMethod(@NotNull final PsiMethodCallExpression methodCallExpr) {
1276       PsiUtilCore.ensureValid(methodCallExpr);
1277       final PsiReferenceExpression reference = methodCallExpr.getMethodExpression();
1278       if (reference.getQualifierExpression() instanceof PsiClassObjectAccessExpression) {
1279         return ExpectedTypeInfo.EMPTY_ARRAY;
1280       }
1281       final PsiManager manager = methodCallExpr.getManager();
1282       final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
1283       Set<PsiMethod> psiMethods = mapToDeepestSuperMethods(myClassProvider.findDeclaredMethods(manager, Objects.requireNonNull(reference.getReferenceName())));
1284       Set<ExpectedTypeInfo> types = new THashSet<>();
1285       for (PsiMethod method : psiMethods) {
1286         final PsiClass aClass = method.getContainingClass();
1287         if (aClass == null || !facade.getResolveHelper().isAccessible(method, reference, aClass)) continue;
1288
1289         final PsiSubstitutor substitutor = ExpectedTypeUtil.inferSubstitutor(method, methodCallExpr, false);
1290         final PsiClassType type =
1291           substitutor == null ? facade.getElementFactory().createType(aClass) : facade.getElementFactory().createType(aClass, substitutor);
1292
1293         if (method.hasModifierProperty(PsiModifier.STATIC) || method.hasModifierProperty(PsiModifier.PRIVATE)) {
1294           types.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_STRICTLY, type, TailType.DOT));
1295         }
1296         else {
1297           types.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.DOT));
1298         }
1299       }
1300
1301       return types.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
1302     }
1303
1304     private static Set<PsiMethod> mapToDeepestSuperMethods(PsiMethod[] methods) {
1305       LinkedHashSet<PsiMethod> psiMethods = new LinkedHashSet<>();
1306       for (PsiMethod m : methods) {
1307         if (m.hasModifierProperty(PsiModifier.STATIC) || m.hasModifierProperty(PsiModifier.PRIVATE)) {
1308           psiMethods.add(m);
1309         }
1310         else {
1311           PsiMethod[] superMethods = m.findDeepestSuperMethods();
1312           if (superMethods.length > 0) {
1313             psiMethods.addAll(Arrays.asList(superMethods));
1314           }
1315           else {
1316             psiMethods.add(m);
1317           }
1318         }
1319       }
1320       return psiMethods;
1321     }
1322
1323     @NotNull
1324     private ExpectedTypeInfo[] findClassesWithDeclaredField(@NotNull PsiReferenceExpression expression) {
1325       final JavaPsiFacade facade = JavaPsiFacade.getInstance(expression.getProject());
1326       PsiField[] fields = myClassProvider.findDeclaredFields(expression.getManager(), Objects.requireNonNull(expression.getReferenceName()));
1327       List<ExpectedTypeInfo> types = new ArrayList<>();
1328       for (PsiField field : fields) {
1329         final PsiClass aClass = field.getContainingClass();
1330         if (aClass == null || !facade.getResolveHelper().isAccessible(field, expression, aClass)) continue;
1331
1332         final PsiType type = facade.getElementFactory().createType(aClass);
1333
1334         int kind = field.hasModifierProperty(PsiModifier.STATIC) ||
1335                    field.hasModifierProperty(PsiModifier.FINAL) ||
1336                    field.hasModifierProperty(PsiModifier.PRIVATE)
1337                    ? ExpectedTypeInfo.TYPE_STRICTLY
1338                    : ExpectedTypeInfo.TYPE_OR_SUBTYPE;
1339         ExpectedTypeInfo info = createInfoImpl(type, kind, type, TailType.DOT);
1340         //Do not filter inheritors!
1341         types.add(info);
1342       }
1343       return types.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
1344     }
1345   }
1346
1347   /**
1348    * Finds fields and methods of specified name whenever corresponding reference has been encountered.
1349    * By default searches in the global scope (see ourGlobalScopeClassProvider), but caller can provide its own algorithm e.g. to narrow search scope
1350    */
1351   public interface ExpectedClassProvider {
1352     @NotNull
1353     PsiField[] findDeclaredFields(@NotNull PsiManager manager, @NotNull String name);
1354
1355     @NotNull
1356     PsiMethod[] findDeclaredMethods(@NotNull PsiManager manager, @NotNull String name);
1357   }
1358
1359   @NotNull
1360   public static TailType getFinalCallParameterTailType(@NotNull PsiElement call, @Nullable PsiType returnType, @NotNull PsiMethod method) {
1361     if (method.isConstructor() &&
1362         call instanceof PsiMethodCallExpression && ((PsiMethodCallExpression)call).getMethodExpression() instanceof PsiSuperExpression) {
1363       return TailTypes.CALL_RPARENTH_SEMICOLON;
1364     }
1365
1366     final boolean chainable = !PsiType.VOID.equals(returnType) && returnType != null || method.isConstructor() && call instanceof PsiNewExpression;
1367
1368     final PsiElement parent = call.getParent();
1369     final boolean statementContext = parent instanceof PsiExpressionStatement || parent instanceof PsiVariable ||
1370                                      parent instanceof PsiCodeBlock;
1371
1372     if (parent instanceof PsiThrowStatement || statementContext && !chainable) {
1373       return TailTypes.CALL_RPARENTH_SEMICOLON;
1374     }
1375
1376     return TailTypes.CALL_RPARENTH;
1377   }
1378
1379   private static class CommaTailTypeWithSyncHintUpdate extends TailType {
1380     private final PsiCall myOriginalCall;
1381
1382     private CommaTailTypeWithSyncHintUpdate(@NotNull PsiCall originalCall) {myOriginalCall = originalCall;}
1383
1384     @Override
1385     public boolean isApplicable(@NotNull InsertionContext context) {
1386       return TailType.COMMA.isApplicable(context);
1387     }
1388
1389     @Override
1390     public int processTail(Editor editor, int tailOffset) {
1391       int result = TailType.COMMA.processTail(editor, tailOffset);
1392       if (myOriginalCall.isValid()) {
1393         PsiDocumentManager.getInstance(myOriginalCall.getProject()).commitDocument(editor.getDocument());
1394         if (myOriginalCall.isValid()) ParameterHintsPass.syncUpdate(myOriginalCall, editor);
1395       }
1396       return result;
1397     }
1398
1399     @Override
1400     public boolean equals(Object o) {
1401       if (this == o) return true;
1402       if (o == null || getClass() != o.getClass()) return false;
1403       CommaTailTypeWithSyncHintUpdate update = (CommaTailTypeWithSyncHintUpdate)o;
1404       return myOriginalCall.equals(update.myOriginalCall);
1405     }
1406
1407     @Override
1408     public int hashCode() {
1409       return Objects.hash(myOriginalCall);
1410     }
1411   }
1412 }