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