6cba2e140fc8aa329028fbebf6d0efb253e6531e
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInsight / daemon / impl / analysis / HighlightMethodUtil.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.codeInsight.daemon.impl.analysis;
17
18 import com.intellij.codeInsight.ExceptionUtil;
19 import com.intellij.codeInsight.daemon.DaemonBundle;
20 import com.intellij.codeInsight.daemon.JavaErrorMessages;
21 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
22 import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
23 import com.intellij.codeInsight.daemon.impl.quickfix.*;
24 import com.intellij.codeInsight.intention.IntentionAction;
25 import com.intellij.codeInsight.intention.QuickFixFactory;
26 import com.intellij.codeInspection.LocalQuickFixOnPsiElementAsIntentionAdapter;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.project.IndexNotReadyException;
29 import com.intellij.openapi.projectRoots.JavaSdkVersion;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.openapi.util.Ref;
32 import com.intellij.openapi.util.TextRange;
33 import com.intellij.openapi.util.text.StringUtil;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import com.intellij.pom.java.LanguageLevel;
36 import com.intellij.psi.*;
37 import com.intellij.psi.impl.PsiSuperMethodImplUtil;
38 import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
39 import com.intellij.psi.infos.CandidateInfo;
40 import com.intellij.psi.infos.MethodCandidateInfo;
41 import com.intellij.psi.util.*;
42 import com.intellij.refactoring.util.RefactoringChangeUtil;
43 import com.intellij.ui.ColorUtil;
44 import com.intellij.util.containers.MostlySingularMultiMap;
45 import com.intellij.util.ui.UIUtil;
46 import com.intellij.xml.util.XmlStringUtil;
47 import org.intellij.lang.annotations.Language;
48 import org.jetbrains.annotations.NonNls;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
51
52 import java.text.MessageFormat;
53 import java.util.ArrayList;
54 import java.util.Collection;
55 import java.util.List;
56
57 /**
58  * Highlight method problems
59  *
60  * @author cdr
61  * Date: Aug 14, 2002
62  */
63 public class HighlightMethodUtil {
64   private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance();
65   private static final String MISMATCH_COLOR = UIUtil.isUnderDarcula() ? "ff6464" : "red";
66
67   private HighlightMethodUtil() { }
68
69   static String createClashMethodMessage(PsiMethod method1, PsiMethod method2, boolean showContainingClasses) {
70     @NonNls String pattern = showContainingClasses ? "clash.methods.message.show.classes" : "clash.methods.message";
71     return JavaErrorMessages.message(pattern,
72                                      JavaHighlightUtil.formatMethod(method1),
73                                      JavaHighlightUtil.formatMethod(method2),
74                                      HighlightUtil.formatClass(method1.getContainingClass()),
75                                      HighlightUtil.formatClass(method2.getContainingClass()));
76   }
77
78   static HighlightInfo checkMethodWeakerPrivileges(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
79                                                    @NotNull List<HierarchicalMethodSignature> superMethodSignatures,
80                                                    boolean includeRealPositionInfo,
81                                                    @NotNull PsiFile containingFile) {
82     PsiMethod method = methodSignature.getMethod();
83     PsiModifierList modifierList = method.getModifierList();
84     if (modifierList.hasModifierProperty(PsiModifier.PUBLIC)) return null;
85     int accessLevel = PsiUtil.getAccessLevel(modifierList);
86     String accessModifier = PsiUtil.getAccessModifier(accessLevel);
87     for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
88       PsiMethod superMethod = superMethodSignature.getMethod();
89       if (method.hasModifierProperty(PsiModifier.ABSTRACT) && !MethodSignatureUtil.isSuperMethod(superMethod, method)) continue;
90       if (!PsiUtil.isAccessible(containingFile.getProject(), superMethod, method, null)) continue;
91       if (!includeRealPositionInfo && MethodSignatureUtil.isSuperMethod(superMethod, method)) continue;
92       HighlightInfo info = isWeaker(method, modifierList, accessModifier, accessLevel, superMethod, includeRealPositionInfo);
93       if (info != null) return info;
94     }
95     return null;
96   }
97
98   private static HighlightInfo isWeaker(final PsiMethod method, final PsiModifierList modifierList, final String accessModifier, final int accessLevel,
99                                         final PsiMethod superMethod,
100                                         final boolean includeRealPositionInfo) {
101     int superAccessLevel = PsiUtil.getAccessLevel(superMethod.getModifierList());
102     if (accessLevel < superAccessLevel) {
103       String description = JavaErrorMessages.message("weaker.privileges",
104                                             createClashMethodMessage(method, superMethod, true),
105                                             accessModifier,
106                                             PsiUtil.getAccessModifier(superAccessLevel));
107       TextRange textRange;
108       if (includeRealPositionInfo) {
109         if (modifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) {
110           textRange = method.getNameIdentifier().getTextRange();
111         }
112         else {
113           PsiElement keyword = PsiUtil.findModifierInList(modifierList, accessModifier);
114           textRange = keyword.getTextRange();
115         }
116       }
117       else {
118         textRange = TextRange.EMPTY_RANGE;
119       }
120       HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
121       QuickFixAction.registerQuickFixAction(highlightInfo,
122                                             QUICK_FIX_FACTORY.createModifierListFix(method, PsiUtil.getAccessModifier(superAccessLevel), true, false));
123       return highlightInfo;
124     }
125     return null;
126   }
127
128
129   static HighlightInfo checkMethodIncompatibleReturnType(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
130                                                          @NotNull List<HierarchicalMethodSignature> superMethodSignatures,
131                                                          boolean includeRealPositionInfo,
132                                                          @NotNull LanguageLevel languageLevel) {
133     return checkMethodIncompatibleReturnType(methodSignature, superMethodSignatures, includeRealPositionInfo, null, languageLevel);
134   }
135
136   static HighlightInfo checkMethodIncompatibleReturnType(MethodSignatureBackedByPsiMethod methodSignature,
137                                                          List<HierarchicalMethodSignature> superMethodSignatures,
138                                                          boolean includeRealPositionInfo,
139                                                          TextRange textRange,
140                                                          @NotNull LanguageLevel languageLevel) {
141     PsiMethod method = methodSignature.getMethod();
142     PsiType returnType = methodSignature.getSubstitutor().substitute(method.getReturnType());
143     PsiClass aClass = method.getContainingClass();
144     if (aClass == null) return null;
145     for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
146       PsiMethod superMethod = superMethodSignature.getMethod();
147       PsiType declaredReturnType = superMethod.getReturnType();
148       PsiType superReturnType = declaredReturnType;
149       if (superMethodSignature.isRaw()) superReturnType = TypeConversionUtil.erasure(declaredReturnType);
150       if (returnType == null || superReturnType == null || method == superMethod) continue;
151       PsiClass superClass = superMethod.getContainingClass();
152       if (superClass == null) continue;
153       TextRange toHighlight = textRange != null ? textRange
154                                           : includeRealPositionInfo ? method.getReturnTypeElement().getTextRange() : TextRange.EMPTY_RANGE;
155       HighlightInfo highlightInfo = checkSuperMethodSignature(superMethod, superMethodSignature, superReturnType, method, methodSignature,
156                                                               returnType, JavaErrorMessages.message("incompatible.return.type"),
157                                                               toHighlight, PsiUtil.getLanguageLevel(method));
158       if (highlightInfo != null) return highlightInfo;
159     }
160
161     return null;
162   }
163
164   private static HighlightInfo checkSuperMethodSignature(@NotNull PsiMethod superMethod,
165                                                          @NotNull MethodSignatureBackedByPsiMethod superMethodSignature,
166                                                          PsiType superReturnType,
167                                                          @NotNull PsiMethod method,
168                                                          @NotNull MethodSignatureBackedByPsiMethod methodSignature,
169                                                          @NotNull PsiType returnType,
170                                                          @NotNull String detailMessage,
171                                                          @NotNull TextRange range,
172                                                          @NotNull LanguageLevel languageLevel) {
173     if (superReturnType == null) return null;
174     if ("clone".equals(method.getName())) {
175       final PsiClass containingClass = method.getContainingClass();
176       final PsiClass superContainingClass = superMethod.getContainingClass();
177       if (containingClass != null && superContainingClass != null && containingClass.isInterface() && !superContainingClass.isInterface()) {
178         return null;
179       }
180     }
181
182     PsiType substitutedSuperReturnType;
183     final boolean isJdk15 = languageLevel.isAtLeast(LanguageLevel.JDK_1_5);
184     if (isJdk15 && !superMethodSignature.isRaw() && superMethodSignature.equals(methodSignature)) { //see 8.4.5
185       PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature,
186                                                                                                   superMethodSignature);
187       substitutedSuperReturnType = unifyingSubstitutor == null
188                                    ? superReturnType
189                                    : unifyingSubstitutor.substitute(superReturnType);
190     }
191     else {
192       substitutedSuperReturnType = TypeConversionUtil.erasure(superMethodSignature.getSubstitutor().substitute(superReturnType));
193     }
194
195     if (returnType.equals(substitutedSuperReturnType)) return null;
196     if (!(returnType instanceof PsiPrimitiveType) && substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType) {
197       if (isJdk15 && TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType)) {
198         return null;
199       }
200     }
201
202     return createIncompatibleReturnTypeMessage(method, superMethod, substitutedSuperReturnType, returnType, detailMessage, range);
203   }
204
205   private static HighlightInfo createIncompatibleReturnTypeMessage(@NotNull PsiMethod method,
206                                                                    @NotNull PsiMethod superMethod,
207                                                                    @NotNull PsiType substitutedSuperReturnType,
208                                                                    @NotNull PsiType returnType,
209                                                                    @NotNull String detailMessage,
210                                                                    @NotNull TextRange textRange) {
211     String description = MessageFormat.format("{0}; {1}", createClashMethodMessage(method, superMethod, true), detailMessage);
212     HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(
213       description).create();
214     QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createMethodReturnFix(method, substitutedSuperReturnType, false));
215     QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createSuperMethodReturnFix(superMethod, returnType));
216
217     return errorResult;
218   }
219
220
221   static HighlightInfo checkMethodOverridesFinal(MethodSignatureBackedByPsiMethod methodSignature,
222                                                  List<HierarchicalMethodSignature> superMethodSignatures) {
223     PsiMethod method = methodSignature.getMethod();
224     for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
225       PsiMethod superMethod = superMethodSignature.getMethod();
226       HighlightInfo info = checkSuperMethodIsFinal(method, superMethod);
227       if (info != null) return info;
228     }
229     return null;
230   }
231
232   private static HighlightInfo checkSuperMethodIsFinal(PsiMethod method, PsiMethod superMethod) {
233     // strange things happen when super method is from Object and method from interface
234     if (superMethod.hasModifierProperty(PsiModifier.FINAL)) {
235       String description = JavaErrorMessages.message("final.method.override",
236                                                  JavaHighlightUtil.formatMethod(method),
237                                                  JavaHighlightUtil.formatMethod(superMethod),
238                                                  HighlightUtil.formatClass(superMethod.getContainingClass()));
239       TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
240       HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
241       QuickFixAction.registerQuickFixAction(errorResult,
242                                             QUICK_FIX_FACTORY.createModifierListFix(superMethod, PsiModifier.FINAL, false, true));
243       return errorResult;
244     }
245     return null;
246   }
247
248   static HighlightInfo checkMethodIncompatibleThrows(MethodSignatureBackedByPsiMethod methodSignature,
249                                                             List<HierarchicalMethodSignature> superMethodSignatures,
250                                                             boolean includeRealPositionInfo, PsiClass analyzedClass) {
251     PsiMethod method = methodSignature.getMethod();
252     PsiClass aClass = method.getContainingClass();
253     if (aClass == null) return null;
254     PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(aClass, analyzedClass, PsiSubstitutor.EMPTY);
255     PsiClassType[] exceptions = method.getThrowsList().getReferencedTypes();
256     PsiJavaCodeReferenceElement[] referenceElements;
257     List<PsiElement> exceptionContexts;
258     if (includeRealPositionInfo) {
259       exceptionContexts = new ArrayList<PsiElement>();
260       referenceElements = method.getThrowsList().getReferenceElements();
261     }
262     else {
263       exceptionContexts = null;
264       referenceElements = null;
265     }
266     List<PsiClassType> checkedExceptions = new ArrayList<PsiClassType>();
267     for (int i = 0; i < exceptions.length; i++) {
268       PsiClassType exception = exceptions[i];
269       if (!ExceptionUtil.isUncheckedException(exception)) {
270         checkedExceptions.add(exception);
271         if (includeRealPositionInfo && i < referenceElements.length) {
272           PsiJavaCodeReferenceElement exceptionRef = referenceElements[i];
273           exceptionContexts.add(exceptionRef);
274         }
275       }
276     }
277     for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
278       PsiMethod superMethod = superMethodSignature.getMethod();
279       int index = getExtraExceptionNum(methodSignature, superMethodSignature, checkedExceptions, superSubstitutor);
280       if (index != -1) {
281         if (aClass.isInterface()) {
282           final PsiClass superContainingClass = superMethod.getContainingClass();
283           if (superContainingClass != null && !superContainingClass.isInterface()) continue;
284           if (superContainingClass != null && !aClass.isInheritor(superContainingClass, true)) continue;
285         }
286         PsiClassType exception = checkedExceptions.get(index);
287         String description = JavaErrorMessages.message("overridden.method.does.not.throw",
288                                                    createClashMethodMessage(method, superMethod, true),
289                                                    JavaHighlightUtil.formatType(exception));
290         TextRange textRange;
291         if (includeRealPositionInfo) {
292           PsiElement exceptionContext = exceptionContexts.get(index);
293           textRange = exceptionContext.getTextRange();
294         }
295         else {
296           textRange = TextRange.EMPTY_RANGE;
297         }
298         HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
299         QuickFixAction.registerQuickFixAction(errorResult, new LocalQuickFixOnPsiElementAsIntentionAdapter(QUICK_FIX_FACTORY.createMethodThrowsFix(method, exception, false, false)));
300         QuickFixAction.registerQuickFixAction(errorResult, new LocalQuickFixOnPsiElementAsIntentionAdapter(QUICK_FIX_FACTORY.createMethodThrowsFix(superMethod, exception, true, true)));
301         return errorResult;
302       }
303     }
304     return null;
305   }
306
307   // return number of exception  which was not declared in super method or -1
308   private static int getExtraExceptionNum(final MethodSignature methodSignature,
309                                           final MethodSignatureBackedByPsiMethod superSignature,
310                                           List<PsiClassType> checkedExceptions, PsiSubstitutor substitutorForDerivedClass) {
311     PsiMethod superMethod = superSignature.getMethod();
312     PsiSubstitutor substitutorForMethod = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superSignature);
313     for (int i = 0; i < checkedExceptions.size(); i++) {
314       final PsiClassType checkedEx = checkedExceptions.get(i);
315       final PsiType substituted = substitutorForMethod != null ? substitutorForMethod.substitute(checkedEx) : TypeConversionUtil.erasure(checkedEx);
316       PsiType exception = substitutorForDerivedClass.substitute(substituted);
317       if (!isMethodThrows(superMethod, substitutorForMethod, exception, substitutorForDerivedClass)) {
318         return i;
319       }
320     }
321     return -1;
322   }
323
324   private static boolean isMethodThrows(PsiMethod method, @Nullable PsiSubstitutor substitutorForMethod, PsiType exception, PsiSubstitutor substitutorForDerivedClass) {
325     PsiClassType[] thrownExceptions = method.getThrowsList().getReferencedTypes();
326     for (PsiClassType thrownException1 : thrownExceptions) {
327       PsiType thrownException = substitutorForMethod != null ? substitutorForMethod.substitute(thrownException1) : TypeConversionUtil.erasure(thrownException1);
328       thrownException = substitutorForDerivedClass.substitute(thrownException);
329       if (TypeConversionUtil.isAssignable(thrownException, exception)) return true;
330     }
331     return false;
332   }
333
334   @Nullable
335   static HighlightInfo checkMethodCall(@NotNull PsiMethodCallExpression methodCall,
336                                        @NotNull PsiResolveHelper resolveHelper,
337                                        @NotNull LanguageLevel languageLevel,
338                                        @NotNull JavaSdkVersion javaSdkVersion) {
339     PsiExpressionList list = methodCall.getArgumentList();
340     PsiReferenceExpression referenceToMethod = methodCall.getMethodExpression();
341     JavaResolveResult[] results = referenceToMethod.multiResolve(true);
342     JavaResolveResult resolveResult = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
343     PsiElement resolved = resolveResult.getElement();
344
345     boolean isDummy = isDummyConstructorCall(methodCall, resolveHelper, list, referenceToMethod);
346     if (isDummy) return null;
347     HighlightInfo highlightInfo;
348
349     final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
350     if (resolved instanceof PsiMethod && resolveResult.isValidResult()) {
351       TextRange fixRange = getFixRange(methodCall);
352       highlightInfo = HighlightUtil.checkUnhandledExceptions(methodCall, fixRange);
353       if (highlightInfo == null) {
354         final String invalidCallMessage = 
355           LambdaUtil.getInvalidQualifier4StaticInterfaceMethodMessage((PsiMethod)resolved, methodCall.getMethodExpression(), resolveResult.getCurrentFileResolveScope(), languageLevel);
356         if (invalidCallMessage != null) {
357           highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(invalidCallMessage).range(fixRange).create();
358           if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
359             QuickFixAction.registerQuickFixAction(highlightInfo, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_8));
360           }
361         } else {
362           highlightInfo = GenericsHighlightUtil.checkInferredIntersections(substitutor, fixRange);
363         }
364       }
365     }
366     else {
367       PsiMethod resolvedMethod = null;
368       MethodCandidateInfo candidateInfo = null;
369       if (resolveResult instanceof MethodCandidateInfo) {
370         candidateInfo = (MethodCandidateInfo)resolveResult;
371         resolvedMethod = candidateInfo.getElement();
372       }
373
374       if (!resolveResult.isAccessible() || !resolveResult.isStaticsScopeCorrect()) {
375         highlightInfo = null;
376       }
377       else if (candidateInfo != null && !candidateInfo.isApplicable()) {
378         if (candidateInfo.isTypeArgumentsApplicable()) {
379           String methodName = HighlightMessageUtil.getSymbolName(resolved, substitutor);
380           PsiElement parent = resolved.getParent();
381           String containerName = parent == null ? "" : HighlightMessageUtil.getSymbolName(parent, substitutor);
382           String argTypes = buildArgTypesList(list);
383           String description = JavaErrorMessages.message("wrong.method.arguments", methodName, containerName, argTypes);
384           final Ref<PsiElement> elementToHighlight = new Ref<PsiElement>(list);
385           String toolTip;
386           if (parent instanceof PsiClass && !ApplicationManager.getApplication().isUnitTestMode()) {
387             toolTip = buildOneLineMismatchDescription(list, candidateInfo, elementToHighlight);
388             if (toolTip == null) {
389               toolTip = createMismatchedArgumentsHtmlTooltip(candidateInfo, list);
390             }
391           }
392           else {
393             toolTip = description;
394           }
395           PsiElement element = elementToHighlight.get();
396           int navigationShift = element instanceof PsiExpressionList ? +1 : 0; // argument list starts with paren which there is no need to highlight
397           highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element)
398             .description(description).escapedToolTip(toolTip).navigationShift(navigationShift).create();
399           if (highlightInfo != null) {
400             registerMethodCallIntentions(highlightInfo, methodCall, list, resolveHelper);
401           }
402         }
403         else {
404           PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
405           PsiReferenceParameterList typeArgumentList = methodCall.getTypeArgumentList();
406           if (typeArgumentList.getTypeArguments().length == 0 && resolvedMethod.hasTypeParameters()) {
407             highlightInfo = GenericsHighlightUtil.checkInferredTypeArguments(resolvedMethod, methodCall, substitutor);
408           }
409           else {
410             highlightInfo = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, methodExpression, substitutor, javaSdkVersion);
411           }
412         }
413       }
414       else {
415         String description = JavaErrorMessages.message("method.call.expected");
416         highlightInfo =
417           HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCall).descriptionAndTooltip(description).create();
418         if (resolved instanceof PsiClass) {
419           QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createInsertNewFix(methodCall, (PsiClass)resolved));
420         }
421         else {
422           TextRange range = getFixRange(methodCall);
423           QuickFixAction.registerQuickFixAction(highlightInfo, range, QUICK_FIX_FACTORY.createCreateMethodFromUsageFix(methodCall));
424           QuickFixAction.registerQuickFixAction(highlightInfo, range, QUICK_FIX_FACTORY.createCreateAbstractMethodFromUsageFix(methodCall));
425           QuickFixAction.registerQuickFixAction(highlightInfo, range, QUICK_FIX_FACTORY.createCreatePropertyFromUsageFix(methodCall));
426         }
427       }
428     }
429     if (highlightInfo == null) {
430       highlightInfo = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, substitutor,
431                                                                                      javaSdkVersion);
432     }
433     return highlightInfo;
434   }
435
436   private static String buildOneLineMismatchDescription(@NotNull PsiExpressionList list,
437                                                         @NotNull MethodCandidateInfo candidateInfo,
438                                                         @NotNull Ref<PsiElement> elementToHighlight) {
439     final PsiExpression[] expressions = list.getExpressions();
440     final PsiMethod resolvedMethod = candidateInfo.getElement();
441     final PsiSubstitutor substitutor = candidateInfo.getSubstitutor();
442     final PsiParameter[] parameters = resolvedMethod.getParameterList().getParameters();
443     if (expressions.length == parameters.length && parameters.length > 1) {
444       int idx = -1;
445       for (int i = 0; i < expressions.length; i++) {
446         PsiExpression expression = expressions[i];
447         if (!TypeConversionUtil.areTypesAssignmentCompatible(substitutor.substitute(parameters[i].getType()), expression)) {
448           if (idx != -1) {
449             idx = -1;
450             break;
451           }
452           else {
453             idx = i;
454           }
455         }
456       }
457
458       if (idx > -1) {
459         final PsiExpression wrongArg = expressions[idx];
460         final PsiType argType = wrongArg.getType();
461         if (argType != null) {
462           elementToHighlight.set(wrongArg);
463           final String message = JavaErrorMessages
464             .message("incompatible.call.types", idx + 1, substitutor.substitute(parameters[idx].getType()).getCanonicalText(), argType.getCanonicalText());
465
466           return XmlStringUtil.wrapInHtml("<body>" + XmlStringUtil.escapeString(message) +
467                                           " <a href=\"#assignment/" + XmlStringUtil.escapeString(createMismatchedArgumentsHtmlTooltip(candidateInfo, list)) + "\"" +
468                                           (UIUtil.isUnderDarcula() ? " color=\"7AB4C9\" " : "") +
469                                           ">" + DaemonBundle.message("inspection.extended.description") + "</a></body>");
470         }
471       }
472     }
473     return null;
474   }
475
476   static boolean isDummyConstructorCall(PsiMethodCallExpression methodCall,
477                                         PsiResolveHelper resolveHelper,
478                                         PsiExpressionList list,
479                                         PsiReferenceExpression referenceToMethod) {
480     boolean isDummy = false;
481     boolean isThisOrSuper = referenceToMethod.getReferenceNameElement() instanceof PsiKeyword;
482     if (isThisOrSuper) {
483       // super(..) or this(..)
484       if (list.getExpressions().length == 0) { // implicit ctr call
485         CandidateInfo[] candidates = resolveHelper.getReferencedMethodCandidates(methodCall, true);
486         if (candidates.length == 1 && !candidates[0].getElement().isPhysical()) {
487           isDummy = true;// dummy constructor
488         }
489       }
490     }
491     return isDummy;
492   }
493
494   @Nullable
495   static HighlightInfo checkAmbiguousMethodCallIdentifier(@NotNull PsiReferenceExpression referenceToMethod,
496                                                 @NotNull JavaResolveResult[] resolveResults,
497                                                 @NotNull PsiExpressionList list,
498                                                 final PsiElement element,
499                                                 @NotNull JavaResolveResult resolveResult,
500                                                 @NotNull PsiMethodCallExpression methodCall,
501                                                 @NotNull PsiResolveHelper resolveHelper) {
502     MethodCandidateInfo methodCandidate1 = null;
503     MethodCandidateInfo methodCandidate2 = null;
504     for (JavaResolveResult result : resolveResults) {
505       if (!(result instanceof MethodCandidateInfo)) continue;
506       MethodCandidateInfo candidate = (MethodCandidateInfo)result;
507       if (candidate.isApplicable() && !candidate.getElement().isConstructor()) {
508         if (methodCandidate1 == null) {
509           methodCandidate1 = candidate;
510         }
511         else {
512           methodCandidate2 = candidate;
513           break;
514         }
515       }
516     }
517     MethodCandidateInfo[] candidates = toMethodCandidates(resolveResults);
518
519     String description;
520     String toolTip;
521     PsiElement elementToHighlight;
522     HighlightInfoType highlightInfoType = HighlightInfoType.ERROR;
523     if (methodCandidate2 != null) {
524       return null;
525     }
526     if (element != null && !resolveResult.isAccessible()) {
527       description = HighlightUtil.buildProblemWithAccessDescription(referenceToMethod, resolveResult);
528       elementToHighlight = referenceToMethod.getReferenceNameElement();
529     }
530     else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
531       final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(referenceToMethod);
532       final String staticInterfaceMethodMessage = 
533         element instanceof PsiMethod 
534         ? LambdaUtil.getInvalidQualifier4StaticInterfaceMethodMessage((PsiMethod)element, referenceToMethod, 
535                                                                       resolveResult.getCurrentFileResolveScope(), languageLevel) 
536         : null;
537       description = staticInterfaceMethodMessage != null 
538                     ? staticInterfaceMethodMessage 
539                     : HighlightUtil.buildProblemWithStaticDescription(element);
540       elementToHighlight = referenceToMethod.getReferenceNameElement();
541     }
542     else {
543       String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list);
544       description = JavaErrorMessages.message("cannot.resolve.method", methodName);
545       if (candidates.length == 0) {
546         elementToHighlight = referenceToMethod.getReferenceNameElement();
547         highlightInfoType = HighlightInfoType.WRONG_REF;
548       }
549       else {
550         return null;
551       }
552     }
553     toolTip = XmlStringUtil.escapeString(description);
554     HighlightInfo info =
555       HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description).escapedToolTip(toolTip).create();
556     registerMethodCallIntentions(info, methodCall, list, resolveHelper);
557     if (element != null && !resolveResult.isStaticsScopeCorrect()) {
558       HighlightUtil.registerStaticProblemQuickFixAction(element, info, referenceToMethod);
559     }
560
561     TextRange fixRange = getFixRange(elementToHighlight);
562     CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, info, fixRange);
563     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
564     PermuteArgumentsFix.registerFix(info, methodCall, candidates, fixRange);
565     WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), info);
566     registerChangeParameterClassFix(methodCall, list, info);
567     return info;
568   }
569
570   @Nullable
571   static HighlightInfo checkAmbiguousMethodCallArguments(@NotNull PsiReferenceExpression referenceToMethod,
572                                                 @NotNull JavaResolveResult[] resolveResults,
573                                                 @NotNull PsiExpressionList list,
574                                                 final PsiElement element,
575                                                 @NotNull JavaResolveResult resolveResult,
576                                                 @NotNull PsiMethodCallExpression methodCall,
577                                                 @NotNull PsiResolveHelper resolveHelper) {
578     MethodCandidateInfo methodCandidate1 = null;
579     MethodCandidateInfo methodCandidate2 = null;
580     for (JavaResolveResult result : resolveResults) {
581       if (!(result instanceof MethodCandidateInfo)) continue;
582       MethodCandidateInfo candidate = (MethodCandidateInfo)result;
583       if (candidate.isApplicable() && !candidate.getElement().isConstructor()) {
584         if (methodCandidate1 == null) {
585           methodCandidate1 = candidate;
586         }
587         else {
588           methodCandidate2 = candidate;
589           break;
590         }
591       }
592     }
593     MethodCandidateInfo[] candidates = toMethodCandidates(resolveResults);
594
595     String description;
596     String toolTip;
597     PsiElement elementToHighlight;
598     HighlightInfoType highlightInfoType = HighlightInfoType.ERROR;
599     if (methodCandidate2 != null) {
600       PsiMethod element1 = methodCandidate1.getElement();
601       String m1 = PsiFormatUtil.formatMethod(element1,
602                                              methodCandidate1.getSubstitutor(),
603                                              PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
604                                              PsiFormatUtilBase.SHOW_PARAMETERS,
605                                              PsiFormatUtilBase.SHOW_TYPE);
606       PsiMethod element2 = methodCandidate2.getElement();
607       String m2 = PsiFormatUtil.formatMethod(element2,
608                                              methodCandidate2.getSubstitutor(),
609                                              PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
610                                              PsiFormatUtilBase.SHOW_PARAMETERS,
611                                              PsiFormatUtilBase.SHOW_TYPE);
612       VirtualFile virtualFile1 = PsiUtilCore.getVirtualFile(element1);
613       VirtualFile virtualFile2 = PsiUtilCore.getVirtualFile(element2);
614       if (!Comparing.equal(virtualFile1, virtualFile2)) {
615         if (virtualFile1 != null) m1 += " (In " + virtualFile1.getPresentableUrl() + ")";
616         if (virtualFile2 != null) m2 += " (In " + virtualFile2.getPresentableUrl() + ")";
617       }
618       description = JavaErrorMessages.message("ambiguous.method.call", m1, m2);
619       toolTip = createAmbiguousMethodHtmlTooltip(new MethodCandidateInfo[]{methodCandidate1, methodCandidate2});
620       elementToHighlight = list;
621     }
622     else {
623       if (element != null && !resolveResult.isAccessible()) {
624         return null;
625       }
626       else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
627         return null;
628       }
629       else {
630         String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list);
631         description = JavaErrorMessages.message("cannot.resolve.method", methodName);
632         if (candidates.length == 0) {
633           return null;
634         }
635         else {
636           elementToHighlight = list;
637         }
638       }
639       toolTip = XmlStringUtil.escapeString(description);
640     }
641     HighlightInfo info =
642       HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description).escapedToolTip(toolTip).create();
643     if (methodCandidate2 == null) {
644       registerMethodCallIntentions(info, methodCall, list, resolveHelper);
645     }
646     if (!resolveResult.isAccessible() && resolveResult.isStaticsScopeCorrect() && methodCandidate2 != null) {
647       HighlightUtil.registerAccessQuickFixAction((PsiMember)element, referenceToMethod, info, resolveResult.getCurrentFileResolveScope());
648     }
649     if (element != null && !resolveResult.isStaticsScopeCorrect()) {
650       HighlightUtil.registerStaticProblemQuickFixAction(element, info, referenceToMethod);
651     }
652
653     TextRange fixRange = getFixRange(elementToHighlight);
654     CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, info, fixRange);
655     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
656     PermuteArgumentsFix.registerFix(info, methodCall, candidates, fixRange);
657     WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), info);
658     registerChangeParameterClassFix(methodCall, list, info);
659     return info;
660   }
661
662   @NotNull
663   private static MethodCandidateInfo[] toMethodCandidates(@NotNull JavaResolveResult[] resolveResults) {
664     List<MethodCandidateInfo> candidateList = new ArrayList<MethodCandidateInfo>(resolveResults.length);
665
666     for (JavaResolveResult result : resolveResults) {
667       if (!(result instanceof MethodCandidateInfo)) continue;
668       MethodCandidateInfo candidate = (MethodCandidateInfo)result;
669       if (candidate.isAccessible()) candidateList.add(candidate);
670     }
671     return candidateList.toArray(new MethodCandidateInfo[candidateList.size()]);
672   }
673
674   private static void registerMethodCallIntentions(@Nullable HighlightInfo highlightInfo,
675                                                    PsiMethodCallExpression methodCall,
676                                                    PsiExpressionList list,
677                                                    PsiResolveHelper resolveHelper) {
678     TextRange fixRange = getFixRange(methodCall);
679     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateMethodFromUsageFix(methodCall));
680     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateAbstractMethodFromUsageFix(methodCall));
681     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateConstructorFromSuperFix(methodCall));
682     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateConstructorFromThisFix(methodCall));
683     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreatePropertyFromUsageFix(methodCall));
684     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateGetterSetterPropertyFromUsageFix(methodCall));
685     CandidateInfo[] methodCandidates = resolveHelper.getReferencedMethodCandidates(methodCall, false);
686     CastMethodArgumentFix.REGISTRAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
687     PermuteArgumentsFix.registerFix(highlightInfo, methodCall, methodCandidates, fixRange);
688     AddTypeArgumentsFix.REGISTRAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
689     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
690     registerMethodAccessLevelIntentions(methodCandidates, methodCall, list, highlightInfo);
691     registerChangeMethodSignatureFromUsageIntentions(methodCandidates, list, highlightInfo, fixRange);
692     RemoveRedundantArgumentsFix.registerIntentions(methodCandidates, list, highlightInfo, fixRange);
693     ConvertDoubleToFloatFix.registerIntentions(methodCandidates, list, highlightInfo, fixRange);
694     WrapExpressionFix.registerWrapAction(methodCandidates, list.getExpressions(), highlightInfo);
695     registerChangeParameterClassFix(methodCall, list, highlightInfo);
696     if (methodCandidates.length == 0) {
697       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createStaticImportMethodFix(methodCall));
698       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.addMethodQualifierFix(methodCall));
699     }
700     for (IntentionAction action : QUICK_FIX_FACTORY.getVariableTypeFromCallFixes(methodCall, list)) {
701       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, action);
702     }
703     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createReplaceAddAllArrayToCollectionFix(methodCall));
704     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createSurroundWithArrayFix(methodCall, null));
705     QualifyThisArgumentFix.registerQuickFixAction(methodCandidates, methodCall, highlightInfo, fixRange);
706
707     CandidateInfo[] candidates = resolveHelper.getReferencedMethodCandidates(methodCall, true);
708     ChangeStringLiteralToCharInMethodCallFix.registerFixes(candidates, methodCall, highlightInfo);
709   }
710
711   private static void registerMethodAccessLevelIntentions(CandidateInfo[] methodCandidates,
712                                                           PsiMethodCallExpression methodCall,
713                                                           PsiExpressionList exprList,
714                                                           HighlightInfo highlightInfo) {
715     for (CandidateInfo methodCandidate : methodCandidates) {
716       PsiMethod method = (PsiMethod)methodCandidate.getElement();
717       if (!methodCandidate.isAccessible() && PsiUtil.isApplicable(method, methodCandidate.getSubstitutor(), exprList)) {
718         HighlightUtil.registerAccessQuickFixAction(method, methodCall.getMethodExpression(), highlightInfo, methodCandidate.getCurrentFileResolveScope());
719       }
720     }
721   }
722
723   @NotNull
724   private static String createAmbiguousMethodHtmlTooltip(MethodCandidateInfo[] methodCandidates) {
725     return JavaErrorMessages.message("ambiguous.method.html.tooltip",
726                                      Integer.valueOf(methodCandidates[0].getElement().getParameterList().getParametersCount() + 2),
727                                      createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[0]),
728                                      getContainingClassName(methodCandidates[0]),
729                                      createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[1]),
730                                      getContainingClassName(methodCandidates[1]));
731   }
732
733   private static String getContainingClassName(final MethodCandidateInfo methodCandidate) {
734     PsiMethod method = methodCandidate.getElement();
735     PsiClass containingClass = method.getContainingClass();
736     return containingClass == null ? method.getContainingFile().getName() : HighlightUtil.formatClass(containingClass, false);
737   }
738
739   @Language("HTML")
740   private static String createAmbiguousMethodHtmlTooltipMethodRow(final MethodCandidateInfo methodCandidate) {
741     PsiMethod method = methodCandidate.getElement();
742     PsiParameter[] parameters = method.getParameterList().getParameters();
743     PsiSubstitutor substitutor = methodCandidate.getSubstitutor();
744     @NonNls @Language("HTML") String ms = "<td><b>" + method.getName() + "</b></td>";
745
746     for (int j = 0; j < parameters.length; j++) {
747       PsiParameter parameter = parameters[j];
748       PsiType type = substitutor.substitute(parameter.getType());
749       ms += "<td><b>" + (j == 0 ? "(" : "") +
750             XmlStringUtil.escapeString(type.getPresentableText())
751             + (j == parameters.length - 1 ? ")" : ",") + "</b></td>";
752     }
753     if (parameters.length == 0) {
754       ms += "<td><b>()</b></td>";
755     }
756     return ms;
757   }
758
759   private static String createMismatchedArgumentsHtmlTooltip(MethodCandidateInfo info, PsiExpressionList list) {
760     PsiMethod method = info.getElement();
761     PsiSubstitutor substitutor = info.getSubstitutor();
762     PsiClass aClass = method.getContainingClass();
763     PsiParameter[] parameters = method.getParameterList().getParameters();
764     String methodName = method.getName();
765     return createMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass);
766   }
767
768   private static String createShortMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
769                                                              PsiParameter[] parameters,
770                                                              String methodName,
771                                                              PsiSubstitutor substitutor,
772                                                              PsiClass aClass) {
773     PsiExpression[] expressions = list.getExpressions();
774     int cols = Math.max(parameters.length, expressions.length);
775
776     @Language("HTML")
777     @NonNls String parensizedName = methodName + (parameters.length == 0 ? "(&nbsp;)&nbsp;" : "");
778     final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent());
779     return JavaErrorMessages.message(
780       "argument.mismatch.html.tooltip",
781       Integer.valueOf(cols - parameters.length + 1), parensizedName,
782       HighlightUtil.formatClass(aClass, false),
783       createMismatchedArgsHtmlTooltipParamsRow(parameters, substitutor, expressions),
784       createMismatchedArgsHtmlTooltipArgumentsRow(expressions, parameters, substitutor, cols),
785       errorMessage != null ? "<br/>reason: " + XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "<br/>") : ""
786     );
787   }
788
789   private static String esctrim(@NotNull String s) {
790     return XmlStringUtil.escapeString(trimNicely(s));
791   }
792
793   private static String trimNicely(String s) {
794     if (s.length() <= 40) return s;
795
796     List<TextRange> wordIndices = StringUtil.getWordIndicesIn(s);
797     if (wordIndices.size() > 2) {
798       int firstWordEnd = wordIndices.get(0).getEndOffset();
799
800       // try firstWord...remainder
801       for (int i = 1; i<wordIndices.size();i++) {
802         int stringLength = firstWordEnd + s.length() - wordIndices.get(i).getStartOffset();
803         if (stringLength <= 40) {
804           return s.substring(0, firstWordEnd) + "..." + s.substring(wordIndices.get(i).getStartOffset());
805         }
806       }
807     }
808     // maybe one last word will fit?
809     if (!wordIndices.isEmpty() && s.length() - wordIndices.get(wordIndices.size()-1).getStartOffset() <= 40) {
810       return "..." + s.substring(wordIndices.get(wordIndices.size()-1).getStartOffset());
811     }
812
813     return StringUtil.last(s, 40, true).toString();
814   }
815
816   private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
817                                                              PsiParameter[] parameters,
818                                                              String methodName,
819                                                              PsiSubstitutor substitutor,
820                                                              PsiClass aClass) {
821     return Math.max(parameters.length, list.getExpressions().length) <= 2
822            ? createShortMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass)
823            : createLongMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass);
824   }
825
826   @Language("HTML")
827   private static String createLongMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
828                                                              PsiParameter[] parameters,
829                                                              String methodName,
830                                                              PsiSubstitutor substitutor,
831                                                              PsiClass aClass) {
832     PsiExpression[] expressions = list.getExpressions();
833
834     @NonNls
835     String s = "<html><body><table border=0>" +
836                "<tr><td colspan=3>" +
837                "<nobr><b>" + methodName + "()</b> in <b>" + HighlightUtil.formatClass(aClass, false) +"</b> cannot be applied to:</nobr>" +
838                "</td></tr>"+
839                "<tr><td colspan=2 align=left>Expected<br>Parameters:</td><td align=left>Actual<br>Arguments:</td></tr>"+
840                "<tr><td colspan=3><hr></td></tr>"
841       ;
842
843     for (int i = 0; i < Math.max(parameters.length,expressions.length); i++) {
844       PsiParameter parameter = i < parameters.length ? parameters[i] : null;
845       PsiExpression expression = i < expressions.length ? expressions[i] : null;
846       boolean showShort = showShortType(i, parameters, expressions, substitutor);
847       @NonNls String mismatchColor = showShort ? null : UIUtil.isUnderDarcula() ? "FF6B68" : "red";
848
849       s += "<tr" + (i % 2 == 0 ? " style='background-color: #"
850                                  + (UIUtil.isUnderDarcula() ? ColorUtil.toHex(ColorUtil.shift(UIUtil.getToolTipBackground(), 1.1)) : "eeeeee")
851                                  + "'" : "") + ">";
852       s += "<td><b><nobr>";
853       if (parameter != null) {
854         String name = parameter.getName();
855         if (name != null) {
856           s += esctrim(name) +":";
857         }
858       }
859       s += "</nobr></b></td>";
860
861       s += "<td><b><nobr>";
862       if (parameter != null) {
863         PsiType type = substitutor.substitute(parameter.getType());
864         s +=  "<font " + (mismatchColor == null ? "" : "color=" + mismatchColor) + ">" +
865               esctrim(showShort ? type.getPresentableText() : JavaHighlightUtil.formatType(type))
866               + "</font>"
867               ;
868       }
869       s += "</nobr></b></td>";
870
871       s += "<td><b><nobr>";
872       if (expression != null) {
873         PsiType type = expression.getType();
874         s += "<font " + (mismatchColor == null ? "" : "color='" + mismatchColor + "'") + ">" +
875                esctrim(expression.getText()) + "&nbsp;&nbsp;"+
876               (mismatchColor == null || type == null || type == PsiType.NULL ? "" : "("+esctrim(JavaHighlightUtil.formatType(type))+")")
877               + "</font>"
878               ;
879
880       }
881       s += "</nobr></b></td>";
882
883       s += "</tr>";
884     }
885
886     s+= "</table>";
887     final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent());
888     if (errorMessage != null) {
889       s+= "reason: "; 
890       s += XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "<br/>");
891     }
892     s+= "</body></html>";
893     return s;
894   }
895
896   @Language("HTML")
897   private static String createMismatchedArgsHtmlTooltipArgumentsRow(final PsiExpression[] expressions, final PsiParameter[] parameters,
898                                                                       final PsiSubstitutor substitutor, final int cols) {
899     @Language("HTML")
900
901     @NonNls String ms = "";
902     for (int i = 0; i < expressions.length; i++) {
903       PsiExpression expression = expressions[i];
904       PsiType type = expression.getType();
905
906       boolean showShort = showShortType(i, parameters, expressions, substitutor);
907       @NonNls String mismatchColor = showShort ? null : MISMATCH_COLOR;
908       ms += "<td> " + "<b><nobr>" + (i == 0 ? "(" : "")
909             + "<font " + (showShort ? "" : "color=" + mismatchColor) + ">" +
910             XmlStringUtil.escapeString(showShort ? type.getPresentableText() : JavaHighlightUtil.formatType(type))
911             + "</font>"
912             + (i == expressions.length - 1 ? ")" : ",") + "</nobr></b></td>";
913     }
914     for (int i = expressions.length; i < cols + 1; i++) {
915       ms += "<td>" + (i == 0 ? "<b>()</b>" : "") +
916             "&nbsp;</td>";
917     }
918     return ms;
919   }
920
921   @Language("HTML")
922   private static String createMismatchedArgsHtmlTooltipParamsRow(final PsiParameter[] parameters,
923                                                                  final PsiSubstitutor substitutor,
924                                                                  final PsiExpression[] expressions) {
925     @NonNls String ms = "";
926     for (int i = 0; i < parameters.length; i++) {
927       PsiParameter parameter = parameters[i];
928       PsiType type = substitutor.substitute(parameter.getType());
929       ms += "<td><b><nobr>" + (i == 0 ? "(" : "") +
930             XmlStringUtil.escapeString(showShortType(i, parameters, expressions, substitutor)
931                                  ? type.getPresentableText()
932                                  : JavaHighlightUtil.formatType(type))
933             + (i == parameters.length - 1 ? ")" : ",") + "</nobr></b></td>";
934     }
935     return ms;
936   }
937
938   private static boolean showShortType(int i,
939                                        PsiParameter[] parameters,
940                                        PsiExpression[] expressions,
941                                        PsiSubstitutor substitutor) {
942     PsiExpression expression = i < expressions.length ? expressions[i] : null;
943     if (expression == null) return true;
944     PsiType paramType = i < parameters.length && parameters[i] != null
945                         ? substitutor.substitute(parameters[i].getType())
946                         : null;
947     PsiType expressionType = expression.getType();
948     return paramType != null && expressionType != null && TypeConversionUtil.isAssignable(paramType, expressionType);
949   }
950
951
952   static HighlightInfo checkMethodMustHaveBody(PsiMethod method, PsiClass aClass) {
953     HighlightInfo errorResult = null;
954     if (method.getBody() == null
955         && !method.hasModifierProperty(PsiModifier.ABSTRACT)
956         && !method.hasModifierProperty(PsiModifier.NATIVE)
957         && aClass != null
958         && !aClass.isInterface()
959         && !PsiUtilCore.hasErrorElementChild(method)) {
960       int start = method.getModifierList().getTextRange().getStartOffset();
961       int end = method.getTextRange().getEndOffset();
962
963       String description = JavaErrorMessages.message("missing.method.body");
964       errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(start, end).descriptionAndTooltip(description).create();
965       if (HighlightUtil.getIncompatibleModifier(PsiModifier.ABSTRACT, method.getModifierList()) == null) {
966         QuickFixAction.registerQuickFixAction(errorResult,
967                                               QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, true, false));
968       }
969       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
970     }
971     return errorResult;
972   }
973
974
975   static HighlightInfo checkAbstractMethodInConcreteClass(PsiMethod method, PsiElement elementToHighlight) {
976     HighlightInfo errorResult = null;
977     PsiClass aClass = method.getContainingClass();
978     if (method.hasModifierProperty(PsiModifier.ABSTRACT)
979         && aClass != null
980         && !aClass.hasModifierProperty(PsiModifier.ABSTRACT)
981         && !aClass.isEnum()
982         && !PsiUtilCore.hasErrorElementChild(method)) {
983       String description = JavaErrorMessages.message("abstract.method.in.non.abstract.class");
984       errorResult =
985         HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(elementToHighlight).descriptionAndTooltip(description).create();
986       if (method.getBody() != null) {
987         QuickFixAction.registerQuickFixAction(errorResult,
988                                               QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, false, false));
989       }
990       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
991       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createModifierListFix(aClass, PsiModifier.ABSTRACT, true, false));
992     }
993     return errorResult;
994   }
995
996
997   static HighlightInfo checkConstructorName(PsiMethod method) {
998     String methodName = method.getName();
999     PsiClass aClass = method.getContainingClass();
1000     HighlightInfo errorResult = null;
1001
1002     if (aClass != null) {
1003       String className = aClass instanceof PsiAnonymousClass ? null : aClass.getName();
1004       if (className == null || !Comparing.strEqual(methodName, className)) {
1005         PsiElement element = method.getNameIdentifier();
1006         String description = JavaErrorMessages.message("missing.return.type");
1007         errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
1008         if (className != null) {
1009           QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createRenameElementFix(method, className));
1010         }
1011       }
1012     }
1013     return errorResult;
1014   }
1015
1016   @Nullable
1017   static HighlightInfo checkDuplicateMethod(PsiClass aClass,
1018                                             @NotNull PsiMethod method,
1019                                             @NotNull MostlySingularMultiMap<MethodSignature, PsiMethod> duplicateMethods) {
1020     if (aClass == null || method instanceof ExternallyDefinedPsiElement) return null;
1021     MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
1022     int methodCount = 1;
1023     List<PsiMethod> methods = (List<PsiMethod>)duplicateMethods.get(methodSignature);
1024     if (methods.size() > 1) {
1025       methodCount++;
1026     }
1027
1028     if (methodCount == 1 && aClass.isEnum() &&
1029         GenericsHighlightUtil.isEnumSyntheticMethod(methodSignature, aClass.getProject())) {
1030       methodCount++;
1031     }
1032     if (methodCount > 1) {
1033       String description = JavaErrorMessages.message("duplicate.method",
1034                                                  JavaHighlightUtil.formatMethod(method),
1035                                                  HighlightUtil.formatClass(aClass));
1036       TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1037       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).
1038         range(method, textRange.getStartOffset(), textRange.getEndOffset()).
1039         descriptionAndTooltip(description).create();
1040     }
1041     return null;
1042   }
1043
1044   @Nullable
1045   static HighlightInfo checkMethodCanHaveBody(@NotNull PsiMethod method, @NotNull LanguageLevel languageLevel) {
1046     PsiClass aClass = method.getContainingClass();
1047     boolean hasNoBody = method.getBody() == null;
1048     boolean isInterface = aClass != null && aClass.isInterface();
1049     boolean isExtension = method.hasModifierProperty(PsiModifier.DEFAULT);
1050     boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
1051     boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE);
1052
1053     final List<IntentionAction> additionalFixes = new ArrayList<IntentionAction>();
1054     String description = null;
1055     if (hasNoBody) {
1056       if (isExtension) {
1057         description = JavaErrorMessages.message("extension.method.should.have.a.body");
1058         additionalFixes.add(QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
1059       }
1060       else if (isInterface && isStatic && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
1061         description = "Static methods in interfaces should have a body";
1062       }
1063     }
1064     else if (isInterface) {
1065       if (!isExtension && !isStatic && !isPrivate) {
1066         description = JavaErrorMessages.message("interface.methods.cannot.have.body");
1067         if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
1068           additionalFixes.add(QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.DEFAULT, true, false));
1069           additionalFixes.add(QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.STATIC, true, false));
1070         }
1071       }
1072     }
1073     else if (isExtension) {
1074       description = JavaErrorMessages.message("extension.method.in.class");
1075     }
1076     else if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
1077       description = JavaErrorMessages.message("abstract.methods.cannot.have.a.body");
1078     }
1079     else if (method.hasModifierProperty(PsiModifier.NATIVE)) {
1080       description = JavaErrorMessages.message("native.methods.cannot.have.a.body");
1081     }
1082     if (description == null) return null;
1083
1084     TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1085     HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1086     if (!hasNoBody) {
1087       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createDeleteMethodBodyFix(method));
1088     }
1089     if (method.hasModifierProperty(PsiModifier.ABSTRACT) && !isInterface) {
1090       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, false, false));
1091     }
1092     for (IntentionAction intentionAction : additionalFixes) {
1093       QuickFixAction.registerQuickFixAction(info, intentionAction);
1094     }
1095     return info;
1096   }
1097
1098   @Nullable
1099   static HighlightInfo checkConstructorCallMustBeFirstStatement(@NotNull PsiMethodCallExpression methodCall) {
1100     if (!RefactoringChangeUtil.isSuperOrThisMethodCall(methodCall)) return null;
1101     PsiElement codeBlock = methodCall.getParent().getParent();
1102     if (codeBlock instanceof PsiCodeBlock
1103         && codeBlock.getParent() instanceof PsiMethod
1104         && ((PsiMethod)codeBlock.getParent()).isConstructor()) {
1105       PsiElement prevSibling = methodCall.getParent().getPrevSibling();
1106       while (true) {
1107         if (prevSibling == null) return null;
1108         if (prevSibling instanceof PsiStatement) break;
1109         prevSibling = prevSibling.getPrevSibling();
1110       }
1111     }
1112     PsiReferenceExpression expression = methodCall.getMethodExpression();
1113     String message = JavaErrorMessages.message("constructor.call.must.be.first.statement", expression.getText() + "()");
1114     return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCall).descriptionAndTooltip(message).create();
1115   }
1116
1117
1118   static HighlightInfo checkSuperAbstractMethodDirectCall(@NotNull PsiMethodCallExpression methodCallExpression) {
1119     PsiReferenceExpression expression = methodCallExpression.getMethodExpression();
1120     if (!(expression.getQualifierExpression() instanceof PsiSuperExpression)) return null;
1121     PsiMethod method = methodCallExpression.resolveMethod();
1122     if (method != null && method.hasModifierProperty(PsiModifier.ABSTRACT)) {
1123       String message = JavaErrorMessages.message("direct.abstract.method.access", JavaHighlightUtil.formatMethod(method));
1124       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCallExpression).descriptionAndTooltip(message).create();
1125     }
1126     return null;
1127   }
1128
1129
1130   static HighlightInfo checkConstructorCallsBaseClassConstructor(PsiMethod constructor,
1131                                                                         RefCountHolder refCountHolder,
1132                                                                         PsiResolveHelper resolveHelper) {
1133     if (!constructor.isConstructor()) return null;
1134     PsiClass aClass = constructor.getContainingClass();
1135     if (aClass == null) return null;
1136     if (aClass.isEnum()) return null;
1137     PsiCodeBlock body = constructor.getBody();
1138     if (body == null) return null;
1139
1140     // check whether constructor call super(...) or this(...)
1141     PsiElement element = new PsiMatcherImpl(body)
1142       .firstChild(PsiMatchers.hasClass(PsiExpressionStatement.class))
1143       .firstChild(PsiMatchers.hasClass(PsiMethodCallExpression.class))
1144       .firstChild(PsiMatchers.hasClass(PsiReferenceExpression.class))
1145       .firstChild(PsiMatchers.hasClass(PsiKeyword.class))
1146       .getElement();
1147     if (element != null) return null;
1148     TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(constructor);
1149     PsiClassType[] handledExceptions = constructor.getThrowsList().getReferencedTypes();
1150     HighlightInfo info = HighlightClassUtil.checkBaseClassDefaultConstructorProblem(aClass, refCountHolder, resolveHelper, textRange, handledExceptions);
1151     if (info != null) {
1152       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createInsertSuperFix(constructor));
1153       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddDefaultConstructorFix(aClass.getSuperClass()));
1154     }
1155     return info;
1156   }
1157
1158
1159   /**
1160    * @return error if static method overrides instance method or
1161    *         instance method overrides static. see JLS 8.4.6.1, 8.4.6.2
1162    */
1163   static HighlightInfo checkStaticMethodOverride(@NotNull PsiMethod method,@NotNull PsiFile containingFile) {
1164     // constructors are not members and therefor don't override class methods
1165     if (method.isConstructor()) {
1166       return null;
1167     }
1168
1169     PsiClass aClass = method.getContainingClass();
1170     if (aClass == null) return null;
1171     final HierarchicalMethodSignature methodSignature = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(method);
1172     final List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures();
1173     if (superSignatures.isEmpty()) {
1174       return null;
1175     }
1176
1177     boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
1178     for (HierarchicalMethodSignature signature : superSignatures) {
1179       final PsiMethod superMethod = signature.getMethod();
1180       final PsiClass superClass = superMethod.getContainingClass();
1181       if (superClass == null) continue;
1182       final HighlightInfo highlightInfo = checkStaticMethodOverride(aClass, method, isStatic, superClass, superMethod,containingFile);
1183       if (highlightInfo != null) {
1184         return highlightInfo;
1185       }
1186     }
1187     return null;
1188   }
1189
1190   private static HighlightInfo checkStaticMethodOverride(PsiClass aClass, PsiMethod method, boolean isMethodStatic, PsiClass superClass, PsiMethod superMethod,@NotNull PsiFile containingFile) {
1191     if (superMethod == null) return null;
1192     PsiManager manager = containingFile.getManager();
1193     PsiModifierList superModifierList = superMethod.getModifierList();
1194     PsiModifierList modifierList = method.getModifierList();
1195     if (superModifierList.hasModifierProperty(PsiModifier.PRIVATE)) return null;
1196     if (superModifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)
1197         && !JavaPsiFacade.getInstance(manager.getProject()).arePackagesTheSame(aClass, superClass)) {
1198       return null;
1199     }
1200     boolean isSuperMethodStatic = superModifierList.hasModifierProperty(PsiModifier.STATIC);
1201     if (isMethodStatic != isSuperMethodStatic) {
1202       TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1203       @NonNls final String messageKey = isMethodStatic
1204                                 ? "static.method.cannot.override.instance.method"
1205                                 : "instance.method.cannot.override.static.method";
1206
1207       String description = JavaErrorMessages.message(messageKey,
1208                                                  JavaHighlightUtil.formatMethod(method),
1209                                                  HighlightUtil.formatClass(aClass),
1210                                                  JavaHighlightUtil.formatMethod(superMethod),
1211                                                  HighlightUtil.formatClass(superClass));
1212
1213       HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1214       if (!isSuperMethodStatic || HighlightUtil.getIncompatibleModifier(PsiModifier.STATIC, modifierList) == null) {
1215         QuickFixAction.registerQuickFixAction(info,
1216                                               QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.STATIC, isSuperMethodStatic, false));
1217       }
1218       if (manager.isInProject(superMethod) &&
1219           (!isMethodStatic || HighlightUtil.getIncompatibleModifier(PsiModifier.STATIC, superModifierList) == null)) {
1220         QuickFixAction.registerQuickFixAction(info,
1221                                               QUICK_FIX_FACTORY.createModifierListFix(superMethod, PsiModifier.STATIC, isMethodStatic, true));
1222       }
1223       return info;
1224     }
1225
1226     if (isMethodStatic) {
1227       if (superClass.isInterface()) return null;
1228       int accessLevel = PsiUtil.getAccessLevel(modifierList);
1229       String accessModifier = PsiUtil.getAccessModifier(accessLevel);
1230       HighlightInfo info = isWeaker(method, modifierList, accessModifier, accessLevel, superMethod, true);
1231       if (info != null) return info;
1232       info = checkSuperMethodIsFinal(method, superMethod);
1233       if (info != null) return info;
1234     }
1235     return null;
1236   }
1237
1238   private static HighlightInfo checkInterfaceInheritedMethodsReturnTypes(@NotNull List<? extends MethodSignatureBackedByPsiMethod> superMethodSignatures,
1239                                                                          @NotNull LanguageLevel languageLevel) {
1240     if (superMethodSignatures.size() < 2) return null;
1241     MethodSignatureBackedByPsiMethod returnTypeSubstitutable = superMethodSignatures.get(0);
1242     for (int i = 1; i < superMethodSignatures.size(); i++) {
1243       PsiMethod currentMethod = returnTypeSubstitutable.getMethod();
1244       PsiType currentType = returnTypeSubstitutable.getSubstitutor().substitute(currentMethod.getReturnType());
1245
1246       MethodSignatureBackedByPsiMethod otherSuperSignature = superMethodSignatures.get(i);
1247       PsiMethod otherSuperMethod = otherSuperSignature.getMethod();
1248       PsiType otherSuperReturnType = otherSuperSignature.getSubstitutor().substitute(otherSuperMethod.getReturnType());
1249
1250       PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable,
1251                                                                                                   otherSuperSignature);
1252       if (unifyingSubstitutor != null) {
1253         otherSuperReturnType = unifyingSubstitutor.substitute(otherSuperReturnType);
1254         currentType = unifyingSubstitutor.substitute(currentType);
1255       }
1256
1257       if (otherSuperReturnType == null || currentType == null || otherSuperReturnType.equals(currentType)) continue;
1258
1259       if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) {
1260         //http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8 Example 8.1.5-3
1261         if (!(otherSuperReturnType instanceof PsiPrimitiveType || currentType instanceof PsiPrimitiveType)) {
1262           if (otherSuperReturnType.isAssignableFrom(currentType)) continue;
1263           if (currentType.isAssignableFrom(otherSuperReturnType)) {
1264             returnTypeSubstitutable = otherSuperSignature;
1265             continue;
1266           }
1267         }
1268         if (currentMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(currentType, otherSuperReturnType)) continue;
1269       }
1270       return createIncompatibleReturnTypeMessage(currentMethod, otherSuperMethod, otherSuperReturnType,
1271                                                  currentType, JavaErrorMessages.message("unrelated.overriding.methods.return.types"),
1272                                                  TextRange.EMPTY_RANGE);
1273     }
1274     return null;
1275   }
1276
1277   static HighlightInfo checkOverrideEquivalentInheritedMethods(PsiClass aClass, PsiFile containingFile, @NotNull LanguageLevel languageLevel) {
1278     String description = null;
1279     final Collection<HierarchicalMethodSignature> visibleSignatures = aClass.getVisibleSignatures();
1280     PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
1281     Ultimate:
1282     for (HierarchicalMethodSignature signature : visibleSignatures) {
1283       PsiMethod method = signature.getMethod();
1284       if (!resolveHelper.isAccessible(method, aClass, null)) continue;
1285       List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
1286
1287       boolean allAbstracts = method.hasModifierProperty(PsiModifier.ABSTRACT);
1288       final PsiClass containingClass = method.getContainingClass();
1289       if (aClass.equals(containingClass)) continue; //to be checked at method level
1290
1291       if (aClass.isInterface() && !containingClass.isInterface()) continue;
1292       HighlightInfo highlightInfo;
1293       if (allAbstracts) {
1294         superSignatures = new ArrayList<HierarchicalMethodSignature>(superSignatures);
1295         superSignatures.add(signature);
1296         highlightInfo = checkInterfaceInheritedMethodsReturnTypes(superSignatures, languageLevel);
1297       }
1298       else {
1299         highlightInfo = checkMethodIncompatibleReturnType(signature, superSignatures, false, languageLevel);
1300       }
1301       if (highlightInfo != null) description = highlightInfo.getDescription();
1302
1303       if (method.hasModifierProperty(PsiModifier.STATIC)) {
1304         for (HierarchicalMethodSignature superSignature : superSignatures) {
1305           PsiMethod superMethod = superSignature.getMethod();
1306           if (!superMethod.hasModifierProperty(PsiModifier.STATIC)) {
1307             description = JavaErrorMessages.message("static.method.cannot.override.instance.method",
1308                                                          JavaHighlightUtil.formatMethod(method),
1309                                                          HighlightUtil.formatClass(containingClass),
1310                                                          JavaHighlightUtil.formatMethod(superMethod),
1311                                                          HighlightUtil.formatClass(superMethod.getContainingClass()));
1312             break Ultimate;
1313           }
1314         }
1315         continue;
1316       }
1317
1318       if (description == null) {
1319         highlightInfo = checkMethodIncompatibleThrows(signature, superSignatures, false, aClass);
1320         if (highlightInfo != null) description = highlightInfo.getDescription();
1321       }
1322
1323       if (description == null) {
1324         highlightInfo = checkMethodWeakerPrivileges(signature, superSignatures, false, containingFile);
1325         if (highlightInfo != null) description = highlightInfo.getDescription();
1326       }
1327
1328       if (description != null) break;
1329     }
1330
1331
1332     if (description != null) {
1333       // show error info at the class level
1334       TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
1335       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1336     }
1337     return null;
1338   }
1339
1340
1341   static HighlightInfo checkConstructorHandleSuperClassExceptions(PsiMethod method) {
1342     if (!method.isConstructor()) {
1343       return null;
1344     }
1345     PsiCodeBlock body = method.getBody();
1346     PsiStatement[] statements = body == null ? null : body.getStatements();
1347     if (statements == null) return null;
1348
1349     // if we have unhandled exception inside method body, we could not have been called here,
1350     // so the only problem it can catch here is with super ctr only
1351     Collection<PsiClassType> unhandled = ExceptionUtil.collectUnhandledExceptions(method, method.getContainingClass());
1352     if (unhandled.isEmpty()) return null;
1353     String description = HighlightUtil.getUnhandledExceptionsDescriptor(unhandled);
1354     TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1355     HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1356     for (PsiClassType exception : unhandled) {
1357       QuickFixAction.registerQuickFixAction(highlightInfo, new LocalQuickFixOnPsiElementAsIntentionAdapter(QUICK_FIX_FACTORY.createMethodThrowsFix(method, exception, true, false)));
1358     }
1359     return highlightInfo;
1360   }
1361
1362
1363   static HighlightInfo checkRecursiveConstructorInvocation(@NotNull PsiMethod method) {
1364     if (HighlightControlFlowUtil.isRecursivelyCalledConstructor(method)) {
1365       TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1366       String description = JavaErrorMessages.message("recursive.constructor.invocation");
1367       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1368     }
1369     return null;
1370   }
1371
1372   @NotNull
1373   public static TextRange getFixRange(@NotNull PsiElement element) {
1374     TextRange range = element.getTextRange();
1375     int start = range.getStartOffset();
1376     int end = range.getEndOffset();
1377
1378     PsiElement nextSibling = element.getNextSibling();
1379     if (nextSibling instanceof PsiJavaToken && ((PsiJavaToken)nextSibling).getTokenType() == JavaTokenType.SEMICOLON) {
1380       return new TextRange(start, end + 1);
1381     }
1382     return range;
1383   }
1384
1385
1386   static void checkNewExpression(@NotNull PsiNewExpression expression,
1387                                  PsiType type,
1388                                  @NotNull HighlightInfoHolder holder,
1389                                  @NotNull JavaSdkVersion javaSdkVersion) {
1390     if (!(type instanceof PsiClassType)) return;
1391     PsiClassType.ClassResolveResult typeResult = ((PsiClassType)type).resolveGenerics();
1392     PsiClass aClass = typeResult.getElement();
1393     if (aClass == null) return;
1394     if (aClass instanceof PsiAnonymousClass) {
1395       type = ((PsiAnonymousClass)aClass).getBaseClassType();
1396       typeResult = ((PsiClassType)type).resolveGenerics();
1397       aClass = typeResult.getElement();
1398       if (aClass == null) return;
1399     }
1400
1401     PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
1402     checkConstructorCall(typeResult, expression, type, classReference, holder, javaSdkVersion);
1403   }
1404
1405
1406   public static void checkConstructorCall(PsiClassType.ClassResolveResult typeResolveResult,
1407                                           PsiConstructorCall constructorCall,
1408                                           PsiType type,
1409                                           PsiJavaCodeReferenceElement classReference,
1410                                           final HighlightInfoHolder holder,
1411                                           @NotNull JavaSdkVersion javaSdkVersion) {
1412     PsiExpressionList list = constructorCall.getArgumentList();
1413     if (list == null) return;
1414     PsiClass aClass = typeResolveResult.getElement();
1415     if (aClass == null) return;
1416     final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(holder.getProject()).getResolveHelper();
1417     PsiClass accessObjectClass = null;
1418     if (constructorCall instanceof PsiNewExpression) {
1419       PsiExpression qualifier = ((PsiNewExpression)constructorCall).getQualifier();
1420       if (qualifier != null) {
1421         accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
1422       }
1423     }
1424     if (classReference != null && !resolveHelper.isAccessible(aClass, constructorCall, accessObjectClass)) {
1425       String description = HighlightUtil.buildProblemWithAccessDescription(classReference, typeResolveResult);
1426       PsiElement element = classReference.getReferenceNameElement();
1427       HighlightInfo info =
1428         HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
1429       HighlightUtil.registerAccessQuickFixAction(aClass, classReference, info, null);
1430       holder.add(info);
1431       return;
1432     }
1433     PsiMethod[] constructors = aClass.getConstructors();
1434
1435     if (constructors.length == 0) {
1436       if (list.getExpressions().length != 0) {
1437         String constructorName = aClass.getName();
1438         String argTypes = buildArgTypesList(list);
1439         String description = JavaErrorMessages.message("wrong.constructor.arguments", constructorName+"()", argTypes);
1440         String tooltip = createMismatchedArgumentsHtmlTooltip(list, PsiParameter.EMPTY_ARRAY, constructorName, PsiSubstitutor.EMPTY, aClass);
1441         HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).description(description).escapedToolTip(tooltip).navigationShift(+1).create();
1442         QuickFixAction.registerQuickFixAction(info, constructorCall.getTextRange(), QUICK_FIX_FACTORY.createCreateConstructorFromCallFix(constructorCall));
1443         if (classReference != null) {
1444           ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info,getFixRange(list));
1445         }
1446         holder.add(info);
1447         return;
1448       }
1449       if (classReference != null && aClass.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass)) {
1450         holder.add(buildAccessProblem(classReference, typeResolveResult, aClass));
1451       } else if (aClass.isInterface() && constructorCall instanceof PsiNewExpression) {
1452         final PsiReferenceParameterList typeArgumentList = ((PsiNewExpression)constructorCall).getTypeArgumentList();
1453         if (typeArgumentList.getTypeArguments().length > 0) {
1454           holder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeArgumentList)
1455             .descriptionAndTooltip("Anonymous class implements interface; cannot have type arguments").create());
1456         }
1457       }
1458     }
1459     else {
1460       PsiElement place = list;
1461       if (constructorCall instanceof PsiNewExpression) {
1462         final PsiAnonymousClass anonymousClass = ((PsiNewExpression)constructorCall).getAnonymousClass();
1463         if (anonymousClass != null) place = anonymousClass;
1464       }
1465
1466       JavaResolveResult[] results = resolveHelper.multiResolveConstructor((PsiClassType)type, list, place);
1467       MethodCandidateInfo result = null;
1468       if (results.length == 1) result = (MethodCandidateInfo)results[0];
1469
1470       PsiMethod constructor = result == null ? null : result.getElement();
1471
1472       boolean applicable = true;
1473       try {
1474         applicable = constructor != null && result.isApplicable();
1475       }
1476       catch (IndexNotReadyException e) {
1477         // ignore
1478       }
1479
1480       PsiElement infoElement = list.getTextLength() > 0 ? list : constructorCall;
1481       if (constructor == null) {
1482         String name = aClass.getName();
1483         name += buildArgTypesList(list);
1484         String description = JavaErrorMessages.message("cannot.resolve.constructor", name);
1485         HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).navigationShift(+1).create();
1486         WrapExpressionFix.registerWrapAction(results, list.getExpressions(), info);
1487         registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, infoElement, info);
1488         holder.add(info);
1489       }
1490       else {
1491         if (classReference != null && (!result.isAccessible() ||
1492                                        constructor.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass))) {
1493           holder.add(buildAccessProblem(classReference, result, constructor));
1494         }
1495         else if (!applicable) {
1496           String constructorName = HighlightMessageUtil.getSymbolName(constructor, result.getSubstitutor());
1497           String containerName = HighlightMessageUtil.getSymbolName(constructor.getContainingClass(), result.getSubstitutor());
1498           String argTypes = buildArgTypesList(list);
1499           String description = JavaErrorMessages.message("wrong.method.arguments", constructorName, containerName, argTypes);
1500           String toolTip = createMismatchedArgumentsHtmlTooltip(result, list);
1501           
1502           HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(infoElement).description(description).escapedToolTip(toolTip).navigationShift(+1).create();
1503           if (info != null) {
1504             registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, infoElement, info);
1505             holder.add(info);
1506           }
1507         }
1508         else {
1509           if (constructorCall instanceof PsiNewExpression) {
1510             PsiReferenceParameterList typeArgumentList = ((PsiNewExpression)constructorCall).getTypeArgumentList();
1511             HighlightInfo info = GenericsHighlightUtil.checkReferenceTypeArgumentList(constructor, typeArgumentList, result.getSubstitutor(), false, javaSdkVersion);
1512             if (info != null) {
1513               holder.add(info);
1514             }
1515           }
1516         }
1517       }
1518     }
1519   }
1520
1521   private static void registerFixesOnInvalidConstructorCall(PsiConstructorCall constructorCall,
1522                                                             PsiJavaCodeReferenceElement classReference,
1523                                                             PsiExpressionList list,
1524                                                             PsiClass aClass,
1525                                                             PsiMethod[] constructors,
1526                                                             JavaResolveResult[] results, PsiElement infoElement, HighlightInfo info) {
1527     QuickFixAction
1528       .registerQuickFixAction(info, constructorCall.getTextRange(), QUICK_FIX_FACTORY.createCreateConstructorFromCallFix(constructorCall));
1529     if (classReference != null) {
1530       ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info, getFixRange(infoElement));
1531       ChangeTypeArgumentsFix.registerIntentions(results, list, info, aClass);
1532       ConvertDoubleToFloatFix.registerIntentions(results, list, info, null);
1533     }
1534     registerChangeMethodSignatureFromUsageIntentions(results, list, info, null);
1535     PermuteArgumentsFix.registerFix(info, constructorCall, toMethodCandidates(results), getFixRange(list));
1536     registerChangeParameterClassFix(constructorCall, list, info);
1537     QuickFixAction.registerQuickFixAction(info, getFixRange(list), QUICK_FIX_FACTORY.createSurroundWithArrayFix(constructorCall,null));
1538     ChangeStringLiteralToCharInMethodCallFix.registerFixes(constructors, constructorCall, info);
1539   }
1540
1541   private static HighlightInfo buildAccessProblem(@NotNull PsiJavaCodeReferenceElement classReference, JavaResolveResult result, PsiMember elementToFix) {
1542     String description = HighlightUtil.buildProblemWithAccessDescription(classReference, result);
1543     HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classReference).descriptionAndTooltip(
1544       description).navigationShift(+1).create();
1545     if (result.isStaticsScopeCorrect()) {
1546       HighlightUtil.registerAccessQuickFixAction(elementToFix, classReference, info, result.getCurrentFileResolveScope());
1547     }
1548     return info;
1549   }
1550
1551   private static boolean callingProtectedConstructorFromDerivedClass(PsiConstructorCall place, PsiClass constructorClass) {
1552     if (constructorClass == null) return false;
1553     // indirect instantiation via anonymous class is ok
1554     if (place instanceof PsiNewExpression && ((PsiNewExpression)place).getAnonymousClass() != null) return false;
1555     PsiElement curElement = place;
1556     PsiClass containingClass = constructorClass.getContainingClass();
1557     while (true) {
1558       PsiClass aClass = PsiTreeUtil.getParentOfType(curElement, PsiClass.class);
1559       if (aClass == null) return false;
1560       curElement = aClass;
1561       if ((aClass.isInheritor(constructorClass, true) || containingClass != null && aClass.isInheritor(containingClass, true))
1562           && !JavaPsiFacade.getInstance(aClass.getProject()).arePackagesTheSame(aClass, constructorClass)) {
1563         return true;
1564       }
1565     }
1566   }
1567
1568   private static String buildArgTypesList(PsiExpressionList list) {
1569     StringBuilder builder = new StringBuilder();
1570     builder.append("(");
1571     PsiExpression[] args = list.getExpressions();
1572     for (int i = 0; i < args.length; i++) {
1573       if (i > 0) {
1574         builder.append(", ");
1575       }
1576       PsiType argType = args[i].getType();
1577       builder.append(argType != null ? JavaHighlightUtil.formatType(argType) : "?");
1578     }
1579     builder.append(")");
1580     return builder.toString();
1581   }
1582
1583   private static void registerChangeParameterClassFix(@NotNull PsiCall methodCall,
1584                                                       @NotNull PsiExpressionList list,
1585                                                       HighlightInfo highlightInfo) {
1586     final JavaResolveResult result = methodCall.resolveMethodGenerics();
1587     PsiMethod method = (PsiMethod)result.getElement();
1588     final PsiSubstitutor substitutor = result.getSubstitutor();
1589     PsiExpression[] expressions = list.getExpressions();
1590     if (method == null) return;
1591     final PsiParameter[] parameters = method.getParameterList().getParameters();
1592     if (parameters.length != expressions.length) return;
1593     for (int i = 0; i < expressions.length; i++) {
1594       final PsiExpression expression = expressions[i];
1595       final PsiParameter parameter = parameters[i];
1596       final PsiType expressionType = expression.getType();
1597       final PsiType parameterType = substitutor.substitute(parameter.getType());
1598       if (expressionType == null || expressionType instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(expressionType) || expressionType instanceof PsiArrayType) continue;
1599       if (parameterType instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(parameterType) || parameterType instanceof PsiArrayType) continue;
1600       if (parameterType.isAssignableFrom(expressionType)) continue;
1601       PsiClass parameterClass = PsiUtil.resolveClassInType(parameterType);
1602       PsiClass expressionClass = PsiUtil.resolveClassInType(expressionType);
1603       if (parameterClass == null || expressionClass == null) continue;
1604       if (expressionClass instanceof PsiAnonymousClass) continue;
1605       if (parameterClass.isInheritor(expressionClass, true)) continue;
1606       QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createChangeParameterClassFix(expressionClass, (PsiClassType)parameterType));
1607     }
1608   }
1609
1610   public static void registerChangeMethodSignatureFromUsageIntentions(@NotNull JavaResolveResult[] candidates,
1611                                                                       @NotNull PsiExpressionList list,
1612                                                                       @Nullable HighlightInfo highlightInfo,
1613                                                                       TextRange fixRange) {
1614     if (candidates.length == 0) return;
1615     PsiExpression[] expressions = list.getExpressions();
1616     for (JavaResolveResult candidate : candidates) {
1617       registerChangeMethodSignatureFromUsageIntention(expressions, highlightInfo, fixRange, candidate, list);
1618     }
1619   }
1620
1621   private static void registerChangeMethodSignatureFromUsageIntention(@NotNull PsiExpression[] expressions,
1622                                                                       @Nullable HighlightInfo highlightInfo,
1623                                                                       TextRange fixRange,
1624                                                                       @NotNull JavaResolveResult candidate,
1625                                                                       @NotNull PsiElement context) {
1626     if (!candidate.isStaticsScopeCorrect()) return;
1627     PsiMethod method = (PsiMethod)candidate.getElement();
1628     PsiSubstitutor substitutor = candidate.getSubstitutor();
1629     if (method != null && context.getManager().isInProject(method)) {
1630       IntentionAction fix = QUICK_FIX_FACTORY.createChangeMethodSignatureFromUsageFix(method, expressions, substitutor, context, false, 2);
1631       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, fix);
1632       IntentionAction f2 = QUICK_FIX_FACTORY.createChangeMethodSignatureFromUsageReverseOrderFix(method, expressions, substitutor, context,
1633                                                                                                  false, 2);
1634       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, f2);
1635     }
1636   }
1637 }