private static boolean isOverrideObjectDirect(@NotNull PsiMethod method) { List<HierarchicalMethodSignature> superSignatures = method.getHierarchicalMethodSignature().getSuperSignatures(); if (superSignatures.size() == 1) { PsiClass containingClass = superSignatures.get(0).getMethod().getContainingClass(); String qualifiedName = containingClass != null ? containingClass.getQualifiedName() : ""; if (qualifiedName != null && qualifiedName.equals(JAVA_LANG_OBJECT)) { return true; } } return false; }
private static boolean isInheritFromObject(@NotNull PsiMethod method) { List<HierarchicalMethodSignature> superSignatures = method.getHierarchicalMethodSignature().getSuperSignatures(); for (HierarchicalMethodSignature s : superSignatures) { PsiClass containingClass = s.getMethod().getContainingClass(); String qualifiedName = containingClass != null ? containingClass.getQualifiedName() : ""; if (qualifiedName != null && qualifiedName.equals(JAVA_LANG_OBJECT)) { return true; } } return false; }
private static boolean normalCase(@NotNull PsiMethod method) { int counter = 0; for (HierarchicalMethodSignature s : method.getHierarchicalMethodSignature().getSuperSignatures()) { PsiClass containingClass = s.getMethod().getContainingClass(); String qualifiedName = containingClass != null ? containingClass.getQualifiedName() : ""; if (qualifiedName != null && !qualifiedName.equals(JAVA_LANG_OBJECT)) { counter++; } } return counter > 0; }
// uses hierarchy signature tree if available, traverses class structure by itself otherwise public static boolean processDirectSuperMethodsSmart( @NotNull PsiMethod method, @NotNull Processor<PsiMethod> superMethodProcessor) { // boolean old = PsiSuperMethodUtil.isSuperMethod(method, superMethod); PsiClass aClass = method.getContainingClass(); if (aClass == null) return false; if (!canHaveSuperMethod(method, true, false)) return false; Map<MethodSignature, HierarchicalMethodSignature> cachedMap = SIGNATURES_KEY.getCachedValueOrNull(aClass); if (cachedMap != null) { HierarchicalMethodSignature signature = cachedMap.get(method.getSignature(PsiSubstitutor.EMPTY)); if (signature != null) { List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures(); for (HierarchicalMethodSignature superSignature : superSignatures) { if (!superMethodProcessor.process(superSignature.getMethod())) return false; } return true; } } PsiClassType[] directSupers = aClass.getSuperTypes(); for (PsiClassType directSuper : directSupers) { PsiClassType.ClassResolveResult resolveResult = directSuper.resolveGenerics(); if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) { // generics break; } PsiClass directSuperClass = resolveResult.getElement(); if (directSuperClass == null) continue; PsiMethod[] candidates = directSuperClass.findMethodsBySignature(method, false); for (PsiMethod candidate : candidates) { if (PsiUtil.canBeOverriden(candidate)) { if (!superMethodProcessor.process(candidate)) return false; } } return true; } List<HierarchicalMethodSignature> superSignatures = method.getHierarchicalMethodSignature().getSuperSignatures(); for (HierarchicalMethodSignature superSignature : superSignatures) { if (!superMethodProcessor.process(superSignature.getMethod())) return false; } return true; }
private void checkMember(@NotNull final PsiMember member) { if (member.hasModifierProperty(PsiModifier.PRIVATE) || member.hasModifierProperty(PsiModifier.NATIVE)) return; if (member instanceof PsiMethod && member instanceof SyntheticElement || !member.isPhysical()) return; if (member instanceof PsiMethod) { PsiMethod method = (PsiMethod) member; if (!method.getHierarchicalMethodSignature().getSuperSignatures().isEmpty()) { log(member.getName() + " overrides"); return; // overrides } if (MethodUtils.isOverridden(method)) { log(member.getName() + " overridden"); return; } } if (member instanceof PsiEnumConstant) return; if (member instanceof PsiClass && (member instanceof PsiAnonymousClass || member instanceof PsiTypeParameter || member instanceof PsiSyntheticClass || PsiUtil.isLocalClass((PsiClass) member))) { return; } final PsiClass memberClass = member.getContainingClass(); if (memberClass != null && (memberClass.isInterface() || memberClass.isEnum() || memberClass.isAnnotationType() || PsiUtil.isLocalClass(memberClass) && member instanceof PsiClass)) { return; } final PsiFile memberFile = member.getContainingFile(); Project project = memberFile.getProject(); if (myDeadCodeInspection.isEntryPoint(member)) { log(member.getName() + " is entry point"); return; } PsiModifierList memberModifierList = member.getModifierList(); if (memberModifierList == null) return; final int currentLevel = PsiUtil.getAccessLevel(memberModifierList); final AtomicInteger maxLevel = new AtomicInteger(PsiUtil.ACCESS_LEVEL_PRIVATE); final AtomicBoolean foundUsage = new AtomicBoolean(); PsiDirectory memberDirectory = memberFile.getContainingDirectory(); final PsiPackage memberPackage = memberDirectory == null ? null : JavaDirectoryService.getInstance().getPackage(memberDirectory); log(member.getName() + ": checking effective level for " + member); boolean result = UnusedSymbolUtil.processUsages( project, memberFile, member, new EmptyProgressIndicator(), null, new Processor<UsageInfo>() { @Override public boolean process(UsageInfo info) { foundUsage.set(true); PsiFile psiFile = info.getFile(); if (psiFile == null) return true; if (!(psiFile instanceof PsiJavaFile)) { log(" refd from " + psiFile.getName() + "; set to public"); maxLevel.set(PsiUtil.ACCESS_LEVEL_PUBLIC); if (memberClass != null) { childMembersAreUsedOutsideMyPackage.add(memberClass); } return false; // referenced from XML, has to be public } // int offset = info.getNavigationOffset(); // if (offset == -1) return true; PsiElement element = info.getElement(); if (element == null) return true; @PsiUtil.AccessLevel int level = getEffectiveLevel(element, psiFile, memberFile, memberClass, memberPackage); log( " ref in file " + psiFile.getName() + "; level = " + PsiUtil.getAccessModifier(level) + "; (" + element + ")"); while (true) { int oldLevel = maxLevel.get(); if (level <= oldLevel || maxLevel.compareAndSet(oldLevel, level)) break; } if (level == PsiUtil.ACCESS_LEVEL_PUBLIC && memberClass != null) { childMembersAreUsedOutsideMyPackage.add(memberClass); } return level != PsiUtil.ACCESS_LEVEL_PUBLIC; } }); if (!foundUsage.get()) { log(member.getName() + " unused; ignore"); return; // do not propose private for unused method } int max = maxLevel.get(); if (max == PsiUtil.ACCESS_LEVEL_PRIVATE && memberClass == null) { max = PsiUtil.ACCESS_LEVEL_PACKAGE_LOCAL; } log(member.getName() + ": effective level is '" + PsiUtil.getAccessModifier(max) + "'"); if (max < currentLevel) { if (max == PsiUtil.ACCESS_LEVEL_PACKAGE_LOCAL && member instanceof PsiClass && childMembersAreUsedOutsideMyPackage.contains(member)) { log(member.getName() + " children used outside my package; ignore"); return; // e.g. some public method is used outside my package (without importing class) } PsiElement toHighlight = currentLevel == PsiUtil.ACCESS_LEVEL_PACKAGE_LOCAL ? ((PsiNameIdentifierOwner) member).getNameIdentifier() : ContainerUtil.find( memberModifierList.getChildren(), new Condition<PsiElement>() { @Override public boolean value(PsiElement element) { return element instanceof PsiKeyword && element.getText().equals(PsiUtil.getAccessModifier(currentLevel)); } }); assert toHighlight != null : member + " ; " + ((PsiNameIdentifierOwner) member).getNameIdentifier() + "; " + memberModifierList.getText(); myHolder.registerProblem( toHighlight, "Access can be " + PsiUtil.getAccessModifier(max), new ChangeModifierFix(PsiUtil.getAccessModifier(max))); } }
// uses hierarchy signature tree if available, traverses class structure by itself otherwise public static boolean isSuperMethodSmart( @NotNull PsiMethod method, @NotNull PsiMethod superMethod) { // boolean old = PsiSuperMethodUtil.isSuperMethod(method, superMethod); if (method == superMethod) return false; PsiClass aClass = method.getContainingClass(); PsiClass superClass = superMethod.getContainingClass(); if (aClass == null || superClass == null || superClass == aClass) return false; if (!canHaveSuperMethod(method, true, false)) return false; PsiMethod[] superMethods = null; Map<MethodSignature, HierarchicalMethodSignature> cachedMap = SIGNATURES_KEY.getCachedValueOrNull(aClass); if (cachedMap != null) { HierarchicalMethodSignature signature = cachedMap.get(method.getSignature(PsiSubstitutor.EMPTY)); if (signature != null) { superMethods = MethodSignatureUtil.convertMethodSignaturesToMethods(signature.getSuperSignatures()); } } if (superMethods == null) { PsiClassType[] directSupers = aClass.getSuperTypes(); List<PsiMethod> found = null; boolean canceled = false; for (PsiClassType directSuper : directSupers) { PsiClassType.ClassResolveResult resolveResult = directSuper.resolveGenerics(); if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) { // generics canceled = true; break; } PsiClass directSuperClass = resolveResult.getElement(); if (directSuperClass == null) continue; PsiMethod[] candidates = directSuperClass.findMethodsBySignature(method, false); if (candidates.length != 0) { if (found == null) found = new ArrayList<PsiMethod>(); for (PsiMethod candidate : candidates) { if (PsiUtil.canBeOverriden(candidate)) found.add(candidate); } } } superMethods = canceled ? null : found == null ? PsiMethod.EMPTY_ARRAY : found.toArray(new PsiMethod[found.size()]); } if (superMethods == null) { superMethods = MethodSignatureUtil.convertMethodSignaturesToMethods( method.getHierarchicalMethodSignature().getSuperSignatures()); } for (PsiMethod superCandidate : superMethods) { if (superMethod.equals(superCandidate) || isSuperMethodSmart(superCandidate, superMethod)) return true; } return false; }
protected void checkSameSignatures(@NotNull List<CandidateInfo> conflicts) { // candidates should go in order of class hierarchy traversal // in order for this to work Map<MethodSignature, CandidateInfo> signatures = new THashMap<MethodSignature, CandidateInfo>(conflicts.size()); Set<PsiMethod> superMethods = new HashSet<PsiMethod>(); for (CandidateInfo conflict : conflicts) { final PsiMethod method = ((MethodCandidateInfo) conflict).getElement(); for (HierarchicalMethodSignature methodSignature : method.getHierarchicalMethodSignature().getSuperSignatures()) { final PsiMethod superMethod = methodSignature.getMethod(); final PsiClass aClass = superMethod.getContainingClass(); if (aClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) { superMethods.add(superMethod); } } } nextConflict: for (int i = 0; i < conflicts.size(); i++) { ProgressManager.checkCanceled(); CandidateInfo info = conflicts.get(i); PsiMethod method = (PsiMethod) info.getElement(); if (!method.hasModifierProperty(PsiModifier.STATIC) && superMethods.contains(method)) { conflicts.remove(i); i--; continue; } PsiClass class1 = method.getContainingClass(); PsiSubstitutor infoSubstitutor = ((MethodCandidateInfo) info).getSubstitutor(false); MethodSignature signature = method.getSignature(infoSubstitutor); CandidateInfo existing = signatures.get(signature); if (existing == null) { signatures.put(signature, info); continue; } PsiMethod existingMethod = (PsiMethod) existing.getElement(); PsiClass existingClass = existingMethod.getContainingClass(); if (class1 != null && existingClass != null) { // prefer interface methods to methods from Object if (class1.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(existingClass.getQualifiedName())) { signatures.put(signature, info); continue; } else if (existingClass.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(class1.getQualifiedName())) { conflicts.remove(info); i--; continue; } } if (method == existingMethod) { PsiElement scope1 = info.getCurrentFileResolveScope(); PsiElement scope2 = existing.getCurrentFileResolveScope(); if (scope1 instanceof PsiClass && scope2 instanceof PsiClass && PsiTreeUtil.isAncestor(scope1, scope2, true) && !existing .isAccessible()) { // prefer methods from outer class to inaccessible base class // methods signatures.put(signature, info); continue; } } // filter out methods with incorrect inferred bounds (for unrelated methods only) boolean existingTypeParamAgree = areTypeParametersAgree(existing); boolean infoTypeParamAgree = areTypeParametersAgree(info); if (existingTypeParamAgree && !infoTypeParamAgree && !PsiSuperMethodImplUtil.isSuperMethodSmart(method, existingMethod)) { conflicts.remove(i); i--; continue; } if (!existingTypeParamAgree && infoTypeParamAgree && !PsiSuperMethodImplUtil.isSuperMethodSmart(existingMethod, method)) { signatures.put(signature, info); int index = conflicts.indexOf(existing); conflicts.remove(index); i--; continue; } if (InheritanceUtil.isInheritorOrSelf(class1, existingClass, true) || InheritanceUtil.isInheritorOrSelf(existingClass, class1, true)) { PsiParameter[] parameters = method.getParameterList().getParameters(); final PsiParameter[] existingParameters = existingMethod.getParameterList().getParameters(); for (int i1 = 0, parametersLength = parameters.length; i1 < parametersLength; i1++) { if (parameters[i1].getType() instanceof PsiArrayType && !(existingParameters[i1].getType() instanceof PsiArrayType)) { // prefer more specific type signatures.put(signature, info); continue nextConflict; } } PsiType returnType1 = method.getReturnType(); PsiType returnType2 = existingMethod.getReturnType(); if (returnType1 != null && returnType2 != null) { returnType1 = infoSubstitutor.substitute(returnType1); returnType2 = ((MethodCandidateInfo) existing).getSubstitutor(false).substitute(returnType2); if (!returnType1.equals(returnType2) && returnType1.isAssignableFrom(returnType2)) { conflicts.remove(i); i--; continue; } } // prefer derived class signatures.put(signature, info); } else { final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(myArgumentsList, PsiMethodCallExpression.class); if (methodCallExpression != null) { final PsiReferenceExpression expression = methodCallExpression.getMethodExpression(); final PsiExpression qualifierExpression = expression.getQualifierExpression(); PsiClass currentClass; if (qualifierExpression != null) { currentClass = PsiUtil.resolveClassInClassTypeOnly(qualifierExpression.getType()); } else { currentClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); } if (currentClass != null && existingClass != null && class1 != null) { final PsiSubstitutor eSubstitutor = TypeConversionUtil.getMaybeSuperClassSubstitutor( existingClass, currentClass, PsiSubstitutor.EMPTY, null); final PsiSubstitutor cSubstitutor = TypeConversionUtil.getMaybeSuperClassSubstitutor( class1, currentClass, PsiSubstitutor.EMPTY, null); if (eSubstitutor != null && cSubstitutor != null && MethodSignatureUtil.areSignaturesEqual( existingMethod.getSignature(eSubstitutor), method.getSignature(cSubstitutor))) { final PsiType returnType = eSubstitutor.substitute(existingMethod.getReturnType()); final PsiType returnType1 = cSubstitutor.substitute(method.getReturnType()); if (returnType != null && returnType1 != null && !returnType1.equals(returnType)) { if (TypeConversionUtil.isAssignable(returnType, returnType1, false)) { if (class1.isInterface() && !existingClass.isInterface()) continue; conflicts.remove(existing); } else { if (!TypeConversionUtil.isAssignable(returnType1, returnType, false)) continue; conflicts.remove(i); } i--; break; } } } } } } }