IndexNotReadyException
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / analysis / HighlightMethodUtil.java
blob2e20a76576107df94e4ea7e1c100f803af896579
1 /*
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
19 * User: cdr
20 * Date: Aug 14, 2002
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;
76 return null;
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),
86 accessModifier,
87 PsiUtil.getAccessModifier(superAccessLevel));
88 TextRange textRange;
89 if (includeRealPositionInfo) {
90 if (modifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) {
91 textRange = method.getNameIdentifier().getTextRange();
93 else {
94 PsiElement keyword = PsiUtil.findModifierInList(modifierList, accessModifier);
95 textRange = keyword.getTextRange();
98 else {
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;
106 return null;
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;
130 return null;
133 private static HighlightInfo checkSuperMethodSignature(PsiMethod superMethod,
134 MethodSignatureBackedByPsiMethod superMethodSignature,
135 PsiType superReturnType,
136 PsiMethod method,
137 MethodSignatureBackedByPsiMethod methodSignature,
138 PsiType returnType,
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
149 ? superReturnType
150 : unifyingSubstitutor.substitute(superMethodSignature.getSubstitutor().substitute(superReturnType));
152 else {
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)) {
159 return null;
163 return createIncompatibleReturnTypeMessage(methodToHighlight, method, superMethod, includeRealPositionInfo,
164 substitutedSuperReturnType, returnType, detailMessage);
167 private static HighlightInfo createIncompatibleReturnTypeMessage(PsiMethod methodToHighlight,
168 PsiMethod method,
169 PsiMethod superMethod,
170 boolean includeRealPositionInfo,
171 PsiType substitutedSuperReturnType,
172 PsiType returnType,
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));
181 return errorResult;
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;
193 return null;
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,
205 textRange,
206 message);
207 IntentionAction fix = QUICK_FIX_FACTORY.createModifierListFix(superMethod, PsiModifier.FINAL, false, true);
208 QuickFixAction.registerQuickFixAction(errorResult, fix);
209 return errorResult;
211 return null;
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();
228 else {
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);
246 if (index != -1) {
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));
251 TextRange textRange;
252 if (includeRealPositionInfo) {
253 PsiElement exceptionContext = exceptionContexts.get(index);
254 textRange = exceptionContext.getTextRange();
256 else {
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));
262 return errorResult;
265 return null;
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)) {
278 return i;
281 return -1;
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;
291 return false;
294 @Nullable
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;
307 if (isThisOrSuper) {
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);
329 else {
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;
355 else {
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());
361 else {
362 highlightInfo = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(element, methodExpression,
363 resolveResult.getSubstitutor());
367 else {
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));
372 else {
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) {
381 highlightInfo =
382 GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(element, referenceToMethod, resolveResult.getSubstitutor());
384 result.add(highlightInfo);
385 return result;
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;
403 else {
404 methodCandidate2 = candidate;
405 break;
409 MethodCandidateInfo[] candidates = toMethodCandidates(resolveResults);
411 String description;
412 String toolTip;
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;
430 else {
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();
439 else {
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;
446 else {
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>";
549 return ms;
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,
563 String methodName,
564 PsiSubstitutor substitutor,
565 PsiClass aClass) {
566 PsiExpression[] expressions = list.getExpressions();
567 int cols = Math.max(parameters.length, expressions.length);
569 @NonNls String parensizedName = methodName + (parameters.length == 0 ? "(&nbsp;)&nbsp;" : "");
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))
592 + "</font>"
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>" : "") +
597 "&nbsp;</td>";
599 return ms;
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>";
615 return ms;
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())
626 : null;
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)
636 && aClass != null
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,
643 start, end,
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));
651 return errorResult;
655 static HighlightInfo checkAbstractMethodInConcreteClass(PsiMethod method, PsiElement elementToHighlight) {
656 HighlightInfo errorResult = null;
657 PsiClass aClass = method.getContainingClass();
658 if (method.hasModifierProperty(PsiModifier.ABSTRACT)
659 && aClass != null
660 && !aClass.hasModifierProperty(PsiModifier.ABSTRACT)
661 && !aClass.isEnum()
662 && !PsiUtil.hasErrorElementChild(method)) {
663 errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
664 elementToHighlight,
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);
674 return errorResult;
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"));
690 return errorResult;
694 static HighlightInfo checkDuplicateMethod(PsiClass aClass, PsiMethod method) {
695 if (aClass == null) return null;
696 MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
697 int methodCount = 0;
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)) {
702 methodCount++;
703 if (methodCount > 1) break;
707 if (methodCount == 1 && aClass.isEnum() &&
708 GenericsHighlightUtil.isEnumSyntheticMethod(methodSignature, aClass.getProject())) {
709 methodCount++;
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);
718 return null;
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);
745 return info;
747 return null;
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();
760 while (true) {
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);
781 return null;
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))
801 .getElement();
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);
806 if (info != null) {
807 QuickFixAction.registerQuickFixAction(info, new InsertSuperFix(constructor));
808 QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddDefaultConstructorFix(aClass.getSuperClass()));
810 return info;
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()) {
821 return null;
824 PsiClass aClass = method.getContainingClass();
825 if (aClass == null) return null;
826 PsiClass superClass = aClass.getSuperClass();
827 PsiMethod superMethod = superClass == null
828 ? 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;
834 if (!isStatic) {
835 // all methods in interface are instance, so no possible errors in this case
836 return null;
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;
845 return null;
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)) {
856 return null;
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);
881 return info;
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;
892 return null;
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;
919 continue;
922 return createIncompatibleReturnTypeMessage(currentMethod, currentMethod, otherSuperMethod, false, otherSuperReturnType,
923 currentType, JavaErrorMessages.message("unrelated.overriding.methods.return.types"));
925 return null;
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();
932 Ultimate:
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;
944 if (allAbstracts) {
945 if (!containingClass.equals(aClass)) {
946 superSignatures = new ArrayList<HierarchicalMethodSignature>(superSignatures);
947 superSignatures.add(signature);
949 highlightInfo = checkInterfaceInheritedMethodsReturnTypes(superSignatures);
951 else {
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()));
967 break Ultimate;
970 continue;
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,
991 textRange,
992 errorDescription);
994 return null;
998 public static HighlightInfo checkConstructorHandleSuperClassExceptions(PsiMethod method) {
999 if (!method.isConstructor()) {
1000 return null;
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,
1013 textRange,
1014 description);
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"));
1027 return null;
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);
1039 return range;
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,
1063 PsiType type,
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);
1082 holder.add(info);
1083 return;
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;
1099 holder.add(info);
1100 return;
1102 if (classReference != null && aClass.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass)) {
1103 holder.add(buildAccessProblem(classReference, typeResolveResult, aClass));
1106 else {
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;
1120 try {
1121 applicable = result.isApplicable();
1123 catch (IndexNotReadyException e) {
1124 // ignore
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;
1144 holder.add(info);
1146 else {
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;
1167 holder.add(info);
1169 else {
1170 HighlightInfo highlightInfo = GenericsHighlightUtil.checkUncheckedCall(result, constructorCall);
1171 if (highlightInfo != null) {
1172 holder.add(highlightInfo);
1173 return;
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);
1181 return;
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());
1200 return info;
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();
1209 while (true) {
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)) {
1215 return true;
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);
1254 return false;
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++) {
1262 if (i > 0) {
1263 builder.append(", ");
1265 PsiType argType = args[i].getType();
1266 builder.append(argType != null ? HighlightUtil.formatType(argType) : "?");
1268 builder.append(")");
1269 return builder.toString();