2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Highlight method problems
22 package com
.intellij
.codeInsight
.daemon
.impl
.analysis
;
24 import com
.intellij
.codeInsight
.ExceptionUtil
;
25 import com
.intellij
.codeInsight
.daemon
.JavaErrorMessages
;
26 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfo
;
27 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfoType
;
28 import com
.intellij
.codeInsight
.daemon
.impl
.RefCountHolder
;
29 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.*;
30 import com
.intellij
.codeInsight
.intention
.IntentionAction
;
31 import com
.intellij
.codeInsight
.intention
.QuickFixFactory
;
32 import com
.intellij
.openapi
.util
.Comparing
;
33 import com
.intellij
.openapi
.util
.TextRange
;
34 import com
.intellij
.psi
.*;
35 import com
.intellij
.psi
.infos
.CandidateInfo
;
36 import com
.intellij
.psi
.infos
.MethodCandidateInfo
;
37 import com
.intellij
.psi
.util
.*;
38 import com
.intellij
.xml
.util
.XmlStringUtil
;
39 import org
.jetbrains
.annotations
.NonNls
;
40 import org
.jetbrains
.annotations
.NotNull
;
41 import org
.jetbrains
.annotations
.Nullable
;
43 import java
.text
.MessageFormat
;
44 import java
.util
.ArrayList
;
45 import java
.util
.Collection
;
46 import java
.util
.List
;
48 public class HighlightMethodUtil
{
49 private static final QuickFixFactory QUICK_FIX_FACTORY
= QuickFixFactory
.getInstance();
51 public static String
createClashMethodMessage(PsiMethod method1
, PsiMethod method2
, boolean showContainingClasses
) {
52 @NonNls String pattern
= showContainingClasses ?
"clash.methods.message.show.classes" : "clash.methods.message";
54 return JavaErrorMessages
.message(pattern
,
55 HighlightUtil
.formatMethod(method1
),
56 HighlightUtil
.formatMethod(method2
),
57 HighlightUtil
.formatClass(method1
.getContainingClass()),
58 HighlightUtil
.formatClass(method2
.getContainingClass()));
61 public static HighlightInfo
checkMethodWeakerPrivileges(MethodSignatureBackedByPsiMethod methodSignature
,
62 List
<HierarchicalMethodSignature
> superMethodSignatures
,
63 boolean includeRealPositionInfo
) {
64 PsiMethod method
= methodSignature
.getMethod();
65 PsiModifierList modifierList
= method
.getModifierList();
66 if (modifierList
.hasModifierProperty(PsiModifier
.PUBLIC
)) return null;
67 int accessLevel
= PsiUtil
.getAccessLevel(modifierList
);
68 String accessModifier
= PsiUtil
.getAccessModifier(accessLevel
);
69 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
70 PsiMethod superMethod
= superMethodSignature
.getMethod();
71 if (!PsiUtil
.isAccessible(superMethod
, method
, null)) continue;
72 HighlightInfo info
= isWeaker(method
, modifierList
, accessModifier
, accessLevel
, superMethod
, includeRealPositionInfo
);
73 if (info
!= null) return info
;
78 private static HighlightInfo
isWeaker(final PsiMethod method
, final PsiModifierList modifierList
, final String accessModifier
, final int accessLevel
,
79 final PsiMethod superMethod
,
80 final boolean includeRealPositionInfo
) {
81 int superAccessLevel
= PsiUtil
.getAccessLevel(superMethod
.getModifierList());
82 if (accessLevel
< superAccessLevel
) {
83 String message
= JavaErrorMessages
.message("weaker.privileges",
84 createClashMethodMessage(method
, superMethod
, true),
86 PsiUtil
.getAccessModifier(superAccessLevel
));
88 if (includeRealPositionInfo
) {
89 if (modifierList
.hasModifierProperty(PsiModifier
.PACKAGE_LOCAL
)) {
90 textRange
= method
.getNameIdentifier().getTextRange();
93 PsiElement keyword
= PsiUtil
.findModifierInList(modifierList
, accessModifier
);
94 textRange
= keyword
.getTextRange();
98 textRange
= new TextRange(0, 0);
100 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
101 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiUtil
.getAccessModifier(superAccessLevel
), true, false);
102 QuickFixAction
.registerQuickFixAction(highlightInfo
, fix
);
103 return highlightInfo
;
109 public static HighlightInfo
checkMethodIncompatibleReturnType(MethodSignatureBackedByPsiMethod methodSignature
,
110 List
<HierarchicalMethodSignature
> superMethodSignatures
,
111 boolean includeRealPositionInfo
) {
112 PsiMethod method
= methodSignature
.getMethod();
113 PsiType returnType
= methodSignature
.getSubstitutor().substitute(method
.getReturnType());
114 PsiClass aClass
= method
.getContainingClass();
115 if (aClass
== null) return null;
116 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
117 PsiMethod superMethod
= superMethodSignature
.getMethod();
118 PsiType declaredReturnType
= superMethod
.getReturnType();
119 PsiType superReturnType
= superMethodSignature
.getSubstitutor().substitute(declaredReturnType
);
120 if (superMethodSignature
.isRaw()) superReturnType
= TypeConversionUtil
.erasure(declaredReturnType
);
121 if (returnType
== null || superReturnType
== null || method
== superMethod
) continue;
122 PsiClass superClass
= superMethod
.getContainingClass();
123 if (superClass
== null) continue;
124 HighlightInfo highlightInfo
= checkSuperMethodSignature(superMethod
, superMethodSignature
, superReturnType
, method
, methodSignature
,
125 returnType
, includeRealPositionInfo
, JavaErrorMessages
.message("incompatible.return.type"), method
);
126 if (highlightInfo
!= null) return highlightInfo
;
132 private static HighlightInfo
checkSuperMethodSignature(PsiMethod superMethod
,
133 MethodSignatureBackedByPsiMethod superMethodSignature
,
134 PsiType superReturnType
,
136 MethodSignatureBackedByPsiMethod methodSignature
,
138 boolean includeRealPositionInfo
,
139 String detailMessage
,
140 PsiMethod methodToHighlight
) {
141 if (superReturnType
== null) return null;
142 PsiType substitutedSuperReturnType
;
143 final boolean isJdk15
= PsiUtil
.isLanguageLevel5OrHigher(method
);
144 if (isJdk15
&& !superMethodSignature
.isRaw() && superMethodSignature
.equals(methodSignature
)) { //see 8.4.5
145 PsiSubstitutor unifyingSubstitutor
= MethodSignatureUtil
.getSuperMethodSignatureSubstitutor(methodSignature
,
146 superMethodSignature
);
147 substitutedSuperReturnType
= unifyingSubstitutor
== null
149 : unifyingSubstitutor
.substitute(superMethodSignature
.getSubstitutor().substitute(superReturnType
));
152 substitutedSuperReturnType
= TypeConversionUtil
.erasure(superReturnType
);
155 if (returnType
.equals(substitutedSuperReturnType
)) return null;
156 if (!(returnType
instanceof PsiPrimitiveType
) && substitutedSuperReturnType
.getDeepComponentType() instanceof PsiClassType
) {
157 if (isJdk15
&& TypeConversionUtil
.isAssignable(substitutedSuperReturnType
, returnType
)) {
162 return createIncompatibleReturnTypeMessage(methodToHighlight
, method
, superMethod
, includeRealPositionInfo
,
163 substitutedSuperReturnType
, returnType
, detailMessage
);
166 private static HighlightInfo
createIncompatibleReturnTypeMessage(PsiMethod methodToHighlight
,
168 PsiMethod superMethod
,
169 boolean includeRealPositionInfo
,
170 PsiType substitutedSuperReturnType
,
172 String detailMessage
) {
173 String message
= MessageFormat
.format("{0}; {1}", createClashMethodMessage(method
, superMethod
, true), detailMessage
);
174 TextRange textRange
= includeRealPositionInfo ? methodToHighlight
.getReturnTypeElement().getTextRange() : new TextRange(0, 0);
175 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
176 IntentionAction fix
= QUICK_FIX_FACTORY
.createMethodReturnFix(method
, substitutedSuperReturnType
, false);
177 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
178 QuickFixAction
.registerQuickFixAction(errorResult
, new SuperMethodReturnFix(superMethod
, returnType
));
184 static HighlightInfo
checkMethodOverridesFinal(MethodSignatureBackedByPsiMethod methodSignature
,
185 List
<HierarchicalMethodSignature
> superMethodSignatures
) {
186 PsiMethod method
= methodSignature
.getMethod();
187 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
188 PsiMethod superMethod
= superMethodSignature
.getMethod();
189 // strange things happen when super method is from Object and method from interface
190 if (superMethod
.hasModifierProperty(PsiModifier
.FINAL
)) {
191 String message
= JavaErrorMessages
.message("final.method.override",
192 HighlightUtil
.formatMethod(method
),
193 HighlightUtil
.formatMethod(superMethod
),
194 HighlightUtil
.formatClass(superMethod
.getContainingClass()));
195 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
196 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
199 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(superMethod
, PsiModifier
.FINAL
, false, true);
200 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
208 public static HighlightInfo
checkMethodIncompatibleThrows(MethodSignatureBackedByPsiMethod methodSignature
,
209 List
<HierarchicalMethodSignature
> superMethodSignatures
,
210 boolean includeRealPositionInfo
, PsiClass analyzedClass
) {
211 PsiMethod method
= methodSignature
.getMethod();
212 PsiClass aClass
= method
.getContainingClass();
213 if (aClass
== null) return null;
214 PsiSubstitutor superSubstitutor
= TypeConversionUtil
.getSuperClassSubstitutor(aClass
, analyzedClass
, PsiSubstitutor
.EMPTY
);
215 PsiClassType
[] exceptions
= method
.getThrowsList().getReferencedTypes();
216 PsiJavaCodeReferenceElement
[] referenceElements
;
217 List
<PsiElement
> exceptionContexts
;
218 if (includeRealPositionInfo
) {
219 exceptionContexts
= new ArrayList
<PsiElement
>();
220 referenceElements
= method
.getThrowsList().getReferenceElements();
223 exceptionContexts
= null;
224 referenceElements
= null;
226 List
<PsiClassType
> checkedExceptions
= new ArrayList
<PsiClassType
>();
227 for (int i
= 0; i
< exceptions
.length
; i
++) {
228 PsiClassType exception
= exceptions
[i
];
229 if (!ExceptionUtil
.isUncheckedException(exception
)) {
230 checkedExceptions
.add(exception
);
231 if (includeRealPositionInfo
&& i
< referenceElements
.length
) {
232 PsiJavaCodeReferenceElement exceptionRef
= referenceElements
[i
];
233 exceptionContexts
.add(exceptionRef
);
237 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
238 PsiMethod superMethod
= superMethodSignature
.getMethod();
239 int index
= getExtraExceptionNum(methodSignature
, superMethodSignature
, checkedExceptions
, superSubstitutor
);
241 PsiClassType exception
= checkedExceptions
.get(index
);
242 String message
= JavaErrorMessages
.message("overridden.method.does.not.throw",
243 createClashMethodMessage(method
, superMethod
, true),
244 HighlightUtil
.formatType(exception
));
246 if (includeRealPositionInfo
) {
247 PsiElement exceptionContext
= exceptionContexts
.get(index
);
248 textRange
= exceptionContext
.getTextRange();
251 textRange
= new TextRange(0, 0);
253 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
254 QuickFixAction
.registerQuickFixAction(errorResult
, QUICK_FIX_FACTORY
.createMethodThrowsFix(method
, exception
, false, false));
255 QuickFixAction
.registerQuickFixAction(errorResult
, QUICK_FIX_FACTORY
.createMethodThrowsFix(superMethod
, exception
, true, true));
262 // return number of exception which was not declared in super method or -1
263 private static int getExtraExceptionNum(final MethodSignature methodSignature
,
264 final MethodSignatureBackedByPsiMethod superSignature
,
265 List
<PsiClassType
> checkedExceptions
, PsiSubstitutor substitutorForDerivedClass
) {
266 PsiMethod superMethod
= superSignature
.getMethod();
267 PsiSubstitutor substitutorForMethod
= MethodSignatureUtil
.getSuperMethodSignatureSubstitutor(methodSignature
, superSignature
);
268 if (substitutorForMethod
== null) return -1;
269 for (int i
= 0; i
< checkedExceptions
.size(); i
++) {
270 PsiType exception
= substitutorForDerivedClass
.substitute(substitutorForMethod
.substitute(checkedExceptions
.get(i
)));
271 if (!isMethodThrows(superMethod
, substitutorForMethod
, exception
, substitutorForDerivedClass
)) {
278 private static boolean isMethodThrows(PsiMethod method
, PsiSubstitutor substitutorForMethod
, PsiType exception
, PsiSubstitutor substitutorForDerivedClass
) {
279 PsiClassType
[] thrownExceptions
= method
.getThrowsList().getReferencedTypes();
280 for (PsiClassType thrownException1
: thrownExceptions
) {
281 PsiType thrownException
= substitutorForMethod
.substitute(thrownException1
);
282 thrownException
= substitutorForDerivedClass
.substitute(thrownException
);
283 if (TypeConversionUtil
.isAssignable(thrownException
, exception
)) return true;
289 public static List
<HighlightInfo
> checkMethodCall(PsiMethodCallExpression methodCall
, PsiResolveHelper resolveHelper
) {
290 final List
<HighlightInfo
> result
= new ArrayList
<HighlightInfo
>();
292 PsiExpressionList list
= methodCall
.getArgumentList();
293 PsiReferenceExpression referenceToMethod
= methodCall
.getMethodExpression();
294 JavaResolveResult resolveResult
= referenceToMethod
.advancedResolve(true);
295 PsiElement element
= resolveResult
.getElement();
297 final CandidateInfo
[] candidates
= resolveHelper
.getReferencedMethodCandidates(methodCall
, true);
299 boolean isDummy
= false;
300 boolean isThisOrSuper
= referenceToMethod
.getReferenceNameElement() instanceof PsiKeyword
;
302 // super(..) or this(..)
303 if (list
.getExpressions().length
== 0) { // implicit ctr call
304 if (candidates
.length
== 1 && !candidates
[0].getElement().isPhysical()) {
305 isDummy
= true;// dummy constructor
309 if (isDummy
) return null;
310 HighlightInfo highlightInfo
;
312 if (element
instanceof PsiMethod
&& resolveResult
.isValidResult()) {
313 TextRange fixRange
= getFixRange(methodCall
);
314 highlightInfo
= HighlightUtil
.checkUnhandledExceptions(methodCall
, fixRange
);
316 if (highlightInfo
== null) {
317 highlightInfo
= GenericsHighlightUtil
.checkUncheckedCall(resolveResult
, methodCall
);
319 if (highlightInfo
== null) {
320 highlightInfo
= GenericsHighlightUtil
.checkGenericCallWithRawArguments(resolveResult
, methodCall
);
324 PsiMethod resolvedMethod
= null;
325 MethodCandidateInfo candidateInfo
= null;
326 if (resolveResult
instanceof MethodCandidateInfo
) {
327 candidateInfo
= (MethodCandidateInfo
)resolveResult
;
328 resolvedMethod
= candidateInfo
.getElement();
331 ChangeStringLiteralToCharInMethodCallFix
.createHighLighting(candidates
, methodCall
, result
);
333 if (!resolveResult
.isAccessible() || !resolveResult
.isStaticsScopeCorrect()) {
334 highlightInfo
= checkAmbiguousMethodCall(referenceToMethod
, list
, element
, resolveResult
, methodCall
, resolveHelper
);
336 else if (candidateInfo
!= null && !candidateInfo
.isApplicable()) {
337 if (candidateInfo
.isTypeArgumentsApplicable()) {
338 String methodName
= HighlightMessageUtil
.getSymbolName(element
, resolveResult
.getSubstitutor());
339 PsiElement parent
= element
.getParent();
340 String containerName
= parent
== null ?
"" : HighlightMessageUtil
.getSymbolName(parent
, resolveResult
.getSubstitutor());
341 String argTypes
= buildArgTypesList(list
);
342 String description
= JavaErrorMessages
.message("wrong.method.arguments", methodName
, containerName
, argTypes
);
343 String toolTip
= parent
instanceof PsiClass ?
344 createMismatchedArgumentsHtmlTooltip(candidateInfo
, list
) : description
;
345 highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, description
, toolTip
);
346 registerMethodCallIntentions(highlightInfo
, methodCall
, list
, resolveHelper
);
347 highlightInfo
.navigationShift
= +1;
350 PsiReferenceExpression methodExpression
= methodCall
.getMethodExpression();
351 PsiReferenceParameterList typeArgumentList
= methodCall
.getTypeArgumentList();
352 if (typeArgumentList
.getTypeArguments().length
== 0 && resolvedMethod
.hasTypeParameters()) {
353 highlightInfo
= GenericsHighlightUtil
.checkInferredTypeArguments(resolvedMethod
, methodCall
, resolveResult
.getSubstitutor());
356 highlightInfo
= GenericsHighlightUtil
.checkParameterizedReferenceTypeArguments(element
, methodExpression
,
357 resolveResult
.getSubstitutor());
362 highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, methodCall
, JavaErrorMessages
.message("method.call.expected"));
363 if (element
instanceof PsiClass
) {
364 QuickFixAction
.registerQuickFixAction(highlightInfo
, new InsertNewFix(methodCall
, (PsiClass
)element
));
367 TextRange range
= getFixRange(methodCall
);
368 QuickFixAction
.registerQuickFixAction(highlightInfo
, range
, new CreateMethodFromUsageFix(methodCall
), null);
369 QuickFixAction
.registerQuickFixAction(highlightInfo
, range
, new CreateAbstractMethodFromUsageFix(methodCall
), null);
370 QuickFixAction
.registerQuickFixAction(highlightInfo
, range
, new CreatePropertyFromUsageFix(methodCall
), null);
374 if (highlightInfo
== null) {
376 GenericsHighlightUtil
.checkParameterizedReferenceTypeArguments(element
, referenceToMethod
, resolveResult
.getSubstitutor());
378 result
.add(highlightInfo
);
382 private static HighlightInfo
checkAmbiguousMethodCall(final PsiReferenceExpression referenceToMethod
,
383 final PsiExpressionList list
,
384 final PsiElement element
,
385 final JavaResolveResult resolveResult
,
386 final PsiMethodCallExpression methodCall
, final PsiResolveHelper resolveHelper
) {
387 JavaResolveResult
[] resolveResults
= referenceToMethod
.multiResolve(true);
388 MethodCandidateInfo methodCandidate1
= null;
389 MethodCandidateInfo methodCandidate2
= null;
390 for (JavaResolveResult result
: resolveResults
) {
391 if (!(result
instanceof MethodCandidateInfo
)) continue;
392 MethodCandidateInfo candidate
= (MethodCandidateInfo
)result
;
393 if (candidate
.isApplicable() && !candidate
.getElement().isConstructor()) {
394 if (methodCandidate1
== null) {
395 methodCandidate1
= candidate
;
398 methodCandidate2
= candidate
;
403 MethodCandidateInfo
[] candidates
= toMethodCandidates(resolveResults
);
407 PsiElement elementToHighlight
;
408 HighlightInfoType highlightInfoType
= HighlightInfoType
.ERROR
;
409 if (methodCandidate2
!= null) {
410 String m1
= PsiFormatUtil
.formatMethod(methodCandidate1
.getElement(),
411 methodCandidate1
.getSubstitutor(),
412 PsiFormatUtil
.SHOW_CONTAINING_CLASS
| PsiFormatUtil
.SHOW_NAME
|
413 PsiFormatUtil
.SHOW_PARAMETERS
,
414 PsiFormatUtil
.SHOW_TYPE
);
415 String m2
= PsiFormatUtil
.formatMethod(methodCandidate2
.getElement(),
416 methodCandidate2
.getSubstitutor(),
417 PsiFormatUtil
.SHOW_CONTAINING_CLASS
| PsiFormatUtil
.SHOW_NAME
|
418 PsiFormatUtil
.SHOW_PARAMETERS
,
419 PsiFormatUtil
.SHOW_TYPE
);
420 description
= JavaErrorMessages
.message("ambiguous.method.call", m1
, m2
);
421 toolTip
= createAmbiguousMethodHtmlTooltip(new MethodCandidateInfo
[]{methodCandidate1
, methodCandidate2
});
422 elementToHighlight
= list
;
425 if (element
!= null && !resolveResult
.isAccessible()) {
426 description
= HighlightUtil
.buildProblemWithAccessDescription(referenceToMethod
, resolveResult
);
427 elementToHighlight
= referenceToMethod
.getReferenceNameElement();
429 else if (element
!= null && !resolveResult
.isStaticsScopeCorrect()) {
430 description
= HighlightUtil
.buildProblemWithStaticDescription(element
);
431 elementToHighlight
= referenceToMethod
.getReferenceNameElement();
434 String methodName
= referenceToMethod
.getReferenceName() + buildArgTypesList(list
);
435 description
= JavaErrorMessages
.message("cannot.resolve.method", methodName
);
436 if (candidates
.length
== 0) {
437 elementToHighlight
= referenceToMethod
.getReferenceNameElement();
438 highlightInfoType
= HighlightInfoType
.WRONG_REF
;
441 elementToHighlight
= list
;
444 toolTip
= description
;
446 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(highlightInfoType
, elementToHighlight
, description
, toolTip
);
447 if (methodCandidate2
== null) {
448 registerMethodCallIntentions(highlightInfo
, methodCall
, list
, resolveHelper
);
450 if (!resolveResult
.isAccessible() && resolveResult
.isStaticsScopeCorrect() && methodCandidate2
!= null) {
451 HighlightUtil
.registerAccessQuickFixAction((PsiMember
)element
, referenceToMethod
, highlightInfo
, resolveResult
.getCurrentFileResolveScope());
453 if (element
!= null && !resolveResult
.isStaticsScopeCorrect()) {
454 HighlightUtil
.registerStaticProblemQuickFixAction(element
, highlightInfo
, referenceToMethod
);
457 TextRange fixRange
= getFixRange(elementToHighlight
);
458 CastMethodArgumentFix
.REGISTRAR
.registerCastActions(candidates
, methodCall
, highlightInfo
, fixRange
);
459 PermuteArgumentsFix
.registerFix(highlightInfo
, methodCall
, candidates
, fixRange
);
460 WrapExpressionFix
.registerWrapAction(candidates
, list
.getExpressions(), highlightInfo
);
461 ChangeParameterClassFix
.registerQuickFixActions(methodCall
, list
, highlightInfo
);
462 return highlightInfo
;
465 private static MethodCandidateInfo
[] toMethodCandidates(JavaResolveResult
[] resolveResults
) {
466 List
<MethodCandidateInfo
> candidateList
= new ArrayList
<MethodCandidateInfo
>();
468 for (JavaResolveResult result
: resolveResults
) {
469 if (!(result
instanceof MethodCandidateInfo
)) continue;
470 MethodCandidateInfo candidate
= (MethodCandidateInfo
)result
;
471 if (candidate
.isAccessible()) candidateList
.add(candidate
);
473 return candidateList
.toArray(new MethodCandidateInfo
[candidateList
.size()]);
476 private static void registerMethodCallIntentions(HighlightInfo highlightInfo
,
477 PsiMethodCallExpression methodCall
,
478 PsiExpressionList list
, PsiResolveHelper resolveHelper
) {
479 TextRange fixRange
= getFixRange(methodCall
);
480 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateMethodFromUsageFix(methodCall
), null);
481 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateAbstractMethodFromUsageFix(methodCall
), null);
482 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateConstructorFromSuperFix(methodCall
), null);
483 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateConstructorFromThisFix(methodCall
), null);
484 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreatePropertyFromUsageFix(methodCall
), null);
485 CandidateInfo
[] methodCandidates
= resolveHelper
.getReferencedMethodCandidates(methodCall
, false);
486 CastMethodArgumentFix
.REGISTRAR
.registerCastActions(methodCandidates
, methodCall
, highlightInfo
, fixRange
);
487 PermuteArgumentsFix
.registerFix(highlightInfo
, methodCall
, methodCandidates
, fixRange
);
488 AddTypeArgumentsFix
.REGISTRAR
.registerCastActions(methodCandidates
, methodCall
, highlightInfo
, fixRange
);
489 registerMethodAccessLevelIntentions(methodCandidates
, methodCall
, list
, highlightInfo
);
490 ChangeMethodSignatureFromUsageFix
.registerIntentions(methodCandidates
, list
, highlightInfo
, fixRange
);
491 WrapExpressionFix
.registerWrapAction(methodCandidates
, list
.getExpressions(), highlightInfo
);
492 ChangeParameterClassFix
.registerQuickFixActions(methodCall
, list
, highlightInfo
);
493 if (methodCandidates
.length
== 0) {
494 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new StaticImportMethodFix(methodCall
), null);
496 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new ReplaceAddAllArrayToCollectionFix(methodCall
), null);
497 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new SurroundWithArrayFix(methodCall
), null);
500 private static void registerMethodAccessLevelIntentions(CandidateInfo
[] methodCandidates
,
501 PsiMethodCallExpression methodCall
,
502 PsiExpressionList exprList
,
503 HighlightInfo highlightInfo
) {
504 for (CandidateInfo methodCandidate
: methodCandidates
) {
505 PsiMethod method
= (PsiMethod
)methodCandidate
.getElement();
506 if (!methodCandidate
.isAccessible() && PsiUtil
.isApplicable(method
, methodCandidate
.getSubstitutor(), exprList
)) {
507 HighlightUtil
.registerAccessQuickFixAction(method
, methodCall
.getMethodExpression(), highlightInfo
, methodCandidate
.getCurrentFileResolveScope());
512 private static String
createAmbiguousMethodHtmlTooltip(MethodCandidateInfo
[] methodCandidates
) {
513 return JavaErrorMessages
.message("ambiguous.method.html.tooltip",
514 Integer
.valueOf(methodCandidates
[0].getElement().getParameterList().getParametersCount() + 2),
515 createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates
[0]),
516 getContainingClassName(methodCandidates
[0]),
517 createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates
[1]),
518 getContainingClassName(methodCandidates
[1]));
521 private static String
getContainingClassName(final MethodCandidateInfo methodCandidate
) {
522 PsiMethod method
= methodCandidate
.getElement();
523 PsiClass containingClass
= method
.getContainingClass();
524 return containingClass
== null ? method
.getContainingFile().getName() : HighlightUtil
.formatClass(containingClass
, false);
527 private static String
createAmbiguousMethodHtmlTooltipMethodRow(final MethodCandidateInfo methodCandidate
) {
528 PsiMethod method
= methodCandidate
.getElement();
529 PsiParameter
[] parameters
= method
.getParameterList().getParameters();
530 PsiSubstitutor substitutor
= methodCandidate
.getSubstitutor();
531 @NonNls String ms
= "<td><b>" + method
.getName() + "</b></td>";
533 for (int j
= 0; j
< parameters
.length
; j
++) {
534 PsiParameter parameter
= parameters
[j
];
535 PsiType type
= substitutor
.substitute(parameter
.getType());
536 ms
+= "<td><b>" + (j
== 0 ?
"(" : "") +
537 XmlStringUtil
.escapeString(type
.getPresentableText())
538 + (j
== parameters
.length
- 1 ?
")" : ",") + "</b></td>";
540 if (parameters
.length
== 0) {
541 ms
+= "<td><b>()</b></td>";
546 private static String
createMismatchedArgumentsHtmlTooltip(MethodCandidateInfo info
, PsiExpressionList list
) {
547 PsiMethod method
= info
.getElement();
548 PsiSubstitutor substitutor
= info
.getSubstitutor();
549 PsiClass aClass
= method
.getContainingClass();
550 PsiParameter
[] parameters
= method
.getParameterList().getParameters();
551 String methodName
= method
.getName();
552 return createMismatchedArgumentsHtmlTooltip(list
, parameters
, methodName
, substitutor
, aClass
);
555 private static String
createMismatchedArgumentsHtmlTooltip(PsiExpressionList list
,
556 PsiParameter
[] parameters
,
558 PsiSubstitutor substitutor
,
560 PsiExpression
[] expressions
= list
.getExpressions();
561 int cols
= Math
.max(parameters
.length
, expressions
.length
);
563 @NonNls String parensizedName
= methodName
+ (parameters
.length
== 0 ?
"( ) " : "");
564 return JavaErrorMessages
.message(
565 "argument.mismatch.html.tooltip",
566 Integer
.valueOf(cols
- parameters
.length
+ 1), parensizedName
,
567 HighlightUtil
.formatClass(aClass
, false),
568 createMismatchedArgsHtmlTooltipParamsRow(parameters
, substitutor
, expressions
),
569 createMismatchedArgsHtmlTooltipExpressionsRow(expressions
, parameters
, substitutor
, cols
)
573 private static String
createMismatchedArgsHtmlTooltipExpressionsRow(final PsiExpression
[] expressions
, final PsiParameter
[] parameters
,
574 final PsiSubstitutor substitutor
, final int cols
) {
575 @NonNls String ms
= "";
576 for (int i
= 0; i
< expressions
.length
; i
++) {
577 PsiExpression expression
= expressions
[i
];
578 PsiType type
= expression
.getType();
580 @NonNls String mismatchColor
= showShortType(i
, parameters
, expressions
, substitutor
) ?
null : "red";
581 ms
+= "<td> " + "<b><nobr>" + (i
== 0 ?
"(" : "")
582 + "<font " + (mismatchColor
== null ?
"" : "color=" + mismatchColor
) + ">" +
583 XmlStringUtil
.escapeString(showShortType(i
, parameters
, expressions
, substitutor
)
584 ? type
.getPresentableText()
585 : HighlightUtil
.formatType(type
))
587 + (i
== expressions
.length
- 1 ?
")" : ",") + "</nobr></b></td>";
589 for (int i
= expressions
.length
; i
< cols
+ 1; i
++) {
590 ms
+= "<td>" + (i
== 0 ?
"<b>()</b>" : "") +
596 private static String
createMismatchedArgsHtmlTooltipParamsRow(final PsiParameter
[] parameters
,
597 final PsiSubstitutor substitutor
,
598 final PsiExpression
[] expressions
) {
599 @NonNls String ms
= "";
600 for (int i
= 0; i
< parameters
.length
; i
++) {
601 PsiParameter parameter
= parameters
[i
];
602 PsiType type
= substitutor
.substitute(parameter
.getType());
603 ms
+= "<td><b><nobr>" + (i
== 0 ?
"(" : "") +
604 XmlStringUtil
.escapeString(showShortType(i
, parameters
, expressions
, substitutor
)
605 ? type
.getPresentableText()
606 : HighlightUtil
.formatType(type
))
607 + (i
== parameters
.length
- 1 ?
")" : ",") + "</nobr></b></td>";
612 private static boolean showShortType(int i
,
613 PsiParameter
[] parameters
,
614 PsiExpression
[] expressions
,
615 PsiSubstitutor substitutor
) {
616 PsiExpression expression
= i
< expressions
.length ? expressions
[i
] : null;
617 if (expression
== null) return true;
618 PsiType paramType
= i
< parameters
.length
&& parameters
[i
] != null
619 ? substitutor
.substitute(parameters
[i
].getType())
621 return paramType
!= null && TypeConversionUtil
.areTypesAssignmentCompatible(paramType
, expression
);
625 static HighlightInfo
checkMethodMustHaveBody(PsiMethod method
, PsiClass aClass
) {
626 HighlightInfo errorResult
= null;
627 if (method
.getBody() == null
628 && !method
.hasModifierProperty(PsiModifier
.ABSTRACT
)
629 && !method
.hasModifierProperty(PsiModifier
.NATIVE
)
631 && !aClass
.isInterface()
632 && !PsiUtil
.hasErrorElementChild(method
)) {
633 int start
= method
.getModifierList().getTextRange().getStartOffset();
634 int end
= method
.getTextRange().getEndOffset();
636 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
638 JavaErrorMessages
.message("missing.method.body"));
639 if (HighlightUtil
.getIncompatibleModifier(PsiModifier
.ABSTRACT
, method
.getModifierList()) == null) {
640 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.ABSTRACT
, true, false);
641 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
643 QuickFixAction
.registerQuickFixAction(errorResult
, new AddMethodBodyFix(method
));
649 static HighlightInfo
checkAbstractMethodInConcreteClass(PsiMethod method
, PsiElement elementToHighlight
) {
650 HighlightInfo errorResult
= null;
651 PsiClass aClass
= method
.getContainingClass();
652 if (method
.hasModifierProperty(PsiModifier
.ABSTRACT
)
654 && !aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)
656 && !PsiUtil
.hasErrorElementChild(method
)) {
657 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
659 JavaErrorMessages
.message("abstract.method.in.non.abstract.class"));
660 if (method
.getBody() != null) {
661 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.ABSTRACT
, false, false);
662 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
664 QuickFixAction
.registerQuickFixAction(errorResult
, new AddMethodBodyFix(method
));
665 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.ABSTRACT
, true, false);
666 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
672 static HighlightInfo
checkConstructorName(PsiMethod method
) {
673 String methodName
= method
.getName();
674 PsiClass aClass
= method
.getContainingClass();
675 HighlightInfo errorResult
= null;
677 if (aClass
!= null) {
678 String className
= aClass
instanceof PsiAnonymousClass ?
null : aClass
.getName();
679 if (!Comparing
.strEqual(methodName
, className
)) {
680 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, method
.getNameIdentifier(),
681 JavaErrorMessages
.message("missing.return.type"));
688 static HighlightInfo
checkDuplicateMethod(PsiClass aClass
, PsiMethod method
) {
689 if (aClass
== null) return null;
690 MethodSignature methodSignature
= method
.getSignature(PsiSubstitutor
.EMPTY
);
692 final PsiMethod
[] methodsByName
= aClass
.findMethodsByName(method
.getName(), false);
693 for (PsiMethod other
: methodsByName
) {
694 if (other
== method
||
695 other
.isConstructor() == method
.isConstructor() && other
.getSignature(PsiSubstitutor
.EMPTY
).equals(methodSignature
)) {
697 if (methodCount
> 1) break;
701 if (methodCount
== 1 && aClass
.isEnum() &&
702 GenericsHighlightUtil
.isEnumSyntheticMethod(methodSignature
, aClass
.getProject())) {
705 if (methodCount
> 1) {
706 String message
= JavaErrorMessages
.message("duplicate.method",
707 HighlightUtil
.formatMethod(method
),
708 HighlightUtil
.formatClass(aClass
));
709 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
710 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
716 public static HighlightInfo
checkMethodCanHaveBody(PsiMethod method
) {
717 if (method
.getBody() == null) return null;
718 PsiClass aClass
= method
.getContainingClass();
720 String message
= null;
721 if (aClass
!= null && aClass
.isInterface()) {
722 message
= JavaErrorMessages
.message("interface.methods.cannot.have.body");
724 else if (method
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
725 message
= JavaErrorMessages
.message("abstract.methods.cannot.have.a.body");
727 else if (method
.hasModifierProperty(PsiModifier
.NATIVE
)) {
728 message
= JavaErrorMessages
.message("native.methods.cannot.have.a.body");
731 if (message
!= null) {
732 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
733 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
734 QuickFixAction
.registerQuickFixAction(info
, new DeleteMethodBodyFix(method
));
735 if (method
.hasModifierProperty(PsiModifier
.ABSTRACT
) && aClass
!= null && !aClass
.isInterface()) {
736 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.ABSTRACT
, false, false);
737 QuickFixAction
.registerQuickFixAction(info
, fix
);
745 static HighlightInfo
checkConstructorCallMustBeFirstStatement(PsiReferenceExpression expression
) {
746 String text
= expression
.getText();
747 PsiElement methodCall
= expression
.getParent();
748 if (!HighlightUtil
.isSuperOrThisMethodCall(methodCall
)) return null;
749 PsiElement codeBlock
= methodCall
.getParent().getParent();
750 if (codeBlock
instanceof PsiCodeBlock
751 && codeBlock
.getParent() instanceof PsiMethod
752 && ((PsiMethod
)codeBlock
.getParent()).isConstructor()) {
753 PsiElement prevSibling
= methodCall
.getParent().getPrevSibling();
755 if (prevSibling
== null) return null;
756 if (prevSibling
instanceof PsiStatement
) break;
757 prevSibling
= prevSibling
.getPrevSibling();
760 String message
= JavaErrorMessages
.message("constructor.call.must.be.first.statement", text
+ "()");
761 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, expression
.getParent(), message
);
765 public static HighlightInfo
checkAbstractMethodDirectCall(PsiSuperExpression expr
) {
766 if (expr
.getParent() instanceof PsiReferenceExpression
767 && expr
.getParent().getParent() instanceof PsiMethodCallExpression
) {
768 PsiMethodCallExpression methodCallExpression
= (PsiMethodCallExpression
)expr
.getParent().getParent();
769 PsiMethod method
= methodCallExpression
.resolveMethod();
770 if (method
!= null && method
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
771 String message
= JavaErrorMessages
.message("direct.abstract.method.access", HighlightUtil
.formatMethod(method
));
772 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, methodCallExpression
, message
);
779 public static HighlightInfo
checkConstructorCallsBaseClassConstructor(PsiMethod constructor
,
780 RefCountHolder refCountHolder
,
781 PsiResolveHelper resolveHelper
) {
782 if (!constructor
.isConstructor()) return null;
783 PsiClass aClass
= constructor
.getContainingClass();
784 if (aClass
== null) return null;
785 if (aClass
.isEnum()) return null;
786 PsiCodeBlock body
= constructor
.getBody();
787 if (body
== null) return null;
789 // check whether constructor call super(...) or this(...)
790 PsiElement element
= new PsiMatcherImpl(body
)
791 .firstChild(PsiMatchers
.hasClass(PsiExpressionStatement
.class))
792 .firstChild(PsiMatchers
.hasClass(PsiMethodCallExpression
.class))
793 .firstChild(PsiMatchers
.hasClass(PsiReferenceExpression
.class))
794 .firstChild(PsiMatchers
.hasClass(PsiKeyword
.class))
796 if (element
!= null) return null;
797 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(constructor
);
798 PsiClassType
[] handledExceptions
= constructor
.getThrowsList().getReferencedTypes();
799 HighlightInfo info
= HighlightClassUtil
.checkBaseClassDefaultConstructorProblem(aClass
, refCountHolder
, resolveHelper
, textRange
, handledExceptions
);
801 QuickFixAction
.registerQuickFixAction(info
, new InsertSuperFix(constructor
));
802 QuickFixAction
.registerQuickFixAction(info
, QUICK_FIX_FACTORY
.createAddDefaultConstructorFix(aClass
.getSuperClass()));
809 * @return error if static method overrides instance method or
810 * instance method overrides static. see JLS 8.4.6.1, 8.4.6.2
812 public static HighlightInfo
checkStaticMethodOverride(PsiMethod method
) {
813 PsiClass aClass
= method
.getContainingClass();
814 if (aClass
== null) return null;
815 PsiClass superClass
= aClass
.getSuperClass();
816 PsiMethod superMethod
= superClass
== null
818 : MethodSignatureUtil
.findMethodBySignature(superClass
, method
, true);
820 HighlightInfo highlightInfo
= checkStaticMethodOverride(aClass
, method
, superClass
, superMethod
);
821 if (highlightInfo
!= null) return highlightInfo
;
822 PsiClass
[] interfaces
= aClass
.getInterfaces();
823 for (PsiClass aInterfaces
: interfaces
) {
824 superClass
= aInterfaces
;
825 superMethod
= MethodSignatureUtil
.findMethodBySignature(superClass
, method
, true);
826 highlightInfo
= checkStaticMethodOverride(aClass
, method
, superClass
, superMethod
);
827 if (highlightInfo
!= null) return highlightInfo
;
829 return highlightInfo
;
832 private static HighlightInfo
checkStaticMethodOverride(PsiClass aClass
, PsiMethod method
, PsiClass superClass
, PsiMethod superMethod
) {
833 if (superMethod
== null) return null;
834 PsiManager manager
= superMethod
.getManager();
835 PsiModifierList superModifierList
= superMethod
.getModifierList();
836 PsiModifierList modifierList
= method
.getModifierList();
837 if (superModifierList
.hasModifierProperty(PsiModifier
.PRIVATE
)) return null;
838 if (superModifierList
.hasModifierProperty(PsiModifier
.PACKAGE_LOCAL
)
839 && !JavaPsiFacade
.getInstance(manager
.getProject()).arePackagesTheSame(aClass
, superClass
)) {
842 boolean isMethodStatic
= modifierList
.hasModifierProperty(PsiModifier
.STATIC
);
843 boolean isSuperMethodStatic
= superModifierList
.hasModifierProperty(PsiModifier
.STATIC
);
844 if (isMethodStatic
!= isSuperMethodStatic
) {
845 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
846 @NonNls final String messageKey
= isMethodStatic
847 ?
"static.method.cannot.override.instance.method"
848 : "instance.method.cannot.override.static.method";
850 String message
= JavaErrorMessages
.message(messageKey
,
851 HighlightUtil
.formatMethod(method
),
852 HighlightUtil
.formatClass(aClass
),
853 HighlightUtil
.formatMethod(superMethod
),
854 HighlightUtil
.formatClass(superClass
));
856 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
857 if (!isSuperMethodStatic
|| HighlightUtil
.getIncompatibleModifier(PsiModifier
.STATIC
, modifierList
) == null) {
858 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.STATIC
, isSuperMethodStatic
, false);
859 QuickFixAction
.registerQuickFixAction(info
, fix
);
861 if (manager
.isInProject(superMethod
) &&
862 (!isMethodStatic
|| HighlightUtil
.getIncompatibleModifier(PsiModifier
.STATIC
, superModifierList
) == null)) {
863 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(superMethod
, PsiModifier
.STATIC
, isMethodStatic
, true);
864 QuickFixAction
.registerQuickFixAction(info
, fix
);
869 if (isMethodStatic
&& isSuperMethodStatic
) {
870 int accessLevel
= PsiUtil
.getAccessLevel(modifierList
);
871 String accessModifier
= PsiUtil
.getAccessModifier(accessLevel
);
872 HighlightInfo info
= isWeaker(method
, modifierList
, accessModifier
, accessLevel
, superMethod
, true);
873 if (info
!= null) return info
;
878 private static HighlightInfo
checkInterfaceInheritedMethodsReturnTypes(List
<?
extends MethodSignatureBackedByPsiMethod
> superMethodSignatures
) {
879 if (superMethodSignatures
.size() < 2) return null;
880 MethodSignatureBackedByPsiMethod returnTypeSubstitutable
= superMethodSignatures
.get(0);
881 for (int i
= 1; i
< superMethodSignatures
.size(); i
++) {
882 PsiMethod currentMethod
= returnTypeSubstitutable
.getMethod();
883 PsiType currentType
= returnTypeSubstitutable
.getSubstitutor().substitute(currentMethod
.getReturnType());
885 MethodSignatureBackedByPsiMethod otherSuperSignature
= superMethodSignatures
.get(i
);
886 PsiMethod otherSuperMethod
= otherSuperSignature
.getMethod();
887 PsiType otherSuperReturnType
= otherSuperSignature
.getSubstitutor().substitute(otherSuperMethod
.getReturnType());
889 PsiSubstitutor unifyingSubstitutor
= MethodSignatureUtil
.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable
,
890 otherSuperSignature
);
891 if (unifyingSubstitutor
!= null) {
892 otherSuperReturnType
= unifyingSubstitutor
.substitute(otherSuperReturnType
);
893 currentType
= unifyingSubstitutor
.substitute(currentType
);
896 if (otherSuperReturnType
== null || currentType
== null || otherSuperReturnType
.equals(currentType
)) continue;
898 if (PsiUtil
.isLanguageLevel5OrHigher(currentMethod
)) {
899 if (otherSuperReturnType
.isAssignableFrom(currentType
)) continue;
900 if (currentType
.isAssignableFrom(otherSuperReturnType
)) {
901 returnTypeSubstitutable
= otherSuperSignature
;
905 return createIncompatibleReturnTypeMessage(currentMethod
, currentMethod
, otherSuperMethod
, false, otherSuperReturnType
,
906 currentType
, JavaErrorMessages
.message("unrelated.overriding.methods.return.types"));
911 public static HighlightInfo
checkOverrideEquivalentInheritedMethods(PsiClass aClass
) {
912 String errorDescription
= null;
913 final Collection
<HierarchicalMethodSignature
> visibleSignatures
= aClass
.getVisibleSignatures();
914 PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(aClass
.getProject()).getResolveHelper();
916 for (HierarchicalMethodSignature signature
: visibleSignatures
) {
917 PsiMethod method
= signature
.getMethod();
918 if (!resolveHelper
.isAccessible(method
, aClass
, null)) continue;
919 List
<HierarchicalMethodSignature
> superSignatures
= signature
.getSuperSignatures();
921 boolean allAbstracts
= method
.hasModifierProperty(PsiModifier
.ABSTRACT
);
922 final PsiClass containingClass
= method
.getContainingClass();
923 if (aClass
.equals(containingClass
)) continue; //to be checked at method level
925 if (aClass
.isInterface() && !containingClass
.isInterface()) continue;
926 HighlightInfo highlightInfo
= null;
928 if (!containingClass
.equals(aClass
)) {
929 superSignatures
= new ArrayList
<HierarchicalMethodSignature
>(superSignatures
);
930 superSignatures
.add(signature
);
932 highlightInfo
= checkInterfaceInheritedMethodsReturnTypes(superSignatures
);
935 if (!aClass
.equals(containingClass
)) {
936 highlightInfo
= checkMethodIncompatibleReturnType(signature
, superSignatures
, false);
939 if (highlightInfo
!= null) errorDescription
= highlightInfo
.description
;
941 if (method
.hasModifierProperty(PsiModifier
.STATIC
)) {
942 for (HierarchicalMethodSignature superSignature
: superSignatures
) {
943 PsiMethod superMethod
= superSignature
.getMethod();
944 if (!superMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
945 errorDescription
= JavaErrorMessages
.message("static.method.cannot.override.instance.method",
946 HighlightUtil
.formatMethod(method
),
947 HighlightUtil
.formatClass(containingClass
),
948 HighlightUtil
.formatMethod(superMethod
),
949 HighlightUtil
.formatClass(superMethod
.getContainingClass()));
956 if (errorDescription
== null) {
957 highlightInfo
= checkMethodIncompatibleThrows(signature
, superSignatures
, false, aClass
);
958 if (highlightInfo
!= null) errorDescription
= highlightInfo
.description
;
961 if (errorDescription
== null) {
962 highlightInfo
= checkMethodWeakerPrivileges(signature
, superSignatures
, false);
963 if (highlightInfo
!= null) errorDescription
= highlightInfo
.description
;
966 if (errorDescription
!= null) break;
970 if (errorDescription
!= null) {
971 // show error info at the class level
972 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
973 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
981 public static HighlightInfo
checkConstructorHandleSuperClassExceptions(PsiMethod method
) {
982 if (!method
.isConstructor()) {
985 PsiCodeBlock body
= method
.getBody();
986 PsiStatement
[] statements
= body
== null ?
null : body
.getStatements();
987 if (statements
== null) return null;
989 // if we have unhandled exception inside method body, we could not have been called here,
990 // so the only problem it can catch here is with super ctr only
991 Collection
<PsiClassType
> unhandled
= ExceptionUtil
.collectUnhandledExceptions(method
, method
.getContainingClass());
992 if (unhandled
.isEmpty()) return null;
993 String description
= HighlightUtil
.getUnhandledExceptionsDescriptor(unhandled
);
994 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
995 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
998 for (PsiClassType exception
: unhandled
) {
999 QuickFixAction
.registerQuickFixAction(highlightInfo
, QUICK_FIX_FACTORY
.createMethodThrowsFix(method
, exception
, true, false));
1001 return highlightInfo
;
1005 public static HighlightInfo
checkRecursiveConstructorInvocation(PsiMethod method
) {
1006 if (HighlightControlFlowUtil
.isRecursivelyCalledConstructor(method
)) {
1007 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
1008 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, JavaErrorMessages
.message("recursive.constructor.invocation"));
1013 public static TextRange
getFixRange(PsiElement element
) {
1014 TextRange range
= element
.getTextRange();
1015 int start
= range
.getStartOffset();
1016 int end
= range
.getEndOffset();
1018 PsiElement nextSibling
= element
.getNextSibling();
1019 if (nextSibling
instanceof PsiJavaToken
&& ((PsiJavaToken
)nextSibling
).getTokenType() == JavaTokenType
.SEMICOLON
) {
1020 return new TextRange(start
, end
+ 1);
1026 static void checkNewExpression(@NotNull PsiNewExpression expression
, @NotNull HighlightInfoHolder holder
) {
1027 PsiType type
= expression
.getType();
1028 if (!(type
instanceof PsiClassType
)) return;
1029 PsiClassType
.ClassResolveResult typeResult
= ((PsiClassType
)type
).resolveGenerics();
1030 PsiClass aClass
= typeResult
.getElement();
1031 if (aClass
== null) return;
1032 if (aClass
instanceof PsiAnonymousClass
) {
1033 type
= ((PsiAnonymousClass
)aClass
).getBaseClassType();
1034 typeResult
= ((PsiClassType
)type
).resolveGenerics();
1035 aClass
= typeResult
.getElement();
1036 if (aClass
== null) return;
1039 PsiJavaCodeReferenceElement classReference
= expression
.getClassOrAnonymousClassReference();
1040 checkConstructorCall(typeResult
, expression
, type
, classReference
, holder
);
1044 public static void checkConstructorCall(PsiClassType
.ClassResolveResult typeResolveResult
,
1045 PsiConstructorCall constructorCall
,
1047 PsiJavaCodeReferenceElement classReference
,
1048 final HighlightInfoHolder holder
) {
1049 PsiExpressionList list
= constructorCall
.getArgumentList();
1050 if (list
== null) return;
1051 PsiClass aClass
= typeResolveResult
.getElement();
1052 if (aClass
== null) return;
1053 final PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(constructorCall
.getProject()).getResolveHelper();
1054 PsiClass accessObjectClass
= null;
1055 if (constructorCall
instanceof PsiNewExpression
) {
1056 PsiExpression qualifier
= ((PsiNewExpression
)constructorCall
).getQualifier();
1057 if (qualifier
!= null) {
1058 accessObjectClass
= (PsiClass
)PsiUtil
.getAccessObjectClass(qualifier
).getElement();
1061 if (classReference
!= null && !resolveHelper
.isAccessible(aClass
, constructorCall
, accessObjectClass
)) {
1062 String description
= HighlightUtil
.buildProblemWithAccessDescription(classReference
, typeResolveResult
);
1063 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, classReference
.getReferenceNameElement(), description
);
1064 HighlightUtil
.registerAccessQuickFixAction(aClass
, classReference
, info
, null);
1068 PsiMethod
[] constructors
= aClass
.getConstructors();
1070 if (constructors
.length
== 0) {
1071 if (list
.getExpressions().length
!= 0) {
1072 String constructorName
= aClass
.getName();
1073 String argTypes
= buildArgTypesList(list
);
1074 String description
= JavaErrorMessages
.message("wrong.constructor.arguments", constructorName
+"()", argTypes
);
1075 String tooltip
= createMismatchedArgumentsHtmlTooltip(list
, PsiParameter
.EMPTY_ARRAY
, constructorName
, PsiSubstitutor
.EMPTY
, aClass
);
1076 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, description
, tooltip
);
1077 QuickFixAction
.registerQuickFixAction(info
, constructorCall
.getTextRange(), new CreateConstructorFromCallFix(constructorCall
), null);
1078 if (classReference
!= null) {
1079 ConstructorParametersFixer
.registerFixActions(classReference
, constructorCall
, info
,getFixRange(list
));
1081 info
.navigationShift
= +1;
1085 if (classReference
!= null && aClass
.hasModifierProperty(PsiModifier
.PROTECTED
) && callingProtectedConstructorFromDerivedClass(constructorCall
, aClass
)) {
1086 holder
.add(buildAccessProblem(classReference
, typeResolveResult
, aClass
));
1090 PsiElement place
= list
;
1091 if (constructorCall
instanceof PsiNewExpression
) {
1092 final PsiAnonymousClass anonymousClass
= ((PsiNewExpression
)constructorCall
).getAnonymousClass();
1093 if (anonymousClass
!= null) place
= anonymousClass
;
1096 JavaResolveResult
[] results
= resolveHelper
.multiResolveConstructor((PsiClassType
)type
, list
, place
);
1097 MethodCandidateInfo result
= null;
1098 if (results
.length
== 1) result
= (MethodCandidateInfo
)results
[0];
1100 PsiMethod constructor
= result
== null ?
null : result
.getElement();
1102 if (constructor
== null || !result
.isApplicable()) {
1103 final List
<HighlightInfo
> resultHighlighting
= new ArrayList
<HighlightInfo
>();
1104 ChangeStringLiteralToCharInMethodCallFix
.createHighLighting(constructors
, constructorCall
, resultHighlighting
);
1105 holder
.addAll(resultHighlighting
);
1108 if (constructor
== null) {
1109 String name
= aClass
.getName();
1110 name
+= buildArgTypesList(list
);
1111 String description
= JavaErrorMessages
.message("cannot.resolve.constructor", name
);
1112 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, description
);
1113 QuickFixAction
.registerQuickFixAction(info
, constructorCall
.getTextRange(), new CreateConstructorFromCallFix(constructorCall
), null);
1114 if (classReference
!= null) {
1115 ConstructorParametersFixer
.registerFixActions(classReference
, constructorCall
, info
,getFixRange(list
));
1116 PermuteArgumentsFix
.registerFix(info
, constructorCall
, toMethodCandidates(results
), getFixRange(list
));
1118 WrapExpressionFix
.registerWrapAction(results
, list
.getExpressions(), info
);
1119 info
.navigationShift
= +1;
1123 if (classReference
!= null && (!result
.isAccessible() ||
1124 constructor
.hasModifierProperty(PsiModifier
.PROTECTED
) && callingProtectedConstructorFromDerivedClass(constructorCall
, aClass
))) {
1125 holder
.add(buildAccessProblem(classReference
, result
, constructor
));
1127 else if (!result
.isApplicable()) {
1128 String constructorName
= HighlightMessageUtil
.getSymbolName(constructor
, result
.getSubstitutor());
1129 String containerName
= HighlightMessageUtil
.getSymbolName(constructor
.getContainingClass(), result
.getSubstitutor());
1130 String argTypes
= buildArgTypesList(list
);
1131 String description
= JavaErrorMessages
.message("wrong.method.arguments", constructorName
, containerName
, argTypes
);
1132 String toolTip
= createMismatchedArgumentsHtmlTooltip(result
, list
);
1133 PsiElement infoElement
= list
.getTextLength() > 0 ? list
: constructorCall
;
1134 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, infoElement
, description
, toolTip
);
1135 QuickFixAction
.registerQuickFixAction(info
, constructorCall
.getTextRange(), new CreateConstructorFromCallFix(constructorCall
), null);
1136 if (classReference
!= null) {
1137 ConstructorParametersFixer
.registerFixActions(classReference
, constructorCall
, info
, getFixRange(infoElement
));
1138 ChangeMethodSignatureFromUsageFix
.registerIntentions(results
, list
, info
, null);
1139 PermuteArgumentsFix
.registerFix(info
, constructorCall
, toMethodCandidates(results
), getFixRange(list
));
1140 QuickFixAction
.registerQuickFixAction(info
, getFixRange(list
), new SurroundWithArrayFix(constructorCall
), null);
1142 info
.navigationShift
= +1;
1146 HighlightInfo highlightInfo
= GenericsHighlightUtil
.checkUncheckedCall(result
, constructorCall
);
1147 if (highlightInfo
!= null) {
1148 holder
.add(highlightInfo
);
1151 if (constructorCall
instanceof PsiNewExpression
) {
1152 highlightInfo
= GenericsHighlightUtil
.checkReferenceTypeArgumentList(constructor
,
1153 ((PsiNewExpression
)constructorCall
).getTypeArgumentList(),
1154 result
.getSubstitutor(), false);
1155 if (highlightInfo
!= null) {
1156 holder
.add(highlightInfo
);
1159 highlightInfo
= GenericsHighlightUtil
.checkGenericCallWithRawArguments(result
, (PsiCallExpression
)constructorCall
);
1161 if (highlightInfo
!= null) {
1162 holder
.add(highlightInfo
);
1169 private static HighlightInfo
buildAccessProblem(@NotNull PsiJavaCodeReferenceElement classReference
, JavaResolveResult result
, PsiMember elementToFix
) {
1170 String description
= HighlightUtil
.buildProblemWithAccessDescription(classReference
, result
);
1171 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, classReference
, description
);
1172 info
.navigationShift
= +1;
1173 if (result
.isStaticsScopeCorrect()) {
1174 HighlightUtil
.registerAccessQuickFixAction(elementToFix
, classReference
, info
, result
.getCurrentFileResolveScope());
1179 private static boolean callingProtectedConstructorFromDerivedClass(PsiConstructorCall place
, PsiClass constructorClass
) {
1180 if (constructorClass
== null) return false;
1181 // indirect instantiation via anonymous class is ok
1182 if (place
instanceof PsiNewExpression
&& ((PsiNewExpression
)place
).getAnonymousClass() != null) return false;
1183 PsiElement curElement
= place
;
1184 PsiClass containingClass
= constructorClass
.getContainingClass();
1186 PsiClass aClass
= PsiTreeUtil
.getParentOfType(curElement
, PsiClass
.class);
1187 if (aClass
== null) return false;
1188 curElement
= aClass
;
1189 if ((aClass
.isInheritor(constructorClass
, true) || containingClass
!= null && aClass
.isInheritor(containingClass
, true))
1190 && !JavaPsiFacade
.getInstance(aClass
.getProject()).arePackagesTheSame(aClass
, constructorClass
)) {
1196 public static boolean isSerializationRelatedMethod(PsiMethod method
, PsiClass containingClass
) {
1197 if (containingClass
== null || method
.isConstructor()) return false;
1198 if (method
.hasModifierProperty(PsiModifier
.STATIC
)) return false;
1199 @NonNls String name
= method
.getName();
1200 PsiParameter
[] parameters
= method
.getParameterList().getParameters();
1201 PsiType returnType
= method
.getReturnType();
1202 if ("readObjectNoData".equals(name
)) {
1203 return parameters
.length
== 0 && TypeConversionUtil
.isVoidType(returnType
) && HighlightUtil
.isSerializable(containingClass
);
1205 if ("readObject".equals(name
)) {
1206 return parameters
.length
== 1
1207 && parameters
[0].getType().equalsToText("java.io.ObjectInputStream")
1208 && TypeConversionUtil
.isVoidType(returnType
) && method
.hasModifierProperty(PsiModifier
.PRIVATE
)
1209 && HighlightUtil
.isSerializable(containingClass
);
1211 if ("readResolve".equals(name
)) {
1212 return parameters
.length
== 0
1213 && returnType
!= null
1214 && returnType
.equalsToText("java.lang.Object")
1215 && HighlightUtil
.isSerializable(containingClass
);
1217 if ("writeReplace".equals(name
)) {
1218 return parameters
.length
== 0
1219 && returnType
!= null
1220 && returnType
.equalsToText("java.lang.Object")
1221 && HighlightUtil
.isSerializable(containingClass
);
1223 if ("writeObject".equals(name
)) {
1224 return parameters
.length
== 1
1225 && TypeConversionUtil
.isVoidType(returnType
)
1226 && parameters
[0].getType().equalsToText("java.io.ObjectOutputStream")
1227 && method
.hasModifierProperty(PsiModifier
.PRIVATE
)
1228 && HighlightUtil
.isSerializable(containingClass
);
1233 private static String
buildArgTypesList(PsiExpressionList list
) {
1234 StringBuilder builder
= new StringBuilder();
1235 builder
.append("(");
1236 PsiExpression
[] args
= list
.getExpressions();
1237 for (int i
= 0; i
< args
.length
; i
++) {
1239 builder
.append(", ");
1241 PsiType argType
= args
[i
].getType();
1242 builder
.append(argType
!= null ? HighlightUtil
.formatType(argType
) : "?");
1244 builder
.append(")");
1245 return builder
.toString();