lambda: highlight containing method ambiguity on method name: this avoids lots of...
[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                                                          @NotNull PsiElement elementToHighlight) {
579     MethodCandidateInfo methodCandidate1 = null;
580     MethodCandidateInfo methodCandidate2 = null;
581     for (JavaResolveResult result : resolveResults) {
582       if (!(result instanceof MethodCandidateInfo)) continue;
583       MethodCandidateInfo candidate = (MethodCandidateInfo)result;
584       if (candidate.isApplicable() && !candidate.getElement().isConstructor()) {
585         if (methodCandidate1 == null) {
586           methodCandidate1 = candidate;
587         }
588         else {
589           methodCandidate2 = candidate;
590           break;
591         }
592       }
593     }
594     MethodCandidateInfo[] candidates = toMethodCandidates(resolveResults);
595
596     String description;
597     String toolTip;
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     }
621     else {
622       if (element != null && !resolveResult.isAccessible()) {
623         return null;
624       }
625       else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
626         return null;
627       }
628       else {
629         String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list);
630         description = JavaErrorMessages.message("cannot.resolve.method", methodName);
631         if (candidates.length == 0) {
632           return null;
633         }
634       }
635       toolTip = XmlStringUtil.escapeString(description);
636     }
637     HighlightInfo info =
638       HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description).escapedToolTip(toolTip).create();
639     if (methodCandidate2 == null) {
640       registerMethodCallIntentions(info, methodCall, list, resolveHelper);
641     }
642     if (!resolveResult.isAccessible() && resolveResult.isStaticsScopeCorrect() && methodCandidate2 != null) {
643       HighlightUtil.registerAccessQuickFixAction((PsiMember)element, referenceToMethod, info, resolveResult.getCurrentFileResolveScope());
644     }
645     if (element != null && !resolveResult.isStaticsScopeCorrect()) {
646       HighlightUtil.registerStaticProblemQuickFixAction(element, info, referenceToMethod);
647     }
648
649     TextRange fixRange = getFixRange(elementToHighlight);
650     CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, info, fixRange);
651     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
652     PermuteArgumentsFix.registerFix(info, methodCall, candidates, fixRange);
653     WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), info);
654     registerChangeParameterClassFix(methodCall, list, info);
655     return info;
656   }
657
658   @NotNull
659   private static MethodCandidateInfo[] toMethodCandidates(@NotNull JavaResolveResult[] resolveResults) {
660     List<MethodCandidateInfo> candidateList = new ArrayList<MethodCandidateInfo>(resolveResults.length);
661
662     for (JavaResolveResult result : resolveResults) {
663       if (!(result instanceof MethodCandidateInfo)) continue;
664       MethodCandidateInfo candidate = (MethodCandidateInfo)result;
665       if (candidate.isAccessible()) candidateList.add(candidate);
666     }
667     return candidateList.toArray(new MethodCandidateInfo[candidateList.size()]);
668   }
669
670   private static void registerMethodCallIntentions(@Nullable HighlightInfo highlightInfo,
671                                                    PsiMethodCallExpression methodCall,
672                                                    PsiExpressionList list,
673                                                    PsiResolveHelper resolveHelper) {
674     TextRange fixRange = getFixRange(methodCall);
675     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateMethodFromUsageFix(methodCall));
676     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateAbstractMethodFromUsageFix(methodCall));
677     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateConstructorFromSuperFix(methodCall));
678     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateConstructorFromThisFix(methodCall));
679     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreatePropertyFromUsageFix(methodCall));
680     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateGetterSetterPropertyFromUsageFix(methodCall));
681     CandidateInfo[] methodCandidates = resolveHelper.getReferencedMethodCandidates(methodCall, false);
682     CastMethodArgumentFix.REGISTRAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
683     PermuteArgumentsFix.registerFix(highlightInfo, methodCall, methodCandidates, fixRange);
684     AddTypeArgumentsFix.REGISTRAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
685     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
686     registerMethodAccessLevelIntentions(methodCandidates, methodCall, list, highlightInfo);
687     registerChangeMethodSignatureFromUsageIntentions(methodCandidates, list, highlightInfo, fixRange);
688     RemoveRedundantArgumentsFix.registerIntentions(methodCandidates, list, highlightInfo, fixRange);
689     ConvertDoubleToFloatFix.registerIntentions(methodCandidates, list, highlightInfo, fixRange);
690     WrapExpressionFix.registerWrapAction(methodCandidates, list.getExpressions(), highlightInfo);
691     registerChangeParameterClassFix(methodCall, list, highlightInfo);
692     if (methodCandidates.length == 0) {
693       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createStaticImportMethodFix(methodCall));
694       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.addMethodQualifierFix(methodCall));
695     }
696     for (IntentionAction action : QUICK_FIX_FACTORY.getVariableTypeFromCallFixes(methodCall, list)) {
697       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, action);
698     }
699     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createReplaceAddAllArrayToCollectionFix(methodCall));
700     QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createSurroundWithArrayFix(methodCall, null));
701     QualifyThisArgumentFix.registerQuickFixAction(methodCandidates, methodCall, highlightInfo, fixRange);
702
703     CandidateInfo[] candidates = resolveHelper.getReferencedMethodCandidates(methodCall, true);
704     ChangeStringLiteralToCharInMethodCallFix.registerFixes(candidates, methodCall, highlightInfo);
705   }
706
707   private static void registerMethodAccessLevelIntentions(CandidateInfo[] methodCandidates,
708                                                           PsiMethodCallExpression methodCall,
709                                                           PsiExpressionList exprList,
710                                                           HighlightInfo highlightInfo) {
711     for (CandidateInfo methodCandidate : methodCandidates) {
712       PsiMethod method = (PsiMethod)methodCandidate.getElement();
713       if (!methodCandidate.isAccessible() && PsiUtil.isApplicable(method, methodCandidate.getSubstitutor(), exprList)) {
714         HighlightUtil.registerAccessQuickFixAction(method, methodCall.getMethodExpression(), highlightInfo, methodCandidate.getCurrentFileResolveScope());
715       }
716     }
717   }
718
719   @NotNull
720   private static String createAmbiguousMethodHtmlTooltip(MethodCandidateInfo[] methodCandidates) {
721     return JavaErrorMessages.message("ambiguous.method.html.tooltip",
722                                      Integer.valueOf(methodCandidates[0].getElement().getParameterList().getParametersCount() + 2),
723                                      createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[0]),
724                                      getContainingClassName(methodCandidates[0]),
725                                      createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[1]),
726                                      getContainingClassName(methodCandidates[1]));
727   }
728
729   private static String getContainingClassName(final MethodCandidateInfo methodCandidate) {
730     PsiMethod method = methodCandidate.getElement();
731     PsiClass containingClass = method.getContainingClass();
732     return containingClass == null ? method.getContainingFile().getName() : HighlightUtil.formatClass(containingClass, false);
733   }
734
735   @Language("HTML")
736   private static String createAmbiguousMethodHtmlTooltipMethodRow(final MethodCandidateInfo methodCandidate) {
737     PsiMethod method = methodCandidate.getElement();
738     PsiParameter[] parameters = method.getParameterList().getParameters();
739     PsiSubstitutor substitutor = methodCandidate.getSubstitutor();
740     @NonNls @Language("HTML") String ms = "<td><b>" + method.getName() + "</b></td>";
741
742     for (int j = 0; j < parameters.length; j++) {
743       PsiParameter parameter = parameters[j];
744       PsiType type = substitutor.substitute(parameter.getType());
745       ms += "<td><b>" + (j == 0 ? "(" : "") +
746             XmlStringUtil.escapeString(type.getPresentableText())
747             + (j == parameters.length - 1 ? ")" : ",") + "</b></td>";
748     }
749     if (parameters.length == 0) {
750       ms += "<td><b>()</b></td>";
751     }
752     return ms;
753   }
754
755   private static String createMismatchedArgumentsHtmlTooltip(MethodCandidateInfo info, PsiExpressionList list) {
756     PsiMethod method = info.getElement();
757     PsiSubstitutor substitutor = info.getSubstitutor();
758     PsiClass aClass = method.getContainingClass();
759     PsiParameter[] parameters = method.getParameterList().getParameters();
760     String methodName = method.getName();
761     return createMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass);
762   }
763
764   private static String createShortMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
765                                                              PsiParameter[] parameters,
766                                                              String methodName,
767                                                              PsiSubstitutor substitutor,
768                                                              PsiClass aClass) {
769     PsiExpression[] expressions = list.getExpressions();
770     int cols = Math.max(parameters.length, expressions.length);
771
772     @Language("HTML")
773     @NonNls String parensizedName = methodName + (parameters.length == 0 ? "(&nbsp;)&nbsp;" : "");
774     final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent());
775     return JavaErrorMessages.message(
776       "argument.mismatch.html.tooltip",
777       Integer.valueOf(cols - parameters.length + 1), parensizedName,
778       HighlightUtil.formatClass(aClass, false),
779       createMismatchedArgsHtmlTooltipParamsRow(parameters, substitutor, expressions),
780       createMismatchedArgsHtmlTooltipArgumentsRow(expressions, parameters, substitutor, cols),
781       errorMessage != null ? "<br/>reason: " + XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "<br/>") : ""
782     );
783   }
784
785   private static String esctrim(@NotNull String s) {
786     return XmlStringUtil.escapeString(trimNicely(s));
787   }
788
789   private static String trimNicely(String s) {
790     if (s.length() <= 40) return s;
791
792     List<TextRange> wordIndices = StringUtil.getWordIndicesIn(s);
793     if (wordIndices.size() > 2) {
794       int firstWordEnd = wordIndices.get(0).getEndOffset();
795
796       // try firstWord...remainder
797       for (int i = 1; i<wordIndices.size();i++) {
798         int stringLength = firstWordEnd + s.length() - wordIndices.get(i).getStartOffset();
799         if (stringLength <= 40) {
800           return s.substring(0, firstWordEnd) + "..." + s.substring(wordIndices.get(i).getStartOffset());
801         }
802       }
803     }
804     // maybe one last word will fit?
805     if (!wordIndices.isEmpty() && s.length() - wordIndices.get(wordIndices.size()-1).getStartOffset() <= 40) {
806       return "..." + s.substring(wordIndices.get(wordIndices.size()-1).getStartOffset());
807     }
808
809     return StringUtil.last(s, 40, true).toString();
810   }
811
812   private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
813                                                              PsiParameter[] parameters,
814                                                              String methodName,
815                                                              PsiSubstitutor substitutor,
816                                                              PsiClass aClass) {
817     return Math.max(parameters.length, list.getExpressions().length) <= 2
818            ? createShortMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass)
819            : createLongMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass);
820   }
821
822   @Language("HTML")
823   private static String createLongMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
824                                                              PsiParameter[] parameters,
825                                                              String methodName,
826                                                              PsiSubstitutor substitutor,
827                                                              PsiClass aClass) {
828     PsiExpression[] expressions = list.getExpressions();
829
830     @NonNls
831     String s = "<html><body><table border=0>" +
832                "<tr><td colspan=3>" +
833                "<nobr><b>" + methodName + "()</b> in <b>" + HighlightUtil.formatClass(aClass, false) +"</b> cannot be applied to:</nobr>" +
834                "</td></tr>"+
835                "<tr><td colspan=2 align=left>Expected<br>Parameters:</td><td align=left>Actual<br>Arguments:</td></tr>"+
836                "<tr><td colspan=3><hr></td></tr>"
837       ;
838
839     for (int i = 0; i < Math.max(parameters.length,expressions.length); i++) {
840       PsiParameter parameter = i < parameters.length ? parameters[i] : null;
841       PsiExpression expression = i < expressions.length ? expressions[i] : null;
842       boolean showShort = showShortType(i, parameters, expressions, substitutor);
843       @NonNls String mismatchColor = showShort ? null : UIUtil.isUnderDarcula() ? "FF6B68" : "red";
844
845       s += "<tr" + (i % 2 == 0 ? " style='background-color: #"
846                                  + (UIUtil.isUnderDarcula() ? ColorUtil.toHex(ColorUtil.shift(UIUtil.getToolTipBackground(), 1.1)) : "eeeeee")
847                                  + "'" : "") + ">";
848       s += "<td><b><nobr>";
849       if (parameter != null) {
850         String name = parameter.getName();
851         if (name != null) {
852           s += esctrim(name) +":";
853         }
854       }
855       s += "</nobr></b></td>";
856
857       s += "<td><b><nobr>";
858       if (parameter != null) {
859         PsiType type = substitutor.substitute(parameter.getType());
860         s +=  "<font " + (mismatchColor == null ? "" : "color=" + mismatchColor) + ">" +
861               esctrim(showShort ? type.getPresentableText() : JavaHighlightUtil.formatType(type))
862               + "</font>"
863               ;
864       }
865       s += "</nobr></b></td>";
866
867       s += "<td><b><nobr>";
868       if (expression != null) {
869         PsiType type = expression.getType();
870         s += "<font " + (mismatchColor == null ? "" : "color='" + mismatchColor + "'") + ">" +
871                esctrim(expression.getText()) + "&nbsp;&nbsp;"+
872               (mismatchColor == null || type == null || type == PsiType.NULL ? "" : "("+esctrim(JavaHighlightUtil.formatType(type))+")")
873               + "</font>"
874               ;
875
876       }
877       s += "</nobr></b></td>";
878
879       s += "</tr>";
880     }
881
882     s+= "</table>";
883     final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent());
884     if (errorMessage != null) {
885       s+= "reason: "; 
886       s += XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "<br/>");
887     }
888     s+= "</body></html>";
889     return s;
890   }
891
892   @Language("HTML")
893   private static String createMismatchedArgsHtmlTooltipArgumentsRow(final PsiExpression[] expressions, final PsiParameter[] parameters,
894                                                                       final PsiSubstitutor substitutor, final int cols) {
895     @Language("HTML")
896
897     @NonNls String ms = "";
898     for (int i = 0; i < expressions.length; i++) {
899       PsiExpression expression = expressions[i];
900       PsiType type = expression.getType();
901
902       boolean showShort = showShortType(i, parameters, expressions, substitutor);
903       @NonNls String mismatchColor = showShort ? null : MISMATCH_COLOR;
904       ms += "<td> " + "<b><nobr>" + (i == 0 ? "(" : "")
905             + "<font " + (showShort ? "" : "color=" + mismatchColor) + ">" +
906             XmlStringUtil.escapeString(showShort ? type.getPresentableText() : JavaHighlightUtil.formatType(type))
907             + "</font>"
908             + (i == expressions.length - 1 ? ")" : ",") + "</nobr></b></td>";
909     }
910     for (int i = expressions.length; i < cols + 1; i++) {
911       ms += "<td>" + (i == 0 ? "<b>()</b>" : "") +
912             "&nbsp;</td>";
913     }
914     return ms;
915   }
916
917   @Language("HTML")
918   private static String createMismatchedArgsHtmlTooltipParamsRow(final PsiParameter[] parameters,
919                                                                  final PsiSubstitutor substitutor,
920                                                                  final PsiExpression[] expressions) {
921     @NonNls String ms = "";
922     for (int i = 0; i < parameters.length; i++) {
923       PsiParameter parameter = parameters[i];
924       PsiType type = substitutor.substitute(parameter.getType());
925       ms += "<td><b><nobr>" + (i == 0 ? "(" : "") +
926             XmlStringUtil.escapeString(showShortType(i, parameters, expressions, substitutor)
927                                  ? type.getPresentableText()
928                                  : JavaHighlightUtil.formatType(type))
929             + (i == parameters.length - 1 ? ")" : ",") + "</nobr></b></td>";
930     }
931     return ms;
932   }
933
934   private static boolean showShortType(int i,
935                                        PsiParameter[] parameters,
936                                        PsiExpression[] expressions,
937                                        PsiSubstitutor substitutor) {
938     PsiExpression expression = i < expressions.length ? expressions[i] : null;
939     if (expression == null) return true;
940     PsiType paramType = i < parameters.length && parameters[i] != null
941                         ? substitutor.substitute(parameters[i].getType())
942                         : null;
943     PsiType expressionType = expression.getType();
944     return paramType != null && expressionType != null && TypeConversionUtil.isAssignable(paramType, expressionType);
945   }
946
947
948   static HighlightInfo checkMethodMustHaveBody(PsiMethod method, PsiClass aClass) {
949     HighlightInfo errorResult = null;
950     if (method.getBody() == null
951         && !method.hasModifierProperty(PsiModifier.ABSTRACT)
952         && !method.hasModifierProperty(PsiModifier.NATIVE)
953         && aClass != null
954         && !aClass.isInterface()
955         && !PsiUtilCore.hasErrorElementChild(method)) {
956       int start = method.getModifierList().getTextRange().getStartOffset();
957       int end = method.getTextRange().getEndOffset();
958
959       String description = JavaErrorMessages.message("missing.method.body");
960       errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(start, end).descriptionAndTooltip(description).create();
961       if (HighlightUtil.getIncompatibleModifier(PsiModifier.ABSTRACT, method.getModifierList()) == null) {
962         QuickFixAction.registerQuickFixAction(errorResult,
963                                               QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, true, false));
964       }
965       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
966     }
967     return errorResult;
968   }
969
970
971   static HighlightInfo checkAbstractMethodInConcreteClass(PsiMethod method, PsiElement elementToHighlight) {
972     HighlightInfo errorResult = null;
973     PsiClass aClass = method.getContainingClass();
974     if (method.hasModifierProperty(PsiModifier.ABSTRACT)
975         && aClass != null
976         && !aClass.hasModifierProperty(PsiModifier.ABSTRACT)
977         && !aClass.isEnum()
978         && !PsiUtilCore.hasErrorElementChild(method)) {
979       String description = JavaErrorMessages.message("abstract.method.in.non.abstract.class");
980       errorResult =
981         HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(elementToHighlight).descriptionAndTooltip(description).create();
982       if (method.getBody() != null) {
983         QuickFixAction.registerQuickFixAction(errorResult,
984                                               QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, false, false));
985       }
986       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
987       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createModifierListFix(aClass, PsiModifier.ABSTRACT, true, false));
988     }
989     return errorResult;
990   }
991
992
993   static HighlightInfo checkConstructorName(PsiMethod method) {
994     String methodName = method.getName();
995     PsiClass aClass = method.getContainingClass();
996     HighlightInfo errorResult = null;
997
998     if (aClass != null) {
999       String className = aClass instanceof PsiAnonymousClass ? null : aClass.getName();
1000       if (className == null || !Comparing.strEqual(methodName, className)) {
1001         PsiElement element = method.getNameIdentifier();
1002         String description = JavaErrorMessages.message("missing.return.type");
1003         errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
1004         if (className != null) {
1005           QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createRenameElementFix(method, className));
1006         }
1007       }
1008     }
1009     return errorResult;
1010   }
1011
1012   @Nullable
1013   static HighlightInfo checkDuplicateMethod(PsiClass aClass,
1014                                             @NotNull PsiMethod method,
1015                                             @NotNull MostlySingularMultiMap<MethodSignature, PsiMethod> duplicateMethods) {
1016     if (aClass == null || method instanceof ExternallyDefinedPsiElement) return null;
1017     MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
1018     int methodCount = 1;
1019     List<PsiMethod> methods = (List<PsiMethod>)duplicateMethods.get(methodSignature);
1020     if (methods.size() > 1) {
1021       methodCount++;
1022     }
1023
1024     if (methodCount == 1 && aClass.isEnum() &&
1025         GenericsHighlightUtil.isEnumSyntheticMethod(methodSignature, aClass.getProject())) {
1026       methodCount++;
1027     }
1028     if (methodCount > 1) {
1029       String description = JavaErrorMessages.message("duplicate.method",
1030                                                  JavaHighlightUtil.formatMethod(method),
1031                                                  HighlightUtil.formatClass(aClass));
1032       TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1033       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).
1034         range(method, textRange.getStartOffset(), textRange.getEndOffset()).
1035         descriptionAndTooltip(description).create();
1036     }
1037     return null;
1038   }
1039
1040   @Nullable
1041   static HighlightInfo checkMethodCanHaveBody(@NotNull PsiMethod method, @NotNull LanguageLevel languageLevel) {
1042     PsiClass aClass = method.getContainingClass();
1043     boolean hasNoBody = method.getBody() == null;
1044     boolean isInterface = aClass != null && aClass.isInterface();
1045     boolean isExtension = method.hasModifierProperty(PsiModifier.DEFAULT);
1046     boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
1047     boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE);
1048
1049     final List<IntentionAction> additionalFixes = new ArrayList<IntentionAction>();
1050     String description = null;
1051     if (hasNoBody) {
1052       if (isExtension) {
1053         description = JavaErrorMessages.message("extension.method.should.have.a.body");
1054         additionalFixes.add(QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
1055       }
1056       else if (isInterface && isStatic && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
1057         description = "Static methods in interfaces should have a body";
1058       }
1059     }
1060     else if (isInterface) {
1061       if (!isExtension && !isStatic && !isPrivate) {
1062         description = JavaErrorMessages.message("interface.methods.cannot.have.body");
1063         if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
1064           additionalFixes.add(QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.DEFAULT, true, false));
1065           additionalFixes.add(QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.STATIC, true, false));
1066         }
1067       }
1068     }
1069     else if (isExtension) {
1070       description = JavaErrorMessages.message("extension.method.in.class");
1071     }
1072     else if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
1073       description = JavaErrorMessages.message("abstract.methods.cannot.have.a.body");
1074     }
1075     else if (method.hasModifierProperty(PsiModifier.NATIVE)) {
1076       description = JavaErrorMessages.message("native.methods.cannot.have.a.body");
1077     }
1078     if (description == null) return null;
1079
1080     TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1081     HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1082     if (!hasNoBody) {
1083       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createDeleteMethodBodyFix(method));
1084     }
1085     if (method.hasModifierProperty(PsiModifier.ABSTRACT) && !isInterface) {
1086       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, false, false));
1087     }
1088     for (IntentionAction intentionAction : additionalFixes) {
1089       QuickFixAction.registerQuickFixAction(info, intentionAction);
1090     }
1091     return info;
1092   }
1093
1094   @Nullable
1095   static HighlightInfo checkConstructorCallMustBeFirstStatement(@NotNull PsiMethodCallExpression methodCall) {
1096     if (!RefactoringChangeUtil.isSuperOrThisMethodCall(methodCall)) return null;
1097     PsiElement codeBlock = methodCall.getParent().getParent();
1098     if (codeBlock instanceof PsiCodeBlock
1099         && codeBlock.getParent() instanceof PsiMethod
1100         && ((PsiMethod)codeBlock.getParent()).isConstructor()) {
1101       PsiElement prevSibling = methodCall.getParent().getPrevSibling();
1102       while (true) {
1103         if (prevSibling == null) return null;
1104         if (prevSibling instanceof PsiStatement) break;
1105         prevSibling = prevSibling.getPrevSibling();
1106       }
1107     }
1108     PsiReferenceExpression expression = methodCall.getMethodExpression();
1109     String message = JavaErrorMessages.message("constructor.call.must.be.first.statement", expression.getText() + "()");
1110     return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCall).descriptionAndTooltip(message).create();
1111   }
1112
1113
1114   static HighlightInfo checkSuperAbstractMethodDirectCall(@NotNull PsiMethodCallExpression methodCallExpression) {
1115     PsiReferenceExpression expression = methodCallExpression.getMethodExpression();
1116     if (!(expression.getQualifierExpression() instanceof PsiSuperExpression)) return null;
1117     PsiMethod method = methodCallExpression.resolveMethod();
1118     if (method != null && method.hasModifierProperty(PsiModifier.ABSTRACT)) {
1119       String message = JavaErrorMessages.message("direct.abstract.method.access", JavaHighlightUtil.formatMethod(method));
1120       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCallExpression).descriptionAndTooltip(message).create();
1121     }
1122     return null;
1123   }
1124
1125
1126   static HighlightInfo checkConstructorCallsBaseClassConstructor(PsiMethod constructor,
1127                                                                         RefCountHolder refCountHolder,
1128                                                                         PsiResolveHelper resolveHelper) {
1129     if (!constructor.isConstructor()) return null;
1130     PsiClass aClass = constructor.getContainingClass();
1131     if (aClass == null) return null;
1132     if (aClass.isEnum()) return null;
1133     PsiCodeBlock body = constructor.getBody();
1134     if (body == null) return null;
1135
1136     // check whether constructor call super(...) or this(...)
1137     PsiElement element = new PsiMatcherImpl(body)
1138       .firstChild(PsiMatchers.hasClass(PsiExpressionStatement.class))
1139       .firstChild(PsiMatchers.hasClass(PsiMethodCallExpression.class))
1140       .firstChild(PsiMatchers.hasClass(PsiReferenceExpression.class))
1141       .firstChild(PsiMatchers.hasClass(PsiKeyword.class))
1142       .getElement();
1143     if (element != null) return null;
1144     TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(constructor);
1145     PsiClassType[] handledExceptions = constructor.getThrowsList().getReferencedTypes();
1146     HighlightInfo info = HighlightClassUtil.checkBaseClassDefaultConstructorProblem(aClass, refCountHolder, resolveHelper, textRange, handledExceptions);
1147     if (info != null) {
1148       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createInsertSuperFix(constructor));
1149       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddDefaultConstructorFix(aClass.getSuperClass()));
1150     }
1151     return info;
1152   }
1153
1154
1155   /**
1156    * @return error if static method overrides instance method or
1157    *         instance method overrides static. see JLS 8.4.6.1, 8.4.6.2
1158    */
1159   static HighlightInfo checkStaticMethodOverride(@NotNull PsiMethod method,@NotNull PsiFile containingFile) {
1160     // constructors are not members and therefor don't override class methods
1161     if (method.isConstructor()) {
1162       return null;
1163     }
1164
1165     PsiClass aClass = method.getContainingClass();
1166     if (aClass == null) return null;
1167     final HierarchicalMethodSignature methodSignature = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(method);
1168     final List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures();
1169     if (superSignatures.isEmpty()) {
1170       return null;
1171     }
1172
1173     boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
1174     for (HierarchicalMethodSignature signature : superSignatures) {
1175       final PsiMethod superMethod = signature.getMethod();
1176       final PsiClass superClass = superMethod.getContainingClass();
1177       if (superClass == null) continue;
1178       final HighlightInfo highlightInfo = checkStaticMethodOverride(aClass, method, isStatic, superClass, superMethod,containingFile);
1179       if (highlightInfo != null) {
1180         return highlightInfo;
1181       }
1182     }
1183     return null;
1184   }
1185
1186   private static HighlightInfo checkStaticMethodOverride(PsiClass aClass, PsiMethod method, boolean isMethodStatic, PsiClass superClass, PsiMethod superMethod,@NotNull PsiFile containingFile) {
1187     if (superMethod == null) return null;
1188     PsiManager manager = containingFile.getManager();
1189     PsiModifierList superModifierList = superMethod.getModifierList();
1190     PsiModifierList modifierList = method.getModifierList();
1191     if (superModifierList.hasModifierProperty(PsiModifier.PRIVATE)) return null;
1192     if (superModifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)
1193         && !JavaPsiFacade.getInstance(manager.getProject()).arePackagesTheSame(aClass, superClass)) {
1194       return null;
1195     }
1196     boolean isSuperMethodStatic = superModifierList.hasModifierProperty(PsiModifier.STATIC);
1197     if (isMethodStatic != isSuperMethodStatic) {
1198       TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1199       @NonNls final String messageKey = isMethodStatic
1200                                 ? "static.method.cannot.override.instance.method"
1201                                 : "instance.method.cannot.override.static.method";
1202
1203       String description = JavaErrorMessages.message(messageKey,
1204                                                  JavaHighlightUtil.formatMethod(method),
1205                                                  HighlightUtil.formatClass(aClass),
1206                                                  JavaHighlightUtil.formatMethod(superMethod),
1207                                                  HighlightUtil.formatClass(superClass));
1208
1209       HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1210       if (!isSuperMethodStatic || HighlightUtil.getIncompatibleModifier(PsiModifier.STATIC, modifierList) == null) {
1211         QuickFixAction.registerQuickFixAction(info,
1212                                               QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.STATIC, isSuperMethodStatic, false));
1213       }
1214       if (manager.isInProject(superMethod) &&
1215           (!isMethodStatic || HighlightUtil.getIncompatibleModifier(PsiModifier.STATIC, superModifierList) == null)) {
1216         QuickFixAction.registerQuickFixAction(info,
1217                                               QUICK_FIX_FACTORY.createModifierListFix(superMethod, PsiModifier.STATIC, isMethodStatic, true));
1218       }
1219       return info;
1220     }
1221
1222     if (isMethodStatic) {
1223       if (superClass.isInterface()) return null;
1224       int accessLevel = PsiUtil.getAccessLevel(modifierList);
1225       String accessModifier = PsiUtil.getAccessModifier(accessLevel);
1226       HighlightInfo info = isWeaker(method, modifierList, accessModifier, accessLevel, superMethod, true);
1227       if (info != null) return info;
1228       info = checkSuperMethodIsFinal(method, superMethod);
1229       if (info != null) return info;
1230     }
1231     return null;
1232   }
1233
1234   private static HighlightInfo checkInterfaceInheritedMethodsReturnTypes(@NotNull List<? extends MethodSignatureBackedByPsiMethod> superMethodSignatures,
1235                                                                          @NotNull LanguageLevel languageLevel) {
1236     if (superMethodSignatures.size() < 2) return null;
1237     MethodSignatureBackedByPsiMethod returnTypeSubstitutable = superMethodSignatures.get(0);
1238     for (int i = 1; i < superMethodSignatures.size(); i++) {
1239       PsiMethod currentMethod = returnTypeSubstitutable.getMethod();
1240       PsiType currentType = returnTypeSubstitutable.getSubstitutor().substitute(currentMethod.getReturnType());
1241
1242       MethodSignatureBackedByPsiMethod otherSuperSignature = superMethodSignatures.get(i);
1243       PsiMethod otherSuperMethod = otherSuperSignature.getMethod();
1244       PsiType otherSuperReturnType = otherSuperSignature.getSubstitutor().substitute(otherSuperMethod.getReturnType());
1245
1246       PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable,
1247                                                                                                   otherSuperSignature);
1248       if (unifyingSubstitutor != null) {
1249         otherSuperReturnType = unifyingSubstitutor.substitute(otherSuperReturnType);
1250         currentType = unifyingSubstitutor.substitute(currentType);
1251       }
1252
1253       if (otherSuperReturnType == null || currentType == null || otherSuperReturnType.equals(currentType)) continue;
1254
1255       if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) {
1256         //http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8 Example 8.1.5-3
1257         if (!(otherSuperReturnType instanceof PsiPrimitiveType || currentType instanceof PsiPrimitiveType)) {
1258           if (otherSuperReturnType.isAssignableFrom(currentType)) continue;
1259           if (currentType.isAssignableFrom(otherSuperReturnType)) {
1260             returnTypeSubstitutable = otherSuperSignature;
1261             continue;
1262           }
1263         }
1264         if (currentMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(currentType, otherSuperReturnType)) continue;
1265       }
1266       return createIncompatibleReturnTypeMessage(currentMethod, otherSuperMethod, otherSuperReturnType,
1267                                                  currentType, JavaErrorMessages.message("unrelated.overriding.methods.return.types"),
1268                                                  TextRange.EMPTY_RANGE);
1269     }
1270     return null;
1271   }
1272
1273   static HighlightInfo checkOverrideEquivalentInheritedMethods(PsiClass aClass, PsiFile containingFile, @NotNull LanguageLevel languageLevel) {
1274     String description = null;
1275     final Collection<HierarchicalMethodSignature> visibleSignatures = aClass.getVisibleSignatures();
1276     PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
1277     Ultimate:
1278     for (HierarchicalMethodSignature signature : visibleSignatures) {
1279       PsiMethod method = signature.getMethod();
1280       if (!resolveHelper.isAccessible(method, aClass, null)) continue;
1281       List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
1282
1283       boolean allAbstracts = method.hasModifierProperty(PsiModifier.ABSTRACT);
1284       final PsiClass containingClass = method.getContainingClass();
1285       if (aClass.equals(containingClass)) continue; //to be checked at method level
1286
1287       if (aClass.isInterface() && !containingClass.isInterface()) continue;
1288       HighlightInfo highlightInfo;
1289       if (allAbstracts) {
1290         superSignatures = new ArrayList<HierarchicalMethodSignature>(superSignatures);
1291         superSignatures.add(signature);
1292         highlightInfo = checkInterfaceInheritedMethodsReturnTypes(superSignatures, languageLevel);
1293       }
1294       else {
1295         highlightInfo = checkMethodIncompatibleReturnType(signature, superSignatures, false, languageLevel);
1296       }
1297       if (highlightInfo != null) description = highlightInfo.getDescription();
1298
1299       if (method.hasModifierProperty(PsiModifier.STATIC)) {
1300         for (HierarchicalMethodSignature superSignature : superSignatures) {
1301           PsiMethod superMethod = superSignature.getMethod();
1302           if (!superMethod.hasModifierProperty(PsiModifier.STATIC)) {
1303             description = JavaErrorMessages.message("static.method.cannot.override.instance.method",
1304                                                          JavaHighlightUtil.formatMethod(method),
1305                                                          HighlightUtil.formatClass(containingClass),
1306                                                          JavaHighlightUtil.formatMethod(superMethod),
1307                                                          HighlightUtil.formatClass(superMethod.getContainingClass()));
1308             break Ultimate;
1309           }
1310         }
1311         continue;
1312       }
1313
1314       if (description == null) {
1315         highlightInfo = checkMethodIncompatibleThrows(signature, superSignatures, false, aClass);
1316         if (highlightInfo != null) description = highlightInfo.getDescription();
1317       }
1318
1319       if (description == null) {
1320         highlightInfo = checkMethodWeakerPrivileges(signature, superSignatures, false, containingFile);
1321         if (highlightInfo != null) description = highlightInfo.getDescription();
1322       }
1323
1324       if (description != null) break;
1325     }
1326
1327
1328     if (description != null) {
1329       // show error info at the class level
1330       TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
1331       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1332     }
1333     return null;
1334   }
1335
1336
1337   static HighlightInfo checkConstructorHandleSuperClassExceptions(PsiMethod method) {
1338     if (!method.isConstructor()) {
1339       return null;
1340     }
1341     PsiCodeBlock body = method.getBody();
1342     PsiStatement[] statements = body == null ? null : body.getStatements();
1343     if (statements == null) return null;
1344
1345     // if we have unhandled exception inside method body, we could not have been called here,
1346     // so the only problem it can catch here is with super ctr only
1347     Collection<PsiClassType> unhandled = ExceptionUtil.collectUnhandledExceptions(method, method.getContainingClass());
1348     if (unhandled.isEmpty()) return null;
1349     String description = HighlightUtil.getUnhandledExceptionsDescriptor(unhandled);
1350     TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1351     HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1352     for (PsiClassType exception : unhandled) {
1353       QuickFixAction.registerQuickFixAction(highlightInfo, new LocalQuickFixOnPsiElementAsIntentionAdapter(QUICK_FIX_FACTORY.createMethodThrowsFix(method, exception, true, false)));
1354     }
1355     return highlightInfo;
1356   }
1357
1358
1359   static HighlightInfo checkRecursiveConstructorInvocation(@NotNull PsiMethod method) {
1360     if (HighlightControlFlowUtil.isRecursivelyCalledConstructor(method)) {
1361       TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
1362       String description = JavaErrorMessages.message("recursive.constructor.invocation");
1363       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1364     }
1365     return null;
1366   }
1367
1368   @NotNull
1369   public static TextRange getFixRange(@NotNull PsiElement element) {
1370     TextRange range = element.getTextRange();
1371     int start = range.getStartOffset();
1372     int end = range.getEndOffset();
1373
1374     PsiElement nextSibling = element.getNextSibling();
1375     if (nextSibling instanceof PsiJavaToken && ((PsiJavaToken)nextSibling).getTokenType() == JavaTokenType.SEMICOLON) {
1376       return new TextRange(start, end + 1);
1377     }
1378     return range;
1379   }
1380
1381
1382   static void checkNewExpression(@NotNull PsiNewExpression expression,
1383                                  PsiType type,
1384                                  @NotNull HighlightInfoHolder holder,
1385                                  @NotNull JavaSdkVersion javaSdkVersion) {
1386     if (!(type instanceof PsiClassType)) return;
1387     PsiClassType.ClassResolveResult typeResult = ((PsiClassType)type).resolveGenerics();
1388     PsiClass aClass = typeResult.getElement();
1389     if (aClass == null) return;
1390     if (aClass instanceof PsiAnonymousClass) {
1391       type = ((PsiAnonymousClass)aClass).getBaseClassType();
1392       typeResult = ((PsiClassType)type).resolveGenerics();
1393       aClass = typeResult.getElement();
1394       if (aClass == null) return;
1395     }
1396
1397     PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
1398     checkConstructorCall(typeResult, expression, type, classReference, holder, javaSdkVersion);
1399   }
1400
1401
1402   public static void checkConstructorCall(PsiClassType.ClassResolveResult typeResolveResult,
1403                                           PsiConstructorCall constructorCall,
1404                                           PsiType type,
1405                                           PsiJavaCodeReferenceElement classReference,
1406                                           final HighlightInfoHolder holder,
1407                                           @NotNull JavaSdkVersion javaSdkVersion) {
1408     PsiExpressionList list = constructorCall.getArgumentList();
1409     if (list == null) return;
1410     PsiClass aClass = typeResolveResult.getElement();
1411     if (aClass == null) return;
1412     final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(holder.getProject()).getResolveHelper();
1413     PsiClass accessObjectClass = null;
1414     if (constructorCall instanceof PsiNewExpression) {
1415       PsiExpression qualifier = ((PsiNewExpression)constructorCall).getQualifier();
1416       if (qualifier != null) {
1417         accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
1418       }
1419     }
1420     if (classReference != null && !resolveHelper.isAccessible(aClass, constructorCall, accessObjectClass)) {
1421       String description = HighlightUtil.buildProblemWithAccessDescription(classReference, typeResolveResult);
1422       PsiElement element = classReference.getReferenceNameElement();
1423       HighlightInfo info =
1424         HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
1425       HighlightUtil.registerAccessQuickFixAction(aClass, classReference, info, null);
1426       holder.add(info);
1427       return;
1428     }
1429     PsiMethod[] constructors = aClass.getConstructors();
1430
1431     if (constructors.length == 0) {
1432       if (list.getExpressions().length != 0) {
1433         String constructorName = aClass.getName();
1434         String argTypes = buildArgTypesList(list);
1435         String description = JavaErrorMessages.message("wrong.constructor.arguments", constructorName+"()", argTypes);
1436         String tooltip = createMismatchedArgumentsHtmlTooltip(list, PsiParameter.EMPTY_ARRAY, constructorName, PsiSubstitutor.EMPTY, aClass);
1437         HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).description(description).escapedToolTip(tooltip).navigationShift(+1).create();
1438         QuickFixAction.registerQuickFixAction(info, constructorCall.getTextRange(), QUICK_FIX_FACTORY.createCreateConstructorFromCallFix(constructorCall));
1439         if (classReference != null) {
1440           ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info,getFixRange(list));
1441         }
1442         holder.add(info);
1443         return;
1444       }
1445       if (classReference != null && aClass.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass)) {
1446         holder.add(buildAccessProblem(classReference, typeResolveResult, aClass));
1447       } else if (aClass.isInterface() && constructorCall instanceof PsiNewExpression) {
1448         final PsiReferenceParameterList typeArgumentList = ((PsiNewExpression)constructorCall).getTypeArgumentList();
1449         if (typeArgumentList.getTypeArguments().length > 0) {
1450           holder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeArgumentList)
1451             .descriptionAndTooltip("Anonymous class implements interface; cannot have type arguments").create());
1452         }
1453       }
1454     }
1455     else {
1456       PsiElement place = list;
1457       if (constructorCall instanceof PsiNewExpression) {
1458         final PsiAnonymousClass anonymousClass = ((PsiNewExpression)constructorCall).getAnonymousClass();
1459         if (anonymousClass != null) place = anonymousClass;
1460       }
1461
1462       JavaResolveResult[] results = resolveHelper.multiResolveConstructor((PsiClassType)type, list, place);
1463       MethodCandidateInfo result = null;
1464       if (results.length == 1) result = (MethodCandidateInfo)results[0];
1465
1466       PsiMethod constructor = result == null ? null : result.getElement();
1467
1468       boolean applicable = true;
1469       try {
1470         applicable = constructor != null && result.isApplicable();
1471       }
1472       catch (IndexNotReadyException e) {
1473         // ignore
1474       }
1475
1476       PsiElement infoElement = list.getTextLength() > 0 ? list : constructorCall;
1477       if (constructor == null) {
1478         String name = aClass.getName();
1479         name += buildArgTypesList(list);
1480         String description = JavaErrorMessages.message("cannot.resolve.constructor", name);
1481         HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).navigationShift(+1).create();
1482         WrapExpressionFix.registerWrapAction(results, list.getExpressions(), info);
1483         registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, infoElement, info);
1484         holder.add(info);
1485       }
1486       else {
1487         if (classReference != null && (!result.isAccessible() ||
1488                                        constructor.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass))) {
1489           holder.add(buildAccessProblem(classReference, result, constructor));
1490         }
1491         else if (!applicable) {
1492           String constructorName = HighlightMessageUtil.getSymbolName(constructor, result.getSubstitutor());
1493           String containerName = HighlightMessageUtil.getSymbolName(constructor.getContainingClass(), result.getSubstitutor());
1494           String argTypes = buildArgTypesList(list);
1495           String description = JavaErrorMessages.message("wrong.method.arguments", constructorName, containerName, argTypes);
1496           String toolTip = createMismatchedArgumentsHtmlTooltip(result, list);
1497           
1498           HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(infoElement).description(description).escapedToolTip(toolTip).navigationShift(+1).create();
1499           if (info != null) {
1500             registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, infoElement, info);
1501             holder.add(info);
1502           }
1503         }
1504         else {
1505           if (constructorCall instanceof PsiNewExpression) {
1506             PsiReferenceParameterList typeArgumentList = ((PsiNewExpression)constructorCall).getTypeArgumentList();
1507             HighlightInfo info = GenericsHighlightUtil.checkReferenceTypeArgumentList(constructor, typeArgumentList, result.getSubstitutor(), false, javaSdkVersion);
1508             if (info != null) {
1509               holder.add(info);
1510             }
1511           }
1512         }
1513       }
1514     }
1515   }
1516
1517   private static void registerFixesOnInvalidConstructorCall(PsiConstructorCall constructorCall,
1518                                                             PsiJavaCodeReferenceElement classReference,
1519                                                             PsiExpressionList list,
1520                                                             PsiClass aClass,
1521                                                             PsiMethod[] constructors,
1522                                                             JavaResolveResult[] results, PsiElement infoElement, HighlightInfo info) {
1523     QuickFixAction
1524       .registerQuickFixAction(info, constructorCall.getTextRange(), QUICK_FIX_FACTORY.createCreateConstructorFromCallFix(constructorCall));
1525     if (classReference != null) {
1526       ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info, getFixRange(infoElement));
1527       ChangeTypeArgumentsFix.registerIntentions(results, list, info, aClass);
1528       ConvertDoubleToFloatFix.registerIntentions(results, list, info, null);
1529     }
1530     registerChangeMethodSignatureFromUsageIntentions(results, list, info, null);
1531     PermuteArgumentsFix.registerFix(info, constructorCall, toMethodCandidates(results), getFixRange(list));
1532     registerChangeParameterClassFix(constructorCall, list, info);
1533     QuickFixAction.registerQuickFixAction(info, getFixRange(list), QUICK_FIX_FACTORY.createSurroundWithArrayFix(constructorCall,null));
1534     ChangeStringLiteralToCharInMethodCallFix.registerFixes(constructors, constructorCall, info);
1535   }
1536
1537   private static HighlightInfo buildAccessProblem(@NotNull PsiJavaCodeReferenceElement classReference, JavaResolveResult result, PsiMember elementToFix) {
1538     String description = HighlightUtil.buildProblemWithAccessDescription(classReference, result);
1539     HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classReference).descriptionAndTooltip(
1540       description).navigationShift(+1).create();
1541     if (result.isStaticsScopeCorrect()) {
1542       HighlightUtil.registerAccessQuickFixAction(elementToFix, classReference, info, result.getCurrentFileResolveScope());
1543     }
1544     return info;
1545   }
1546
1547   private static boolean callingProtectedConstructorFromDerivedClass(PsiConstructorCall place, PsiClass constructorClass) {
1548     if (constructorClass == null) return false;
1549     // indirect instantiation via anonymous class is ok
1550     if (place instanceof PsiNewExpression && ((PsiNewExpression)place).getAnonymousClass() != null) return false;
1551     PsiElement curElement = place;
1552     PsiClass containingClass = constructorClass.getContainingClass();
1553     while (true) {
1554       PsiClass aClass = PsiTreeUtil.getParentOfType(curElement, PsiClass.class);
1555       if (aClass == null) return false;
1556       curElement = aClass;
1557       if ((aClass.isInheritor(constructorClass, true) || containingClass != null && aClass.isInheritor(containingClass, true))
1558           && !JavaPsiFacade.getInstance(aClass.getProject()).arePackagesTheSame(aClass, constructorClass)) {
1559         return true;
1560       }
1561     }
1562   }
1563
1564   private static String buildArgTypesList(PsiExpressionList list) {
1565     StringBuilder builder = new StringBuilder();
1566     builder.append("(");
1567     PsiExpression[] args = list.getExpressions();
1568     for (int i = 0; i < args.length; i++) {
1569       if (i > 0) {
1570         builder.append(", ");
1571       }
1572       PsiType argType = args[i].getType();
1573       builder.append(argType != null ? JavaHighlightUtil.formatType(argType) : "?");
1574     }
1575     builder.append(")");
1576     return builder.toString();
1577   }
1578
1579   private static void registerChangeParameterClassFix(@NotNull PsiCall methodCall,
1580                                                       @NotNull PsiExpressionList list,
1581                                                       HighlightInfo highlightInfo) {
1582     final JavaResolveResult result = methodCall.resolveMethodGenerics();
1583     PsiMethod method = (PsiMethod)result.getElement();
1584     final PsiSubstitutor substitutor = result.getSubstitutor();
1585     PsiExpression[] expressions = list.getExpressions();
1586     if (method == null) return;
1587     final PsiParameter[] parameters = method.getParameterList().getParameters();
1588     if (parameters.length != expressions.length) return;
1589     for (int i = 0; i < expressions.length; i++) {
1590       final PsiExpression expression = expressions[i];
1591       final PsiParameter parameter = parameters[i];
1592       final PsiType expressionType = expression.getType();
1593       final PsiType parameterType = substitutor.substitute(parameter.getType());
1594       if (expressionType == null || expressionType instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(expressionType) || expressionType instanceof PsiArrayType) continue;
1595       if (parameterType instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(parameterType) || parameterType instanceof PsiArrayType) continue;
1596       if (parameterType.isAssignableFrom(expressionType)) continue;
1597       PsiClass parameterClass = PsiUtil.resolveClassInType(parameterType);
1598       PsiClass expressionClass = PsiUtil.resolveClassInType(expressionType);
1599       if (parameterClass == null || expressionClass == null) continue;
1600       if (expressionClass instanceof PsiAnonymousClass) continue;
1601       if (parameterClass.isInheritor(expressionClass, true)) continue;
1602       QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createChangeParameterClassFix(expressionClass, (PsiClassType)parameterType));
1603     }
1604   }
1605
1606   public static void registerChangeMethodSignatureFromUsageIntentions(@NotNull JavaResolveResult[] candidates,
1607                                                                       @NotNull PsiExpressionList list,
1608                                                                       @Nullable HighlightInfo highlightInfo,
1609                                                                       TextRange fixRange) {
1610     if (candidates.length == 0) return;
1611     PsiExpression[] expressions = list.getExpressions();
1612     for (JavaResolveResult candidate : candidates) {
1613       registerChangeMethodSignatureFromUsageIntention(expressions, highlightInfo, fixRange, candidate, list);
1614     }
1615   }
1616
1617   private static void registerChangeMethodSignatureFromUsageIntention(@NotNull PsiExpression[] expressions,
1618                                                                       @Nullable HighlightInfo highlightInfo,
1619                                                                       TextRange fixRange,
1620                                                                       @NotNull JavaResolveResult candidate,
1621                                                                       @NotNull PsiElement context) {
1622     if (!candidate.isStaticsScopeCorrect()) return;
1623     PsiMethod method = (PsiMethod)candidate.getElement();
1624     PsiSubstitutor substitutor = candidate.getSubstitutor();
1625     if (method != null && context.getManager().isInProject(method)) {
1626       IntentionAction fix = QUICK_FIX_FACTORY.createChangeMethodSignatureFromUsageFix(method, expressions, substitutor, context, false, 2);
1627       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, fix);
1628       IntentionAction f2 = QUICK_FIX_FACTORY.createChangeMethodSignatureFromUsageReverseOrderFix(method, expressions, substitutor, context,
1629                                                                                                  false, 2);
1630       QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, f2);
1631     }
1632   }
1633 }