116458dae6da97ef6f509b802ffa21e53bfe76ab
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / hint / api / impls / MethodParameterInfoHandler.java
1 // Copyright 2000-2018 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.hint.api.impls;
3
4 import com.intellij.codeInsight.AnnotationTargetUtil;
5 import com.intellij.codeInsight.AnnotationUtil;
6 import com.intellij.codeInsight.CodeInsightBundle;
7 import com.intellij.codeInsight.CodeInsightSettings;
8 import com.intellij.codeInsight.completion.CompletionMemory;
9 import com.intellij.codeInsight.completion.JavaCompletionUtil;
10 import com.intellij.codeInsight.completion.JavaMethodCallElement;
11 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
12 import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
13 import com.intellij.codeInsight.hints.ParameterHintsPass;
14 import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator;
15 import com.intellij.codeInsight.lookup.LookupElement;
16 import com.intellij.injected.editor.EditorWindow;
17 import com.intellij.lang.parameterInfo.*;
18 import com.intellij.openapi.editor.Document;
19 import com.intellij.openapi.editor.Editor;
20 import com.intellij.openapi.editor.Inlay;
21 import com.intellij.openapi.project.DumbAware;
22 import com.intellij.openapi.project.DumbService;
23 import com.intellij.openapi.util.Computable;
24 import com.intellij.openapi.util.Key;
25 import com.intellij.openapi.util.TextRange;
26 import com.intellij.openapi.util.UserDataHolder;
27 import com.intellij.openapi.util.registry.Registry;
28 import com.intellij.openapi.util.text.StringUtil;
29 import com.intellij.psi.*;
30 import com.intellij.psi.impl.PsiImplUtil;
31 import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy;
32 import com.intellij.psi.infos.CandidateInfo;
33 import com.intellij.psi.infos.MethodCandidateInfo;
34 import com.intellij.psi.scope.MethodProcessorSetupFailedException;
35 import com.intellij.psi.scope.PsiConflictResolver;
36 import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
37 import com.intellij.psi.scope.processor.MethodResolverProcessor;
38 import com.intellij.psi.scope.util.PsiScopesUtil;
39 import com.intellij.psi.tree.IElementType;
40 import com.intellij.psi.util.MethodSignatureUtil;
41 import com.intellij.util.DocumentUtil;
42 import com.intellij.util.ObjectUtils;
43 import com.intellij.util.containers.ContainerUtil;
44 import com.intellij.util.text.CharArrayUtil;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47
48 import java.awt.*;
49 import java.util.*;
50 import java.util.List;
51
52 /**
53  * @author Maxim.Mossienko
54  */
55 public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabActionSupport<PsiExpressionList, Object, PsiExpression>, DumbAware {
56   private static final Set<Class> ourArgumentListAllowedParentClassesSet = ContainerUtil.newHashSet(
57     PsiMethodCallExpression.class, PsiNewExpression.class, PsiAnonymousClass.class, PsiEnumConstant.class);
58   private static final Set<? extends Class> ourStopSearch = Collections.singleton(PsiMethod.class);
59   private static final String WHITESPACE = " \t";
60   private static final Key<Inlay> CURRENT_HINT = Key.create("current.hint");
61   private static final Key<List<Inlay>> HIGHLIGHTED_HINTS = Key.create("highlighted.hints");
62
63   @Override
64   public Object[] getParametersForLookup(LookupElement item, ParameterInfoContext context) {
65     final List<? extends PsiElement> elements = JavaCompletionUtil.getAllPsiElements(item);
66     return elements != null && !elements.isEmpty() && elements.get(0) instanceof PsiMethod ? elements.toArray() : null;
67   }
68
69   @Override
70   public boolean couldShowInLookup() {
71     return true;
72   }
73
74   @Override
75   @Nullable
76   public PsiExpressionList findElementForParameterInfo(@NotNull final CreateParameterInfoContext context) {
77     PsiExpressionList argumentList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), true);
78
79     if (argumentList != null) {
80       return findMethodsForArgumentList(context, argumentList);
81     }
82     return null;
83   }
84
85   private PsiExpressionList findArgumentList(final PsiFile file, int offset, int parameterStart, boolean allowOuter) {
86     PsiExpressionList argumentList = ParameterInfoUtils.findArgumentList(file, offset, parameterStart, this, allowOuter);
87     if (argumentList == null && allowOuter) {
88       PsiCall call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiMethodCallExpression.class, PsiMethod.class);
89       if (call == null) {
90         call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiNewExpression.class, PsiMethod.class);
91       }
92       if (call != null) {
93         argumentList = call.getArgumentList();
94       }
95     }
96     return argumentList;
97   }
98
99   private static PsiExpressionList findMethodsForArgumentList(final CreateParameterInfoContext context,
100                                                               @NotNull final PsiExpressionList argumentList) {
101
102     CandidateInfo[] candidates = getMethods(argumentList);
103     if (candidates.length == 0) {
104       DaemonCodeAnalyzer.getInstance(context.getProject()).updateVisibleHighlighters(context.getEditor());
105       return null;
106     }
107     context.setItemsToShow(candidates);
108     return argumentList;
109   }
110
111   @Override
112   public void showParameterInfo(@NotNull final PsiExpressionList element, @NotNull final CreateParameterInfoContext context) {
113     context.showHint(element, element.getTextRange().getStartOffset(), this);
114   }
115
116   @Override
117   public PsiExpressionList findElementForUpdatingParameterInfo(@NotNull final UpdateParameterInfoContext context) {
118     if (context.isPreservedOnHintHidden() && isOutsideOfCompletedInvocation(context)) {
119       context.setPreservedOnHintHidden(false);
120       return null;
121     }
122     PsiExpressionList expressionList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), false);
123     if (expressionList != null) {
124       Object[] candidates = context.getObjectsToView();
125       if (candidates != null && candidates.length != 0) {
126         Object currentMethodInfo = context.getHighlightedParameter();
127         if (currentMethodInfo == null) currentMethodInfo = candidates[0];
128         PsiElement element = currentMethodInfo instanceof CandidateInfo ? ((CandidateInfo)currentMethodInfo).getElement() : 
129                              currentMethodInfo instanceof PsiElement ? (PsiElement) currentMethodInfo : 
130                              null;
131         if ((element instanceof PsiMethod)) {
132           PsiMethod method = (PsiMethod)element;
133           PsiElement parent = expressionList.getParent();
134
135           String originalMethodName = method.getName();
136           PsiQualifiedReference currentMethodReference = null;
137           if (parent instanceof PsiMethodCallExpression && !method.isConstructor()) {
138             currentMethodReference = ((PsiMethodCallExpression)parent).getMethodExpression();
139           }
140           else if (parent instanceof PsiNewExpression) {
141             currentMethodReference = ((PsiNewExpression)parent).getClassReference();
142           }
143           else if (parent instanceof PsiAnonymousClass) {
144             currentMethodReference = ((PsiAnonymousClass)parent).getBaseClassReference();
145           }
146           if (currentMethodReference == null || originalMethodName.equals(currentMethodReference.getReferenceName())) {
147
148             int currentNumberOfParameters = expressionList.getExpressionCount();
149             PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(context.getProject());
150             Document document = psiDocumentManager.getCachedDocument(context.getFile());
151             if (parent instanceof PsiCallExpression && JavaMethodCallElement.isCompletionMode((PsiCall)parent)) {
152               PsiMethod chosenMethod = CompletionMemory.getChosenMethod((PsiCall)parent);
153               if ((context.getHighlightedParameter() != null || candidates.length == 1) && chosenMethod != null &&
154                   document != null && psiDocumentManager.isCommitted(document) &&
155                   isIncompatibleParameterCount(chosenMethod, currentNumberOfParameters)) {
156                 JavaMethodCallElement.setCompletionMode((PsiCall)parent, false);
157                 highlightHints(context.getEditor(), null, -1, context.getCustomContext());
158               }
159               else {
160                 int index = ParameterInfoUtils.getCurrentParameterIndex(expressionList.getNode(), 
161                                                                         context.getOffset(), JavaTokenType.COMMA);
162                 TextRange textRange = expressionList.getTextRange();
163                 if (context.getOffset() <= textRange.getStartOffset() || context.getOffset() >= textRange.getEndOffset()) index = -1;
164                 highlightHints(context.getEditor(), expressionList, context.isInnermostContext() ? index : -1, context.getCustomContext());
165               }
166             }
167
168             return expressionList;
169           }
170         }
171       }
172     }
173     highlightHints(context.getEditor(), null, -1, context.getCustomContext());
174     return null;
175   }
176
177   private static boolean isOutsideOfCompletedInvocation(UpdateParameterInfoContext context) {
178     PsiElement owner = context.getParameterOwner();
179     if (owner != null && owner.isValid()) {
180       TextRange ownerTextRange = getRelatedRange(owner, context.getEditor());
181       int caretOffset = context.getOffset();
182       if (ownerTextRange != null) {
183         if (caretOffset >= ownerTextRange.getStartOffset() && caretOffset <= ownerTextRange.getEndOffset()) {
184           return false;
185         }
186         else {
187           for (PsiElement element : owner.getChildren()) {
188             if (element instanceof PsiErrorElement) return false;
189           }
190           if (owner instanceof PsiExpressionList && ((PsiExpressionList)owner).isEmpty()) {
191             PsiElement parent = owner.getParent();
192             if (parent instanceof PsiCall) {
193               PsiMethod chosenMethod = CompletionMemory.getChosenMethod((PsiCall)parent);
194               if (chosenMethod != null) {
195                 int parametersCount = chosenMethod.getParameterList().getParametersCount();
196                 if ((parametersCount == 1 && !chosenMethod.isVarArgs() || parametersCount == 2 && chosenMethod.isVarArgs()) && 
197                                             !overloadWithNoParametersExists(chosenMethod, context.getObjectsToView())) return false;
198               }
199             }
200           }
201         }
202       }
203     }
204     return true;
205   }
206
207   private static TextRange getRelatedRange(PsiElement owner, Editor editor) {
208     TextRange range = owner.getTextRange();
209     if (range == null) return null;
210     Document document = editor.getDocument();
211     if (Registry.is("editor.keep.completion.hints.even.longer")) {
212       int startY = editor.visualPositionToXY(editor.offsetToVisualPosition(range.getStartOffset())).y;
213       int endY = editor.visualPositionToXY(editor.offsetToVisualPosition(range.getEndOffset())).y;
214       Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
215       return startY > visibleArea.getMaxY() || endY < visibleArea.getMinY() ? null : new TextRange(0, document.getTextLength()); 
216     }
217     if (!Registry.is("editor.keep.completion.hints.longer")) return range;
218     return new TextRange(DocumentUtil.getLineStartOffset(range.getStartOffset(), document), 
219                          DocumentUtil.getLineEndOffset(range.getEndOffset(), document));
220   }
221
222   private static boolean overloadWithNoParametersExists(PsiMethod method, Object[] candidates) {
223     String methodName = method.getName();
224     return ContainerUtil.find(candidates, c -> {
225       if (!(c instanceof CandidateInfo)) return false;
226       PsiElement e = ((CandidateInfo)c).getElement();
227       if (!(e instanceof PsiMethod)) return false;
228       PsiMethod m = (PsiMethod)e;
229       return m.getParameterList().isEmpty() && m.getName().equals(methodName);
230     }) != null;
231   }
232
233   private static boolean isIncompatibleParameterCount(@NotNull PsiMethod method, int numberOfParameters) {
234     int originalNumberOfParameters = method.getParameterList().getParametersCount();
235     return PsiImplUtil.isVarArgs(method) 
236            ? originalNumberOfParameters > 2 && numberOfParameters < originalNumberOfParameters - 1 
237            : originalNumberOfParameters != numberOfParameters && !(originalNumberOfParameters == 1 && numberOfParameters == 0);
238   }
239
240   @Override
241   public void updateParameterInfo(@NotNull final PsiExpressionList o, @NotNull final UpdateParameterInfoContext context) {
242     PsiElement parameterOwner = context.getParameterOwner();
243     if (parameterOwner != o) {
244       context.removeHint();
245       return;
246     }
247
248     int offset = context.getOffset();
249     TextRange elRange = o.getTextRange();
250     int index = offset <= elRange.getStartOffset() || offset >= elRange.getEndOffset()
251                 ? -1 : ParameterInfoUtils.getCurrentParameterIndex(o.getNode(), offset, JavaTokenType.COMMA);
252     context.setCurrentParameter(index);
253
254     Object[] candidates = context.getObjectsToView();
255     PsiExpression[] args = o.getExpressions();
256     PsiCall call = getCall(o);
257     PsiElement realResolve = call != null ? call.resolveMethod() : null;
258
259     PsiMethod chosenMethod = CompletionMemory.getChosenMethod(call);
260     CandidateInfo chosenInfo = null;
261     CandidateInfo completeMatch = null;
262
263     for (int i = 0; i < candidates.length; i++) {
264       CandidateInfo candidate = (CandidateInfo)candidates[i];
265       PsiMethod method = (PsiMethod)candidate.getElement();
266       if (!method.isValid()) continue;
267       if (candidate instanceof MethodCandidateInfo && !((MethodCandidateInfo)candidate).getSiteSubstitutor().isValid()) continue;
268       PsiSubstitutor substitutor = getCandidateInfoSubstitutor(o, candidate, method == realResolve);
269       assert substitutor != null;
270
271       if (!method.isValid() || !substitutor.isValid()) {
272           // this may sometimes happen e,g, when editing method call in field initializer candidates in the same file get invalidated
273         context.setUIComponentEnabled(i, false);
274         continue;
275       }
276
277       PsiParameter[] parms = method.getParameterList().getParameters();
278       boolean enabled = true;
279       if (parms.length <= index) {
280         if (parms.length > 0) {
281           if (method.isVarArgs()) {
282             for (int j = 0; j < parms.length - 1; j++) {
283               PsiType parmType = substitutor.substitute(parms[j].getType());
284               PsiType argType = args[j].getType();
285               if (argType != null && !parmType.isAssignableFrom(argType)) {
286                 enabled = false;
287                 break;
288               }
289             }
290
291             if (enabled) {
292               PsiArrayType lastParmType = (PsiArrayType)substitutor.substitute(parms[parms.length - 1].getType());
293               PsiType componentType = lastParmType.getComponentType();
294
295               if (parms.length == args.length) {
296                 PsiType lastArgType = args[args.length - 1].getType();
297                 if (lastArgType != null && !lastParmType.isAssignableFrom(lastArgType) &&
298                     !componentType.isAssignableFrom(lastArgType)) {
299                   enabled = false;
300                 }
301               }
302               else {
303                 for (int j = parms.length; j <= index && j < args.length; j++) {
304                   PsiExpression arg = args[j];
305                   PsiType argType = arg.getType();
306                   if (argType != null && !componentType.isAssignableFrom(argType)) {
307                     enabled = false;
308                     break;
309                   }
310                 }
311               }
312             }
313           }
314           else {
315             enabled = false;
316           }
317         }
318         else {
319           enabled = index == 0;
320         }
321       }
322       else {
323         enabled = isAssignableParametersBeforeGivenIndex(parms, args, index, substitutor);
324       }
325
326       context.setUIComponentEnabled(i, enabled);
327       if (!enabled && context.getHighlightedParameter() == candidate) {
328         context.setHighlightedParameter(null);
329       }
330       if (candidates.length > 1 && enabled) {
331         if (PsiManager.getInstance(context.getProject()).areElementsEquivalent(chosenMethod, method)) {
332           chosenInfo = candidate;
333         }
334
335         if (parms.length == args.length && realResolve == method &&
336             isAssignableParametersBeforeGivenIndex(parms, args, args.length, substitutor)) {
337           completeMatch = candidate;
338         }
339       }
340     }
341
342     if (chosenInfo != null) {
343       context.setHighlightedParameter(chosenInfo);
344     }
345     else if (completeMatch != null) {
346       context.setHighlightedParameter(completeMatch);
347     }
348
349     Object highlightedCandidate = candidates.length == 1 ? candidates[0] : context.getHighlightedParameter();
350     if (highlightedCandidate != null) {
351       PsiMethod method = (PsiMethod)(highlightedCandidate instanceof CandidateInfo 
352                                      ? ((CandidateInfo)highlightedCandidate).getElement() : highlightedCandidate);
353       if (!method.isVarArgs() && index >= method.getParameterList().getParametersCount()) context.setCurrentParameter(-1);
354     }
355   }
356
357   private static void highlightHints(@NotNull Editor editor, @Nullable PsiExpressionList expressionList, int currentHintIndex,
358                                      @NotNull UserDataHolder context) {
359     if (editor.isDisposed() || editor instanceof EditorWindow) return;
360     ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
361     Inlay currentHint = null;
362     List<Inlay> highlightedHints = null;
363     if (expressionList != null && expressionList.isValid()) {
364       int expressionCount = expressionList.getExpressionCount();
365       if (currentHintIndex == 0 || currentHintIndex > 0 && currentHintIndex < expressionCount) {
366         highlightedHints = new ArrayList<>(expressionCount);
367         ParameterHintsPass.syncUpdate(expressionList.getParent(), editor);
368         PsiElement prevDelimiter, nextDelimiter;
369         for (int i = 0; i < Math.max(expressionCount, currentHintIndex == 0 ? 1 : 0); i++) {
370           if (i < expressionCount) {
371             PsiExpression expression = expressionList.getExpressions()[i];
372             //noinspection StatementWithEmptyBody
373             for (prevDelimiter = expression;
374                  prevDelimiter != null && !(prevDelimiter instanceof PsiJavaToken);
375                  prevDelimiter = prevDelimiter.getPrevSibling())
376               ;
377             //noinspection StatementWithEmptyBody
378             for (nextDelimiter = expression;
379                  nextDelimiter != null && !(nextDelimiter instanceof PsiJavaToken);
380                  nextDelimiter = nextDelimiter.getNextSibling())
381               ;
382           }
383           else {
384             prevDelimiter = expressionList.getFirstChild(); // left parenthesis
385             nextDelimiter = expressionList.getLastChild(); // right parenthesis
386           }
387           if (prevDelimiter != null && nextDelimiter != null) {
388             CharSequence text = editor.getDocument().getImmutableCharSequence();
389             int firstRangeStartOffset = prevDelimiter.getTextRange().getEndOffset();
390             int firstRangeEndOffset = CharArrayUtil.shiftForward(text, firstRangeStartOffset, WHITESPACE);
391             for (Inlay inlay : editor.getInlayModel().getInlineElementsInRange(firstRangeStartOffset, firstRangeEndOffset)) {
392               if (presentationManager.isParameterHint(inlay)) {
393                 highlightedHints.add(inlay);
394                 if (i == currentHintIndex && currentHint == null) currentHint = inlay;
395               }
396             }
397             int secondRangeEndOffset = nextDelimiter.getTextRange().getStartOffset();
398             if (secondRangeEndOffset > firstRangeEndOffset) {
399               int secondRangeStartOffset = CharArrayUtil.shiftBackward(text, secondRangeEndOffset - 1, WHITESPACE) + 1;
400               for (Inlay inlay : editor.getInlayModel().getInlineElementsInRange(secondRangeStartOffset, secondRangeEndOffset)) {
401                 if (presentationManager.isParameterHint(inlay)) {
402                   highlightedHints.add(inlay);
403                 }
404               }
405             }
406           }
407         }
408       }
409     }
410     if (currentHint == context.getUserData(CURRENT_HINT) && 
411         Objects.equals(highlightedHints, context.getUserData(HIGHLIGHTED_HINTS))) return;
412     resetHints(context);
413     if (currentHint != null) {
414       presentationManager.setCurrent(currentHint, true);
415       context.putUserData(CURRENT_HINT, currentHint);
416     }
417     if (!ContainerUtil.isEmpty(highlightedHints)) {
418       for (Inlay highlightedHint : highlightedHints) {
419         presentationManager.setHighlighted(highlightedHint, true);
420       }
421       context.putUserData(HIGHLIGHTED_HINTS, highlightedHints);
422     }
423   }
424
425   private static void resetHints(@NotNull UserDataHolder context) {
426     ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
427     Inlay currentHint = context.getUserData(CURRENT_HINT);
428     if (currentHint != null) {
429       presentationManager.setCurrent(currentHint, false);
430       context.putUserData(CURRENT_HINT, null);
431     }
432     List<Inlay> highlightedHints = context.getUserData(HIGHLIGHTED_HINTS);
433     if (highlightedHints != null) {
434       for (Inlay hint : highlightedHints) {
435         presentationManager.setHighlighted(hint, false);
436       }
437       context.putUserData(HIGHLIGHTED_HINTS, null);
438     }
439   }
440
441   @Override
442   public void dispose(@NotNull DeleteParameterInfoContext context) {
443     Editor editor = context.getEditor();
444     if (!(editor instanceof EditorWindow)) {
445       resetHints(context.getCustomContext());
446       PsiElement parameterOwner = context.getParameterOwner();
447       if (!editor.isDisposed() && parameterOwner != null && parameterOwner.isValid()) {
448         ParameterHintsPass.syncUpdate(parameterOwner.getParent(), editor);
449       }
450     }
451   }
452
453   private static PsiSubstitutor getCandidateInfoSubstitutor(PsiElement argList, CandidateInfo candidate, boolean resolveResult) {
454     Computable<PsiSubstitutor> computeSubstitutor =
455       () -> candidate instanceof MethodCandidateInfo && ((MethodCandidateInfo)candidate).isInferencePossible()
456             ? ((MethodCandidateInfo)candidate).inferTypeArguments(CompletionParameterTypeInferencePolicy.INSTANCE, true)
457             : candidate.getSubstitutor();
458     if (resolveResult && candidate instanceof MethodCandidateInfo && ((MethodCandidateInfo)candidate).isInferencePossible()) {
459       return computeSubstitutor.compute();
460     }
461     return MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(ObjectUtils.notNull(argList, candidate.getElement()),
462                                                                       false,
463                                                                       computeSubstitutor);
464   }
465
466   private static boolean isAssignableParametersBeforeGivenIndex(final PsiParameter[] parms,
467                                                                 final PsiExpression[] args,
468                                                                 int length,
469                                                                 PsiSubstitutor substitutor) {
470     for (int j = 0; j < length; j++) {
471       PsiParameter parm = parms[j];
472       PsiExpression arg = args[j];
473       assert parm.isValid();
474       assert arg.isValid();
475       PsiType parmType = parm.getType();
476       PsiType argType = arg.getType();
477       if (argType == null) continue;
478       if (parmType instanceof PsiEllipsisType ) {
479         parmType = ((PsiEllipsisType)parmType).getComponentType();
480       }
481       parmType = substitutor.substitute(parmType);
482
483       if (!parmType.isAssignableFrom(argType)) {
484         return false;
485       }
486     }
487     return true;
488   }
489
490   @Override
491   @NotNull
492   public Class<PsiExpressionList> getArgumentListClass() {
493     return PsiExpressionList.class;
494   }
495
496   @Override
497   @NotNull
498   public IElementType getActualParametersRBraceType() {
499     return JavaTokenType.RBRACE;
500   }
501
502   @Override
503   @NotNull
504   public Set<Class> getArgumentListAllowedParentClasses() {
505     return ourArgumentListAllowedParentClassesSet;
506   }
507
508   @NotNull
509   @Override
510   public Set<? extends Class> getArgListStopSearchClasses() {
511     return ourStopSearch;
512   }
513
514   @Override
515   @NotNull
516   public IElementType getActualParameterDelimiterType() {
517     return JavaTokenType.COMMA;
518   }
519
520   @Override
521   @NotNull
522   public PsiExpression[] getActualParameters(@NotNull PsiExpressionList psiExpressionList) {
523     return psiExpressionList.getExpressions();
524   }
525
526   private static PsiCall getCall(PsiExpressionList list) {
527     PsiElement listParent = list.getParent();
528     if (listParent instanceof PsiMethodCallExpression) {
529       return (PsiCall)listParent;
530     }
531     if (listParent instanceof PsiNewExpression) {
532       return (PsiCall)listParent;
533     }
534     if (listParent instanceof PsiAnonymousClass) {
535       return (PsiCall)listParent.getParent();
536     }
537     if (listParent instanceof PsiEnumConstant) {
538       return (PsiCall)listParent;
539     }
540     return null;
541   }
542
543   
544   
545   private static CandidateInfo[] getMethods(PsiExpressionList argList) {
546     final PsiCall call = getCall(argList);
547     PsiResolveHelper helper = JavaPsiFacade.getInstance(argList.getProject()).getResolveHelper();
548
549     if (call instanceof PsiCallExpression) {
550       CandidateInfo[] candidates = getCandidates((PsiCallExpression)call);
551       ArrayList<CandidateInfo> result = new ArrayList<>();
552
553       if (!(argList.getParent() instanceof PsiAnonymousClass)) {
554         cand:
555         for (CandidateInfo candidate : candidates) {
556           PsiMethod methodCandidate = (PsiMethod)candidate.getElement();
557
558           for (CandidateInfo info : result) {
559             if (MethodSignatureUtil.isSuperMethod(methodCandidate, (PsiMethod)info.getElement())) {
560               continue cand;
561             }
562           }
563           if (candidate.isStaticsScopeCorrect()) {
564             boolean accessible = candidate.isAccessible();
565             if (!accessible && methodCandidate.getModifierList().hasModifierProperty(PsiModifier.PRIVATE)) {
566               // privates are accessible within one file
567               accessible = JavaPsiFacade.getInstance(methodCandidate.getProject()).getResolveHelper()
568                 .isAccessible(methodCandidate, methodCandidate.getModifierList(), call, null, null);
569             }
570             if (accessible) result.add(candidate);
571           }
572         }
573       }
574       else {
575         PsiClass aClass = (PsiClass)argList.getParent();
576         for (CandidateInfo candidate : candidates) {
577           if (candidate.isStaticsScopeCorrect() && helper.isAccessible((PsiMethod)candidate.getElement(), argList, aClass)) {
578             result.add(candidate);
579           }
580         }
581       }
582       return result.isEmpty() ? candidates : result.toArray(CandidateInfo.EMPTY_ARRAY);
583     }
584     else {
585       assert call instanceof PsiEnumConstant;
586       //We are inside our own enum, no isAccessible check needed
587       PsiMethod[] constructors = ((PsiEnumConstant)call).getContainingClass().getConstructors();
588       CandidateInfo[] result = new CandidateInfo[constructors.length];
589
590       for (int i = 0; i < constructors.length; i++) {
591         result[i] = new CandidateInfo(constructors[i], PsiSubstitutor.EMPTY);
592       }
593       return result;
594     }
595   }
596
597   private static CandidateInfo[] getCandidates(PsiCallExpression call) {
598     final MethodCandidatesProcessor processor = new MethodResolverProcessor(call, call.getContainingFile(), new PsiConflictResolver[0]) {
599       @Override
600       protected boolean acceptVarargs() {
601         return false;
602       }
603     };
604
605     try {
606       PsiScopesUtil.setupAndRunProcessor(processor, call, true);
607     }
608     catch (MethodProcessorSetupFailedException e) {
609       return CandidateInfo.EMPTY_ARRAY;
610     }
611     final List<CandidateInfo> results = processor.getResults();
612     return results.toArray(CandidateInfo.EMPTY_ARRAY);
613   }
614
615   public static String updateMethodPresentation(@NotNull PsiMethod method, @Nullable PsiSubstitutor substitutor, @NotNull ParameterInfoUIContext context) {
616     CodeInsightSettings settings = CodeInsightSettings.getInstance();
617
618     if (!method.isValid() || substitutor != null && !substitutor.isValid()) {
619       context.setUIComponentEnabled(false);
620       return null;
621     }
622
623     StringBuilder buffer = new StringBuilder();
624
625     if (settings.SHOW_FULL_SIGNATURES_IN_PARAMETER_INFO && !context.isSingleParameterInfo()) {
626       if (!method.isConstructor()) {
627         PsiType returnType = method.getReturnType();
628         if (substitutor != null) {
629           returnType = substitutor.substitute(returnType);
630         }
631         assert returnType != null : method;
632
633         appendModifierList(buffer, method);
634         buffer.append(returnType.getPresentableText(true));
635         buffer.append(" ");
636       }
637       buffer.append(method.getName());
638       buffer.append("(");
639     }
640
641     int currentParameter = context.getCurrentParameterIndex();
642
643     PsiParameter[] parms = method.getParameterList().getParameters();
644     int numParams = parms.length;
645     int highlightStartOffset = -1;
646     int highlightEndOffset = -1;
647     if (numParams > 0) {
648       if (context.isSingleParameterInfo() && method.isVarArgs() && currentParameter >= numParams) currentParameter = numParams - 1;
649
650       for (int j = 0; j < numParams; j++) {
651         if (context.isSingleParameterInfo() && j != currentParameter) continue;
652         
653         PsiParameter param = parms[j];
654
655         int startOffset = buffer.length();
656
657         if (param.isValid()) {
658           PsiType paramType = param.getType();
659           assert paramType.isValid();
660           if (substitutor != null) {
661             assert substitutor.isValid();
662             paramType = substitutor.substitute(paramType);
663           }
664           if (context.isSingleParameterInfo()) buffer.append("<b>");
665           appendModifierList(buffer, param);
666           String type = paramType.getPresentableText(true);
667           buffer.append(context.isSingleParameterInfo() ? StringUtil.escapeXml(type) : type);
668           String name = param.getName();
669           if (name != null && !context.isSingleParameterInfo()) {
670             buffer.append(" ");
671             buffer.append(name);
672           }
673           if (context.isSingleParameterInfo()) buffer.append("</b>");
674         }
675
676         if (context.isSingleParameterInfo()) {
677           String javaDoc = new JavaDocInfoGenerator(param.getProject(), param).generateMethodParameterJavaDoc();
678           if (javaDoc != null) {
679             javaDoc = removeHyperlinks(javaDoc);
680             if (javaDoc.length() < 100) {
681               buffer.append("&nbsp;&nbsp;<i>").append(javaDoc).append("</i>");
682             }
683             else {
684               buffer.insert(0, "<table><tr><td valign='top'>")
685                 .append("</td><td style='width:400px'>&nbsp;&nbsp;<i>").append(javaDoc).append("</i></td></tr></table>");              
686             }
687           }
688         }
689         else {
690           int endOffset = buffer.length();
691
692           if (j < numParams - 1) {
693             buffer.append(", ");
694           }
695
696           if (context.isUIComponentEnabled() &&
697               (j == currentParameter || j == numParams - 1 && param.isVarArgs() && currentParameter >= numParams)) {
698             highlightStartOffset = startOffset;
699             highlightEndOffset = endOffset;
700           }
701         }
702       }
703     }
704     else {
705       buffer.append(CodeInsightBundle.message("parameter.info.no.parameters"));
706     }
707
708     if (settings.SHOW_FULL_SIGNATURES_IN_PARAMETER_INFO && !context.isSingleParameterInfo()) {
709       buffer.append(")");
710     }
711
712     String text = buffer.toString();
713     if (context.isSingleParameterInfo()) {
714       context.setupRawUIComponentPresentation(text);
715       return text;
716     }
717     else {
718       return context.setupUIComponentPresentation(
719         text,
720         highlightStartOffset,
721         highlightEndOffset,
722         !context.isUIComponentEnabled(),
723         method.isDeprecated() && !context.isSingleParameterInfo() && !context.isSingleOverload(),
724         false,
725         context.getDefaultParameterColor()
726       );
727     }
728   }
729
730   private static String removeHyperlinks(String html) {
731     return html.replaceAll("<a.*?>", "").replaceAll("</a>", "");
732   }
733
734   private static void appendModifierList(@NotNull StringBuilder buffer, @NotNull PsiModifierListOwner owner) {
735     int lastSize = buffer.length();
736     Set<String> shownAnnotations = ContainerUtil.newHashSet();
737     for (PsiAnnotation annotation : AnnotationUtil.getAllAnnotations(owner, false, null, !DumbService.isDumb(owner.getProject()))) {
738       final PsiJavaCodeReferenceElement element = annotation.getNameReferenceElement();
739       if (element != null) {
740         final PsiElement resolved = element.resolve();
741         if (resolved instanceof PsiClass &&
742             (!JavaDocInfoGenerator.isDocumentedAnnotationType((PsiClass)resolved) ||
743              AnnotationTargetUtil.findAnnotationTarget((PsiClass)resolved, PsiAnnotation.TargetType.TYPE_USE) != null)) {
744           continue;
745         }
746
747         String referenceName = element.getReferenceName();
748         if (shownAnnotations.add(referenceName) || JavaDocInfoGenerator.isRepeatableAnnotationType(resolved)) {
749           if (lastSize != buffer.length()) buffer.append(' ');
750           buffer.append('@').append(referenceName);
751         }
752       }
753     }
754     if (lastSize != buffer.length()) buffer.append(' ');
755   }
756
757   @Override
758   public void updateUI(final Object p, @NotNull final ParameterInfoUIContext context) {
759     if (p instanceof CandidateInfo) {
760       CandidateInfo info = (CandidateInfo)p;
761       PsiMethod method = (PsiMethod)info.getElement();
762       if (!method.isValid() || info instanceof MethodCandidateInfo && !((MethodCandidateInfo)info).getSiteSubstitutor().isValid()) {
763         context.setUIComponentEnabled(false);
764         return;
765       }
766
767       updateMethodPresentation(method, getCandidateInfoSubstitutor(context.getParameterOwner(), info, false), context);
768     }
769     else {
770       updateMethodPresentation((PsiMethod)p, null, context);
771     }
772   }
773
774   @Override
775   public boolean supportsOverloadSwitching() {
776     return CodeInsightSettings.getInstance().SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION;
777   }
778 }