public static void checkMethodConflicts( @Nullable PsiClass aClass, PsiMethod refactoredMethod, PsiMethod prototype, final MultiMap<PsiElement, String> conflicts) { if (prototype == null) return; PsiMethod method = aClass != null ? aClass.findMethodBySignature(prototype, true) : null; if (method != null && method != refactoredMethod) { if (aClass.equals(method.getContainingClass())) { final String classDescr = aClass instanceof PsiAnonymousClass ? RefactoringBundle.message("current.class") : RefactoringUIUtil.getDescription(aClass, false); conflicts.putValue( method, RefactoringBundle.message( "method.0.is.already.defined.in.the.1", getMethodPrototypeString(prototype), classDescr)); } else { // method somewhere in base class if (JavaPsiFacade.getInstance(method.getProject()) .getResolveHelper() .isAccessible(method, aClass, null)) { String protoMethodInfo = getMethodPrototypeString(prototype); String className = CommonRefactoringUtil.htmlEmphasize( UsageViewUtil.getDescriptiveName(method.getContainingClass())); if (PsiUtil.getAccessLevel(prototype.getModifierList()) >= PsiUtil.getAccessLevel(method.getModifierList())) { boolean isMethodAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); boolean isMyMethodAbstract = refactoredMethod != null && refactoredMethod.hasModifierProperty(PsiModifier.ABSTRACT); final String conflict = isMethodAbstract != isMyMethodAbstract ? RefactoringBundle.message( "method.0.will.implement.method.of.the.base.class", protoMethodInfo, className) : RefactoringBundle.message( "method.0.will.override.a.method.of.the.base.class", protoMethodInfo, className); conflicts.putValue(method, conflict); } else { // prototype is private, will be compile-error conflicts.putValue( method, RefactoringBundle.message( "method.0.will.hide.method.of.the.base.class", protoMethodInfo, className)); } } } } }
@NotNull public static SearchScope getMemberUseScope(@NotNull PsiMember member) { PsiFile file = member.getContainingFile(); PsiElement topElement = file == null ? member : file; Project project = topElement.getProject(); final GlobalSearchScope maximalUseScope = ResolveScopeManager.getInstance(project).getUseScope(topElement); if (isInServerPage(file)) return maximalUseScope; PsiClass aClass = member.getContainingClass(); if (aClass instanceof PsiAnonymousClass && !(aClass instanceof PsiEnumConstantInitializer && member instanceof PsiMethod && member.hasModifierProperty(PsiModifier.PUBLIC) && ((PsiMethod) member).findSuperMethods().length > 0)) { // member from anonymous class can be called from outside the class PsiElement methodCallExpr = PsiUtil.isLanguageLevel8OrHigher(aClass) ? PsiTreeUtil.getTopmostParentOfType(aClass, PsiStatement.class) : PsiTreeUtil.getParentOfType(aClass, PsiMethodCallExpression.class); return new LocalSearchScope(methodCallExpr != null ? methodCallExpr : aClass); } PsiModifierList modifierList = member.getModifierList(); int accessLevel = modifierList == null ? PsiUtil.ACCESS_LEVEL_PUBLIC : PsiUtil.getAccessLevel(modifierList); if (accessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC || accessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) { return maximalUseScope; // class use scope doesn't matter, since another very visible class // can inherit from aClass } if (accessLevel == PsiUtil.ACCESS_LEVEL_PRIVATE) { PsiClass topClass = PsiUtil.getTopLevelClass(member); return topClass != null ? new LocalSearchScope(topClass) : file == null ? maximalUseScope : new LocalSearchScope(file); } if (file instanceof PsiJavaFile) { PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(((PsiJavaFile) file).getPackageName()); if (aPackage != null) { SearchScope scope = PackageScope.packageScope(aPackage, false); return scope.intersectWith(maximalUseScope); } } return maximalUseScope; }
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))); } }
@Override public void invoke( @NotNull Project project, @NotNull PsiFile file, @Nullable("is null when called from inspection") Editor editor, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { final PsiModifierList myModifierList = (PsiModifierList) startElement; final PsiVariable variable = myVariable == null ? null : myVariable.getElement(); if (!CodeInsightUtilBase.preparePsiElementForWrite(myModifierList)) return; final List<PsiModifierList> modifierLists = new ArrayList<PsiModifierList>(); final PsiFile containingFile = myModifierList.getContainingFile(); final PsiModifierList modifierList; if (variable != null && variable.isValid()) { ApplicationManager.getApplication() .runWriteAction( new Runnable() { public void run() { try { variable.normalizeDeclaration(); } catch (IncorrectOperationException e) { LOG.error(e); } } }); modifierList = variable.getModifierList(); assert modifierList != null; } else { modifierList = myModifierList; } PsiElement owner = modifierList.getParent(); if (owner instanceof PsiMethod) { PsiModifierList copy = (PsiModifierList) myModifierList.copy(); changeModifierList(copy); final int accessLevel = PsiUtil.getAccessLevel(copy); OverridingMethodsSearch.search((PsiMethod) owner, owner.getResolveScope(), true) .forEach( new PsiElementProcessorAdapter<PsiMethod>( new PsiElementProcessor<PsiMethod>() { public boolean execute(@NotNull PsiMethod inheritor) { PsiModifierList list = inheritor.getModifierList(); if (inheritor.getManager().isInProject(inheritor) && PsiUtil.getAccessLevel(list) < accessLevel) { modifierLists.add(list); } return true; } })); } if (!CodeInsightUtilBase.prepareFileForWrite(containingFile)) return; if (!modifierLists.isEmpty()) { if (Messages.showYesNoDialog( project, QuickFixBundle.message("change.inheritors.visibility.warning.text"), QuickFixBundle.message("change.inheritors.visibility.warning.title"), Messages.getQuestionIcon()) == DialogWrapper.OK_EXIT_CODE) { ApplicationManager.getApplication() .runWriteAction( new Runnable() { public void run() { if (!CodeInsightUtilBase.preparePsiElementsForWrite(modifierLists)) { return; } for (final PsiModifierList modifierList : modifierLists) { changeModifierList(modifierList); } } }); } } ApplicationManager.getApplication() .runWriteAction( new Runnable() { public void run() { changeModifierList(modifierList); UndoUtil.markPsiFileForUndo(containingFile); } }); }