| /* |
| * 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.daemon.JavaErrorMessages; |
| import com.intellij.codeInsight.daemon.impl.HighlightInfo; |
| import com.intellij.codeInsight.daemon.impl.HighlightInfoType; |
| import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; |
| import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixActionRegistrarImpl; |
| import com.intellij.codeInsight.intention.QuickFixFactory; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.DumbService; |
| import com.intellij.openapi.project.IndexNotReadyException; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.projectRoots.JavaSdkVersion; |
| import com.intellij.openapi.projectRoots.JavaVersionService; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.PsiShortNamesCache; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.search.searches.SuperMethodsSearch; |
| import com.intellij.psi.util.*; |
| import com.intellij.util.ArrayUtilRt; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.containers.HashSet; |
| import gnu.trove.THashMap; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| /** |
| * @author cdr |
| */ |
| public class GenericsHighlightUtil { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil"); |
| |
| private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance(); |
| |
| private GenericsHighlightUtil() { } |
| |
| @Nullable |
| public static HighlightInfo checkInferredTypeArguments(PsiTypeParameterListOwner listOwner, |
| PsiElement call, |
| PsiSubstitutor substitutor) { |
| return checkInferredTypeArguments(listOwner.getTypeParameters(), call, substitutor); |
| } |
| |
| @Nullable |
| public static HighlightInfo checkInferredTypeArguments(PsiTypeParameter[] typeParameters, |
| PsiElement call, |
| PsiSubstitutor substitutor) { |
| final Pair<PsiTypeParameter, PsiType> inferredTypeArgument = GenericsUtil.findTypeParameterWithBoundError(typeParameters, substitutor, call, false); |
| if (inferredTypeArgument != null) { |
| final PsiType extendsType = inferredTypeArgument.second; |
| final PsiTypeParameter typeParameter = inferredTypeArgument.first; |
| PsiClass boundClass = extendsType instanceof PsiClassType ? ((PsiClassType)extendsType).resolve() : null; |
| |
| @NonNls String messageKey = boundClass == null || typeParameter.isInterface() == boundClass.isInterface() |
| ? "generics.inferred.type.for.type.parameter.is.not.within.its.bound.extend" |
| : "generics.inferred.type.for.type.parameter.is.not.within.its.bound.implement"; |
| |
| String description = JavaErrorMessages.message( |
| messageKey, |
| HighlightUtil.formatClass(typeParameter), |
| JavaHighlightUtil.formatType(extendsType), |
| JavaHighlightUtil.formatType(substitutor.substitute(typeParameter)) |
| ); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(call).descriptionAndTooltip(description).create(); |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static HighlightInfo checkParameterizedReferenceTypeArguments(final PsiElement resolved, |
| final PsiJavaCodeReferenceElement referenceElement, |
| final PsiSubstitutor substitutor, |
| @NotNull JavaSdkVersion javaSdkVersion) { |
| if (!(resolved instanceof PsiTypeParameterListOwner)) return null; |
| final PsiTypeParameterListOwner typeParameterListOwner = (PsiTypeParameterListOwner)resolved; |
| return checkReferenceTypeArgumentList(typeParameterListOwner, referenceElement.getParameterList(), substitutor, true, javaSdkVersion); |
| } |
| |
| @Nullable |
| public static HighlightInfo checkReferenceTypeArgumentList(final PsiTypeParameterListOwner typeParameterListOwner, |
| final PsiReferenceParameterList referenceParameterList, |
| final PsiSubstitutor substitutor, |
| boolean registerIntentions, |
| @NotNull JavaSdkVersion javaSdkVersion) { |
| PsiDiamondType.DiamondInferenceResult inferenceResult = null; |
| PsiTypeElement[] referenceElements = null; |
| if (referenceParameterList != null) { |
| referenceElements = referenceParameterList.getTypeParameterElements(); |
| if (referenceElements.length == 1 && referenceElements[0].getType() instanceof PsiDiamondType) { |
| if (!typeParameterListOwner.hasTypeParameters()) { |
| final String description = JavaErrorMessages.message("generics.diamond.not.applicable"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceElements[0]).descriptionAndTooltip(description).create(); |
| } |
| inferenceResult = ((PsiDiamondType)referenceElements[0].getType()).resolveInferredTypes(); |
| final String errorMessage = inferenceResult.getErrorMessage(); |
| if (errorMessage != null) { |
| final PsiType expectedType = detectExpectedType(referenceParameterList); |
| if (!(inferenceResult.failedToInfer() && expectedType instanceof PsiClassType && ((PsiClassType)expectedType).isRaw())) { |
| return HighlightInfo |
| .newHighlightInfo(HighlightInfoType.ERROR).range(referenceElements[0]).descriptionAndTooltip(errorMessage).create(); |
| } |
| } |
| } |
| } |
| |
| final PsiTypeParameter[] typeParameters = typeParameterListOwner.getTypeParameters(); |
| final int targetParametersNum = typeParameters.length; |
| final int refParametersNum = referenceParameterList == null ? 0 : referenceParameterList.getTypeArguments().length; |
| if (targetParametersNum != refParametersNum && refParametersNum != 0) { |
| final String description; |
| if (targetParametersNum == 0) { |
| if (PsiTreeUtil.getParentOfType(referenceParameterList, PsiCall.class) != null && |
| typeParameterListOwner instanceof PsiMethod && |
| (javaSdkVersion.isAtLeast(JavaSdkVersion.JDK_1_7) || hasSuperMethodsWithTypeParams((PsiMethod)typeParameterListOwner))) { |
| description = null; |
| } |
| else { |
| description = JavaErrorMessages.message( |
| "generics.type.or.method.does.not.have.type.parameters", |
| typeParameterListOwnerCategoryDescription(typeParameterListOwner), |
| typeParameterListOwnerDescription(typeParameterListOwner) |
| ); |
| } |
| } |
| else { |
| description = JavaErrorMessages.message("generics.wrong.number.of.type.arguments", refParametersNum, targetParametersNum); |
| } |
| |
| if (description != null) { |
| final HighlightInfo highlightInfo = |
| HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceParameterList).descriptionAndTooltip(description).create(); |
| if (registerIntentions) { |
| PsiElement grandParent = referenceParameterList.getParent().getParent(); |
| if (grandParent instanceof PsiTypeElement) { |
| PsiElement variable = grandParent.getParent(); |
| if (variable instanceof PsiVariable) { |
| if (targetParametersNum == 0) { |
| QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createRemoveTypeArgumentsFix(variable)); |
| } |
| if (typeParameterListOwner instanceof PsiClass) { |
| QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createChangeClassSignatureFromUsageFix((PsiClass)typeParameterListOwner, referenceParameterList)); |
| } |
| registerVariableParameterizedTypeFixes(highlightInfo, (PsiVariable)variable, referenceParameterList, javaSdkVersion); |
| } |
| } |
| } |
| return highlightInfo; |
| } |
| } |
| |
| // bounds check |
| if (targetParametersNum > 0 && refParametersNum != 0) { |
| if (inferenceResult != null) { |
| final PsiType[] types = inferenceResult.getTypes(); |
| for (int i = 0; i < typeParameters.length; i++) { |
| final PsiType type = types[i]; |
| final HighlightInfo highlightInfo = checkTypeParameterWithinItsBound(typeParameters[i], substitutor, type, referenceElements[0], referenceParameterList); |
| if (highlightInfo != null) return highlightInfo; |
| } |
| } |
| else { |
| for (int i = 0; i < typeParameters.length; i++) { |
| final PsiTypeElement typeElement = referenceElements[i]; |
| final HighlightInfo highlightInfo = checkTypeParameterWithinItsBound(typeParameters[i], substitutor, typeElement.getType(), typeElement, referenceParameterList); |
| if (highlightInfo != null) return highlightInfo; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private static boolean hasSuperMethodsWithTypeParams(PsiMethod method) { |
| for (PsiMethod superMethod : method.findDeepestSuperMethods()) { |
| if (superMethod.hasTypeParameters()) return true; |
| } |
| return false; |
| } |
| |
| private static PsiType detectExpectedType(PsiReferenceParameterList referenceParameterList) { |
| final PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class); |
| LOG.assertTrue(newExpression != null); |
| final PsiElement parent = newExpression.getParent(); |
| PsiType expectedType = null; |
| if (parent instanceof PsiVariable && newExpression.equals(((PsiVariable)parent).getInitializer())) { |
| expectedType = ((PsiVariable)parent).getType(); |
| } |
| else if (parent instanceof PsiAssignmentExpression && newExpression.equals(((PsiAssignmentExpression)parent).getRExpression())) { |
| expectedType = ((PsiAssignmentExpression)parent).getLExpression().getType(); |
| } |
| else if (parent instanceof PsiReturnStatement) { |
| PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); |
| if (method != null) { |
| expectedType = method.getReturnType(); |
| } |
| } |
| else if (parent instanceof PsiExpressionList) { |
| final PsiElement pParent = parent.getParent(); |
| if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) { |
| final PsiMethod method = ((PsiCallExpression)pParent).resolveMethod(); |
| if (method != null) { |
| final PsiExpression[] expressions = ((PsiCallExpression)pParent).getArgumentList().getExpressions(); |
| final int idx = ArrayUtilRt.find(expressions, newExpression); |
| if (idx > -1) { |
| final PsiParameterList parameterList = method.getParameterList(); |
| if (idx < parameterList.getParametersCount()) { |
| expectedType = parameterList.getParameters()[idx].getType(); |
| } |
| } |
| } |
| } |
| } |
| return expectedType; |
| } |
| |
| @Nullable |
| private static HighlightInfo checkTypeParameterWithinItsBound(PsiTypeParameter classParameter, |
| final PsiSubstitutor substitutor, |
| final PsiType type, |
| final PsiElement typeElement2Highlight, |
| PsiReferenceParameterList referenceParameterList) { |
| final PsiClass referenceClass = type instanceof PsiClassType ? ((PsiClassType)type).resolve() : null; |
| final PsiType psiType = substitutor.substitute(classParameter); |
| if (psiType instanceof PsiClassType && !(PsiUtil.resolveClassInType(psiType) instanceof PsiTypeParameter)) { |
| if (GenericsUtil.checkNotInBounds(type, psiType, referenceParameterList)) { |
| final String description = "Actual type argument and inferred type contradict each other"; |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement2Highlight).descriptionAndTooltip(description).create(); |
| } |
| } |
| |
| final PsiClassType[] bounds = classParameter.getSuperTypes(); |
| for (PsiClassType type1 : bounds) { |
| PsiType bound = substitutor.substitute(type1); |
| if (!bound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && GenericsUtil.checkNotInBounds(type, bound, referenceParameterList)) { |
| PsiClass boundClass = bound instanceof PsiClassType ? ((PsiClassType)bound).resolve() : null; |
| |
| @NonNls final String messageKey = boundClass == null || referenceClass == null || referenceClass.isInterface() == boundClass.isInterface() |
| ? "generics.type.parameter.is.not.within.its.bound.extend" |
| : "generics.type.parameter.is.not.within.its.bound.implement"; |
| |
| String description = JavaErrorMessages.message(messageKey, |
| referenceClass != null ? HighlightUtil.formatClass(referenceClass) : type.getPresentableText(), |
| JavaHighlightUtil.formatType(bound)); |
| |
| final HighlightInfo info = |
| HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement2Highlight).descriptionAndTooltip(description).create(); |
| if (bound instanceof PsiClassType && referenceClass != null && info != null) { |
| QuickFixAction |
| .registerQuickFixAction(info, QUICK_FIX_FACTORY.createExtendsListFix(referenceClass, (PsiClassType)bound, true), |
| null); |
| } |
| return info; |
| } |
| } |
| return null; |
| } |
| |
| private static String typeParameterListOwnerDescription(final PsiTypeParameterListOwner typeParameterListOwner) { |
| if (typeParameterListOwner instanceof PsiClass) { |
| return HighlightUtil.formatClass((PsiClass)typeParameterListOwner); |
| } |
| else if (typeParameterListOwner instanceof PsiMethod) { |
| return JavaHighlightUtil.formatMethod((PsiMethod)typeParameterListOwner); |
| } |
| else { |
| LOG.error("Unknown " + typeParameterListOwner); |
| return "?"; |
| } |
| } |
| |
| private static String typeParameterListOwnerCategoryDescription(final PsiTypeParameterListOwner typeParameterListOwner) { |
| if (typeParameterListOwner instanceof PsiClass) { |
| return JavaErrorMessages.message("generics.holder.type"); |
| } |
| else if (typeParameterListOwner instanceof PsiMethod) { |
| return JavaErrorMessages.message("generics.holder.method"); |
| } |
| else { |
| LOG.error("Unknown " + typeParameterListOwner); |
| return "?"; |
| } |
| } |
| |
| @Nullable |
| public static HighlightInfo checkElementInTypeParameterExtendsList(@NotNull PsiReferenceList referenceList, |
| @NotNull PsiClass aClass, |
| @NotNull JavaResolveResult resolveResult, |
| @NotNull PsiElement element, |
| @NotNull LanguageLevel languageLevel) { |
| final PsiJavaCodeReferenceElement[] referenceElements = referenceList.getReferenceElements(); |
| PsiClass extendFrom = (PsiClass)resolveResult.getElement(); |
| if (extendFrom == null) return null; |
| HighlightInfo errorResult = null; |
| if (!extendFrom.isInterface() && referenceElements.length != 0 && element != referenceElements[0]) { |
| String description = JavaErrorMessages.message("interface.expected"); |
| errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create(); |
| PsiClassType type = |
| JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor()); |
| QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createMoveBoundClassToFrontFix(aClass, type), null); |
| } |
| else if (referenceElements.length != 0 && element != referenceElements[0] && referenceElements[0].resolve() instanceof PsiTypeParameter) { |
| final String description = JavaErrorMessages.message("type.parameter.cannot.be.followed.by.other.bounds"); |
| errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create(); |
| PsiClassType type = |
| JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor()); |
| QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createExtendsListFix(aClass, type, false), null); |
| } |
| if (errorResult == null && languageLevel.isAtLeast(LanguageLevel.JDK_1_7) && |
| referenceElements.length > 1) { |
| //todo suppress erased methods which come from the same class |
| final Collection<HighlightInfo> result = checkOverrideEquivalentMethods(languageLevel, aClass); |
| return result != null && result.size() > 0 ? result.iterator().next() : null; |
| } |
| return errorResult; |
| } |
| |
| public static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass) { |
| final PsiClassType[] types = aClass.getSuperTypes(); |
| if (types.length < 2) return null; |
| Map<PsiClass, PsiSubstitutor> inheritedClasses = new HashMap<PsiClass, PsiSubstitutor>(); |
| final TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass); |
| return checkInterfaceMultipleInheritance(aClass, |
| PsiSubstitutor.EMPTY, inheritedClasses, |
| new HashSet<PsiClass>(), textRange); |
| } |
| |
| private static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass, |
| PsiSubstitutor derivedSubstitutor, |
| Map<PsiClass, PsiSubstitutor> inheritedClasses, |
| Set<PsiClass> visited, |
| TextRange textRange) { |
| final PsiClassType[] superTypes = aClass.getSuperTypes(); |
| for (PsiClassType superType : superTypes) { |
| final PsiClassType.ClassResolveResult result = superType.resolveGenerics(); |
| final PsiClass superClass = result.getElement(); |
| if (superClass == null || visited.contains(superClass)) continue; |
| PsiSubstitutor superTypeSubstitutor = result.getSubstitutor(); |
| superTypeSubstitutor = MethodSignatureUtil.combineSubstitutors(superTypeSubstitutor, derivedSubstitutor); |
| |
| final PsiSubstitutor inheritedSubstitutor = inheritedClasses.get(superClass); |
| if (inheritedSubstitutor != null) { |
| final PsiTypeParameter[] typeParameters = superClass.getTypeParameters(); |
| for (PsiTypeParameter typeParameter : typeParameters) { |
| PsiType type1 = inheritedSubstitutor.substitute(typeParameter); |
| PsiType type2 = superTypeSubstitutor.substitute(typeParameter); |
| |
| if (!Comparing.equal(type1, type2)) { |
| String description = JavaErrorMessages.message("generics.cannot.be.inherited.with.different.type.arguments", |
| HighlightUtil.formatClass(superClass), |
| JavaHighlightUtil.formatType(type1), |
| JavaHighlightUtil.formatType(type2)); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create(); |
| } |
| } |
| } |
| inheritedClasses.put(superClass, superTypeSubstitutor); |
| visited.add(superClass); |
| final HighlightInfo highlightInfo = checkInterfaceMultipleInheritance(superClass, superTypeSubstitutor, inheritedClasses, visited, textRange); |
| visited.remove(superClass); |
| |
| if (highlightInfo != null) return highlightInfo; |
| } |
| return null; |
| } |
| |
| public static Collection<HighlightInfo> checkOverrideEquivalentMethods(@NotNull LanguageLevel languageLevel, |
| @NotNull PsiClass aClass) { |
| List<HighlightInfo> result = new ArrayList<HighlightInfo>(); |
| final Collection<HierarchicalMethodSignature> signaturesWithSupers = aClass.getVisibleSignatures(); |
| PsiManager manager = aClass.getManager(); |
| Map<MethodSignature, MethodSignatureBackedByPsiMethod> sameErasureMethods = |
| new THashMap<MethodSignature, MethodSignatureBackedByPsiMethod>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY); |
| |
| final Set<MethodSignature> foundProblems = new THashSet<MethodSignature>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY); |
| for (HierarchicalMethodSignature signature : signaturesWithSupers) { |
| HighlightInfo info = checkSameErasureNotSubSignatureInner(signature, manager, aClass, sameErasureMethods); |
| if (info != null && foundProblems.add(signature)) { |
| result.add(info); |
| } |
| if (aClass instanceof PsiTypeParameter) { |
| info = HighlightMethodUtil.checkMethodIncompatibleReturnType(signature, signature.getSuperSignatures(), true, HighlightNamesUtil.getClassDeclarationTextRange(aClass)); |
| if (info != null) { |
| result.add(info); |
| } |
| } |
| } |
| |
| return result.isEmpty() ? null : result; |
| } |
| |
| static HighlightInfo checkDefaultMethodOverrideEquivalentToObjectNonPrivate(@NotNull LanguageLevel languageLevel, |
| @NotNull PsiClass aClass, |
| @NotNull PsiMethod method, |
| @NotNull PsiElement methodIdentifier) { |
| if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && aClass.isInterface() && method.hasModifierProperty(PsiModifier.DEFAULT)) { |
| HierarchicalMethodSignature sig = method.getHierarchicalMethodSignature(); |
| for (HierarchicalMethodSignature methodSignature : sig.getSuperSignatures()) { |
| final PsiMethod objectMethod = methodSignature.getMethod(); |
| final PsiClass containingClass = objectMethod.getContainingClass(); |
| if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName()) && objectMethod.hasModifierProperty(PsiModifier.PUBLIC)) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) |
| .descriptionAndTooltip("Default method '" + sig.getName() + "' overrides a member of 'java.lang.Object'") |
| .range(methodIdentifier) |
| .create(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| static HighlightInfo checkUnrelatedDefaultMethods(@NotNull PsiClass aClass, |
| @NotNull Collection<HierarchicalMethodSignature> signaturesWithSupers, |
| @NotNull PsiIdentifier classIdentifier) { |
| for (HierarchicalMethodSignature methodSignature : signaturesWithSupers) { |
| final PsiMethod method = methodSignature.getMethod(); |
| final boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); |
| if (method.hasModifierProperty(PsiModifier.DEFAULT) || isAbstract) { |
| final PsiClass containingClass = method.getContainingClass(); |
| List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures(); |
| if (!superSignatures.isEmpty()) { |
| for (HierarchicalMethodSignature signature : superSignatures) { |
| final PsiMethod superMethod = signature.getMethod(); |
| final PsiClass superContainingClass = superMethod.getContainingClass(); |
| if (containingClass != null && superContainingClass != null && !InheritanceUtil.isInheritorOrSelf(containingClass, superContainingClass, true)) { |
| final boolean isDefault = superMethod.hasModifierProperty(PsiModifier.DEFAULT); |
| if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT) && !isDefault && !isAbstract) { |
| final String message = JavaErrorMessages.message( |
| aClass instanceof PsiEnumConstantInitializer ? "enum.constant.should.implement.method" : "class.must.be.abstract", |
| HighlightUtil.formatClass(superContainingClass), |
| JavaHighlightUtil.formatMethod(superMethod), |
| HighlightUtil.formatClass(superContainingClass, false)); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) |
| .range(classIdentifier).descriptionAndTooltip(message) |
| .create(); |
| } |
| |
| if (isDefault || !isAbstract && superMethod.hasModifierProperty(PsiModifier.ABSTRACT)) { |
| final String message = isDefault |
| ? " inherits unrelated defaults for " |
| : " inherits abstract and default for "; |
| final String inheritUnrelatedDefaultsMessage = HighlightUtil.formatClass(aClass) + |
| message + |
| JavaHighlightUtil.formatMethod(method) + |
| " from types " + |
| HighlightUtil.formatClass(containingClass) + |
| " and " + |
| HighlightUtil.formatClass(superContainingClass); |
| return HighlightInfo |
| .newHighlightInfo(HighlightInfoType.ERROR).range(classIdentifier).descriptionAndTooltip(inheritUnrelatedDefaultsMessage) |
| .create(); |
| } |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| private static HighlightInfo checkSameErasureNotSubSignatureInner(@NotNull HierarchicalMethodSignature signature, |
| @NotNull PsiManager manager, |
| @NotNull PsiClass aClass, |
| @NotNull Map<MethodSignature, MethodSignatureBackedByPsiMethod> sameErasureMethods) { |
| PsiMethod method = signature.getMethod(); |
| JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); |
| if (!facade.getResolveHelper().isAccessible(method, aClass, null)) return null; |
| MethodSignature signatureToErase = method.getSignature(PsiSubstitutor.EMPTY); |
| MethodSignatureBackedByPsiMethod sameErasure = sameErasureMethods.get(signatureToErase); |
| HighlightInfo info; |
| if (sameErasure != null) { |
| if (aClass instanceof PsiTypeParameter || |
| MethodSignatureUtil.findMethodBySuperMethod(aClass, sameErasure.getMethod(), false) != null || |
| !(InheritanceUtil.isInheritorOrSelf(sameErasure.getMethod().getContainingClass(), method.getContainingClass(), true) || |
| InheritanceUtil.isInheritorOrSelf(method.getContainingClass(), sameErasure.getMethod().getContainingClass(), true))) { |
| info = checkSameErasureNotSubSignatureOrSameClass(sameErasure, signature, aClass, method); |
| if (info != null) return info; |
| } |
| } |
| else { |
| sameErasureMethods.put(signatureToErase, signature); |
| } |
| List<HierarchicalMethodSignature> supers = signature.getSuperSignatures(); |
| for (HierarchicalMethodSignature superSignature : supers) { |
| info = checkSameErasureNotSubSignatureInner(superSignature, manager, aClass, sameErasureMethods); |
| if (info != null) return info; |
| |
| if (superSignature.isRaw() && !signature.isRaw()) { |
| final PsiType[] parameterTypes = signature.getParameterTypes(); |
| PsiType[] erasedTypes = superSignature.getErasedParameterTypes(); |
| for (int i = 0; i < erasedTypes.length; i++) { |
| if (!Comparing.equal(parameterTypes[i], erasedTypes[i])) { |
| return getSameErasureMessage(false, method, superSignature.getMethod(), |
| HighlightNamesUtil.getClassDeclarationTextRange(aClass)); |
| } |
| } |
| } |
| |
| } |
| return null; |
| } |
| |
| @Nullable |
| private static HighlightInfo checkSameErasureNotSubSignatureOrSameClass(final MethodSignatureBackedByPsiMethod signatureToCheck, |
| final HierarchicalMethodSignature superSignature, |
| final PsiClass aClass, |
| final PsiMethod superMethod) { |
| final PsiMethod checkMethod = signatureToCheck.getMethod(); |
| if (superMethod.equals(checkMethod)) return null; |
| PsiClass checkContainingClass = checkMethod.getContainingClass(); |
| LOG.assertTrue(checkContainingClass != null); |
| PsiClass superContainingClass = superMethod.getContainingClass(); |
| boolean checkEqualsSuper = checkContainingClass.equals(superContainingClass); |
| if (checkMethod.isConstructor()) { |
| if (!superMethod.isConstructor() || !checkEqualsSuper) return null; |
| } |
| else if (superMethod.isConstructor()) return null; |
| |
| final boolean atLeast17 = JavaVersionService.getInstance().isAtLeast(aClass, JavaSdkVersion.JDK_1_7); |
| if (checkMethod.hasModifierProperty(PsiModifier.STATIC) && !checkEqualsSuper && !atLeast17) { |
| return null; |
| } |
| |
| if (superMethod.hasModifierProperty(PsiModifier.STATIC) && superContainingClass != null && |
| superContainingClass.isInterface() && PsiUtil.isLanguageLevel8OrHigher(superContainingClass)) { |
| return null; |
| } |
| |
| final PsiType retErasure1 = TypeConversionUtil.erasure(checkMethod.getReturnType()); |
| final PsiType retErasure2 = TypeConversionUtil.erasure(superMethod.getReturnType()); |
| |
| boolean differentReturnTypeErasure = !Comparing.equal(retErasure1, retErasure2); |
| if (checkEqualsSuper && atLeast17) { |
| if (retErasure1 != null && retErasure2 != null) { |
| differentReturnTypeErasure = !TypeConversionUtil.isAssignable(retErasure1, retErasure2); |
| } |
| else { |
| differentReturnTypeErasure = !(retErasure1 == null && retErasure2 == null); |
| } |
| } |
| |
| if (differentReturnTypeErasure && |
| !TypeConversionUtil.isVoidType(retErasure1) && |
| !TypeConversionUtil.isVoidType(retErasure2) && |
| !(checkEqualsSuper && Arrays.equals(superSignature.getParameterTypes(), signatureToCheck.getParameterTypes())) && |
| !atLeast17) { |
| int idx = 0; |
| final PsiType[] erasedTypes = signatureToCheck.getErasedParameterTypes(); |
| boolean erasure = erasedTypes.length > 0; |
| for (PsiType type : superSignature.getParameterTypes()) { |
| erasure &= Comparing.equal(type, erasedTypes[idx]); |
| idx++; |
| } |
| |
| if (!erasure) return null; |
| } |
| |
| if (!checkEqualsSuper && MethodSignatureUtil.isSubsignature(superSignature, signatureToCheck)) { |
| return null; |
| } |
| if (superContainingClass != null && !superContainingClass.isInterface() && checkContainingClass.isInterface() && !aClass.equals(superContainingClass)) return null; |
| if (aClass.equals(checkContainingClass)) { |
| boolean sameClass = aClass.equals(superContainingClass); |
| return getSameErasureMessage(sameClass, checkMethod, superMethod, HighlightNamesUtil.getMethodDeclarationTextRange(checkMethod)); |
| } |
| else { |
| return getSameErasureMessage(false, checkMethod, superMethod, HighlightNamesUtil.getClassDeclarationTextRange(aClass)); |
| } |
| } |
| |
| private static HighlightInfo getSameErasureMessage(final boolean sameClass, @NotNull PsiMethod method, @NotNull PsiMethod superMethod, |
| TextRange textRange) { |
| @NonNls final String key = sameClass ? "generics.methods.have.same.erasure" : |
| method.hasModifierProperty(PsiModifier.STATIC) ? |
| "generics.methods.have.same.erasure.hide" : |
| "generics.methods.have.same.erasure.override"; |
| String description = JavaErrorMessages.message(key, HighlightMethodUtil.createClashMethodMessage(method, superMethod, !sameClass)); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create(); |
| } |
| |
| public static HighlightInfo checkTypeParameterInstantiation(PsiNewExpression expression) { |
| PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference(); |
| if (classReference == null) return null; |
| final JavaResolveResult result = classReference.advancedResolve(false); |
| final PsiElement element = result.getElement(); |
| if (element instanceof PsiTypeParameter) { |
| String description = JavaErrorMessages.message("generics.type.parameter.cannot.be.instantiated", |
| HighlightUtil.formatClass((PsiTypeParameter)element)); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classReference).descriptionAndTooltip(description).create(); |
| } |
| return null; |
| } |
| |
| public static HighlightInfo checkWildcardUsage(PsiTypeElement typeElement) { |
| PsiType type = typeElement.getType(); |
| if (type instanceof PsiWildcardType) { |
| if (typeElement.getParent() instanceof PsiReferenceParameterList) { |
| PsiElement parent = typeElement.getParent().getParent(); |
| LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement, parent); |
| PsiElement refParent = parent.getParent(); |
| if (refParent instanceof PsiAnonymousClass) refParent = refParent.getParent(); |
| if (refParent instanceof PsiNewExpression) { |
| PsiNewExpression newExpression = (PsiNewExpression)refParent; |
| if (!(newExpression.getType() instanceof PsiArrayType)) { |
| String description = JavaErrorMessages.message("wildcard.type.cannot.be.instantiated", JavaHighlightUtil.formatType(type)); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create(); |
| } |
| } |
| else if (refParent instanceof PsiReferenceList) { |
| PsiElement refPParent = refParent.getParent(); |
| if (!(refPParent instanceof PsiTypeParameter) || refParent != ((PsiTypeParameter)refPParent).getExtendsList()) { |
| String description = JavaErrorMessages.message("generics.wildcard.not.expected"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create(); |
| } |
| } |
| } |
| else { |
| String description = JavaErrorMessages.message("generics.wildcards.may.be.used.only.as.reference.parameters"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create(); |
| } |
| } |
| |
| return null; |
| } |
| |
| public static HighlightInfo checkReferenceTypeUsedAsTypeArgument(final PsiTypeElement typeElement) { |
| final PsiType type = typeElement.getType(); |
| if (type != PsiType.NULL && type instanceof PsiPrimitiveType || |
| type instanceof PsiWildcardType && ((PsiWildcardType)type).getBound() instanceof PsiPrimitiveType) { |
| final PsiElement element = new PsiMatcherImpl(typeElement) |
| .parent(PsiMatchers.hasClass(PsiReferenceParameterList.class)) |
| .parent(PsiMatchers.hasClass(PsiJavaCodeReferenceElement.class, PsiNewExpression.class)) |
| .getElement(); |
| if (element == null) return null; |
| |
| String description = JavaErrorMessages.message("generics.type.argument.cannot.be.of.primitive.type"); |
| final HighlightInfo highlightInfo = |
| HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create(); |
| PsiType toConvert = type; |
| if (type instanceof PsiWildcardType) { |
| toConvert = ((PsiWildcardType)type).getBound(); |
| } |
| if (toConvert instanceof PsiPrimitiveType) { |
| final PsiClassType boxedType = ((PsiPrimitiveType)toConvert).getBoxedType(typeElement); |
| if (boxedType != null) { |
| QuickFixAction.registerQuickFixAction(highlightInfo, |
| QUICK_FIX_FACTORY.createReplacePrimitiveWithBoxedTypeAction(typeElement, toConvert.getPresentableText(), ((PsiPrimitiveType)toConvert).getBoxedTypeName())); |
| } |
| } |
| return highlightInfo; |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static HighlightInfo checkForeachLoopParameterType(PsiForeachStatement statement) { |
| final PsiParameter parameter = statement.getIterationParameter(); |
| final PsiExpression expression = statement.getIteratedValue(); |
| if (expression == null || expression.getType() == null) return null; |
| final PsiType itemType = JavaGenericsUtil.getCollectionItemType(expression); |
| if (itemType == null) { |
| String description = JavaErrorMessages.message("foreach.not.applicable", |
| JavaHighlightUtil.formatType(expression.getType())); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create(); |
| } |
| final int start = parameter.getTextRange().getStartOffset(); |
| final int end = expression.getTextRange().getEndOffset(); |
| final PsiType parameterType = parameter.getType(); |
| HighlightInfo highlightInfo = HighlightUtil.checkAssignability(parameterType, itemType, null, new TextRange(start, end), 0); |
| if (highlightInfo != null) { |
| HighlightUtil.registerChangeVariableTypeFixes(parameter, itemType, expression, highlightInfo); |
| } |
| return highlightInfo; |
| } |
| |
| //http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9.2 |
| @Nullable |
| public static HighlightInfo checkAccessStaticFieldFromEnumConstructor(@NotNull PsiReferenceExpression expr, @NotNull JavaResolveResult result) { |
| final PsiElement resolved = result.getElement(); |
| |
| if (!(resolved instanceof PsiField)) return null; |
| if (!((PsiModifierListOwner)resolved).hasModifierProperty(PsiModifier.STATIC)) return null; |
| if (expr.getParent() instanceof PsiSwitchLabelStatement) return null; |
| final PsiMember constructorOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expr); |
| if (constructorOrInitializer == null) return null; |
| if (constructorOrInitializer.hasModifierProperty(PsiModifier.STATIC)) return null; |
| final PsiClass aClass = constructorOrInitializer instanceof PsiEnumConstantInitializer ? |
| (PsiClass)constructorOrInitializer : constructorOrInitializer.getContainingClass(); |
| if (aClass == null || !(aClass.isEnum() || aClass instanceof PsiEnumConstantInitializer)) return null; |
| final PsiField field = (PsiField)resolved; |
| if (aClass instanceof PsiEnumConstantInitializer) { |
| if (field.getContainingClass() != aClass.getSuperClass()) return null; |
| } else if (field.getContainingClass() != aClass) return null; |
| |
| |
| if (!JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_6)) { |
| final PsiType type = field.getType(); |
| if (type instanceof PsiClassType && ((PsiClassType)type).resolve() == aClass) return null; |
| } |
| |
| if (PsiUtil.isCompileTimeConstant(field)) return null; |
| |
| String description = JavaErrorMessages.message( |
| "illegal.to.access.static.member.from.enum.constructor.or.instance.initializer", |
| HighlightMessageUtil.getSymbolName(resolved, result.getSubstitutor()) |
| ); |
| |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expr).descriptionAndTooltip(description).create(); |
| } |
| |
| @Nullable |
| public static HighlightInfo checkEnumInstantiation(PsiElement expression, PsiClass aClass) { |
| if (aClass != null && aClass.isEnum() && |
| (!(expression instanceof PsiNewExpression) || |
| ((PsiNewExpression)expression).getArrayDimensions().length == 0 && ((PsiNewExpression)expression).getArrayInitializer() == null)) { |
| String description = JavaErrorMessages.message("enum.types.cannot.be.instantiated"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create(); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static HighlightInfo checkGenericArrayCreation(PsiElement element, PsiType type) { |
| if (type instanceof PsiArrayType) { |
| if (!JavaGenericsUtil.isReifiableType(((PsiArrayType)type).getComponentType())) { |
| String description = JavaErrorMessages.message("generic.array.creation"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create(); |
| } |
| } |
| |
| return null; |
| } |
| |
| private static final MethodSignature ourValuesEnumSyntheticMethod = MethodSignatureUtil.createMethodSignature("values", |
| PsiType.EMPTY_ARRAY, |
| PsiTypeParameter.EMPTY_ARRAY, |
| PsiSubstitutor.EMPTY); |
| |
| public static boolean isEnumSyntheticMethod(MethodSignature methodSignature, Project project) { |
| if (methodSignature.equals(ourValuesEnumSyntheticMethod)) return true; |
| final PsiType javaLangString = PsiType.getJavaLangString(PsiManager.getInstance(project), GlobalSearchScope.allScope(project)); |
| final MethodSignature valueOfMethod = MethodSignatureUtil.createMethodSignature("valueOf", new PsiType[]{javaLangString}, PsiTypeParameter.EMPTY_ARRAY, |
| PsiSubstitutor.EMPTY); |
| return valueOfMethod.equals(methodSignature); |
| } |
| |
| @Nullable |
| public static HighlightInfo checkTypeParametersList(PsiTypeParameterList list, PsiTypeParameter[] parameters, @NotNull LanguageLevel level) { |
| final PsiElement parent = list.getParent(); |
| if (parent instanceof PsiClass && ((PsiClass)parent).isEnum()) { |
| String description = JavaErrorMessages.message("generics.enum.may.not.have.type.parameters"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create(); |
| } |
| if (PsiUtil.isAnnotationMethod(parent)) { |
| String description = JavaErrorMessages.message("generics.annotation.members.may.not.have.type.parameters"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create(); |
| } |
| if (parent instanceof PsiClass && ((PsiClass)parent).isAnnotationType()) { |
| String description = JavaErrorMessages.message("annotation.may.not.have.type.parameters"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create(); |
| } |
| |
| for (int i = 0; i < parameters.length; i++) { |
| final PsiTypeParameter typeParameter1 = parameters[i]; |
| final HighlightInfo cyclicInheritance = HighlightClassUtil.checkCyclicInheritance(typeParameter1); |
| if (cyclicInheritance != null) return cyclicInheritance; |
| String name1 = typeParameter1.getName(); |
| for (int j = i + 1; j < parameters.length; j++) { |
| final PsiTypeParameter typeParameter2 = parameters[j]; |
| String name2 = typeParameter2.getName(); |
| if (Comparing.strEqual(name1, name2)) { |
| String message = JavaErrorMessages.message("generics.duplicate.type.parameter", name1); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeParameter2).descriptionAndTooltip(message).create(); |
| } |
| } |
| if (!level.isAtLeast(LanguageLevel.JDK_1_7)) { |
| for (PsiJavaCodeReferenceElement referenceElement : typeParameter1.getExtendsList().getReferenceElements()) { |
| final PsiElement resolve = referenceElement.resolve(); |
| if (resolve instanceof PsiTypeParameter && ArrayUtilRt.find(parameters, resolve) > i) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceElement.getTextRange()).descriptionAndTooltip("Illegal forward reference").create(); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static Collection<HighlightInfo> checkCatchParameterIsClass(PsiParameter parameter) { |
| if (!(parameter.getDeclarationScope() instanceof PsiCatchSection)) return null; |
| final Collection<HighlightInfo> result = ContainerUtil.newArrayList(); |
| |
| final List<PsiTypeElement> typeElements = PsiUtil.getParameterTypeElements(parameter); |
| for (PsiTypeElement typeElement : typeElements) { |
| final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(typeElement.getType()); |
| if (aClass instanceof PsiTypeParameter) { |
| final String message = JavaErrorMessages.message("generics.cannot.catch.type.parameters"); |
| result.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(message).create()); |
| } |
| } |
| |
| return result; |
| } |
| |
| public static HighlightInfo checkInstanceOfGenericType(PsiInstanceOfExpression expression) { |
| final PsiTypeElement checkTypeElement = expression.getCheckType(); |
| if (checkTypeElement == null) return null; |
| PsiElement ref = checkTypeElement.getInnermostComponentReferenceElement(); |
| while (ref instanceof PsiJavaCodeReferenceElement) { |
| final HighlightInfo result = isIllegalForInstanceOf((PsiJavaCodeReferenceElement)ref, checkTypeElement); |
| if (result != null) return result; |
| ref = ((PsiQualifiedReference)ref).getQualifier(); |
| } |
| return null; |
| } |
| |
| private static HighlightInfo isIllegalForInstanceOf(PsiJavaCodeReferenceElement ref, final PsiTypeElement typeElement) { |
| final PsiElement resolved = ref.resolve(); |
| if (resolved instanceof PsiTypeParameter) { |
| String description = JavaErrorMessages.message("generics.cannot.instanceof.type.parameters"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(ref).descriptionAndTooltip(description).create(); |
| } |
| |
| if (resolved instanceof PsiClass) { |
| final PsiClass containingClass = ((PsiClass)resolved).getContainingClass(); |
| if (containingClass != null && |
| ref.getQualifier() == null && |
| containingClass.getTypeParameters().length > 0 && |
| !((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC) && |
| ((PsiClass)resolved).getTypeParameters().length == 0) { |
| String description = JavaErrorMessages.message("illegal.generic.type.for.instanceof"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create(); |
| } |
| } |
| |
| final PsiType[] parameters = ref.getTypeParameters(); |
| for (PsiType parameterType : parameters) { |
| if (parameterType != null && |
| !(parameterType instanceof PsiWildcardType && ((PsiWildcardType)parameterType).getBound() == null)) { |
| String description = JavaErrorMessages.message("illegal.generic.type.for.instanceof"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create(); |
| } |
| } |
| |
| return null; |
| } |
| |
| public static HighlightInfo checkClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { |
| PsiType type = expression.getOperand().getType(); |
| if (type instanceof PsiClassType) { |
| return canSelectFrom((PsiClassType)type, expression.getOperand()); |
| } |
| if (type instanceof PsiArrayType) { |
| final PsiType arrayComponentType = type.getDeepComponentType(); |
| if (arrayComponentType instanceof PsiClassType) { |
| return canSelectFrom((PsiClassType)arrayComponentType, expression.getOperand()); |
| } |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| private static HighlightInfo canSelectFrom(PsiClassType type, PsiTypeElement operand) { |
| PsiClass aClass = type.resolve(); |
| if (aClass instanceof PsiTypeParameter) { |
| String description = JavaErrorMessages.message("cannot.select.dot.class.from.type.variable"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(operand).descriptionAndTooltip(description).create(); |
| } |
| if (type.getParameters().length > 0) { |
| return HighlightInfo |
| .newHighlightInfo(HighlightInfoType.ERROR).range(operand).descriptionAndTooltip("Cannot select from parameterized type").create(); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static HighlightInfo checkOverrideAnnotation(PsiMethod method, final LanguageLevel languageLevel) { |
| PsiModifierList list = method.getModifierList(); |
| final PsiAnnotation overrideAnnotation = list.findAnnotation("java.lang.Override"); |
| if (overrideAnnotation == null) { |
| return null; |
| } |
| try { |
| MethodSignatureBackedByPsiMethod superMethod = SuperMethodsSearch.search(method, null, true, false).findFirst(); |
| if (superMethod != null && method.getContainingClass().isInterface() && "clone".equals(superMethod.getName())) { |
| final PsiClass containingClass = superMethod.getMethod().getContainingClass(); |
| if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) { |
| superMethod = null; |
| } |
| } |
| if (superMethod == null) { |
| String description = JavaErrorMessages.message("method.does.not.override.super"); |
| HighlightInfo highlightInfo = |
| HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(overrideAnnotation).descriptionAndTooltip(description).create(); |
| QUICK_FIX_FACTORY.registerPullAsAbstractUpFixes(method, new QuickFixActionRegistrarImpl(highlightInfo)); |
| return highlightInfo; |
| } |
| PsiClass superClass = superMethod.getMethod().getContainingClass(); |
| if (languageLevel.equals(LanguageLevel.JDK_1_5) && |
| superClass != null && |
| superClass.isInterface()) { |
| String description = JavaErrorMessages.message("override.not.allowed.in.interfaces"); |
| HighlightInfo info = |
| HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(overrideAnnotation).descriptionAndTooltip(description).create(); |
| QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createIncreaseLanguageLevelFix(LanguageLevel.JDK_1_6)); |
| return info; |
| } |
| return null; |
| } |
| catch (IndexNotReadyException e) { |
| return null; |
| } |
| } |
| |
| @Nullable |
| public static HighlightInfo checkSafeVarargsAnnotation(PsiMethod method) { |
| PsiModifierList list = method.getModifierList(); |
| final PsiAnnotation safeVarargsAnnotation = list.findAnnotation("java.lang.SafeVarargs"); |
| if (safeVarargsAnnotation == null) { |
| return null; |
| } |
| try { |
| if (!method.isVarArgs()) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(safeVarargsAnnotation).descriptionAndTooltip( |
| "@SafeVarargs is not allowed on methods with fixed arity").create(); |
| } |
| if (!method.hasModifierProperty(PsiModifier.STATIC) && !method.hasModifierProperty(PsiModifier.FINAL) && !method.isConstructor()) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(safeVarargsAnnotation).descriptionAndTooltip( |
| "@SafeVarargs is not allowed on non-final instance methods").create(); |
| } |
| |
| final PsiParameter varParameter = method.getParameterList().getParameters()[method.getParameterList().getParametersCount() - 1]; |
| |
| for (PsiReference reference : ReferencesSearch.search(varParameter)) { |
| final PsiElement element = reference.getElement(); |
| if (element instanceof PsiExpression && !PsiUtil.isAccessedForReading((PsiExpression)element)) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(element).descriptionAndTooltip( |
| "@SafeVarargs do not suppress potentially unsafe operations").create(); |
| } |
| } |
| |
| |
| LOG.assertTrue(varParameter.isVarArgs()); |
| final PsiEllipsisType ellipsisType = (PsiEllipsisType)varParameter.getType(); |
| final PsiType componentType = ellipsisType.getComponentType(); |
| if (JavaGenericsUtil.isReifiableType(componentType)) { |
| PsiElement element = varParameter.getTypeElement(); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(element).descriptionAndTooltip( |
| "@SafeVarargs is not applicable for reifiable types").create(); |
| } |
| return null; |
| } |
| catch (IndexNotReadyException e) { |
| return null; |
| } |
| } |
| |
| static void checkEnumConstantForConstructorProblems(@NotNull PsiEnumConstant enumConstant, |
| @NotNull HighlightInfoHolder holder, |
| @NotNull JavaSdkVersion javaSdkVersion) { |
| PsiClass containingClass = enumConstant.getContainingClass(); |
| if (enumConstant.getInitializingClass() == null) { |
| HighlightInfo highlightInfo = HighlightClassUtil.checkInstantiationOfAbstractClass(containingClass, enumConstant.getNameIdentifier()); |
| if (highlightInfo != null) { |
| QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createImplementMethodsFix(enumConstant)); |
| holder.add(highlightInfo); |
| return; |
| } |
| highlightInfo = HighlightClassUtil.checkClassWithAbstractMethods(enumConstant.getContainingClass(), enumConstant, enumConstant.getNameIdentifier().getTextRange()); |
| if (highlightInfo != null) { |
| holder.add(highlightInfo); |
| return; |
| } |
| } |
| PsiClassType type = JavaPsiFacade.getInstance(holder.getProject()).getElementFactory().createType(containingClass); |
| |
| HighlightMethodUtil.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null, holder, javaSdkVersion); |
| } |
| |
| @Nullable |
| public static HighlightInfo checkEnumSuperConstructorCall(PsiMethodCallExpression expr) { |
| PsiReferenceExpression methodExpression = expr.getMethodExpression(); |
| final PsiElement refNameElement = methodExpression.getReferenceNameElement(); |
| if (refNameElement != null && PsiKeyword.SUPER.equals(refNameElement.getText())) { |
| final PsiMember constructor = PsiUtil.findEnclosingConstructorOrInitializer(expr); |
| if (constructor instanceof PsiMethod) { |
| final PsiClass aClass = constructor.getContainingClass(); |
| if (aClass != null && aClass.isEnum()) { |
| final String message = JavaErrorMessages.message("call.to.super.is.not.allowed.in.enum.constructor"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expr).descriptionAndTooltip(message).create(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static HighlightInfo checkVarArgParameterIsLast(@NotNull PsiParameter parameter) { |
| PsiElement declarationScope = parameter.getDeclarationScope(); |
| if (declarationScope instanceof PsiMethod) { |
| PsiParameter[] params = ((PsiMethod)declarationScope).getParameterList().getParameters(); |
| if (params[params.length - 1] != parameter) { |
| String description = JavaErrorMessages.message("vararg.not.last.parameter"); |
| HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parameter).descriptionAndTooltip(description).create(); |
| QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createMakeVarargParameterLastFix(parameter)); |
| return info; |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static List<HighlightInfo> checkEnumConstantModifierList(PsiModifierList modifierList) { |
| List<HighlightInfo> list = null; |
| PsiElement[] children = modifierList.getChildren(); |
| for (PsiElement child : children) { |
| if (child instanceof PsiKeyword) { |
| if (list == null) { |
| list = new ArrayList<HighlightInfo>(); |
| } |
| String description = JavaErrorMessages.message("modifiers.for.enum.constants"); |
| list.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(child).descriptionAndTooltip(description).create()); |
| } |
| } |
| return list; |
| } |
| |
| @Nullable |
| public static HighlightInfo checkParametersAllowed(PsiReferenceParameterList refParamList) { |
| final PsiElement parent = refParamList.getParent(); |
| if (parent instanceof PsiReferenceExpression) { |
| final PsiElement grandParent = parent.getParent(); |
| if (!(grandParent instanceof PsiMethodCallExpression) && !(parent instanceof PsiMethodReferenceExpression)) { |
| final String message = JavaErrorMessages.message("generics.reference.parameters.not.allowed"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refParamList).descriptionAndTooltip(message).create(); |
| } |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static HighlightInfo checkParametersOnRaw(PsiReferenceParameterList refParamList) { |
| JavaResolveResult resolveResult = null; |
| PsiElement parent = refParamList.getParent(); |
| PsiElement qualifier = null; |
| if (parent instanceof PsiJavaCodeReferenceElement) { |
| resolveResult = ((PsiJavaCodeReferenceElement)parent).advancedResolve(false); |
| qualifier = ((PsiJavaCodeReferenceElement)parent).getQualifier(); |
| } |
| else if (parent instanceof PsiCallExpression) { |
| resolveResult = ((PsiCallExpression)parent).resolveMethodGenerics(); |
| if (parent instanceof PsiMethodCallExpression) { |
| final PsiReferenceExpression methodExpression = ((PsiMethodCallExpression)parent).getMethodExpression(); |
| qualifier = methodExpression.getQualifier(); |
| } |
| } |
| if (resolveResult != null) { |
| PsiElement element = resolveResult.getElement(); |
| if (!(element instanceof PsiTypeParameterListOwner)) return null; |
| if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) return null; |
| if (qualifier instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifier).resolve() instanceof PsiTypeParameter) return null; |
| PsiClass containingClass = ((PsiMember)element).getContainingClass(); |
| if (containingClass != null && PsiUtil.isRawSubstitutor(containingClass, resolveResult.getSubstitutor())) { |
| if ((parent instanceof PsiCallExpression || parent instanceof PsiMethodReferenceExpression) && PsiUtil.isLanguageLevel7OrHigher(parent)) { |
| return null; |
| } |
| |
| if (element instanceof PsiMethod) { |
| if (((PsiMethod)element).findSuperMethods().length > 0) return null; |
| if (qualifier instanceof PsiReferenceExpression){ |
| final PsiType type = ((PsiReferenceExpression)qualifier).getType(); |
| final boolean isJavac7 = JavaVersionService.getInstance().isAtLeast(containingClass, JavaSdkVersion.JDK_1_7); |
| if (type instanceof PsiClassType && isJavac7 && ((PsiClassType)type).isRaw()) return null; |
| final PsiClass typeParameter = PsiUtil.resolveClassInType(type); |
| if (typeParameter instanceof PsiTypeParameter) { |
| if (isJavac7) return null; |
| for (PsiClassType classType : typeParameter.getExtendsListTypes()) { |
| final PsiClass resolve = classType.resolve(); |
| if (resolve != null) { |
| final PsiMethod[] superMethods = resolve.findMethodsBySignature((PsiMethod)element, true); |
| for (PsiMethod superMethod : superMethods) { |
| if (!PsiUtil.isRawSubstitutor(superMethod, resolveResult.getSubstitutor())) { |
| return null; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| final String message = element instanceof PsiClass |
| ? JavaErrorMessages.message("generics.type.arguments.on.raw.type") |
| : JavaErrorMessages.message("generics.type.arguments.on.raw.method"); |
| |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refParamList).descriptionAndTooltip(message).create(); |
| } |
| } |
| return null; |
| } |
| |
| public static HighlightInfo checkCannotInheritFromEnum(PsiClass superClass, PsiElement elementToHighlight) { |
| HighlightInfo errorResult = null; |
| if (Comparing.strEqual("java.lang.Enum", superClass.getQualifiedName())) { |
| String message = JavaErrorMessages.message("classes.extends.enum"); |
| errorResult = |
| HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(elementToHighlight).descriptionAndTooltip(message).create(); |
| } |
| return errorResult; |
| } |
| |
| public static HighlightInfo checkGenericCannotExtendException(PsiReferenceList list) { |
| PsiElement parent = list.getParent(); |
| if (!(parent instanceof PsiClass)) return null; |
| PsiClass aClass = (PsiClass)parent; |
| |
| if (!aClass.hasTypeParameters() || aClass.getExtendsList() != list) return null; |
| PsiJavaCodeReferenceElement[] referenceElements = list.getReferenceElements(); |
| PsiClass throwableClass = null; |
| for (PsiJavaCodeReferenceElement referenceElement : referenceElements) { |
| PsiElement resolved = referenceElement.resolve(); |
| if (!(resolved instanceof PsiClass)) continue; |
| if (throwableClass == null) { |
| throwableClass = JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Throwable", aClass.getResolveScope()); |
| } |
| if (InheritanceUtil.isInheritorOrSelf((PsiClass)resolved, throwableClass, true)) { |
| String message = JavaErrorMessages.message("generic.extend.exception"); |
| HighlightInfo highlightInfo = |
| HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceElement).descriptionAndTooltip(message).create(); |
| PsiClassType classType = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType((PsiClass)resolved); |
| QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createExtendsListFix(aClass, classType, false)); |
| return highlightInfo; |
| } |
| } |
| return null; |
| } |
| |
| public static HighlightInfo checkEnumMustNotBeLocal(final PsiClass aClass) { |
| if (!aClass.isEnum()) return null; |
| PsiElement parent = aClass.getParent(); |
| if (!(parent instanceof PsiClass || parent instanceof PsiFile)) { |
| String description = JavaErrorMessages.message("local.enum"); |
| TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create(); |
| } |
| return null; |
| } |
| |
| public static HighlightInfo checkSelectStaticClassFromParameterizedType(final PsiElement resolved, final PsiJavaCodeReferenceElement ref) { |
| if (resolved instanceof PsiClass && ((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC)) { |
| final PsiElement qualifier = ref.getQualifier(); |
| if (qualifier instanceof PsiJavaCodeReferenceElement) { |
| final PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement)qualifier).getParameterList(); |
| if (parameterList != null && parameterList.getTypeArguments().length > 0) { |
| final String message = JavaErrorMessages.message("generics.select.static.class.from.parameterized.type", |
| HighlightUtil.formatClass((PsiClass)resolved)); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parameterList).descriptionAndTooltip(message).create(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static HighlightInfo checkCannotInheritFromTypeParameter(final PsiClass superClass, final PsiJavaCodeReferenceElement toHighlight) { |
| if (superClass instanceof PsiTypeParameter) { |
| String description = JavaErrorMessages.message("class.cannot.inherit.from.its.type.parameter"); |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(toHighlight).descriptionAndTooltip(description).create(); |
| } |
| return null; |
| } |
| |
| /** |
| * http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8 |
| */ |
| static HighlightInfo checkRawOnParameterizedType(@NotNull PsiJavaCodeReferenceElement parent, PsiElement resolved) { |
| PsiReferenceParameterList list = parent.getParameterList(); |
| if (list == null || list.getTypeArguments().length > 0) return null; |
| final PsiElement qualifier = parent.getQualifier(); |
| if (qualifier instanceof PsiJavaCodeReferenceElement && |
| ((PsiJavaCodeReferenceElement)qualifier).getTypeParameters().length > 0 && |
| resolved instanceof PsiTypeParameterListOwner && |
| ((PsiTypeParameterListOwner)resolved).hasTypeParameters() && |
| !((PsiTypeParameterListOwner)resolved).hasModifierProperty(PsiModifier.STATIC)) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parent).descriptionAndTooltip( |
| "Improper formed type; some type parameters are missing").create(); |
| } |
| return null; |
| } |
| |
| public static HighlightInfo checkCannotPassInner(PsiJavaCodeReferenceElement ref) { |
| if (ref.getParent() instanceof PsiTypeElement) { |
| final PsiClass psiClass = PsiTreeUtil.getParentOfType(ref, PsiClass.class); |
| if (psiClass == null) return null; |
| if (PsiTreeUtil.isAncestor(psiClass.getExtendsList(), ref, false) || |
| PsiTreeUtil.isAncestor(psiClass.getImplementsList(), ref, false)) { |
| final PsiElement qualifier = ref.getQualifier(); |
| if (qualifier instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifier).resolve() == psiClass) { |
| final PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.getParentOfType(ref, PsiJavaCodeReferenceElement.class); |
| if (referenceElement == null) return null; |
| final PsiElement typeClass = referenceElement.resolve(); |
| if (!(typeClass instanceof PsiClass)) return null; |
| final PsiElement resolve = ref.resolve(); |
| final PsiClass containingClass = resolve != null ? ((PsiClass)resolve).getContainingClass() : null; |
| if (containingClass == null) return null; |
| if (psiClass.isInheritor(containingClass, true) || |
| unqualifiedNestedClassReferenceAccessedViaContainingClassInheritance((PsiClass)typeClass, ((PsiClass)resolve).getExtendsList()) || |
| unqualifiedNestedClassReferenceAccessedViaContainingClassInheritance((PsiClass)typeClass, ((PsiClass)resolve).getImplementsList())) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(((PsiClass)resolve).getName() + " is not accessible in current context").range(ref).create(); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| private static boolean unqualifiedNestedClassReferenceAccessedViaContainingClassInheritance(PsiClass containingClass, |
| PsiReferenceList referenceList) { |
| if (referenceList != null) { |
| for (PsiJavaCodeReferenceElement referenceElement : referenceList.getReferenceElements()) { |
| if (!referenceElement.isQualified()) { |
| final PsiElement superClass = referenceElement.resolve(); |
| if (superClass instanceof PsiClass) { |
| final PsiClass superContainingClass = ((PsiClass)superClass).getContainingClass(); |
| if (superContainingClass != null && |
| InheritanceUtil.isInheritorOrSelf(containingClass, superContainingClass, true) && |
| !PsiTreeUtil.isAncestor(superContainingClass, containingClass, true)) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| public static void registerVariableParameterizedTypeFixes(HighlightInfo highlightInfo, |
| @NotNull PsiVariable variable, |
| @NotNull PsiReferenceParameterList parameterList, |
| @NotNull JavaSdkVersion version) { |
| PsiType type = variable.getType(); |
| if (!(type instanceof PsiClassType)) return; |
| |
| if (DumbService.getInstance(variable.getProject()).isDumb()) return; |
| |
| String shortName = ((PsiClassType)type).getClassName(); |
| PsiManager manager = parameterList.getManager(); |
| final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); |
| PsiShortNamesCache shortNamesCache = PsiShortNamesCache.getInstance(parameterList.getProject()); |
| PsiClass[] classes = shortNamesCache.getClassesByName(shortName, GlobalSearchScope.allScope(manager.getProject())); |
| PsiElementFactory factory = facade.getElementFactory(); |
| for (PsiClass aClass : classes) { |
| if (checkReferenceTypeArgumentList(aClass, parameterList, PsiSubstitutor.EMPTY, false, version) == null) { |
| PsiType[] actualTypeParameters = parameterList.getTypeArguments(); |
| PsiTypeParameter[] classTypeParameters = aClass.getTypeParameters(); |
| Map<PsiTypeParameter, PsiType> map = new java.util.HashMap<PsiTypeParameter, PsiType>(); |
| for (int j = 0; j < classTypeParameters.length; j++) { |
| PsiTypeParameter classTypeParameter = classTypeParameters[j]; |
| PsiType actualTypeParameter = actualTypeParameters[j]; |
| map.put(classTypeParameter, actualTypeParameter); |
| } |
| PsiSubstitutor substitutor = factory.createSubstitutor(map); |
| PsiType suggestedType = factory.createType(aClass, substitutor); |
| HighlightUtil.registerChangeVariableTypeFixes(variable, suggestedType, variable.getInitializer(), highlightInfo); |
| } |
| } |
| } |
| |
| public static HighlightInfo checkInferredIntersections(PsiSubstitutor substitutor, TextRange ref) { |
| for (Map.Entry<PsiTypeParameter, PsiType> typeEntry : substitutor.getSubstitutionMap().entrySet()) { |
| final PsiType type = typeEntry.getValue(); |
| if (type instanceof PsiIntersectionType) { |
| final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts(); |
| for (int i = 0; i < conjuncts.length; i++) { |
| PsiClass conjunct = PsiUtil.resolveClassInClassTypeOnly(conjuncts[i]); |
| if (conjunct != null && !conjunct.isInterface()) { |
| for (int i1 = i + 1; i1 < conjuncts.length; i1++) { |
| PsiClass oppositeConjunct = PsiUtil.resolveClassInClassTypeOnly(conjuncts[i1]); |
| if (oppositeConjunct != null && !oppositeConjunct.isInterface()) { |
| if (!conjunct.isInheritor(oppositeConjunct, true) && !oppositeConjunct.isInheritor(conjunct, true)) { |
| return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) |
| .descriptionAndTooltip("Type parameter " + typeEntry.getKey().getName() + " has incompatible upper bounds: " + |
| conjunct.getName() + " and " + oppositeConjunct.getName()) |
| .range(ref).create(); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| } |
| |