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
.project
.IndexNotReadyException
;
33 import com
.intellij
.openapi
.util
.Comparing
;
34 import com
.intellij
.openapi
.util
.TextRange
;
35 import com
.intellij
.psi
.*;
36 import com
.intellij
.psi
.infos
.CandidateInfo
;
37 import com
.intellij
.psi
.infos
.MethodCandidateInfo
;
38 import com
.intellij
.psi
.util
.*;
39 import com
.intellij
.xml
.util
.XmlStringUtil
;
40 import org
.jetbrains
.annotations
.NonNls
;
41 import org
.jetbrains
.annotations
.NotNull
;
42 import org
.jetbrains
.annotations
.Nullable
;
44 import java
.text
.MessageFormat
;
45 import java
.util
.ArrayList
;
46 import java
.util
.Collection
;
47 import java
.util
.List
;
49 public class HighlightMethodUtil
{
50 private static final QuickFixFactory QUICK_FIX_FACTORY
= QuickFixFactory
.getInstance();
52 public static String
createClashMethodMessage(PsiMethod method1
, PsiMethod method2
, boolean showContainingClasses
) {
53 @NonNls String pattern
= showContainingClasses ?
"clash.methods.message.show.classes" : "clash.methods.message";
55 return JavaErrorMessages
.message(pattern
,
56 HighlightUtil
.formatMethod(method1
),
57 HighlightUtil
.formatMethod(method2
),
58 HighlightUtil
.formatClass(method1
.getContainingClass()),
59 HighlightUtil
.formatClass(method2
.getContainingClass()));
62 public static HighlightInfo
checkMethodWeakerPrivileges(MethodSignatureBackedByPsiMethod methodSignature
,
63 List
<HierarchicalMethodSignature
> superMethodSignatures
,
64 boolean includeRealPositionInfo
) {
65 PsiMethod method
= methodSignature
.getMethod();
66 PsiModifierList modifierList
= method
.getModifierList();
67 if (modifierList
.hasModifierProperty(PsiModifier
.PUBLIC
)) return null;
68 int accessLevel
= PsiUtil
.getAccessLevel(modifierList
);
69 String accessModifier
= PsiUtil
.getAccessModifier(accessLevel
);
70 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
71 PsiMethod superMethod
= superMethodSignature
.getMethod();
72 if (!PsiUtil
.isAccessible(superMethod
, method
, null)) continue;
73 HighlightInfo info
= isWeaker(method
, modifierList
, accessModifier
, accessLevel
, superMethod
, includeRealPositionInfo
);
74 if (info
!= null) return info
;
79 private static HighlightInfo
isWeaker(final PsiMethod method
, final PsiModifierList modifierList
, final String accessModifier
, final int accessLevel
,
80 final PsiMethod superMethod
,
81 final boolean includeRealPositionInfo
) {
82 int superAccessLevel
= PsiUtil
.getAccessLevel(superMethod
.getModifierList());
83 if (accessLevel
< superAccessLevel
) {
84 String message
= JavaErrorMessages
.message("weaker.privileges",
85 createClashMethodMessage(method
, superMethod
, true),
87 PsiUtil
.getAccessModifier(superAccessLevel
));
89 if (includeRealPositionInfo
) {
90 if (modifierList
.hasModifierProperty(PsiModifier
.PACKAGE_LOCAL
)) {
91 textRange
= method
.getNameIdentifier().getTextRange();
94 PsiElement keyword
= PsiUtil
.findModifierInList(modifierList
, accessModifier
);
95 textRange
= keyword
.getTextRange();
99 textRange
= new TextRange(0, 0);
101 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
102 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiUtil
.getAccessModifier(superAccessLevel
), true, false);
103 QuickFixAction
.registerQuickFixAction(highlightInfo
, fix
);
104 return highlightInfo
;
110 public static HighlightInfo
checkMethodIncompatibleReturnType(MethodSignatureBackedByPsiMethod methodSignature
,
111 List
<HierarchicalMethodSignature
> superMethodSignatures
,
112 boolean includeRealPositionInfo
) {
113 PsiMethod method
= methodSignature
.getMethod();
114 PsiType returnType
= methodSignature
.getSubstitutor().substitute(method
.getReturnType());
115 PsiClass aClass
= method
.getContainingClass();
116 if (aClass
== null) return null;
117 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
118 PsiMethod superMethod
= superMethodSignature
.getMethod();
119 PsiType declaredReturnType
= superMethod
.getReturnType();
120 PsiType superReturnType
= superMethodSignature
.getSubstitutor().substitute(declaredReturnType
);
121 if (superMethodSignature
.isRaw()) superReturnType
= TypeConversionUtil
.erasure(declaredReturnType
);
122 if (returnType
== null || superReturnType
== null || method
== superMethod
) continue;
123 PsiClass superClass
= superMethod
.getContainingClass();
124 if (superClass
== null) continue;
125 HighlightInfo highlightInfo
= checkSuperMethodSignature(superMethod
, superMethodSignature
, superReturnType
, method
, methodSignature
,
126 returnType
, includeRealPositionInfo
, JavaErrorMessages
.message("incompatible.return.type"), method
);
127 if (highlightInfo
!= null) return highlightInfo
;
133 private static HighlightInfo
checkSuperMethodSignature(PsiMethod superMethod
,
134 MethodSignatureBackedByPsiMethod superMethodSignature
,
135 PsiType superReturnType
,
137 MethodSignatureBackedByPsiMethod methodSignature
,
139 boolean includeRealPositionInfo
,
140 String detailMessage
,
141 PsiMethod methodToHighlight
) {
142 if (superReturnType
== null) return null;
143 PsiType substitutedSuperReturnType
;
144 final boolean isJdk15
= PsiUtil
.isLanguageLevel5OrHigher(method
);
145 if (isJdk15
&& !superMethodSignature
.isRaw() && superMethodSignature
.equals(methodSignature
)) { //see 8.4.5
146 PsiSubstitutor unifyingSubstitutor
= MethodSignatureUtil
.getSuperMethodSignatureSubstitutor(methodSignature
,
147 superMethodSignature
);
148 substitutedSuperReturnType
= unifyingSubstitutor
== null
150 : unifyingSubstitutor
.substitute(superMethodSignature
.getSubstitutor().substitute(superReturnType
));
153 substitutedSuperReturnType
= TypeConversionUtil
.erasure(superReturnType
);
156 if (returnType
.equals(substitutedSuperReturnType
)) return null;
157 if (!(returnType
instanceof PsiPrimitiveType
) && substitutedSuperReturnType
.getDeepComponentType() instanceof PsiClassType
) {
158 if (isJdk15
&& TypeConversionUtil
.isAssignable(substitutedSuperReturnType
, returnType
)) {
163 return createIncompatibleReturnTypeMessage(methodToHighlight
, method
, superMethod
, includeRealPositionInfo
,
164 substitutedSuperReturnType
, returnType
, detailMessage
);
167 private static HighlightInfo
createIncompatibleReturnTypeMessage(PsiMethod methodToHighlight
,
169 PsiMethod superMethod
,
170 boolean includeRealPositionInfo
,
171 PsiType substitutedSuperReturnType
,
173 String detailMessage
) {
174 String message
= MessageFormat
.format("{0}; {1}", createClashMethodMessage(method
, superMethod
, true), detailMessage
);
175 TextRange textRange
= includeRealPositionInfo ? methodToHighlight
.getReturnTypeElement().getTextRange() : new TextRange(0, 0);
176 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
177 IntentionAction fix
= QUICK_FIX_FACTORY
.createMethodReturnFix(method
, substitutedSuperReturnType
, false);
178 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
179 QuickFixAction
.registerQuickFixAction(errorResult
, new SuperMethodReturnFix(superMethod
, returnType
));
185 static HighlightInfo
checkMethodOverridesFinal(MethodSignatureBackedByPsiMethod methodSignature
,
186 List
<HierarchicalMethodSignature
> superMethodSignatures
) {
187 PsiMethod method
= methodSignature
.getMethod();
188 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
189 PsiMethod superMethod
= superMethodSignature
.getMethod();
190 HighlightInfo info
= checkSuperMethodIsFinal(method
, superMethod
);
191 if (info
!= null) return info
;
196 private static HighlightInfo
checkSuperMethodIsFinal(PsiMethod method
, PsiMethod superMethod
) {
197 // strange things happen when super method is from Object and method from interface
198 if (superMethod
.hasModifierProperty(PsiModifier
.FINAL
)) {
199 String message
= JavaErrorMessages
.message("final.method.override",
200 HighlightUtil
.formatMethod(method
),
201 HighlightUtil
.formatMethod(superMethod
),
202 HighlightUtil
.formatClass(superMethod
.getContainingClass()));
203 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
204 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
207 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(superMethod
, PsiModifier
.FINAL
, false, true);
208 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
214 public static HighlightInfo
checkMethodIncompatibleThrows(MethodSignatureBackedByPsiMethod methodSignature
,
215 List
<HierarchicalMethodSignature
> superMethodSignatures
,
216 boolean includeRealPositionInfo
, PsiClass analyzedClass
) {
217 PsiMethod method
= methodSignature
.getMethod();
218 PsiClass aClass
= method
.getContainingClass();
219 if (aClass
== null) return null;
220 PsiSubstitutor superSubstitutor
= TypeConversionUtil
.getSuperClassSubstitutor(aClass
, analyzedClass
, PsiSubstitutor
.EMPTY
);
221 PsiClassType
[] exceptions
= method
.getThrowsList().getReferencedTypes();
222 PsiJavaCodeReferenceElement
[] referenceElements
;
223 List
<PsiElement
> exceptionContexts
;
224 if (includeRealPositionInfo
) {
225 exceptionContexts
= new ArrayList
<PsiElement
>();
226 referenceElements
= method
.getThrowsList().getReferenceElements();
229 exceptionContexts
= null;
230 referenceElements
= null;
232 List
<PsiClassType
> checkedExceptions
= new ArrayList
<PsiClassType
>();
233 for (int i
= 0; i
< exceptions
.length
; i
++) {
234 PsiClassType exception
= exceptions
[i
];
235 if (!ExceptionUtil
.isUncheckedException(exception
)) {
236 checkedExceptions
.add(exception
);
237 if (includeRealPositionInfo
&& i
< referenceElements
.length
) {
238 PsiJavaCodeReferenceElement exceptionRef
= referenceElements
[i
];
239 exceptionContexts
.add(exceptionRef
);
243 for (MethodSignatureBackedByPsiMethod superMethodSignature
: superMethodSignatures
) {
244 PsiMethod superMethod
= superMethodSignature
.getMethod();
245 int index
= getExtraExceptionNum(methodSignature
, superMethodSignature
, checkedExceptions
, superSubstitutor
);
247 PsiClassType exception
= checkedExceptions
.get(index
);
248 String message
= JavaErrorMessages
.message("overridden.method.does.not.throw",
249 createClashMethodMessage(method
, superMethod
, true),
250 HighlightUtil
.formatType(exception
));
252 if (includeRealPositionInfo
) {
253 PsiElement exceptionContext
= exceptionContexts
.get(index
);
254 textRange
= exceptionContext
.getTextRange();
257 textRange
= new TextRange(0, 0);
259 HighlightInfo errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
260 QuickFixAction
.registerQuickFixAction(errorResult
, QUICK_FIX_FACTORY
.createMethodThrowsFix(method
, exception
, false, false));
261 QuickFixAction
.registerQuickFixAction(errorResult
, QUICK_FIX_FACTORY
.createMethodThrowsFix(superMethod
, exception
, true, true));
268 // return number of exception which was not declared in super method or -1
269 private static int getExtraExceptionNum(final MethodSignature methodSignature
,
270 final MethodSignatureBackedByPsiMethod superSignature
,
271 List
<PsiClassType
> checkedExceptions
, PsiSubstitutor substitutorForDerivedClass
) {
272 PsiMethod superMethod
= superSignature
.getMethod();
273 PsiSubstitutor substitutorForMethod
= MethodSignatureUtil
.getSuperMethodSignatureSubstitutor(methodSignature
, superSignature
);
274 if (substitutorForMethod
== null) return -1;
275 for (int i
= 0; i
< checkedExceptions
.size(); i
++) {
276 PsiType exception
= substitutorForDerivedClass
.substitute(substitutorForMethod
.substitute(checkedExceptions
.get(i
)));
277 if (!isMethodThrows(superMethod
, substitutorForMethod
, exception
, substitutorForDerivedClass
)) {
284 private static boolean isMethodThrows(PsiMethod method
, PsiSubstitutor substitutorForMethod
, PsiType exception
, PsiSubstitutor substitutorForDerivedClass
) {
285 PsiClassType
[] thrownExceptions
= method
.getThrowsList().getReferencedTypes();
286 for (PsiClassType thrownException1
: thrownExceptions
) {
287 PsiType thrownException
= substitutorForMethod
.substitute(thrownException1
);
288 thrownException
= substitutorForDerivedClass
.substitute(thrownException
);
289 if (TypeConversionUtil
.isAssignable(thrownException
, exception
)) return true;
295 public static List
<HighlightInfo
> checkMethodCall(PsiMethodCallExpression methodCall
, PsiResolveHelper resolveHelper
) {
296 final List
<HighlightInfo
> result
= new ArrayList
<HighlightInfo
>();
298 PsiExpressionList list
= methodCall
.getArgumentList();
299 PsiReferenceExpression referenceToMethod
= methodCall
.getMethodExpression();
300 JavaResolveResult resolveResult
= referenceToMethod
.advancedResolve(true);
301 PsiElement element
= resolveResult
.getElement();
303 final CandidateInfo
[] candidates
= resolveHelper
.getReferencedMethodCandidates(methodCall
, true);
305 boolean isDummy
= false;
306 boolean isThisOrSuper
= referenceToMethod
.getReferenceNameElement() instanceof PsiKeyword
;
308 // super(..) or this(..)
309 if (list
.getExpressions().length
== 0) { // implicit ctr call
310 if (candidates
.length
== 1 && !candidates
[0].getElement().isPhysical()) {
311 isDummy
= true;// dummy constructor
315 if (isDummy
) return null;
316 HighlightInfo highlightInfo
;
318 if (element
instanceof PsiMethod
&& resolveResult
.isValidResult()) {
319 TextRange fixRange
= getFixRange(methodCall
);
320 highlightInfo
= HighlightUtil
.checkUnhandledExceptions(methodCall
, fixRange
);
322 if (highlightInfo
== null) {
323 highlightInfo
= GenericsHighlightUtil
.checkUncheckedCall(resolveResult
, methodCall
);
325 if (highlightInfo
== null) {
326 highlightInfo
= GenericsHighlightUtil
.checkGenericCallWithRawArguments(resolveResult
, methodCall
);
330 PsiMethod resolvedMethod
= null;
331 MethodCandidateInfo candidateInfo
= null;
332 if (resolveResult
instanceof MethodCandidateInfo
) {
333 candidateInfo
= (MethodCandidateInfo
)resolveResult
;
334 resolvedMethod
= candidateInfo
.getElement();
337 ChangeStringLiteralToCharInMethodCallFix
.createHighLighting(candidates
, methodCall
, result
);
339 if (!resolveResult
.isAccessible() || !resolveResult
.isStaticsScopeCorrect()) {
340 highlightInfo
= checkAmbiguousMethodCall(referenceToMethod
, list
, element
, resolveResult
, methodCall
, resolveHelper
);
342 else if (candidateInfo
!= null && !candidateInfo
.isApplicable()) {
343 if (candidateInfo
.isTypeArgumentsApplicable()) {
344 String methodName
= HighlightMessageUtil
.getSymbolName(element
, resolveResult
.getSubstitutor());
345 PsiElement parent
= element
.getParent();
346 String containerName
= parent
== null ?
"" : HighlightMessageUtil
.getSymbolName(parent
, resolveResult
.getSubstitutor());
347 String argTypes
= buildArgTypesList(list
);
348 String description
= JavaErrorMessages
.message("wrong.method.arguments", methodName
, containerName
, argTypes
);
349 String toolTip
= parent
instanceof PsiClass ?
350 createMismatchedArgumentsHtmlTooltip(candidateInfo
, list
) : description
;
351 highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, description
, toolTip
);
352 registerMethodCallIntentions(highlightInfo
, methodCall
, list
, resolveHelper
);
353 highlightInfo
.navigationShift
= +1;
356 PsiReferenceExpression methodExpression
= methodCall
.getMethodExpression();
357 PsiReferenceParameterList typeArgumentList
= methodCall
.getTypeArgumentList();
358 if (typeArgumentList
.getTypeArguments().length
== 0 && resolvedMethod
.hasTypeParameters()) {
359 highlightInfo
= GenericsHighlightUtil
.checkInferredTypeArguments(resolvedMethod
, methodCall
, resolveResult
.getSubstitutor());
362 highlightInfo
= GenericsHighlightUtil
.checkParameterizedReferenceTypeArguments(element
, methodExpression
,
363 resolveResult
.getSubstitutor());
368 highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, methodCall
, JavaErrorMessages
.message("method.call.expected"));
369 if (element
instanceof PsiClass
) {
370 QuickFixAction
.registerQuickFixAction(highlightInfo
, new InsertNewFix(methodCall
, (PsiClass
)element
));
373 TextRange range
= getFixRange(methodCall
);
374 QuickFixAction
.registerQuickFixAction(highlightInfo
, range
, new CreateMethodFromUsageFix(methodCall
), null);
375 QuickFixAction
.registerQuickFixAction(highlightInfo
, range
, new CreateAbstractMethodFromUsageFix(methodCall
), null);
376 QuickFixAction
.registerQuickFixAction(highlightInfo
, range
, new CreatePropertyFromUsageFix(methodCall
), null);
380 if (highlightInfo
== null) {
382 GenericsHighlightUtil
.checkParameterizedReferenceTypeArguments(element
, referenceToMethod
, resolveResult
.getSubstitutor());
384 result
.add(highlightInfo
);
388 private static HighlightInfo
checkAmbiguousMethodCall(final PsiReferenceExpression referenceToMethod
,
389 final PsiExpressionList list
,
390 final PsiElement element
,
391 final JavaResolveResult resolveResult
,
392 final PsiMethodCallExpression methodCall
, final PsiResolveHelper resolveHelper
) {
393 JavaResolveResult
[] resolveResults
= referenceToMethod
.multiResolve(true);
394 MethodCandidateInfo methodCandidate1
= null;
395 MethodCandidateInfo methodCandidate2
= null;
396 for (JavaResolveResult result
: resolveResults
) {
397 if (!(result
instanceof MethodCandidateInfo
)) continue;
398 MethodCandidateInfo candidate
= (MethodCandidateInfo
)result
;
399 if (candidate
.isApplicable() && !candidate
.getElement().isConstructor()) {
400 if (methodCandidate1
== null) {
401 methodCandidate1
= candidate
;
404 methodCandidate2
= candidate
;
409 MethodCandidateInfo
[] candidates
= toMethodCandidates(resolveResults
);
413 PsiElement elementToHighlight
;
414 HighlightInfoType highlightInfoType
= HighlightInfoType
.ERROR
;
415 if (methodCandidate2
!= null) {
416 String m1
= PsiFormatUtil
.formatMethod(methodCandidate1
.getElement(),
417 methodCandidate1
.getSubstitutor(),
418 PsiFormatUtil
.SHOW_CONTAINING_CLASS
| PsiFormatUtil
.SHOW_NAME
|
419 PsiFormatUtil
.SHOW_PARAMETERS
,
420 PsiFormatUtil
.SHOW_TYPE
);
421 String m2
= PsiFormatUtil
.formatMethod(methodCandidate2
.getElement(),
422 methodCandidate2
.getSubstitutor(),
423 PsiFormatUtil
.SHOW_CONTAINING_CLASS
| PsiFormatUtil
.SHOW_NAME
|
424 PsiFormatUtil
.SHOW_PARAMETERS
,
425 PsiFormatUtil
.SHOW_TYPE
);
426 description
= JavaErrorMessages
.message("ambiguous.method.call", m1
, m2
);
427 toolTip
= createAmbiguousMethodHtmlTooltip(new MethodCandidateInfo
[]{methodCandidate1
, methodCandidate2
});
428 elementToHighlight
= list
;
431 if (element
!= null && !resolveResult
.isAccessible()) {
432 description
= HighlightUtil
.buildProblemWithAccessDescription(referenceToMethod
, resolveResult
);
433 elementToHighlight
= referenceToMethod
.getReferenceNameElement();
435 else if (element
!= null && !resolveResult
.isStaticsScopeCorrect()) {
436 description
= HighlightUtil
.buildProblemWithStaticDescription(element
);
437 elementToHighlight
= referenceToMethod
.getReferenceNameElement();
440 String methodName
= referenceToMethod
.getReferenceName() + buildArgTypesList(list
);
441 description
= JavaErrorMessages
.message("cannot.resolve.method", methodName
);
442 if (candidates
.length
== 0) {
443 elementToHighlight
= referenceToMethod
.getReferenceNameElement();
444 highlightInfoType
= HighlightInfoType
.WRONG_REF
;
447 elementToHighlight
= list
;
450 toolTip
= description
;
452 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(highlightInfoType
, elementToHighlight
, description
, toolTip
);
453 if (methodCandidate2
== null) {
454 registerMethodCallIntentions(highlightInfo
, methodCall
, list
, resolveHelper
);
456 if (!resolveResult
.isAccessible() && resolveResult
.isStaticsScopeCorrect() && methodCandidate2
!= null) {
457 HighlightUtil
.registerAccessQuickFixAction((PsiMember
)element
, referenceToMethod
, highlightInfo
, resolveResult
.getCurrentFileResolveScope());
459 if (element
!= null && !resolveResult
.isStaticsScopeCorrect()) {
460 HighlightUtil
.registerStaticProblemQuickFixAction(element
, highlightInfo
, referenceToMethod
);
463 TextRange fixRange
= getFixRange(elementToHighlight
);
464 CastMethodArgumentFix
.REGISTRAR
.registerCastActions(candidates
, methodCall
, highlightInfo
, fixRange
);
465 PermuteArgumentsFix
.registerFix(highlightInfo
, methodCall
, candidates
, fixRange
);
466 WrapExpressionFix
.registerWrapAction(candidates
, list
.getExpressions(), highlightInfo
);
467 ChangeParameterClassFix
.registerQuickFixActions(methodCall
, list
, highlightInfo
);
468 return highlightInfo
;
471 private static MethodCandidateInfo
[] toMethodCandidates(JavaResolveResult
[] resolveResults
) {
472 List
<MethodCandidateInfo
> candidateList
= new ArrayList
<MethodCandidateInfo
>();
474 for (JavaResolveResult result
: resolveResults
) {
475 if (!(result
instanceof MethodCandidateInfo
)) continue;
476 MethodCandidateInfo candidate
= (MethodCandidateInfo
)result
;
477 if (candidate
.isAccessible()) candidateList
.add(candidate
);
479 return candidateList
.toArray(new MethodCandidateInfo
[candidateList
.size()]);
482 private static void registerMethodCallIntentions(HighlightInfo highlightInfo
,
483 PsiMethodCallExpression methodCall
,
484 PsiExpressionList list
, PsiResolveHelper resolveHelper
) {
485 TextRange fixRange
= getFixRange(methodCall
);
486 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateMethodFromUsageFix(methodCall
), null);
487 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateAbstractMethodFromUsageFix(methodCall
), null);
488 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateConstructorFromSuperFix(methodCall
), null);
489 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreateConstructorFromThisFix(methodCall
), null);
490 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new CreatePropertyFromUsageFix(methodCall
), null);
491 CandidateInfo
[] methodCandidates
= resolveHelper
.getReferencedMethodCandidates(methodCall
, false);
492 CastMethodArgumentFix
.REGISTRAR
.registerCastActions(methodCandidates
, methodCall
, highlightInfo
, fixRange
);
493 PermuteArgumentsFix
.registerFix(highlightInfo
, methodCall
, methodCandidates
, fixRange
);
494 AddTypeArgumentsFix
.REGISTRAR
.registerCastActions(methodCandidates
, methodCall
, highlightInfo
, fixRange
);
495 registerMethodAccessLevelIntentions(methodCandidates
, methodCall
, list
, highlightInfo
);
496 ChangeMethodSignatureFromUsageFix
.registerIntentions(methodCandidates
, list
, highlightInfo
, fixRange
);
497 WrapExpressionFix
.registerWrapAction(methodCandidates
, list
.getExpressions(), highlightInfo
);
498 ChangeParameterClassFix
.registerQuickFixActions(methodCall
, list
, highlightInfo
);
499 if (methodCandidates
.length
== 0) {
500 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new StaticImportMethodFix(methodCall
), null);
502 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new ReplaceAddAllArrayToCollectionFix(methodCall
), null);
503 QuickFixAction
.registerQuickFixAction(highlightInfo
, fixRange
, new SurroundWithArrayFix(methodCall
), null);
506 private static void registerMethodAccessLevelIntentions(CandidateInfo
[] methodCandidates
,
507 PsiMethodCallExpression methodCall
,
508 PsiExpressionList exprList
,
509 HighlightInfo highlightInfo
) {
510 for (CandidateInfo methodCandidate
: methodCandidates
) {
511 PsiMethod method
= (PsiMethod
)methodCandidate
.getElement();
512 if (!methodCandidate
.isAccessible() && PsiUtil
.isApplicable(method
, methodCandidate
.getSubstitutor(), exprList
)) {
513 HighlightUtil
.registerAccessQuickFixAction(method
, methodCall
.getMethodExpression(), highlightInfo
, methodCandidate
.getCurrentFileResolveScope());
518 private static String
createAmbiguousMethodHtmlTooltip(MethodCandidateInfo
[] methodCandidates
) {
519 return JavaErrorMessages
.message("ambiguous.method.html.tooltip",
520 Integer
.valueOf(methodCandidates
[0].getElement().getParameterList().getParametersCount() + 2),
521 createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates
[0]),
522 getContainingClassName(methodCandidates
[0]),
523 createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates
[1]),
524 getContainingClassName(methodCandidates
[1]));
527 private static String
getContainingClassName(final MethodCandidateInfo methodCandidate
) {
528 PsiMethod method
= methodCandidate
.getElement();
529 PsiClass containingClass
= method
.getContainingClass();
530 return containingClass
== null ? method
.getContainingFile().getName() : HighlightUtil
.formatClass(containingClass
, false);
533 private static String
createAmbiguousMethodHtmlTooltipMethodRow(final MethodCandidateInfo methodCandidate
) {
534 PsiMethod method
= methodCandidate
.getElement();
535 PsiParameter
[] parameters
= method
.getParameterList().getParameters();
536 PsiSubstitutor substitutor
= methodCandidate
.getSubstitutor();
537 @NonNls String ms
= "<td><b>" + method
.getName() + "</b></td>";
539 for (int j
= 0; j
< parameters
.length
; j
++) {
540 PsiParameter parameter
= parameters
[j
];
541 PsiType type
= substitutor
.substitute(parameter
.getType());
542 ms
+= "<td><b>" + (j
== 0 ?
"(" : "") +
543 XmlStringUtil
.escapeString(type
.getPresentableText())
544 + (j
== parameters
.length
- 1 ?
")" : ",") + "</b></td>";
546 if (parameters
.length
== 0) {
547 ms
+= "<td><b>()</b></td>";
552 private static String
createMismatchedArgumentsHtmlTooltip(MethodCandidateInfo info
, PsiExpressionList list
) {
553 PsiMethod method
= info
.getElement();
554 PsiSubstitutor substitutor
= info
.getSubstitutor();
555 PsiClass aClass
= method
.getContainingClass();
556 PsiParameter
[] parameters
= method
.getParameterList().getParameters();
557 String methodName
= method
.getName();
558 return createMismatchedArgumentsHtmlTooltip(list
, parameters
, methodName
, substitutor
, aClass
);
561 private static String
createMismatchedArgumentsHtmlTooltip(PsiExpressionList list
,
562 PsiParameter
[] parameters
,
564 PsiSubstitutor substitutor
,
566 PsiExpression
[] expressions
= list
.getExpressions();
567 int cols
= Math
.max(parameters
.length
, expressions
.length
);
569 @NonNls String parensizedName
= methodName
+ (parameters
.length
== 0 ?
"( ) " : "");
570 return JavaErrorMessages
.message(
571 "argument.mismatch.html.tooltip",
572 Integer
.valueOf(cols
- parameters
.length
+ 1), parensizedName
,
573 HighlightUtil
.formatClass(aClass
, false),
574 createMismatchedArgsHtmlTooltipParamsRow(parameters
, substitutor
, expressions
),
575 createMismatchedArgsHtmlTooltipExpressionsRow(expressions
, parameters
, substitutor
, cols
)
579 private static String
createMismatchedArgsHtmlTooltipExpressionsRow(final PsiExpression
[] expressions
, final PsiParameter
[] parameters
,
580 final PsiSubstitutor substitutor
, final int cols
) {
581 @NonNls String ms
= "";
582 for (int i
= 0; i
< expressions
.length
; i
++) {
583 PsiExpression expression
= expressions
[i
];
584 PsiType type
= expression
.getType();
586 @NonNls String mismatchColor
= showShortType(i
, parameters
, expressions
, substitutor
) ?
null : "red";
587 ms
+= "<td> " + "<b><nobr>" + (i
== 0 ?
"(" : "")
588 + "<font " + (mismatchColor
== null ?
"" : "color=" + mismatchColor
) + ">" +
589 XmlStringUtil
.escapeString(showShortType(i
, parameters
, expressions
, substitutor
)
590 ? type
.getPresentableText()
591 : HighlightUtil
.formatType(type
))
593 + (i
== expressions
.length
- 1 ?
")" : ",") + "</nobr></b></td>";
595 for (int i
= expressions
.length
; i
< cols
+ 1; i
++) {
596 ms
+= "<td>" + (i
== 0 ?
"<b>()</b>" : "") +
602 private static String
createMismatchedArgsHtmlTooltipParamsRow(final PsiParameter
[] parameters
,
603 final PsiSubstitutor substitutor
,
604 final PsiExpression
[] expressions
) {
605 @NonNls String ms
= "";
606 for (int i
= 0; i
< parameters
.length
; i
++) {
607 PsiParameter parameter
= parameters
[i
];
608 PsiType type
= substitutor
.substitute(parameter
.getType());
609 ms
+= "<td><b><nobr>" + (i
== 0 ?
"(" : "") +
610 XmlStringUtil
.escapeString(showShortType(i
, parameters
, expressions
, substitutor
)
611 ? type
.getPresentableText()
612 : HighlightUtil
.formatType(type
))
613 + (i
== parameters
.length
- 1 ?
")" : ",") + "</nobr></b></td>";
618 private static boolean showShortType(int i
,
619 PsiParameter
[] parameters
,
620 PsiExpression
[] expressions
,
621 PsiSubstitutor substitutor
) {
622 PsiExpression expression
= i
< expressions
.length ? expressions
[i
] : null;
623 if (expression
== null) return true;
624 PsiType paramType
= i
< parameters
.length
&& parameters
[i
] != null
625 ? substitutor
.substitute(parameters
[i
].getType())
627 return paramType
!= null && TypeConversionUtil
.areTypesAssignmentCompatible(paramType
, expression
);
631 static HighlightInfo
checkMethodMustHaveBody(PsiMethod method
, PsiClass aClass
) {
632 HighlightInfo errorResult
= null;
633 if (method
.getBody() == null
634 && !method
.hasModifierProperty(PsiModifier
.ABSTRACT
)
635 && !method
.hasModifierProperty(PsiModifier
.NATIVE
)
637 && !aClass
.isInterface()
638 && !PsiUtil
.hasErrorElementChild(method
)) {
639 int start
= method
.getModifierList().getTextRange().getStartOffset();
640 int end
= method
.getTextRange().getEndOffset();
642 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
644 JavaErrorMessages
.message("missing.method.body"));
645 if (HighlightUtil
.getIncompatibleModifier(PsiModifier
.ABSTRACT
, method
.getModifierList()) == null) {
646 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.ABSTRACT
, true, false);
647 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
649 QuickFixAction
.registerQuickFixAction(errorResult
, new AddMethodBodyFix(method
));
655 static HighlightInfo
checkAbstractMethodInConcreteClass(PsiMethod method
, PsiElement elementToHighlight
) {
656 HighlightInfo errorResult
= null;
657 PsiClass aClass
= method
.getContainingClass();
658 if (method
.hasModifierProperty(PsiModifier
.ABSTRACT
)
660 && !aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)
662 && !PsiUtil
.hasErrorElementChild(method
)) {
663 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
665 JavaErrorMessages
.message("abstract.method.in.non.abstract.class"));
666 if (method
.getBody() != null) {
667 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.ABSTRACT
, false, false);
668 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
670 QuickFixAction
.registerQuickFixAction(errorResult
, new AddMethodBodyFix(method
));
671 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(aClass
, PsiModifier
.ABSTRACT
, true, false);
672 QuickFixAction
.registerQuickFixAction(errorResult
, fix
);
678 static HighlightInfo
checkConstructorName(PsiMethod method
) {
679 String methodName
= method
.getName();
680 PsiClass aClass
= method
.getContainingClass();
681 HighlightInfo errorResult
= null;
683 if (aClass
!= null) {
684 String className
= aClass
instanceof PsiAnonymousClass ?
null : aClass
.getName();
685 if (!Comparing
.strEqual(methodName
, className
)) {
686 errorResult
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, method
.getNameIdentifier(),
687 JavaErrorMessages
.message("missing.return.type"));
694 static HighlightInfo
checkDuplicateMethod(PsiClass aClass
, PsiMethod method
) {
695 if (aClass
== null) return null;
696 MethodSignature methodSignature
= method
.getSignature(PsiSubstitutor
.EMPTY
);
698 final PsiMethod
[] methodsByName
= aClass
.findMethodsByName(method
.getName(), false);
699 for (PsiMethod other
: methodsByName
) {
700 if (other
== method
||
701 other
.isConstructor() == method
.isConstructor() && other
.getSignature(PsiSubstitutor
.EMPTY
).equals(methodSignature
)) {
703 if (methodCount
> 1) break;
707 if (methodCount
== 1 && aClass
.isEnum() &&
708 GenericsHighlightUtil
.isEnumSyntheticMethod(methodSignature
, aClass
.getProject())) {
711 if (methodCount
> 1) {
712 String message
= JavaErrorMessages
.message("duplicate.method",
713 HighlightUtil
.formatMethod(method
),
714 HighlightUtil
.formatClass(aClass
));
715 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
716 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
722 public static HighlightInfo
checkMethodCanHaveBody(PsiMethod method
) {
723 if (method
.getBody() == null) return null;
724 PsiClass aClass
= method
.getContainingClass();
726 String message
= null;
727 if (aClass
!= null && aClass
.isInterface()) {
728 message
= JavaErrorMessages
.message("interface.methods.cannot.have.body");
730 else if (method
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
731 message
= JavaErrorMessages
.message("abstract.methods.cannot.have.a.body");
733 else if (method
.hasModifierProperty(PsiModifier
.NATIVE
)) {
734 message
= JavaErrorMessages
.message("native.methods.cannot.have.a.body");
737 if (message
!= null) {
738 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
739 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
740 QuickFixAction
.registerQuickFixAction(info
, new DeleteMethodBodyFix(method
));
741 if (method
.hasModifierProperty(PsiModifier
.ABSTRACT
) && aClass
!= null && !aClass
.isInterface()) {
742 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.ABSTRACT
, false, false);
743 QuickFixAction
.registerQuickFixAction(info
, fix
);
751 static HighlightInfo
checkConstructorCallMustBeFirstStatement(PsiReferenceExpression expression
) {
752 String text
= expression
.getText();
753 PsiElement methodCall
= expression
.getParent();
754 if (!HighlightUtil
.isSuperOrThisMethodCall(methodCall
)) return null;
755 PsiElement codeBlock
= methodCall
.getParent().getParent();
756 if (codeBlock
instanceof PsiCodeBlock
757 && codeBlock
.getParent() instanceof PsiMethod
758 && ((PsiMethod
)codeBlock
.getParent()).isConstructor()) {
759 PsiElement prevSibling
= methodCall
.getParent().getPrevSibling();
761 if (prevSibling
== null) return null;
762 if (prevSibling
instanceof PsiStatement
) break;
763 prevSibling
= prevSibling
.getPrevSibling();
766 String message
= JavaErrorMessages
.message("constructor.call.must.be.first.statement", text
+ "()");
767 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, expression
.getParent(), message
);
771 public static HighlightInfo
checkAbstractMethodDirectCall(PsiSuperExpression expr
) {
772 if (expr
.getParent() instanceof PsiReferenceExpression
773 && expr
.getParent().getParent() instanceof PsiMethodCallExpression
) {
774 PsiMethodCallExpression methodCallExpression
= (PsiMethodCallExpression
)expr
.getParent().getParent();
775 PsiMethod method
= methodCallExpression
.resolveMethod();
776 if (method
!= null && method
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
777 String message
= JavaErrorMessages
.message("direct.abstract.method.access", HighlightUtil
.formatMethod(method
));
778 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, methodCallExpression
, message
);
785 public static HighlightInfo
checkConstructorCallsBaseClassConstructor(PsiMethod constructor
,
786 RefCountHolder refCountHolder
,
787 PsiResolveHelper resolveHelper
) {
788 if (!constructor
.isConstructor()) return null;
789 PsiClass aClass
= constructor
.getContainingClass();
790 if (aClass
== null) return null;
791 if (aClass
.isEnum()) return null;
792 PsiCodeBlock body
= constructor
.getBody();
793 if (body
== null) return null;
795 // check whether constructor call super(...) or this(...)
796 PsiElement element
= new PsiMatcherImpl(body
)
797 .firstChild(PsiMatchers
.hasClass(PsiExpressionStatement
.class))
798 .firstChild(PsiMatchers
.hasClass(PsiMethodCallExpression
.class))
799 .firstChild(PsiMatchers
.hasClass(PsiReferenceExpression
.class))
800 .firstChild(PsiMatchers
.hasClass(PsiKeyword
.class))
802 if (element
!= null) return null;
803 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(constructor
);
804 PsiClassType
[] handledExceptions
= constructor
.getThrowsList().getReferencedTypes();
805 HighlightInfo info
= HighlightClassUtil
.checkBaseClassDefaultConstructorProblem(aClass
, refCountHolder
, resolveHelper
, textRange
, handledExceptions
);
807 QuickFixAction
.registerQuickFixAction(info
, new InsertSuperFix(constructor
));
808 QuickFixAction
.registerQuickFixAction(info
, QUICK_FIX_FACTORY
.createAddDefaultConstructorFix(aClass
.getSuperClass()));
815 * @return error if static method overrides instance method or
816 * instance method overrides static. see JLS 8.4.6.1, 8.4.6.2
818 public static HighlightInfo
checkStaticMethodOverride(PsiMethod method
) {
819 // constructors are not members and therefor don't override class methods
820 if (method
.isConstructor()) {
824 PsiClass aClass
= method
.getContainingClass();
825 if (aClass
== null) return null;
826 PsiClass superClass
= aClass
.getSuperClass();
827 PsiMethod superMethod
= superClass
== null
829 : MethodSignatureUtil
.findMethodBySignature(superClass
, method
, true);
831 boolean isStatic
= method
.hasModifierProperty(PsiModifier
.STATIC
);
832 HighlightInfo highlightInfo
= checkStaticMethodOverride(aClass
, method
, isStatic
,superClass
, superMethod
);
833 if (highlightInfo
!= null) return highlightInfo
;
835 // all methods in interface are instance, so no possible errors in this case
838 PsiClass
[] interfaces
= aClass
.getInterfaces();
839 for (PsiClass aInterfaces
: interfaces
) {
840 superClass
= aInterfaces
;
841 superMethod
= MethodSignatureUtil
.findMethodBySignature(superClass
, method
, true);
842 highlightInfo
= checkStaticMethodOverride(aClass
, method
, true, superClass
, superMethod
);
843 if (highlightInfo
!= null) return highlightInfo
;
848 private static HighlightInfo
checkStaticMethodOverride(PsiClass aClass
, PsiMethod method
, boolean isMethodStatic
, PsiClass superClass
, PsiMethod superMethod
) {
849 if (superMethod
== null) return null;
850 PsiManager manager
= superMethod
.getManager();
851 PsiModifierList superModifierList
= superMethod
.getModifierList();
852 PsiModifierList modifierList
= method
.getModifierList();
853 if (superModifierList
.hasModifierProperty(PsiModifier
.PRIVATE
)) return null;
854 if (superModifierList
.hasModifierProperty(PsiModifier
.PACKAGE_LOCAL
)
855 && !JavaPsiFacade
.getInstance(manager
.getProject()).arePackagesTheSame(aClass
, superClass
)) {
858 boolean isSuperMethodStatic
= superModifierList
.hasModifierProperty(PsiModifier
.STATIC
);
859 if (isMethodStatic
!= isSuperMethodStatic
) {
860 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
861 @NonNls final String messageKey
= isMethodStatic
862 ?
"static.method.cannot.override.instance.method"
863 : "instance.method.cannot.override.static.method";
865 String message
= JavaErrorMessages
.message(messageKey
,
866 HighlightUtil
.formatMethod(method
),
867 HighlightUtil
.formatClass(aClass
),
868 HighlightUtil
.formatMethod(superMethod
),
869 HighlightUtil
.formatClass(superClass
));
871 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, message
);
872 if (!isSuperMethodStatic
|| HighlightUtil
.getIncompatibleModifier(PsiModifier
.STATIC
, modifierList
) == null) {
873 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(method
, PsiModifier
.STATIC
, isSuperMethodStatic
, false);
874 QuickFixAction
.registerQuickFixAction(info
, fix
);
876 if (manager
.isInProject(superMethod
) &&
877 (!isMethodStatic
|| HighlightUtil
.getIncompatibleModifier(PsiModifier
.STATIC
, superModifierList
) == null)) {
878 IntentionAction fix
= QUICK_FIX_FACTORY
.createModifierListFix(superMethod
, PsiModifier
.STATIC
, isMethodStatic
, true);
879 QuickFixAction
.registerQuickFixAction(info
, fix
);
884 if (isMethodStatic
&& isSuperMethodStatic
) {
885 int accessLevel
= PsiUtil
.getAccessLevel(modifierList
);
886 String accessModifier
= PsiUtil
.getAccessModifier(accessLevel
);
887 HighlightInfo info
= isWeaker(method
, modifierList
, accessModifier
, accessLevel
, superMethod
, true);
888 if (info
!= null) return info
;
889 info
= checkSuperMethodIsFinal(method
, superMethod
);
890 if (info
!= null) return info
;
895 private static HighlightInfo
checkInterfaceInheritedMethodsReturnTypes(List
<?
extends MethodSignatureBackedByPsiMethod
> superMethodSignatures
) {
896 if (superMethodSignatures
.size() < 2) return null;
897 MethodSignatureBackedByPsiMethod returnTypeSubstitutable
= superMethodSignatures
.get(0);
898 for (int i
= 1; i
< superMethodSignatures
.size(); i
++) {
899 PsiMethod currentMethod
= returnTypeSubstitutable
.getMethod();
900 PsiType currentType
= returnTypeSubstitutable
.getSubstitutor().substitute(currentMethod
.getReturnType());
902 MethodSignatureBackedByPsiMethod otherSuperSignature
= superMethodSignatures
.get(i
);
903 PsiMethod otherSuperMethod
= otherSuperSignature
.getMethod();
904 PsiType otherSuperReturnType
= otherSuperSignature
.getSubstitutor().substitute(otherSuperMethod
.getReturnType());
906 PsiSubstitutor unifyingSubstitutor
= MethodSignatureUtil
.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable
,
907 otherSuperSignature
);
908 if (unifyingSubstitutor
!= null) {
909 otherSuperReturnType
= unifyingSubstitutor
.substitute(otherSuperReturnType
);
910 currentType
= unifyingSubstitutor
.substitute(currentType
);
913 if (otherSuperReturnType
== null || currentType
== null || otherSuperReturnType
.equals(currentType
)) continue;
915 if (PsiUtil
.isLanguageLevel5OrHigher(currentMethod
)) {
916 if (otherSuperReturnType
.isAssignableFrom(currentType
)) continue;
917 if (currentType
.isAssignableFrom(otherSuperReturnType
)) {
918 returnTypeSubstitutable
= otherSuperSignature
;
922 return createIncompatibleReturnTypeMessage(currentMethod
, currentMethod
, otherSuperMethod
, false, otherSuperReturnType
,
923 currentType
, JavaErrorMessages
.message("unrelated.overriding.methods.return.types"));
928 public static HighlightInfo
checkOverrideEquivalentInheritedMethods(PsiClass aClass
) {
929 String errorDescription
= null;
930 final Collection
<HierarchicalMethodSignature
> visibleSignatures
= aClass
.getVisibleSignatures();
931 PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(aClass
.getProject()).getResolveHelper();
933 for (HierarchicalMethodSignature signature
: visibleSignatures
) {
934 PsiMethod method
= signature
.getMethod();
935 if (!resolveHelper
.isAccessible(method
, aClass
, null)) continue;
936 List
<HierarchicalMethodSignature
> superSignatures
= signature
.getSuperSignatures();
938 boolean allAbstracts
= method
.hasModifierProperty(PsiModifier
.ABSTRACT
);
939 final PsiClass containingClass
= method
.getContainingClass();
940 if (aClass
.equals(containingClass
)) continue; //to be checked at method level
942 if (aClass
.isInterface() && !containingClass
.isInterface()) continue;
943 HighlightInfo highlightInfo
= null;
945 if (!containingClass
.equals(aClass
)) {
946 superSignatures
= new ArrayList
<HierarchicalMethodSignature
>(superSignatures
);
947 superSignatures
.add(signature
);
949 highlightInfo
= checkInterfaceInheritedMethodsReturnTypes(superSignatures
);
952 if (!aClass
.equals(containingClass
)) {
953 highlightInfo
= checkMethodIncompatibleReturnType(signature
, superSignatures
, false);
956 if (highlightInfo
!= null) errorDescription
= highlightInfo
.description
;
958 if (method
.hasModifierProperty(PsiModifier
.STATIC
)) {
959 for (HierarchicalMethodSignature superSignature
: superSignatures
) {
960 PsiMethod superMethod
= superSignature
.getMethod();
961 if (!superMethod
.hasModifierProperty(PsiModifier
.STATIC
)) {
962 errorDescription
= JavaErrorMessages
.message("static.method.cannot.override.instance.method",
963 HighlightUtil
.formatMethod(method
),
964 HighlightUtil
.formatClass(containingClass
),
965 HighlightUtil
.formatMethod(superMethod
),
966 HighlightUtil
.formatClass(superMethod
.getContainingClass()));
973 if (errorDescription
== null) {
974 highlightInfo
= checkMethodIncompatibleThrows(signature
, superSignatures
, false, aClass
);
975 if (highlightInfo
!= null) errorDescription
= highlightInfo
.description
;
978 if (errorDescription
== null) {
979 highlightInfo
= checkMethodWeakerPrivileges(signature
, superSignatures
, false);
980 if (highlightInfo
!= null) errorDescription
= highlightInfo
.description
;
983 if (errorDescription
!= null) break;
987 if (errorDescription
!= null) {
988 // show error info at the class level
989 TextRange textRange
= HighlightNamesUtil
.getClassDeclarationTextRange(aClass
);
990 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
998 public static HighlightInfo
checkConstructorHandleSuperClassExceptions(PsiMethod method
) {
999 if (!method
.isConstructor()) {
1002 PsiCodeBlock body
= method
.getBody();
1003 PsiStatement
[] statements
= body
== null ?
null : body
.getStatements();
1004 if (statements
== null) return null;
1006 // if we have unhandled exception inside method body, we could not have been called here,
1007 // so the only problem it can catch here is with super ctr only
1008 Collection
<PsiClassType
> unhandled
= ExceptionUtil
.collectUnhandledExceptions(method
, method
.getContainingClass());
1009 if (unhandled
.isEmpty()) return null;
1010 String description
= HighlightUtil
.getUnhandledExceptionsDescriptor(unhandled
);
1011 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
1012 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
,
1015 for (PsiClassType exception
: unhandled
) {
1016 QuickFixAction
.registerQuickFixAction(highlightInfo
, QUICK_FIX_FACTORY
.createMethodThrowsFix(method
, exception
, true, false));
1018 return highlightInfo
;
1022 public static HighlightInfo
checkRecursiveConstructorInvocation(PsiMethod method
) {
1023 if (HighlightControlFlowUtil
.isRecursivelyCalledConstructor(method
)) {
1024 TextRange textRange
= HighlightNamesUtil
.getMethodDeclarationTextRange(method
);
1025 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, textRange
, JavaErrorMessages
.message("recursive.constructor.invocation"));
1030 public static TextRange
getFixRange(PsiElement element
) {
1031 TextRange range
= element
.getTextRange();
1032 int start
= range
.getStartOffset();
1033 int end
= range
.getEndOffset();
1035 PsiElement nextSibling
= element
.getNextSibling();
1036 if (nextSibling
instanceof PsiJavaToken
&& ((PsiJavaToken
)nextSibling
).getTokenType() == JavaTokenType
.SEMICOLON
) {
1037 return new TextRange(start
, end
+ 1);
1043 static void checkNewExpression(@NotNull PsiNewExpression expression
, @NotNull HighlightInfoHolder holder
) {
1044 PsiType type
= expression
.getType();
1045 if (!(type
instanceof PsiClassType
)) return;
1046 PsiClassType
.ClassResolveResult typeResult
= ((PsiClassType
)type
).resolveGenerics();
1047 PsiClass aClass
= typeResult
.getElement();
1048 if (aClass
== null) return;
1049 if (aClass
instanceof PsiAnonymousClass
) {
1050 type
= ((PsiAnonymousClass
)aClass
).getBaseClassType();
1051 typeResult
= ((PsiClassType
)type
).resolveGenerics();
1052 aClass
= typeResult
.getElement();
1053 if (aClass
== null) return;
1056 PsiJavaCodeReferenceElement classReference
= expression
.getClassOrAnonymousClassReference();
1057 checkConstructorCall(typeResult
, expression
, type
, classReference
, holder
);
1061 public static void checkConstructorCall(PsiClassType
.ClassResolveResult typeResolveResult
,
1062 PsiConstructorCall constructorCall
,
1064 PsiJavaCodeReferenceElement classReference
,
1065 final HighlightInfoHolder holder
) {
1066 PsiExpressionList list
= constructorCall
.getArgumentList();
1067 if (list
== null) return;
1068 PsiClass aClass
= typeResolveResult
.getElement();
1069 if (aClass
== null) return;
1070 final PsiResolveHelper resolveHelper
= JavaPsiFacade
.getInstance(constructorCall
.getProject()).getResolveHelper();
1071 PsiClass accessObjectClass
= null;
1072 if (constructorCall
instanceof PsiNewExpression
) {
1073 PsiExpression qualifier
= ((PsiNewExpression
)constructorCall
).getQualifier();
1074 if (qualifier
!= null) {
1075 accessObjectClass
= (PsiClass
)PsiUtil
.getAccessObjectClass(qualifier
).getElement();
1078 if (classReference
!= null && !resolveHelper
.isAccessible(aClass
, constructorCall
, accessObjectClass
)) {
1079 String description
= HighlightUtil
.buildProblemWithAccessDescription(classReference
, typeResolveResult
);
1080 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, classReference
.getReferenceNameElement(), description
);
1081 HighlightUtil
.registerAccessQuickFixAction(aClass
, classReference
, info
, null);
1085 PsiMethod
[] constructors
= aClass
.getConstructors();
1087 if (constructors
.length
== 0) {
1088 if (list
.getExpressions().length
!= 0) {
1089 String constructorName
= aClass
.getName();
1090 String argTypes
= buildArgTypesList(list
);
1091 String description
= JavaErrorMessages
.message("wrong.constructor.arguments", constructorName
+"()", argTypes
);
1092 String tooltip
= createMismatchedArgumentsHtmlTooltip(list
, PsiParameter
.EMPTY_ARRAY
, constructorName
, PsiSubstitutor
.EMPTY
, aClass
);
1093 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, description
, tooltip
);
1094 QuickFixAction
.registerQuickFixAction(info
, constructorCall
.getTextRange(), new CreateConstructorFromCallFix(constructorCall
), null);
1095 if (classReference
!= null) {
1096 ConstructorParametersFixer
.registerFixActions(classReference
, constructorCall
, info
,getFixRange(list
));
1098 info
.navigationShift
= +1;
1102 if (classReference
!= null && aClass
.hasModifierProperty(PsiModifier
.PROTECTED
) && callingProtectedConstructorFromDerivedClass(constructorCall
, aClass
)) {
1103 holder
.add(buildAccessProblem(classReference
, typeResolveResult
, aClass
));
1107 PsiElement place
= list
;
1108 if (constructorCall
instanceof PsiNewExpression
) {
1109 final PsiAnonymousClass anonymousClass
= ((PsiNewExpression
)constructorCall
).getAnonymousClass();
1110 if (anonymousClass
!= null) place
= anonymousClass
;
1113 JavaResolveResult
[] results
= resolveHelper
.multiResolveConstructor((PsiClassType
)type
, list
, place
);
1114 MethodCandidateInfo result
= null;
1115 if (results
.length
== 1) result
= (MethodCandidateInfo
)results
[0];
1117 PsiMethod constructor
= result
== null ?
null : result
.getElement();
1119 boolean applicable
= true;
1121 applicable
= result
.isApplicable();
1123 catch (IndexNotReadyException e
) {
1126 if (constructor
== null || !applicable
) {
1127 final List
<HighlightInfo
> resultHighlighting
= new ArrayList
<HighlightInfo
>();
1128 ChangeStringLiteralToCharInMethodCallFix
.createHighLighting(constructors
, constructorCall
, resultHighlighting
);
1129 holder
.addAll(resultHighlighting
);
1132 if (constructor
== null) {
1133 String name
= aClass
.getName();
1134 name
+= buildArgTypesList(list
);
1135 String description
= JavaErrorMessages
.message("cannot.resolve.constructor", name
);
1136 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, list
, description
);
1137 QuickFixAction
.registerQuickFixAction(info
, constructorCall
.getTextRange(), new CreateConstructorFromCallFix(constructorCall
), null);
1138 if (classReference
!= null) {
1139 ConstructorParametersFixer
.registerFixActions(classReference
, constructorCall
, info
,getFixRange(list
));
1140 PermuteArgumentsFix
.registerFix(info
, constructorCall
, toMethodCandidates(results
), getFixRange(list
));
1142 WrapExpressionFix
.registerWrapAction(results
, list
.getExpressions(), info
);
1143 info
.navigationShift
= +1;
1147 if (classReference
!= null && (!result
.isAccessible() ||
1148 constructor
.hasModifierProperty(PsiModifier
.PROTECTED
) && callingProtectedConstructorFromDerivedClass(constructorCall
, aClass
))) {
1149 holder
.add(buildAccessProblem(classReference
, result
, constructor
));
1151 else if (!applicable
) {
1152 String constructorName
= HighlightMessageUtil
.getSymbolName(constructor
, result
.getSubstitutor());
1153 String containerName
= HighlightMessageUtil
.getSymbolName(constructor
.getContainingClass(), result
.getSubstitutor());
1154 String argTypes
= buildArgTypesList(list
);
1155 String description
= JavaErrorMessages
.message("wrong.method.arguments", constructorName
, containerName
, argTypes
);
1156 String toolTip
= createMismatchedArgumentsHtmlTooltip(result
, list
);
1157 PsiElement infoElement
= list
.getTextLength() > 0 ? list
: constructorCall
;
1158 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, infoElement
, description
, toolTip
);
1159 QuickFixAction
.registerQuickFixAction(info
, constructorCall
.getTextRange(), new CreateConstructorFromCallFix(constructorCall
), null);
1160 if (classReference
!= null) {
1161 ConstructorParametersFixer
.registerFixActions(classReference
, constructorCall
, info
, getFixRange(infoElement
));
1162 ChangeMethodSignatureFromUsageFix
.registerIntentions(results
, list
, info
, null);
1163 PermuteArgumentsFix
.registerFix(info
, constructorCall
, toMethodCandidates(results
), getFixRange(list
));
1164 QuickFixAction
.registerQuickFixAction(info
, getFixRange(list
), new SurroundWithArrayFix(constructorCall
), null);
1166 info
.navigationShift
= +1;
1170 HighlightInfo highlightInfo
= GenericsHighlightUtil
.checkUncheckedCall(result
, constructorCall
);
1171 if (highlightInfo
!= null) {
1172 holder
.add(highlightInfo
);
1175 if (constructorCall
instanceof PsiNewExpression
) {
1176 highlightInfo
= GenericsHighlightUtil
.checkReferenceTypeArgumentList(constructor
,
1177 ((PsiNewExpression
)constructorCall
).getTypeArgumentList(),
1178 result
.getSubstitutor(), false);
1179 if (highlightInfo
!= null) {
1180 holder
.add(highlightInfo
);
1183 highlightInfo
= GenericsHighlightUtil
.checkGenericCallWithRawArguments(result
, (PsiCallExpression
)constructorCall
);
1185 if (highlightInfo
!= null) {
1186 holder
.add(highlightInfo
);
1193 private static HighlightInfo
buildAccessProblem(@NotNull PsiJavaCodeReferenceElement classReference
, JavaResolveResult result
, PsiMember elementToFix
) {
1194 String description
= HighlightUtil
.buildProblemWithAccessDescription(classReference
, result
);
1195 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, classReference
, description
);
1196 info
.navigationShift
= +1;
1197 if (result
.isStaticsScopeCorrect()) {
1198 HighlightUtil
.registerAccessQuickFixAction(elementToFix
, classReference
, info
, result
.getCurrentFileResolveScope());
1203 private static boolean callingProtectedConstructorFromDerivedClass(PsiConstructorCall place
, PsiClass constructorClass
) {
1204 if (constructorClass
== null) return false;
1205 // indirect instantiation via anonymous class is ok
1206 if (place
instanceof PsiNewExpression
&& ((PsiNewExpression
)place
).getAnonymousClass() != null) return false;
1207 PsiElement curElement
= place
;
1208 PsiClass containingClass
= constructorClass
.getContainingClass();
1210 PsiClass aClass
= PsiTreeUtil
.getParentOfType(curElement
, PsiClass
.class);
1211 if (aClass
== null) return false;
1212 curElement
= aClass
;
1213 if ((aClass
.isInheritor(constructorClass
, true) || containingClass
!= null && aClass
.isInheritor(containingClass
, true))
1214 && !JavaPsiFacade
.getInstance(aClass
.getProject()).arePackagesTheSame(aClass
, constructorClass
)) {
1220 public static boolean isSerializationRelatedMethod(PsiMethod method
, PsiClass containingClass
) {
1221 if (containingClass
== null || method
.isConstructor()) return false;
1222 if (method
.hasModifierProperty(PsiModifier
.STATIC
)) return false;
1223 @NonNls String name
= method
.getName();
1224 PsiParameter
[] parameters
= method
.getParameterList().getParameters();
1225 PsiType returnType
= method
.getReturnType();
1226 if ("readObjectNoData".equals(name
)) {
1227 return parameters
.length
== 0 && TypeConversionUtil
.isVoidType(returnType
) && HighlightUtil
.isSerializable(containingClass
);
1229 if ("readObject".equals(name
)) {
1230 return parameters
.length
== 1
1231 && parameters
[0].getType().equalsToText("java.io.ObjectInputStream")
1232 && TypeConversionUtil
.isVoidType(returnType
) && method
.hasModifierProperty(PsiModifier
.PRIVATE
)
1233 && HighlightUtil
.isSerializable(containingClass
);
1235 if ("readResolve".equals(name
)) {
1236 return parameters
.length
== 0
1237 && returnType
!= null
1238 && returnType
.equalsToText("java.lang.Object")
1239 && HighlightUtil
.isSerializable(containingClass
);
1241 if ("writeReplace".equals(name
)) {
1242 return parameters
.length
== 0
1243 && returnType
!= null
1244 && returnType
.equalsToText("java.lang.Object")
1245 && HighlightUtil
.isSerializable(containingClass
);
1247 if ("writeObject".equals(name
)) {
1248 return parameters
.length
== 1
1249 && TypeConversionUtil
.isVoidType(returnType
)
1250 && parameters
[0].getType().equalsToText("java.io.ObjectOutputStream")
1251 && method
.hasModifierProperty(PsiModifier
.PRIVATE
)
1252 && HighlightUtil
.isSerializable(containingClass
);
1257 private static String
buildArgTypesList(PsiExpressionList list
) {
1258 StringBuilder builder
= new StringBuilder();
1259 builder
.append("(");
1260 PsiExpression
[] args
= list
.getExpressions();
1261 for (int i
= 0; i
< args
.length
; i
++) {
1263 builder
.append(", ");
1265 PsiType argType
= args
[i
].getType();
1266 builder
.append(argType
!= null ? HighlightUtil
.formatType(argType
) : "?");
1268 builder
.append(")");
1269 return builder
.toString();