blob: 83af5606d8c88539f10455d8811a6cddf5265f80 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.DaemonBundle;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.RefCountHolder;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.LocalQuickFixOnPsiElementAsIntentionAdapter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.ui.ColorUtil;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Highlight method problems
*
* @author cdr
* Date: Aug 14, 2002
*/
public class HighlightMethodUtil {
private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance();
public static final String MISMATCH_COLOR = UIUtil.isUnderDarcula() ? "ff6464" : "red";
private HighlightMethodUtil() { }
public static String createClashMethodMessage(PsiMethod method1, PsiMethod method2, boolean showContainingClasses) {
@NonNls String pattern = showContainingClasses ? "clash.methods.message.show.classes" : "clash.methods.message";
return JavaErrorMessages.message(pattern,
JavaHighlightUtil.formatMethod(method1),
JavaHighlightUtil.formatMethod(method2),
HighlightUtil.formatClass(method1.getContainingClass()),
HighlightUtil.formatClass(method2.getContainingClass()));
}
static HighlightInfo checkMethodWeakerPrivileges(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo,
@NotNull PsiFile containingFile) {
PsiMethod method = methodSignature.getMethod();
PsiModifierList modifierList = method.getModifierList();
if (modifierList.hasModifierProperty(PsiModifier.PUBLIC)) return null;
int accessLevel = PsiUtil.getAccessLevel(modifierList);
String accessModifier = PsiUtil.getAccessModifier(accessLevel);
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
if (method.hasModifierProperty(PsiModifier.ABSTRACT) && !MethodSignatureUtil.isSuperMethod(superMethod, method)) continue;
if (!PsiUtil.isAccessible(containingFile.getProject(), superMethod, method, null)) continue;
HighlightInfo info = isWeaker(method, modifierList, accessModifier, accessLevel, superMethod, includeRealPositionInfo);
if (info != null) return info;
}
return null;
}
private static HighlightInfo isWeaker(final PsiMethod method, final PsiModifierList modifierList, final String accessModifier, final int accessLevel,
final PsiMethod superMethod,
final boolean includeRealPositionInfo) {
int superAccessLevel = PsiUtil.getAccessLevel(superMethod.getModifierList());
if (accessLevel < superAccessLevel) {
String description = JavaErrorMessages.message("weaker.privileges",
createClashMethodMessage(method, superMethod, true),
accessModifier,
PsiUtil.getAccessModifier(superAccessLevel));
TextRange textRange;
if (includeRealPositionInfo) {
if (modifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) {
textRange = method.getNameIdentifier().getTextRange();
}
else {
PsiElement keyword = PsiUtil.findModifierInList(modifierList, accessModifier);
textRange = keyword.getTextRange();
}
}
else {
textRange = TextRange.EMPTY_RANGE;
}
HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
QuickFixAction.registerQuickFixAction(highlightInfo,
QUICK_FIX_FACTORY.createModifierListFix(method, PsiUtil.getAccessModifier(superAccessLevel), true, false));
return highlightInfo;
}
return null;
}
static HighlightInfo checkMethodIncompatibleReturnType(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo) {
return checkMethodIncompatibleReturnType(methodSignature, superMethodSignatures, includeRealPositionInfo, null);
}
static HighlightInfo checkMethodIncompatibleReturnType(MethodSignatureBackedByPsiMethod methodSignature,
List<HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo,
TextRange textRange) {
PsiMethod method = methodSignature.getMethod();
PsiType returnType = methodSignature.getSubstitutor().substitute(method.getReturnType());
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
PsiType declaredReturnType = superMethod.getReturnType();
PsiType superReturnType = declaredReturnType;
if (superMethodSignature.isRaw()) superReturnType = TypeConversionUtil.erasure(declaredReturnType);
if (returnType == null || superReturnType == null || method == superMethod) continue;
PsiClass superClass = superMethod.getContainingClass();
if (superClass == null) continue;
TextRange toHighlight = textRange != null ? textRange
: includeRealPositionInfo ? method.getReturnTypeElement().getTextRange() : TextRange.EMPTY_RANGE;
HighlightInfo highlightInfo = checkSuperMethodSignature(superMethod, superMethodSignature, superReturnType, method, methodSignature,
returnType, JavaErrorMessages.message("incompatible.return.type"),
toHighlight);
if (highlightInfo != null) return highlightInfo;
}
return null;
}
private static HighlightInfo checkSuperMethodSignature(@NotNull PsiMethod superMethod,
@NotNull MethodSignatureBackedByPsiMethod superMethodSignature,
PsiType superReturnType,
@NotNull PsiMethod method,
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull PsiType returnType,
@NotNull String detailMessage,
@NotNull TextRange range) {
if (superReturnType == null) return null;
if ("clone".equals(method.getName())) {
final PsiClass containingClass = method.getContainingClass();
final PsiClass superContainingClass = superMethod.getContainingClass();
if (containingClass != null && superContainingClass != null && containingClass.isInterface() && !superContainingClass.isInterface()) {
return null;
}
}
PsiType substitutedSuperReturnType;
final boolean isJdk15 = PsiUtil.isLanguageLevel5OrHigher(method);
if (isJdk15 && !superMethodSignature.isRaw() && superMethodSignature.equals(methodSignature)) { //see 8.4.5
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature,
superMethodSignature);
substitutedSuperReturnType = unifyingSubstitutor == null
? superReturnType
: unifyingSubstitutor.substitute(superReturnType);
}
else {
substitutedSuperReturnType = TypeConversionUtil.erasure(superMethodSignature.getSubstitutor().substitute(superReturnType));
}
if (returnType.equals(substitutedSuperReturnType)) return null;
if (!(returnType instanceof PsiPrimitiveType) && substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType) {
if (isJdk15 && TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType)) {
return null;
}
}
return createIncompatibleReturnTypeMessage(method, superMethod, substitutedSuperReturnType, returnType, detailMessage, range);
}
private static HighlightInfo createIncompatibleReturnTypeMessage(@NotNull PsiMethod method,
@NotNull PsiMethod superMethod,
@NotNull PsiType substitutedSuperReturnType,
@NotNull PsiType returnType,
@NotNull String detailMessage,
@NotNull TextRange textRange) {
String description = MessageFormat.format("{0}; {1}", createClashMethodMessage(method, superMethod, true), detailMessage);
HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createMethodReturnFix(method, substitutedSuperReturnType, false));
QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createSuperMethodReturnFix(superMethod, returnType));
return errorResult;
}
static HighlightInfo checkMethodOverridesFinal(MethodSignatureBackedByPsiMethod methodSignature,
List<HierarchicalMethodSignature> superMethodSignatures) {
PsiMethod method = methodSignature.getMethod();
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
HighlightInfo info = checkSuperMethodIsFinal(method, superMethod);
if (info != null) return info;
}
return null;
}
private static HighlightInfo checkSuperMethodIsFinal(PsiMethod method, PsiMethod superMethod) {
// strange things happen when super method is from Object and method from interface
if (superMethod.hasModifierProperty(PsiModifier.FINAL)) {
String description = JavaErrorMessages.message("final.method.override",
JavaHighlightUtil.formatMethod(method),
JavaHighlightUtil.formatMethod(superMethod),
HighlightUtil.formatClass(superMethod.getContainingClass()));
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
QuickFixAction.registerQuickFixAction(errorResult,
QUICK_FIX_FACTORY.createModifierListFix(superMethod, PsiModifier.FINAL, false, true));
return errorResult;
}
return null;
}
static HighlightInfo checkMethodIncompatibleThrows(MethodSignatureBackedByPsiMethod methodSignature,
List<HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo, PsiClass analyzedClass) {
PsiMethod method = methodSignature.getMethod();
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(aClass, analyzedClass, PsiSubstitutor.EMPTY);
PsiClassType[] exceptions = method.getThrowsList().getReferencedTypes();
PsiJavaCodeReferenceElement[] referenceElements;
List<PsiElement> exceptionContexts;
if (includeRealPositionInfo) {
exceptionContexts = new ArrayList<PsiElement>();
referenceElements = method.getThrowsList().getReferenceElements();
}
else {
exceptionContexts = null;
referenceElements = null;
}
List<PsiClassType> checkedExceptions = new ArrayList<PsiClassType>();
for (int i = 0; i < exceptions.length; i++) {
PsiClassType exception = exceptions[i];
if (!ExceptionUtil.isUncheckedException(exception)) {
checkedExceptions.add(exception);
if (includeRealPositionInfo && i < referenceElements.length) {
PsiJavaCodeReferenceElement exceptionRef = referenceElements[i];
exceptionContexts.add(exceptionRef);
}
}
}
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
int index = getExtraExceptionNum(methodSignature, superMethodSignature, checkedExceptions, superSubstitutor);
if (index != -1) {
if (aClass.isInterface()) {
final PsiClass superContainingClass = superMethod.getContainingClass();
if (superContainingClass != null && !superContainingClass.isInterface()) continue;
if (superContainingClass != null && !aClass.isInheritor(superContainingClass, true)) continue;
}
PsiClassType exception = checkedExceptions.get(index);
String description = JavaErrorMessages.message("overridden.method.does.not.throw",
createClashMethodMessage(method, superMethod, true),
JavaHighlightUtil.formatType(exception));
TextRange textRange;
if (includeRealPositionInfo) {
PsiElement exceptionContext = exceptionContexts.get(index);
textRange = exceptionContext.getTextRange();
}
else {
textRange = TextRange.EMPTY_RANGE;
}
HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
QuickFixAction.registerQuickFixAction(errorResult, new LocalQuickFixOnPsiElementAsIntentionAdapter(QUICK_FIX_FACTORY.createMethodThrowsFix(method, exception, false, false)));
QuickFixAction.registerQuickFixAction(errorResult, new LocalQuickFixOnPsiElementAsIntentionAdapter(QUICK_FIX_FACTORY.createMethodThrowsFix(superMethod, exception, true, true)));
return errorResult;
}
}
return null;
}
// return number of exception which was not declared in super method or -1
private static int getExtraExceptionNum(final MethodSignature methodSignature,
final MethodSignatureBackedByPsiMethod superSignature,
List<PsiClassType> checkedExceptions, PsiSubstitutor substitutorForDerivedClass) {
PsiMethod superMethod = superSignature.getMethod();
PsiSubstitutor substitutorForMethod = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superSignature);
for (int i = 0; i < checkedExceptions.size(); i++) {
final PsiClassType checkedEx = checkedExceptions.get(i);
final PsiType substituted = substitutorForMethod != null ? substitutorForMethod.substitute(checkedEx) : TypeConversionUtil.erasure(checkedEx);
PsiType exception = substitutorForDerivedClass.substitute(substituted);
if (!isMethodThrows(superMethod, substitutorForMethod, exception, substitutorForDerivedClass)) {
return i;
}
}
return -1;
}
private static boolean isMethodThrows(PsiMethod method, @Nullable PsiSubstitutor substitutorForMethod, PsiType exception, PsiSubstitutor substitutorForDerivedClass) {
PsiClassType[] thrownExceptions = method.getThrowsList().getReferencedTypes();
for (PsiClassType thrownException1 : thrownExceptions) {
PsiType thrownException = substitutorForMethod != null ? substitutorForMethod.substitute(thrownException1) : TypeConversionUtil.erasure(thrownException1);
thrownException = substitutorForDerivedClass.substitute(thrownException);
if (TypeConversionUtil.isAssignable(thrownException, exception)) return true;
}
return false;
}
@Nullable
static HighlightInfo checkMethodCall(@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper,
@NotNull LanguageLevel languageLevel,
@NotNull JavaSdkVersion javaSdkVersion) {
PsiExpressionList list = methodCall.getArgumentList();
PsiReferenceExpression referenceToMethod = methodCall.getMethodExpression();
JavaResolveResult[] results = referenceToMethod.multiResolve(true);
JavaResolveResult resolveResult = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
PsiElement resolved = resolveResult.getElement();
boolean isDummy = isDummyConstructorCall(methodCall, resolveHelper, list, referenceToMethod);
if (isDummy) return null;
HighlightInfo highlightInfo;
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
if (resolved instanceof PsiMethod && resolveResult.isValidResult()) {
TextRange fixRange = getFixRange(methodCall);
highlightInfo = HighlightUtil.checkUnhandledExceptions(methodCall, fixRange);
if (highlightInfo == null) {
if (!LambdaUtil.isValidQualifier4InterfaceStaticMethodCall((PsiMethod)resolved, methodCall.getMethodExpression(), resolveResult.getCurrentFileResolveScope(), languageLevel)) {
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip("Static method may be invoked on containing interface class only").range(fixRange).create();
} else {
highlightInfo = GenericsHighlightUtil.checkInferredIntersections(substitutor, fixRange);
}
}
}
else {
PsiMethod resolvedMethod = null;
MethodCandidateInfo candidateInfo = null;
if (resolveResult instanceof MethodCandidateInfo) {
candidateInfo = (MethodCandidateInfo)resolveResult;
resolvedMethod = candidateInfo.getElement();
}
if (!resolveResult.isAccessible() || !resolveResult.isStaticsScopeCorrect()) {
highlightInfo = null;
}
else if (candidateInfo != null && !candidateInfo.isApplicable()) {
if (candidateInfo.isTypeArgumentsApplicable()) {
String methodName = HighlightMessageUtil.getSymbolName(resolved, substitutor);
PsiElement parent = resolved.getParent();
String containerName = parent == null ? "" : HighlightMessageUtil.getSymbolName(parent, substitutor);
String argTypes = buildArgTypesList(list);
String description = JavaErrorMessages.message("wrong.method.arguments", methodName, containerName, argTypes);
final Ref<PsiElement> elementToHighlight = new Ref<PsiElement>(list);
String toolTip;
if (parent instanceof PsiClass && !ApplicationManager.getApplication().isUnitTestMode()) {
toolTip = buildOneLineMismatchDescription(list, candidateInfo, elementToHighlight);
if (toolTip == null) {
toolTip = createMismatchedArgumentsHtmlTooltip(candidateInfo, list);
}
}
else {
toolTip = description;
}
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(elementToHighlight.get())
.description(description).escapedToolTip(toolTip).navigationShift(+1).create();
if (highlightInfo != null) {
registerMethodCallIntentions(highlightInfo, methodCall, list, resolveHelper);
}
}
else {
PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
PsiReferenceParameterList typeArgumentList = methodCall.getTypeArgumentList();
if (typeArgumentList.getTypeArguments().length == 0 && resolvedMethod != null && resolvedMethod.hasTypeParameters()) {
highlightInfo = GenericsHighlightUtil.checkInferredTypeArguments(resolvedMethod, methodCall, substitutor);
}
else {
highlightInfo = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, methodExpression, substitutor, javaSdkVersion);
}
}
}
else {
String description = JavaErrorMessages.message("method.call.expected");
highlightInfo =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCall).descriptionAndTooltip(description).create();
if (resolved instanceof PsiClass) {
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createInsertNewFix(methodCall, (PsiClass)resolved));
}
else {
TextRange range = getFixRange(methodCall);
QuickFixAction.registerQuickFixAction(highlightInfo, range, QUICK_FIX_FACTORY.createCreateMethodFromUsageFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, range, QUICK_FIX_FACTORY.createCreateAbstractMethodFromUsageFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, range, QUICK_FIX_FACTORY.createCreatePropertyFromUsageFix(methodCall));
}
}
}
if (highlightInfo == null) {
highlightInfo = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, substitutor,
javaSdkVersion);
}
return highlightInfo;
}
private static String buildOneLineMismatchDescription(final PsiExpressionList list,
final MethodCandidateInfo candidateInfo,
final Ref<PsiElement> elementToHighlight) {
final PsiExpression[] expressions = list.getExpressions();
final PsiMethod resolvedMethod = candidateInfo.getElement();
final PsiSubstitutor substitutor = candidateInfo.getSubstitutor();
final PsiParameter[] parameters = resolvedMethod.getParameterList().getParameters();
if (expressions.length == parameters.length && parameters.length > 1) {
int idx = -1;
for (int i = 0; i < expressions.length; i++) {
PsiExpression expression = expressions[i];
if (!TypeConversionUtil.areTypesAssignmentCompatible(substitutor.substitute(parameters[i].getType()), expression)) {
if (idx != -1) {
idx = -1;
break;
}
else {
idx = i;
}
}
}
if (idx > -1) {
final PsiExpression wrongArg = expressions[idx];
final PsiType argType = wrongArg.getType();
if (argType != null) {
elementToHighlight.set(wrongArg);
final String message = JavaErrorMessages
.message("incompatible.call.types", idx + 1, substitutor.substitute(parameters[idx].getType()).getCanonicalText(), argType.getCanonicalText());
return XmlStringUtil.wrapInHtml("<body>" + XmlStringUtil.escapeString(message) +
" <a href=\"#assignment/" + XmlStringUtil.escapeString(createMismatchedArgumentsHtmlTooltip(candidateInfo, list)) + "\"" +
(UIUtil.isUnderDarcula() ? " color=\"7AB4C9\" " : "") +
">" + DaemonBundle.message("inspection.extended.description") + "</a></body>");
}
}
}
return null;
}
static boolean isDummyConstructorCall(PsiMethodCallExpression methodCall,
PsiResolveHelper resolveHelper,
PsiExpressionList list,
PsiReferenceExpression referenceToMethod) {
boolean isDummy = false;
boolean isThisOrSuper = referenceToMethod.getReferenceNameElement() instanceof PsiKeyword;
if (isThisOrSuper) {
// super(..) or this(..)
if (list.getExpressions().length == 0) { // implicit ctr call
CandidateInfo[] candidates = resolveHelper.getReferencedMethodCandidates(methodCall, true);
if (candidates.length == 1 && !candidates[0].getElement().isPhysical()) {
isDummy = true;// dummy constructor
}
}
}
return isDummy;
}
@Nullable
static HighlightInfo checkAmbiguousMethodCallIdentifier(@NotNull PsiReferenceExpression referenceToMethod,
@NotNull JavaResolveResult[] resolveResults,
@NotNull PsiExpressionList list,
final PsiElement element,
@NotNull JavaResolveResult resolveResult,
@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper) {
MethodCandidateInfo methodCandidate1 = null;
MethodCandidateInfo methodCandidate2 = null;
for (JavaResolveResult result : resolveResults) {
if (!(result instanceof MethodCandidateInfo)) continue;
MethodCandidateInfo candidate = (MethodCandidateInfo)result;
if (candidate.isApplicable() && !candidate.getElement().isConstructor()) {
if (methodCandidate1 == null) {
methodCandidate1 = candidate;
}
else {
methodCandidate2 = candidate;
break;
}
}
}
MethodCandidateInfo[] candidates = toMethodCandidates(resolveResults);
String description;
String toolTip;
PsiElement elementToHighlight;
HighlightInfoType highlightInfoType = HighlightInfoType.ERROR;
if (methodCandidate2 != null) {
return null;
}
if (element != null && !resolveResult.isAccessible()) {
description = HighlightUtil.buildProblemWithAccessDescription(referenceToMethod, resolveResult);
elementToHighlight = referenceToMethod.getReferenceNameElement();
}
else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
description = HighlightUtil.buildProblemWithStaticDescription(element);
elementToHighlight = referenceToMethod.getReferenceNameElement();
}
else {
String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list);
description = JavaErrorMessages.message("cannot.resolve.method", methodName);
if (candidates.length == 0) {
elementToHighlight = referenceToMethod.getReferenceNameElement();
highlightInfoType = HighlightInfoType.WRONG_REF;
}
else {
return null;
}
}
toolTip = XmlStringUtil.escapeString(description);
HighlightInfo info =
HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description).escapedToolTip(toolTip).create();
registerMethodCallIntentions(info, methodCall, list, resolveHelper);
if (element != null && !resolveResult.isStaticsScopeCorrect()) {
HighlightUtil.registerStaticProblemQuickFixAction(element, info, referenceToMethod);
}
TextRange fixRange = getFixRange(elementToHighlight);
CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, info, fixRange);
WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
PermuteArgumentsFix.registerFix(info, methodCall, candidates, fixRange);
WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), info);
registerChangeParameterClassFix(methodCall, list, info);
return info;
}
@Nullable
static HighlightInfo checkAmbiguousMethodCallArguments(@NotNull PsiReferenceExpression referenceToMethod,
@NotNull JavaResolveResult[] resolveResults,
@NotNull PsiExpressionList list,
final PsiElement element,
@NotNull JavaResolveResult resolveResult,
@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper) {
MethodCandidateInfo methodCandidate1 = null;
MethodCandidateInfo methodCandidate2 = null;
for (JavaResolveResult result : resolveResults) {
if (!(result instanceof MethodCandidateInfo)) continue;
MethodCandidateInfo candidate = (MethodCandidateInfo)result;
if (candidate.isApplicable() && !candidate.getElement().isConstructor()) {
if (methodCandidate1 == null) {
methodCandidate1 = candidate;
}
else {
methodCandidate2 = candidate;
break;
}
}
}
MethodCandidateInfo[] candidates = toMethodCandidates(resolveResults);
String description;
String toolTip;
PsiElement elementToHighlight;
HighlightInfoType highlightInfoType = HighlightInfoType.ERROR;
if (methodCandidate2 != null) {
PsiMethod element1 = methodCandidate1.getElement();
String m1 = PsiFormatUtil.formatMethod(element1,
methodCandidate1.getSubstitutor(),
PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
PsiMethod element2 = methodCandidate2.getElement();
String m2 = PsiFormatUtil.formatMethod(element2,
methodCandidate2.getSubstitutor(),
PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
VirtualFile virtualFile1 = PsiUtilCore.getVirtualFile(element1);
VirtualFile virtualFile2 = PsiUtilCore.getVirtualFile(element2);
if (!Comparing.equal(virtualFile1, virtualFile2)) {
if (virtualFile1 != null) m1 += " (In " + virtualFile1.getPresentableUrl() + ")";
if (virtualFile2 != null) m2 += " (In " + virtualFile2.getPresentableUrl() + ")";
}
description = JavaErrorMessages.message("ambiguous.method.call", m1, m2);
toolTip = createAmbiguousMethodHtmlTooltip(new MethodCandidateInfo[]{methodCandidate1, methodCandidate2});
elementToHighlight = list;
}
else {
if (element != null && !resolveResult.isAccessible()) {
return null;
}
else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
return null;
}
else {
String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list);
description = JavaErrorMessages.message("cannot.resolve.method", methodName);
if (candidates.length == 0) {
return null;
}
else {
elementToHighlight = list;
}
}
toolTip = XmlStringUtil.escapeString(description);
}
HighlightInfo info =
HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description).escapedToolTip(toolTip).create();
if (methodCandidate2 == null) {
registerMethodCallIntentions(info, methodCall, list, resolveHelper);
}
if (!resolveResult.isAccessible() && resolveResult.isStaticsScopeCorrect() && methodCandidate2 != null) {
HighlightUtil.registerAccessQuickFixAction((PsiMember)element, referenceToMethod, info, resolveResult.getCurrentFileResolveScope());
}
if (element != null && !resolveResult.isStaticsScopeCorrect()) {
HighlightUtil.registerStaticProblemQuickFixAction(element, info, referenceToMethod);
}
TextRange fixRange = getFixRange(elementToHighlight);
CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, info, fixRange);
WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
PermuteArgumentsFix.registerFix(info, methodCall, candidates, fixRange);
WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), info);
registerChangeParameterClassFix(methodCall, list, info);
return info;
}
@NotNull
private static MethodCandidateInfo[] toMethodCandidates(@NotNull JavaResolveResult[] resolveResults) {
List<MethodCandidateInfo> candidateList = new ArrayList<MethodCandidateInfo>(resolveResults.length);
for (JavaResolveResult result : resolveResults) {
if (!(result instanceof MethodCandidateInfo)) continue;
MethodCandidateInfo candidate = (MethodCandidateInfo)result;
if (candidate.isAccessible()) candidateList.add(candidate);
}
return candidateList.toArray(new MethodCandidateInfo[candidateList.size()]);
}
private static void registerMethodCallIntentions(@Nullable HighlightInfo highlightInfo,
PsiMethodCallExpression methodCall,
PsiExpressionList list,
PsiResolveHelper resolveHelper) {
TextRange fixRange = getFixRange(methodCall);
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateMethodFromUsageFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateAbstractMethodFromUsageFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateConstructorFromSuperFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateConstructorFromThisFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreatePropertyFromUsageFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createCreateGetterSetterPropertyFromUsageFix(methodCall));
CandidateInfo[] methodCandidates = resolveHelper.getReferencedMethodCandidates(methodCall, false);
CastMethodArgumentFix.REGISTRAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
PermuteArgumentsFix.registerFix(highlightInfo, methodCall, methodCandidates, fixRange);
AddTypeArgumentsFix.REGISTRAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
registerMethodAccessLevelIntentions(methodCandidates, methodCall, list, highlightInfo);
registerChangeMethodSignatureFromUsageIntentions(methodCandidates, list, highlightInfo, fixRange);
RemoveRedundantArgumentsFix.registerIntentions(methodCandidates, list, highlightInfo, fixRange);
ConvertDoubleToFloatFix.registerIntentions(methodCandidates, list, highlightInfo, fixRange);
WrapExpressionFix.registerWrapAction(methodCandidates, list.getExpressions(), highlightInfo);
registerChangeParameterClassFix(methodCall, list, highlightInfo);
if (methodCandidates.length == 0) {
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createStaticImportMethodFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.addMethodQualifierFix(methodCall));
}
for (IntentionAction action : QUICK_FIX_FACTORY.getVariableTypeFromCallFixes(methodCall, list)) {
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, action);
}
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createReplaceAddAllArrayToCollectionFix(methodCall));
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createSurroundWithArrayFix(methodCall,null));
QualifyThisArgumentFix.registerQuickFixAction(methodCandidates, methodCall, highlightInfo, fixRange);
CandidateInfo[] candidates = resolveHelper.getReferencedMethodCandidates(methodCall, true);
ChangeStringLiteralToCharInMethodCallFix.registerFixes(candidates, methodCall, highlightInfo);
}
private static void registerMethodAccessLevelIntentions(CandidateInfo[] methodCandidates,
PsiMethodCallExpression methodCall,
PsiExpressionList exprList,
HighlightInfo highlightInfo) {
for (CandidateInfo methodCandidate : methodCandidates) {
PsiMethod method = (PsiMethod)methodCandidate.getElement();
if (!methodCandidate.isAccessible() && PsiUtil.isApplicable(method, methodCandidate.getSubstitutor(), exprList)) {
HighlightUtil.registerAccessQuickFixAction(method, methodCall.getMethodExpression(), highlightInfo, methodCandidate.getCurrentFileResolveScope());
}
}
}
@NotNull
private static String createAmbiguousMethodHtmlTooltip(MethodCandidateInfo[] methodCandidates) {
return JavaErrorMessages.message("ambiguous.method.html.tooltip",
Integer.valueOf(methodCandidates[0].getElement().getParameterList().getParametersCount() + 2),
createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[0]),
getContainingClassName(methodCandidates[0]),
createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[1]),
getContainingClassName(methodCandidates[1]));
}
private static String getContainingClassName(final MethodCandidateInfo methodCandidate) {
PsiMethod method = methodCandidate.getElement();
PsiClass containingClass = method.getContainingClass();
return containingClass == null ? method.getContainingFile().getName() : HighlightUtil.formatClass(containingClass, false);
}
@Language("HTML")
private static String createAmbiguousMethodHtmlTooltipMethodRow(final MethodCandidateInfo methodCandidate) {
PsiMethod method = methodCandidate.getElement();
PsiParameter[] parameters = method.getParameterList().getParameters();
PsiSubstitutor substitutor = methodCandidate.getSubstitutor();
@NonNls @Language("HTML") String ms = "<td><b>" + method.getName() + "</b></td>";
for (int j = 0; j < parameters.length; j++) {
PsiParameter parameter = parameters[j];
PsiType type = substitutor.substitute(parameter.getType());
ms += "<td><b>" + (j == 0 ? "(" : "") +
XmlStringUtil.escapeString(type.getPresentableText())
+ (j == parameters.length - 1 ? ")" : ",") + "</b></td>";
}
if (parameters.length == 0) {
ms += "<td><b>()</b></td>";
}
return ms;
}
private static String createMismatchedArgumentsHtmlTooltip(MethodCandidateInfo info, PsiExpressionList list) {
PsiMethod method = info.getElement();
PsiSubstitutor substitutor = info.getSubstitutor();
PsiClass aClass = method.getContainingClass();
PsiParameter[] parameters = method.getParameterList().getParameters();
String methodName = method.getName();
return createMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass);
}
private static String createShortMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
PsiParameter[] parameters,
String methodName,
PsiSubstitutor substitutor,
PsiClass aClass) {
PsiExpression[] expressions = list.getExpressions();
int cols = Math.max(parameters.length, expressions.length);
@Language("HTML")
@NonNls String parensizedName = methodName + (parameters.length == 0 ? "(&nbsp;)&nbsp;" : "");
return JavaErrorMessages.message(
"argument.mismatch.html.tooltip",
Integer.valueOf(cols - parameters.length + 1), parensizedName,
HighlightUtil.formatClass(aClass, false),
createMismatchedArgsHtmlTooltipParamsRow(parameters, substitutor, expressions),
createMismatchedArgsHtmlTooltipArgumentsRow(expressions, parameters, substitutor, cols)
);
}
private static String esctrim(@NotNull String s) {
return XmlStringUtil.escapeString(trimNicely(s));
}
private static String trimNicely(String s) {
if (s.length() <= 40) return s;
List<TextRange> wordIndices = StringUtil.getWordIndicesIn(s);
if (wordIndices.size() > 2) {
int firstWordEnd = wordIndices.get(0).getEndOffset();
// try firstWord...remainder
for (int i = 1; i<wordIndices.size();i++) {
int stringLength = firstWordEnd + s.length() - wordIndices.get(i).getStartOffset();
if (stringLength <= 40) {
return s.substring(0, firstWordEnd) + "..." + s.substring(wordIndices.get(i).getStartOffset());
}
}
}
// maybe one last word will fit?
if (!wordIndices.isEmpty() && s.length() - wordIndices.get(wordIndices.size()-1).getStartOffset() <= 40) {
return "..." + s.substring(wordIndices.get(wordIndices.size()-1).getStartOffset());
}
return StringUtil.last(s, 40, true).toString();
}
private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
PsiParameter[] parameters,
String methodName,
PsiSubstitutor substitutor,
PsiClass aClass) {
return Math.max(parameters.length, list.getExpressions().length) <= 2
? createShortMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass)
: createLongMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass);
}
@Language("HTML")
private static String createLongMismatchedArgumentsHtmlTooltip(PsiExpressionList list,
PsiParameter[] parameters,
String methodName,
PsiSubstitutor substitutor,
PsiClass aClass) {
PsiExpression[] expressions = list.getExpressions();
@NonNls
String s = "<html><body><table border=0>" +
"<tr><td colspan=3>" +
"<nobr><b>" + methodName + "()</b> in <b>" + HighlightUtil.formatClass(aClass, false) +"</b> cannot be applied to:</nobr>" +
"</td></tr>"+
"<tr><td colspan=2 align=left>Expected<br>Parameters:</td><td align=left>Actual<br>Arguments:</td></tr>"+
"<tr><td colspan=3><hr></td></tr>"
;
for (int i = 0; i < Math.max(parameters.length,expressions.length); i++) {
PsiParameter parameter = i < parameters.length ? parameters[i] : null;
PsiExpression expression = i < expressions.length ? expressions[i] : null;
boolean showShort = showShortType(i, parameters, expressions, substitutor);
@NonNls String mismatchColor = showShort ? null : UIUtil.isUnderDarcula() ? "FF6B68" : "red";
s += "<tr" + (i % 2 == 0 ? " style='background-color: #"
+ (UIUtil.isUnderDarcula() ? ColorUtil.toHex(ColorUtil.shift(UIUtil.getToolTipBackground(), 1.1)) : "eeeeee")
+ "'" : "") + ">";
s += "<td><b><nobr>";
if (parameter != null) {
String name = parameter.getName();
if (name != null) {
s += esctrim(name) +":";
}
}
s += "</nobr></b></td>";
s += "<td><b><nobr>";
if (parameter != null) {
PsiType type = substitutor.substitute(parameter.getType());
s += "<font " + (mismatchColor == null ? "" : "color=" + mismatchColor) + ">" +
esctrim(showShort ? type.getPresentableText() : JavaHighlightUtil.formatType(type))
+ "</font>"
;
}
s += "</nobr></b></td>";
s += "<td><b><nobr>";
if (expression != null) {
PsiType type = expression.getType();
s += "<font " + (mismatchColor == null ? "" : "color='" + mismatchColor + "'") + ">" +
esctrim(expression.getText()) + "&nbsp;&nbsp;"+
(mismatchColor == null || type == null || type == PsiType.NULL ? "" : "("+esctrim(JavaHighlightUtil.formatType(type))+")")
+ "</font>"
;
}
s += "</nobr></b></td>";
s += "</tr>";
}
s+= "</table></body></html>";
return s;
}
@Language("HTML")
private static String createMismatchedArgsHtmlTooltipArgumentsRow(final PsiExpression[] expressions, final PsiParameter[] parameters,
final PsiSubstitutor substitutor, final int cols) {
@Language("HTML")
@NonNls String ms = "";
for (int i = 0; i < expressions.length; i++) {
PsiExpression expression = expressions[i];
PsiType type = expression.getType();
boolean showShort = showShortType(i, parameters, expressions, substitutor);
@NonNls String mismatchColor = showShort ? null : MISMATCH_COLOR;
ms += "<td> " + "<b><nobr>" + (i == 0 ? "(" : "")
+ "<font " + (showShort ? "" : "color=" + mismatchColor) + ">" +
XmlStringUtil.escapeString(showShort ? type.getPresentableText() : JavaHighlightUtil.formatType(type))
+ "</font>"
+ (i == expressions.length - 1 ? ")" : ",") + "</nobr></b></td>";
}
for (int i = expressions.length; i < cols + 1; i++) {
ms += "<td>" + (i == 0 ? "<b>()</b>" : "") +
"&nbsp;</td>";
}
return ms;
}
@Language("HTML")
private static String createMismatchedArgsHtmlTooltipParamsRow(final PsiParameter[] parameters,
final PsiSubstitutor substitutor,
final PsiExpression[] expressions) {
@NonNls String ms = "";
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
PsiType type = substitutor.substitute(parameter.getType());
ms += "<td><b><nobr>" + (i == 0 ? "(" : "") +
XmlStringUtil.escapeString(showShortType(i, parameters, expressions, substitutor)
? type.getPresentableText()
: JavaHighlightUtil.formatType(type))
+ (i == parameters.length - 1 ? ")" : ",") + "</nobr></b></td>";
}
return ms;
}
private static boolean showShortType(int i,
PsiParameter[] parameters,
PsiExpression[] expressions,
PsiSubstitutor substitutor) {
PsiExpression expression = i < expressions.length ? expressions[i] : null;
if (expression == null) return true;
PsiType paramType = i < parameters.length && parameters[i] != null
? substitutor.substitute(parameters[i].getType())
: null;
return paramType != null && TypeConversionUtil.areTypesAssignmentCompatible(paramType, expression);
}
static HighlightInfo checkMethodMustHaveBody(PsiMethod method, PsiClass aClass) {
HighlightInfo errorResult = null;
if (method.getBody() == null
&& !method.hasModifierProperty(PsiModifier.ABSTRACT)
&& !method.hasModifierProperty(PsiModifier.NATIVE)
&& aClass != null
&& !aClass.isInterface()
&& !PsiUtilCore.hasErrorElementChild(method)) {
int start = method.getModifierList().getTextRange().getStartOffset();
int end = method.getTextRange().getEndOffset();
String description = JavaErrorMessages.message("missing.method.body");
errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(start, end).descriptionAndTooltip(description).create();
if (HighlightUtil.getIncompatibleModifier(PsiModifier.ABSTRACT, method.getModifierList()) == null) {
QuickFixAction.registerQuickFixAction(errorResult,
QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, true, false));
}
QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
}
return errorResult;
}
static HighlightInfo checkAbstractMethodInConcreteClass(PsiMethod method, PsiElement elementToHighlight) {
HighlightInfo errorResult = null;
PsiClass aClass = method.getContainingClass();
if (method.hasModifierProperty(PsiModifier.ABSTRACT)
&& aClass != null
&& !aClass.hasModifierProperty(PsiModifier.ABSTRACT)
&& !aClass.isEnum()
&& !PsiUtilCore.hasErrorElementChild(method)) {
String description = JavaErrorMessages.message("abstract.method.in.non.abstract.class");
errorResult =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(elementToHighlight).descriptionAndTooltip(description).create();
if (method.getBody() != null) {
QuickFixAction.registerQuickFixAction(errorResult,
QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, false, false));
}
QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createModifierListFix(aClass, PsiModifier.ABSTRACT, true, false));
}
return errorResult;
}
static HighlightInfo checkConstructorName(PsiMethod method) {
String methodName = method.getName();
PsiClass aClass = method.getContainingClass();
HighlightInfo errorResult = null;
if (aClass != null) {
String className = aClass instanceof PsiAnonymousClass ? null : aClass.getName();
if (className == null || !Comparing.strEqual(methodName, className)) {
PsiElement element = method.getNameIdentifier();
String description = JavaErrorMessages.message("missing.return.type");
errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
if (className != null) {
QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createRenameElementFix(method, className));
}
}
}
return errorResult;
}
@Nullable
static HighlightInfo checkDuplicateMethod(PsiClass aClass,
@NotNull PsiMethod method,
@NotNull MostlySingularMultiMap<MethodSignature, PsiMethod> duplicateMethods) {
if (aClass == null || method instanceof ExternallyDefinedPsiElement) return null;
MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
int methodCount = 1;
List<PsiMethod> methods = (List<PsiMethod>)duplicateMethods.get(methodSignature);
if (methods.size() > 1) {
methodCount++;
}
if (methodCount == 1 && aClass.isEnum() &&
GenericsHighlightUtil.isEnumSyntheticMethod(methodSignature, aClass.getProject())) {
methodCount++;
}
if (methodCount > 1) {
String description = JavaErrorMessages.message("duplicate.method",
JavaHighlightUtil.formatMethod(method),
HighlightUtil.formatClass(aClass));
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).
range(method, textRange.getStartOffset(), textRange.getEndOffset()).
descriptionAndTooltip(description).create();
}
return null;
}
@Nullable
static HighlightInfo checkMethodCanHaveBody(@NotNull PsiMethod method, @NotNull LanguageLevel languageLevel) {
PsiClass aClass = method.getContainingClass();
boolean hasNoBody = method.getBody() == null;
boolean isInterface = aClass != null && aClass.isInterface();
boolean isExtension = method.hasModifierProperty(PsiModifier.DEFAULT);
boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
final List<IntentionAction> additionalFixes = new ArrayList<IntentionAction>();
String description = null;
if (hasNoBody) {
if (isExtension) {
description = JavaErrorMessages.message("extension.method.should.have.a.body");
additionalFixes.add(QUICK_FIX_FACTORY.createAddMethodBodyFix(method));
}
else if (isInterface && isStatic && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
description = "Static methods in interfaces should have a body";
}
}
else if (isInterface) {
if (!isExtension && !isStatic) {
description = JavaErrorMessages.message("interface.methods.cannot.have.body");
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
additionalFixes.add(QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.DEFAULT, true, false));
additionalFixes.add(QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.STATIC, true, false));
}
}
}
else if (isExtension) {
description = JavaErrorMessages.message("extension.method.in.class");
}
else if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
description = JavaErrorMessages.message("abstract.methods.cannot.have.a.body");
}
else if (method.hasModifierProperty(PsiModifier.NATIVE)) {
description = JavaErrorMessages.message("native.methods.cannot.have.a.body");
}
if (description == null) return null;
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
if (!hasNoBody) {
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createDeleteMethodBodyFix(method));
}
if (method.hasModifierProperty(PsiModifier.ABSTRACT) && !isInterface) {
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.ABSTRACT, false, false));
}
for (IntentionAction intentionAction : additionalFixes) {
QuickFixAction.registerQuickFixAction(info, intentionAction);
}
return info;
}
@Nullable
static HighlightInfo checkConstructorCallMustBeFirstStatement(@NotNull PsiMethodCallExpression methodCall) {
if (!RefactoringChangeUtil.isSuperOrThisMethodCall(methodCall)) return null;
PsiElement codeBlock = methodCall.getParent().getParent();
if (codeBlock instanceof PsiCodeBlock
&& codeBlock.getParent() instanceof PsiMethod
&& ((PsiMethod)codeBlock.getParent()).isConstructor()) {
PsiElement prevSibling = methodCall.getParent().getPrevSibling();
while (true) {
if (prevSibling == null) return null;
if (prevSibling instanceof PsiStatement) break;
prevSibling = prevSibling.getPrevSibling();
}
}
PsiReferenceExpression expression = methodCall.getMethodExpression();
String message = JavaErrorMessages.message("constructor.call.must.be.first.statement", expression.getText() + "()");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCall).descriptionAndTooltip(message).create();
}
static HighlightInfo checkSuperAbstractMethodDirectCall(@NotNull PsiMethodCallExpression methodCallExpression) {
PsiReferenceExpression expression = methodCallExpression.getMethodExpression();
if (!(expression.getQualifierExpression() instanceof PsiSuperExpression)) return null;
PsiMethod method = methodCallExpression.resolveMethod();
if (method != null && method.hasModifierProperty(PsiModifier.ABSTRACT)) {
String message = JavaErrorMessages.message("direct.abstract.method.access", JavaHighlightUtil.formatMethod(method));
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(methodCallExpression).descriptionAndTooltip(message).create();
}
return null;
}
static HighlightInfo checkConstructorCallsBaseClassConstructor(PsiMethod constructor,
RefCountHolder refCountHolder,
PsiResolveHelper resolveHelper) {
if (!constructor.isConstructor()) return null;
PsiClass aClass = constructor.getContainingClass();
if (aClass == null) return null;
if (aClass.isEnum()) return null;
PsiCodeBlock body = constructor.getBody();
if (body == null) return null;
// check whether constructor call super(...) or this(...)
PsiElement element = new PsiMatcherImpl(body)
.firstChild(PsiMatchers.hasClass(PsiExpressionStatement.class))
.firstChild(PsiMatchers.hasClass(PsiMethodCallExpression.class))
.firstChild(PsiMatchers.hasClass(PsiReferenceExpression.class))
.firstChild(PsiMatchers.hasClass(PsiKeyword.class))
.getElement();
if (element != null) return null;
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(constructor);
PsiClassType[] handledExceptions = constructor.getThrowsList().getReferencedTypes();
HighlightInfo info = HighlightClassUtil.checkBaseClassDefaultConstructorProblem(aClass, refCountHolder, resolveHelper, textRange, handledExceptions);
if (info != null) {
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createInsertSuperFix(constructor));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddDefaultConstructorFix(aClass.getSuperClass()));
}
return info;
}
/**
* @return error if static method overrides instance method or
* instance method overrides static. see JLS 8.4.6.1, 8.4.6.2
*/
static HighlightInfo checkStaticMethodOverride(@NotNull PsiMethod method,@NotNull PsiFile containingFile) {
// constructors are not members and therefor don't override class methods
if (method.isConstructor()) {
return null;
}
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
PsiClass superClass = aClass.getSuperClass();
PsiMethod superMethod = superClass == null
? null
: MethodSignatureUtil.findMethodBySignature(superClass, method, true);
boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
HighlightInfo highlightInfo = checkStaticMethodOverride(aClass, method, isStatic,superClass, superMethod,containingFile);
if (highlightInfo != null) return highlightInfo;
if (!isStatic) {
// all methods in interface are instance, so no possible errors in this case
return null;
}
PsiClass[] interfaces = aClass.getInterfaces();
for (PsiClass aInterfaces : interfaces) {
superClass = aInterfaces;
superMethod = MethodSignatureUtil.findMethodInSuperClassBySignatureInDerived(aClass, superClass, method.getSignature(PsiSubstitutor.EMPTY), true);
highlightInfo = checkStaticMethodOverride(aClass, method, true, superClass, superMethod,containingFile);
if (highlightInfo != null) return highlightInfo;
}
return null;
}
private static HighlightInfo checkStaticMethodOverride(PsiClass aClass, PsiMethod method, boolean isMethodStatic, PsiClass superClass, PsiMethod superMethod,@NotNull PsiFile containingFile) {
if (superMethod == null) return null;
PsiManager manager = containingFile.getManager();
PsiModifierList superModifierList = superMethod.getModifierList();
PsiModifierList modifierList = method.getModifierList();
if (superModifierList.hasModifierProperty(PsiModifier.PRIVATE)) return null;
if (superModifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)
&& !JavaPsiFacade.getInstance(manager.getProject()).arePackagesTheSame(aClass, superClass)) {
return null;
}
boolean isSuperMethodStatic = superModifierList.hasModifierProperty(PsiModifier.STATIC);
if (isMethodStatic != isSuperMethodStatic) {
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
@NonNls final String messageKey = isMethodStatic
? "static.method.cannot.override.instance.method"
: "instance.method.cannot.override.static.method";
String description = JavaErrorMessages.message(messageKey,
JavaHighlightUtil.formatMethod(method),
HighlightUtil.formatClass(aClass),
JavaHighlightUtil.formatMethod(superMethod),
HighlightUtil.formatClass(superClass));
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
if (!isSuperMethodStatic || HighlightUtil.getIncompatibleModifier(PsiModifier.STATIC, modifierList) == null) {
QuickFixAction.registerQuickFixAction(info,
QUICK_FIX_FACTORY.createModifierListFix(method, PsiModifier.STATIC, isSuperMethodStatic, false));
}
if (manager.isInProject(superMethod) &&
(!isMethodStatic || HighlightUtil.getIncompatibleModifier(PsiModifier.STATIC, superModifierList) == null)) {
QuickFixAction.registerQuickFixAction(info,
QUICK_FIX_FACTORY.createModifierListFix(superMethod, PsiModifier.STATIC, isMethodStatic, true));
}
return info;
}
if (isMethodStatic) {
if (superClass.isInterface()) return null;
int accessLevel = PsiUtil.getAccessLevel(modifierList);
String accessModifier = PsiUtil.getAccessModifier(accessLevel);
HighlightInfo info = isWeaker(method, modifierList, accessModifier, accessLevel, superMethod, true);
if (info != null) return info;
info = checkSuperMethodIsFinal(method, superMethod);
if (info != null) return info;
}
return null;
}
private static HighlightInfo checkInterfaceInheritedMethodsReturnTypes(List<? extends MethodSignatureBackedByPsiMethod> superMethodSignatures) {
if (superMethodSignatures.size() < 2) return null;
MethodSignatureBackedByPsiMethod returnTypeSubstitutable = superMethodSignatures.get(0);
for (int i = 1; i < superMethodSignatures.size(); i++) {
PsiMethod currentMethod = returnTypeSubstitutable.getMethod();
PsiType currentType = returnTypeSubstitutable.getSubstitutor().substitute(currentMethod.getReturnType());
MethodSignatureBackedByPsiMethod otherSuperSignature = superMethodSignatures.get(i);
PsiMethod otherSuperMethod = otherSuperSignature.getMethod();
PsiType otherSuperReturnType = otherSuperSignature.getSubstitutor().substitute(otherSuperMethod.getReturnType());
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable,
otherSuperSignature);
if (unifyingSubstitutor != null) {
otherSuperReturnType = unifyingSubstitutor.substitute(otherSuperReturnType);
currentType = unifyingSubstitutor.substitute(currentType);
}
if (otherSuperReturnType == null || currentType == null || otherSuperReturnType.equals(currentType)) continue;
if (PsiUtil.isLanguageLevel5OrHigher(currentMethod)) {
//http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8 Example 8.1.5-3
if (!(otherSuperReturnType instanceof PsiPrimitiveType || currentType instanceof PsiPrimitiveType)) {
if (otherSuperReturnType.isAssignableFrom(currentType)) continue;
if (currentType.isAssignableFrom(otherSuperReturnType)) {
returnTypeSubstitutable = otherSuperSignature;
continue;
}
}
if (currentMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(currentType, otherSuperReturnType)) continue;
}
return createIncompatibleReturnTypeMessage(currentMethod, otherSuperMethod, otherSuperReturnType,
currentType, JavaErrorMessages.message("unrelated.overriding.methods.return.types"),
TextRange.EMPTY_RANGE);
}
return null;
}
static HighlightInfo checkOverrideEquivalentInheritedMethods(PsiClass aClass, PsiFile containingFile) {
String description = null;
final Collection<HierarchicalMethodSignature> visibleSignatures = aClass.getVisibleSignatures();
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
Ultimate:
for (HierarchicalMethodSignature signature : visibleSignatures) {
PsiMethod method = signature.getMethod();
if (!resolveHelper.isAccessible(method, aClass, null)) continue;
List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
boolean allAbstracts = method.hasModifierProperty(PsiModifier.ABSTRACT);
final PsiClass containingClass = method.getContainingClass();
if (aClass.equals(containingClass)) continue; //to be checked at method level
if (aClass.isInterface() && !containingClass.isInterface()) continue;
HighlightInfo highlightInfo;
if (allAbstracts) {
superSignatures = new ArrayList<HierarchicalMethodSignature>(superSignatures);
superSignatures.add(signature);
highlightInfo = checkInterfaceInheritedMethodsReturnTypes(superSignatures);
}
else {
highlightInfo = checkMethodIncompatibleReturnType(signature, superSignatures, false);
}
if (highlightInfo != null) description = highlightInfo.getDescription();
if (method.hasModifierProperty(PsiModifier.STATIC)) {
for (HierarchicalMethodSignature superSignature : superSignatures) {
PsiMethod superMethod = superSignature.getMethod();
if (!superMethod.hasModifierProperty(PsiModifier.STATIC)) {
description = JavaErrorMessages.message("static.method.cannot.override.instance.method",
JavaHighlightUtil.formatMethod(method),
HighlightUtil.formatClass(containingClass),
JavaHighlightUtil.formatMethod(superMethod),
HighlightUtil.formatClass(superMethod.getContainingClass()));
break Ultimate;
}
}
continue;
}
if (description == null) {
highlightInfo = checkMethodIncompatibleThrows(signature, superSignatures, false, aClass);
if (highlightInfo != null) description = highlightInfo.getDescription();
}
if (description == null) {
highlightInfo = checkMethodWeakerPrivileges(signature, superSignatures, false, containingFile);
if (highlightInfo != null) description = highlightInfo.getDescription();
}
if (description != null) break;
}
if (description != null) {
// show error info at the class level
TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
}
return null;
}
static HighlightInfo checkConstructorHandleSuperClassExceptions(PsiMethod method) {
if (!method.isConstructor()) {
return null;
}
PsiCodeBlock body = method.getBody();
PsiStatement[] statements = body == null ? null : body.getStatements();
if (statements == null) return null;
// if we have unhandled exception inside method body, we could not have been called here,
// so the only problem it can catch here is with super ctr only
Collection<PsiClassType> unhandled = ExceptionUtil.collectUnhandledExceptions(method, method.getContainingClass());
if (unhandled.isEmpty()) return null;
String description = HighlightUtil.getUnhandledExceptionsDescriptor(unhandled);
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
for (PsiClassType exception : unhandled) {
QuickFixAction.registerQuickFixAction(highlightInfo, new LocalQuickFixOnPsiElementAsIntentionAdapter(QUICK_FIX_FACTORY.createMethodThrowsFix(method, exception, true, false)));
}
return highlightInfo;
}
static HighlightInfo checkRecursiveConstructorInvocation(PsiMethod method) {
if (HighlightControlFlowUtil.isRecursivelyCalledConstructor(method)) {
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
String description = JavaErrorMessages.message("recursive.constructor.invocation");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
}
return null;
}
@NotNull
public static TextRange getFixRange(@NotNull PsiElement element) {
TextRange range = element.getTextRange();
int start = range.getStartOffset();
int end = range.getEndOffset();
PsiElement nextSibling = element.getNextSibling();
if (nextSibling instanceof PsiJavaToken && ((PsiJavaToken)nextSibling).getTokenType() == JavaTokenType.SEMICOLON) {
return new TextRange(start, end + 1);
}
return range;
}
static void checkNewExpression(@NotNull PsiNewExpression expression,
PsiType type,
@NotNull HighlightInfoHolder holder,
@NotNull JavaSdkVersion javaSdkVersion) {
if (!(type instanceof PsiClassType)) return;
PsiClassType.ClassResolveResult typeResult = ((PsiClassType)type).resolveGenerics();
PsiClass aClass = typeResult.getElement();
if (aClass == null) return;
if (aClass instanceof PsiAnonymousClass) {
type = ((PsiAnonymousClass)aClass).getBaseClassType();
typeResult = ((PsiClassType)type).resolveGenerics();
aClass = typeResult.getElement();
if (aClass == null) return;
}
PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
checkConstructorCall(typeResult, expression, type, classReference, holder, javaSdkVersion);
}
public static void checkConstructorCall(PsiClassType.ClassResolveResult typeResolveResult,
PsiConstructorCall constructorCall,
PsiType type,
PsiJavaCodeReferenceElement classReference,
final HighlightInfoHolder holder,
@NotNull JavaSdkVersion javaSdkVersion) {
PsiExpressionList list = constructorCall.getArgumentList();
if (list == null) return;
PsiClass aClass = typeResolveResult.getElement();
if (aClass == null) return;
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(holder.getProject()).getResolveHelper();
PsiClass accessObjectClass = null;
if (constructorCall instanceof PsiNewExpression) {
PsiExpression qualifier = ((PsiNewExpression)constructorCall).getQualifier();
if (qualifier != null) {
accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
}
}
if (classReference != null && !resolveHelper.isAccessible(aClass, constructorCall, accessObjectClass)) {
String description = HighlightUtil.buildProblemWithAccessDescription(classReference, typeResolveResult);
PsiElement element = classReference.getReferenceNameElement();
HighlightInfo info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
HighlightUtil.registerAccessQuickFixAction(aClass, classReference, info, null);
holder.add(info);
return;
}
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 0) {
if (list.getExpressions().length != 0) {
String constructorName = aClass.getName();
String argTypes = buildArgTypesList(list);
String description = JavaErrorMessages.message("wrong.constructor.arguments", constructorName+"()", argTypes);
String tooltip = createMismatchedArgumentsHtmlTooltip(list, PsiParameter.EMPTY_ARRAY, constructorName, PsiSubstitutor.EMPTY, aClass);
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).description(description).escapedToolTip(tooltip).navigationShift(+1).create();
QuickFixAction.registerQuickFixAction(info, constructorCall.getTextRange(), QUICK_FIX_FACTORY.createCreateConstructorFromCallFix(constructorCall));
if (classReference != null) {
ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info,getFixRange(list));
}
holder.add(info);
return;
}
if (classReference != null && aClass.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass)) {
holder.add(buildAccessProblem(classReference, typeResolveResult, aClass));
} else if (aClass.isInterface() && constructorCall instanceof PsiNewExpression) {
final PsiReferenceParameterList typeArgumentList = ((PsiNewExpression)constructorCall).getTypeArgumentList();
if (typeArgumentList.getTypeArguments().length > 0) {
holder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeArgumentList)
.descriptionAndTooltip("Anonymous class implements interface; cannot have type arguments").create());
}
}
}
else {
PsiElement place = list;
if (constructorCall instanceof PsiNewExpression) {
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)constructorCall).getAnonymousClass();
if (anonymousClass != null) place = anonymousClass;
}
JavaResolveResult[] results = resolveHelper.multiResolveConstructor((PsiClassType)type, list, place);
MethodCandidateInfo result = null;
if (results.length == 1) result = (MethodCandidateInfo)results[0];
PsiMethod constructor = result == null ? null : result.getElement();
boolean applicable = true;
try {
applicable = constructor != null && result.isApplicable();
}
catch (IndexNotReadyException e) {
// ignore
}
PsiElement infoElement = list.getTextLength() > 0 ? list : constructorCall;
if (constructor == null) {
String name = aClass.getName();
name += buildArgTypesList(list);
String description = JavaErrorMessages.message("cannot.resolve.constructor", name);
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).navigationShift(+1).create();
WrapExpressionFix.registerWrapAction(results, list.getExpressions(), info);
registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, infoElement, info);
holder.add(info);
}
else {
if (classReference != null && (!result.isAccessible() ||
constructor.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass))) {
holder.add(buildAccessProblem(classReference, result, constructor));
}
else if (!applicable) {
String constructorName = HighlightMessageUtil.getSymbolName(constructor, result.getSubstitutor());
String containerName = HighlightMessageUtil.getSymbolName(constructor.getContainingClass(), result.getSubstitutor());
String argTypes = buildArgTypesList(list);
String description = JavaErrorMessages.message("wrong.method.arguments", constructorName, containerName, argTypes);
String toolTip = createMismatchedArgumentsHtmlTooltip(result, list);
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(infoElement).description(description).escapedToolTip(toolTip).navigationShift(+1).create();
if (info != null) {
registerFixesOnInvalidConstructorCall(constructorCall, classReference, list, aClass, constructors, results, infoElement, info);
holder.add(info);
}
}
else {
if (constructorCall instanceof PsiNewExpression) {
PsiReferenceParameterList typeArgumentList = ((PsiNewExpression)constructorCall).getTypeArgumentList();
HighlightInfo info = GenericsHighlightUtil.checkReferenceTypeArgumentList(constructor, typeArgumentList, result.getSubstitutor(), false, javaSdkVersion);
if (info != null) {
holder.add(info);
}
}
}
}
}
}
private static void registerFixesOnInvalidConstructorCall(PsiConstructorCall constructorCall,
PsiJavaCodeReferenceElement classReference,
PsiExpressionList list,
PsiClass aClass,
PsiMethod[] constructors,
JavaResolveResult[] results, PsiElement infoElement, HighlightInfo info) {
QuickFixAction
.registerQuickFixAction(info, constructorCall.getTextRange(), QUICK_FIX_FACTORY.createCreateConstructorFromCallFix(constructorCall));
if (classReference != null) {
ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info, getFixRange(infoElement));
ChangeTypeArgumentsFix.registerIntentions(results, list, info, aClass);
ConvertDoubleToFloatFix.registerIntentions(results, list, info, null);
}
registerChangeMethodSignatureFromUsageIntentions(results, list, info, null);
PermuteArgumentsFix.registerFix(info, constructorCall, toMethodCandidates(results), getFixRange(list));
registerChangeParameterClassFix(constructorCall, list, info);
QuickFixAction.registerQuickFixAction(info, getFixRange(list), QUICK_FIX_FACTORY.createSurroundWithArrayFix(constructorCall,null));
ChangeStringLiteralToCharInMethodCallFix.registerFixes(constructors, constructorCall, info);
}
private static HighlightInfo buildAccessProblem(@NotNull PsiJavaCodeReferenceElement classReference, JavaResolveResult result, PsiMember elementToFix) {
String description = HighlightUtil.buildProblemWithAccessDescription(classReference, result);
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classReference).descriptionAndTooltip(
description).navigationShift(+1).create();
if (result.isStaticsScopeCorrect()) {
HighlightUtil.registerAccessQuickFixAction(elementToFix, classReference, info, result.getCurrentFileResolveScope());
}
return info;
}
private static boolean callingProtectedConstructorFromDerivedClass(PsiConstructorCall place, PsiClass constructorClass) {
if (constructorClass == null) return false;
// indirect instantiation via anonymous class is ok
if (place instanceof PsiNewExpression && ((PsiNewExpression)place).getAnonymousClass() != null) return false;
PsiElement curElement = place;
PsiClass containingClass = constructorClass.getContainingClass();
while (true) {
PsiClass aClass = PsiTreeUtil.getParentOfType(curElement, PsiClass.class);
if (aClass == null) return false;
curElement = aClass;
if ((aClass.isInheritor(constructorClass, true) || containingClass != null && aClass.isInheritor(containingClass, true))
&& !JavaPsiFacade.getInstance(aClass.getProject()).arePackagesTheSame(aClass, constructorClass)) {
return true;
}
}
}
private static String buildArgTypesList(PsiExpressionList list) {
StringBuilder builder = new StringBuilder();
builder.append("(");
PsiExpression[] args = list.getExpressions();
for (int i = 0; i < args.length; i++) {
if (i > 0) {
builder.append(", ");
}
PsiType argType = args[i].getType();
builder.append(argType != null ? JavaHighlightUtil.formatType(argType) : "?");
}
builder.append(")");
return builder.toString();
}
private static void registerChangeParameterClassFix(@NotNull PsiCall methodCall,
@NotNull PsiExpressionList list,
HighlightInfo highlightInfo) {
final JavaResolveResult result = methodCall.resolveMethodGenerics();
PsiMethod method = (PsiMethod)result.getElement();
final PsiSubstitutor substitutor = result.getSubstitutor();
PsiExpression[] expressions = list.getExpressions();
if (method == null) return;
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != expressions.length) return;
for (int i = 0; i < expressions.length; i++) {
final PsiExpression expression = expressions[i];
final PsiParameter parameter = parameters[i];
final PsiType expressionType = expression.getType();
final PsiType parameterType = substitutor.substitute(parameter.getType());
if (expressionType == null || expressionType instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(expressionType) || expressionType instanceof PsiArrayType) continue;
if (parameterType instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(parameterType) || parameterType instanceof PsiArrayType) continue;
if (parameterType.isAssignableFrom(expressionType)) continue;
PsiClass parameterClass = PsiUtil.resolveClassInType(parameterType);
PsiClass expressionClass = PsiUtil.resolveClassInType(expressionType);
if (parameterClass == null || expressionClass == null) continue;
if (expressionClass instanceof PsiAnonymousClass) continue;
if (parameterClass.isInheritor(expressionClass, true)) continue;
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createChangeParameterClassFix(expressionClass, (PsiClassType)parameterType));
}
}
public static void registerChangeMethodSignatureFromUsageIntentions(@NotNull JavaResolveResult[] candidates,
@NotNull PsiExpressionList list,
@Nullable HighlightInfo highlightInfo,
TextRange fixRange) {
if (candidates.length == 0) return;
PsiExpression[] expressions = list.getExpressions();
for (JavaResolveResult candidate : candidates) {
registerChangeMethodSignatureFromUsageIntention(expressions, highlightInfo, fixRange, candidate, list);
}
}
private static void registerChangeMethodSignatureFromUsageIntention(@NotNull PsiExpression[] expressions,
@Nullable HighlightInfo highlightInfo,
TextRange fixRange,
@NotNull JavaResolveResult candidate,
@NotNull PsiElement context) {
if (!candidate.isStaticsScopeCorrect()) return;
PsiMethod method = (PsiMethod)candidate.getElement();
PsiSubstitutor substitutor = candidate.getSubstitutor();
if (method != null && context.getManager().isInProject(method)) {
IntentionAction fix = QUICK_FIX_FACTORY.createChangeMethodSignatureFromUsageFix(method, expressions, substitutor, context, false, 2);
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, fix);
IntentionAction f2 = QUICK_FIX_FACTORY.createChangeMethodSignatureFromUsageReverseOrderFix(method, expressions, substitutor, context,
false, 2);
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, f2);
}
}
}