[java-inspections] Move unused symbol warnings from PostHighlightingVisitor to normal inspection

Fixes IDEA-349083 Reimplement unused inspection not as a part of PostHighlightingVisitor, but as a normal inspection

GitOrigin-RevId: cb6e22eddbaf9db42626a79c7881bd377c4c7863
This commit is contained in:
Tagir Valeev
2024-03-20 17:18:52 +01:00
committed by intellij-monorepo-bot
parent 2a97b0103c
commit 5ceb7551aa
7 changed files with 432 additions and 497 deletions

View File

@@ -60,7 +60,7 @@ public final class LocalRefUseInfo {
}
@NotNull
GlobalUsageHelper getGlobalUsageHelper(@NotNull PsiFile file, @Nullable UnusedDeclarationInspectionBase deadCodeInspection) {
public GlobalUsageHelper getGlobalUsageHelper(@NotNull PsiFile file, @Nullable UnusedDeclarationInspectionBase deadCodeInspection) {
FileViewProvider viewProvider = file.getViewProvider();
Project project = file.getProject();
@@ -134,7 +134,7 @@ public final class LocalRefUseInfo {
* @param element element to check (variable, method, parameter, field, etc.)
* @return true if the element is referenced in the same file
*/
boolean isReferenced(@NotNull PsiElement element) {
public boolean isReferenced(@NotNull PsiElement element) {
Collection<PsiReference> array = myLocalRefsMap.get(element);
if (!array.isEmpty() &&
!isParameterUsedRecursively(element, array) &&
@@ -197,7 +197,7 @@ public final class LocalRefUseInfo {
return true;
}
boolean isReferencedForRead(@NotNull PsiVariable variable) {
public boolean isReferencedForRead(@NotNull PsiVariable variable) {
Collection<PsiReference> array = myLocalRefsMap.get(variable);
if (array.isEmpty()) return false;
for (PsiReference ref : array) {
@@ -231,7 +231,7 @@ public final class LocalRefUseInfo {
refElement.getParent().getParent() instanceof PsiExpressionStatement;
}
boolean isReferencedForWrite(@NotNull PsiVariable variable) {
public boolean isReferencedForWrite(@NotNull PsiVariable variable) {
Collection<PsiReference> array = myLocalRefsMap.get(variable);
if (array.isEmpty()) return false;
for (PsiReference ref : array) {

View File

@@ -3,23 +3,16 @@ package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.UnusedImportProvider;
import com.intellij.codeInsight.daemon.impl.*;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceWithUnnamedPatternFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
import com.intellij.codeInspection.ExternalSourceProblemGroup;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.SuppressionUtil;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.InspectionProfileWrapper;
import com.intellij.codeInspection.unusedImport.MissortedImportsInspection;
import com.intellij.codeInspection.unusedImport.UnusedImportInspection;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection;
import com.intellij.codeInspection.util.SpecialAnnotationsUtilBase;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.lang.annotation.ProblemGroup;
@@ -29,29 +22,15 @@ import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Predicates;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.*;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ObjectUtils;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ConcurrentFactoryMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@@ -60,12 +39,8 @@ class PostHighlightingVisitor extends JavaElementVisitor {
@NotNull private final Project myProject;
private final PsiFile myFile;
@NotNull private final Document myDocument;
private final GlobalUsageHelper myGlobalUsageHelper;
private IntentionAction myOptimizeImportsFix; // when not null, there are not-optimized imports in the file
private int myCurrentEntryIndex = -1;
private final UnusedSymbolLocalInspection myUnusedSymbolInspection;
private final HighlightDisplayKey myDeadCodeKey;
private final HighlightInfoType myDeadCodeInfoType;
private boolean errorFound;
PostHighlightingVisitor(@NotNull PsiFile file, @NotNull Document document) throws ProcessCanceledException {
@@ -75,45 +50,10 @@ class PostHighlightingVisitor extends JavaElementVisitor {
myFile = file;
myDocument = document;
myRefCountHolder = LocalRefUseInfo.forFile(file);
InspectionProfileImpl profile = InspectionProjectProfileManager.getInstance(myProject).getCurrentProfile();
myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME);
UnusedDeclarationInspectionBase deadCodeInspection = (UnusedDeclarationInspectionBase)profile.getUnwrappedTool(UnusedDeclarationInspectionBase.SHORT_NAME, myFile);
myUnusedSymbolInspection = deadCodeInspection == null ? null : deadCodeInspection.getSharedLocalInspectionTool();
myDeadCodeInfoType = myDeadCodeKey == null ? HighlightInfoType.UNUSED_SYMBOL
: new HighlightInfoType.HighlightInfoTypeImpl(profile.getErrorLevel(myDeadCodeKey, myFile).getSeverity(),
ObjectUtils.notNull(profile.getEditorAttributes(myDeadCodeKey.toString(), myFile),
HighlightInfoType.UNUSED_SYMBOL.getAttributesKey()));
myGlobalUsageHelper = myRefCountHolder.getGlobalUsageHelper(myFile, deadCodeInspection);
}
void collectHighlights(@NotNull HighlightInfoHolder holder) {
ApplicationManager.getApplication().assertIsNonDispatchThread();
if (myDeadCodeKey != null && isToolEnabled(myDeadCodeKey)) {
TextRange priorityRange = holder.getAnnotationSession().getPriorityRange();
JavaElementVisitor identifierVisitor = new JavaElementVisitor() {
@Override
public void visitIdentifier(@NotNull PsiIdentifier identifier) {
processIdentifier(holder, identifier);
}
};
Divider.divideInsideAndOutsideAllRoots(myFile, myFile.getTextRange(), priorityRange, Predicates.alwaysTrue(), dividedElements -> {
ProgressManager.checkCanceled();
PsiFile psiRoot = dividedElements.psiRoot();
HighlightingLevelManager highlightingLevelManager = HighlightingLevelManager.getInstance(myProject);
if (!highlightingLevelManager.shouldInspect(psiRoot) || highlightingLevelManager.runEssentialHighlightingOnly(psiRoot)) {
return true;
}
for (PsiElement element : dividedElements.inside()) {
ProgressManager.checkCanceled();
element.accept(identifierVisitor);
}
for (PsiElement element : dividedElements.outside()) {
ProgressManager.checkCanceled();
element.accept(identifierVisitor);
}
return true;
});
}
HighlightDisplayKey unusedImportKey = HighlightDisplayKey.find(UnusedImportInspection.SHORT_NAME);
PsiJavaFile javaFile = ObjectUtils.tryCast(myFile, PsiJavaFile.class);
@@ -161,7 +101,7 @@ class PostHighlightingVisitor extends JavaElementVisitor {
}
private boolean isToolEnabled(@NotNull HighlightDisplayKey displayKey) {
if (!(myFile instanceof PsiJavaFile) || myUnusedSymbolInspection == null) {
if (!(myFile instanceof PsiJavaFile)) {
return false;
}
InspectionProfile profile = getCurrentProfile(myFile);
@@ -177,360 +117,6 @@ class PostHighlightingVisitor extends JavaElementVisitor {
return custom != null ? custom.apply(currentProfile).getInspectionProfile() : currentProfile;
}
@NlsContexts.DetailedDescription private String message;
private final List<IntentionAction> quickFixes = new ArrayList<>();
private final List<IntentionAction> quickFixOptions = new ArrayList<>();
@Override
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
if (myUnusedSymbolInspection.LOCAL_VARIABLE) {
processLocalVariable(variable);
}
}
@Override
public void visitField(@NotNull PsiField field) {
if (compareVisibilities(field, myUnusedSymbolInspection.getFieldVisibility())) {
processField(myProject, field);
}
}
@Override
public void visitParameter(@NotNull PsiParameter parameter) {
PsiElement declarationScope = parameter.getDeclarationScope();
boolean needToProcessParameter;
if (declarationScope instanceof PsiMethod || declarationScope instanceof PsiLambdaExpression) {
if (declarationScope instanceof PsiLambdaExpression) {
declarationScope = PsiTreeUtil.getParentOfType(declarationScope, PsiModifierListOwner.class);
}
needToProcessParameter = compareVisibilities((PsiModifierListOwner)declarationScope, myUnusedSymbolInspection.getParameterVisibility());
}
else {
needToProcessParameter = myUnusedSymbolInspection.LOCAL_VARIABLE;
}
if (needToProcessParameter) {
processParameter(myProject, parameter);
}
}
@Override
public void visitMethod(@NotNull PsiMethod method) {
if (myUnusedSymbolInspection.isIgnoreAccessors() && PropertyUtilBase.isSimplePropertyAccessor(method)) {
return;
}
if (compareVisibilities(method, myUnusedSymbolInspection.getMethodVisibility())) {
processMethod(myProject, method);
}
}
@Override
public void visitClass(@NotNull PsiClass aClass) {
String acceptedVisibility = aClass.getContainingClass() == null ? myUnusedSymbolInspection.getClassVisibility()
: myUnusedSymbolInspection.getInnerClassVisibility();
if (compareVisibilities(aClass, acceptedVisibility)) {
processClass(myProject, aClass);
}
}
private void processIdentifier(@NotNull HighlightInfoHolder holder, @NotNull PsiIdentifier identifier) {
PsiElement parent = identifier.getParent();
if (parent == null) return;
if ((parent instanceof PsiVariable || parent instanceof PsiMember) && SuppressionUtil.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) return;
if (parent instanceof PsiParameter && SuppressionUtil.isSuppressed(identifier, UnusedSymbolLocalInspection.UNUSED_PARAMETERS_SHORT_NAME)) return;
parent.accept(this);
if (message != null) {
HighlightInfo.Builder builder =
UnusedSymbolUtil.createUnusedSymbolInfoBuilder(identifier, message, myDeadCodeInfoType, UnusedDeclarationInspectionBase.SHORT_NAME);
for (IntentionAction fix : quickFixes) {
TextRange fixRange = parent instanceof PsiField ? HighlightMethodUtil.getFixRange(parent) : null;
builder.registerFix(fix, null, HighlightDisplayKey.getDisplayNameByKey(myDeadCodeKey), fixRange, myDeadCodeKey);
}
for (IntentionAction fix : quickFixOptions) {
TextRange fixRange = parent instanceof PsiField ? HighlightMethodUtil.getFixRange(parent) : null;
builder.registerFix(fix, null, HighlightDisplayKey.getDisplayNameByKey(myDeadCodeKey), fixRange, null);
}
addInfo(holder, builder);
message = null;
quickFixes.clear();
quickFixOptions.clear();
}
else {
assert quickFixes.isEmpty();
assert quickFixOptions.isEmpty();
}
}
private static boolean compareVisibilities(PsiModifierListOwner listOwner, String visibility) {
if (visibility != null) {
while (listOwner != null) {
if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(listOwner.getModifierList()), visibility) >= 0) {
return true;
}
listOwner = PsiTreeUtil.getParentOfType(listOwner, PsiModifierListOwner.class, true);
}
}
return false;
}
private void processLocalVariable(@NotNull PsiLocalVariable variable) {
if (variable.isUnnamed() || PsiUtil.isIgnoredName(variable.getName())) return;
if (UnusedSymbolUtil.isImplicitUsage(myProject, variable)) return;
if (!myRefCountHolder.isReferenced(variable)) {
message = JavaErrorBundle.message("local.variable.is.never.used", variable.getName());
quickFixes.add(variable instanceof PsiResourceVariable ? QuickFixFactory.getInstance().createRenameToIgnoredFix(variable, false)
: QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable));
}
else if (!myRefCountHolder.isReferencedForRead(variable) && !UnusedSymbolUtil.isImplicitRead(myProject, variable)) {
message = JavaErrorBundle.message("local.variable.is.not.used.for.reading", variable.getName());
quickFixes.add(QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable));
}
else if (!variable.hasInitializer() &&
!myRefCountHolder.isReferencedForWrite(variable) &&
!UnusedSymbolUtil.isImplicitWrite(myProject, variable)) {
message = JavaErrorBundle.message("local.variable.is.not.assigned", variable.getName());
quickFixes.add(QuickFixFactory.getInstance().createAddVariableInitializerFix(variable));
}
}
private void processField(@NotNull Project project, @NotNull PsiField field) {
if (HighlightUtil.isSerializationImplicitlyUsedField(field)) {
return;
}
if (field.hasModifierProperty(PsiModifier.PRIVATE)) {
if (!myRefCountHolder.isReferenced(field) && !UnusedSymbolUtil.isImplicitUsage(myProject, field)) {
message = JavaErrorBundle.message("private.field.is.not.used", field.getName());
suggestionsToMakeFieldUsed(field);
if (!field.hasInitializer() && !field.hasModifierProperty(PsiModifier.FINAL)) {
quickFixes.add(QuickFixFactory.getInstance().createCreateConstructorParameterFromFieldFix(field));
}
return;
}
boolean readReferenced = myRefCountHolder.isReferencedForRead(field);
if (!readReferenced && !UnusedSymbolUtil.isImplicitRead(project, field)) {
message = getNotUsedForReadingMessage(field);
suggestionsToMakeFieldUsed(field);
return;
}
if (field.hasInitializer()) {
return;
}
boolean writeReferenced = myRefCountHolder.isReferencedForWrite(field);
if (!writeReferenced && !UnusedSymbolUtil.isImplicitWrite(project, field)) {
message = JavaErrorBundle.message("private.field.is.not.assigned", field.getName());
quickFixes.add(QuickFixFactory.getInstance().createCreateGetterOrSetterFix(false, true, field));
if (!field.hasModifierProperty(PsiModifier.FINAL)) {
quickFixes.add(QuickFixFactory.getInstance().createCreateConstructorParameterFromFieldFix(field));
}
SpecialAnnotationsUtilBase.processUnknownAnnotations(field, annoName ->
quickFixes.add(QuickFixFactory.getInstance().createAddToImplicitlyWrittenFieldsFix(project, annoName)));
}
}
else if (!UnusedSymbolUtil.isFieldUsed(myProject, myFile, field, myGlobalUsageHelper)) {
if (UnusedSymbolUtil.isImplicitWrite(myProject, field)) {
message = getNotUsedForReadingMessage(field);
quickFixes.add(QuickFixFactory.getInstance().createSafeDeleteFix(field));
}
else if (!UnusedSymbolUtil.isImplicitUsage(myProject, field)) {
formatUnusedSymbolHighlightInfo(project, "field.is.not.used", field);
}
}
}
@NotNull
private static @NlsContexts.DetailedDescription String getNotUsedForReadingMessage(@NotNull PsiField field) {
String visibility = VisibilityUtil.getVisibilityStringToDisplay(field);
String message = JavaErrorBundle.message("field.is.not.used.for.reading", visibility, field.getName());
return StringUtil.capitalize(message);
}
private void suggestionsToMakeFieldUsed(@NotNull PsiField field) {
SpecialAnnotationsUtilBase.processUnknownAnnotations(field, annoName ->
quickFixes.add(QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(field.getProject(), annoName)));
quickFixes.add(QuickFixFactory.getInstance().createRemoveUnusedVariableFix(field));
quickFixes.add(QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, false, field));
quickFixes.add(QuickFixFactory.getInstance().createCreateGetterOrSetterFix(false, true, field));
quickFixes.add(QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, true, field));
}
private final Map<PsiMethod, Boolean> isOverriddenOrOverrides = ConcurrentFactoryMap.createMap(method-> {
boolean overrides = SuperMethodsSearch.search(method, null, true, false).findFirst() != null;
return overrides || OverridingMethodsSearch.search(method).findFirst() != null;
}
);
private boolean isOverriddenOrOverrides(@NotNull PsiMethod method) {
return isOverriddenOrOverrides.get(method);
}
private void processParameter(@NotNull Project project, @NotNull PsiParameter parameter) {
if (parameter.isUnnamed() || PsiUtil.isIgnoredName(parameter.getName())) return;
PsiElement declarationScope = parameter.getDeclarationScope();
QuickFixFactory quickFixFactory = QuickFixFactory.getInstance();
if (declarationScope instanceof PsiMethod method) {
if (PsiUtilCore.hasErrorElementChild(method)) return;
if ((method.isConstructor() ||
method.hasModifierProperty(PsiModifier.PRIVATE) ||
method.hasModifierProperty(PsiModifier.STATIC) ||
!method.hasModifierProperty(PsiModifier.ABSTRACT) &&
(!isOverriddenOrOverrides(method) || myUnusedSymbolInspection.checkParameterExcludingHierarchy())) &&
!method.hasModifierProperty(PsiModifier.NATIVE) &&
!JavaHighlightUtil.isSerializationRelatedMethod(method, method.getContainingClass()) &&
!isUsedMainOrPremainMethod(method)) {
if (UnusedSymbolUtil.isInjected(project, method)) return;
checkUnusedParameter(parameter, method);
if (message != null) {
quickFixes.add(quickFixFactory.createRenameToIgnoredFix(parameter, true));
quickFixes.add(PriorityIntentionActionWrapper.highPriority(
quickFixFactory.createSafeDeleteUnusedParameterInHierarchyFix(parameter,
myUnusedSymbolInspection.checkParameterExcludingHierarchy() &&
isOverriddenOrOverrides(method))));
}
}
}
else if (declarationScope instanceof PsiForeachStatement) {
checkUnusedParameter(parameter, null);
if (message != null) {
quickFixes.add(quickFixFactory.createRenameToIgnoredFix(parameter, false));
}
}
else if (parameter instanceof PsiPatternVariable variable) {
checkUnusedParameter(parameter, null);
if (message != null) {
PsiPattern pattern = variable.getPattern();
IntentionAction action = null;
if (PsiUtil.isAvailable(JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES, parameter)) {
if (pattern instanceof PsiTypeTestPattern ttPattern && pattern.getParent() instanceof PsiDeconstructionList) {
PsiRecordComponent component = JavaPsiPatternUtil.getRecordComponentForPattern(pattern);
PsiTypeElement checkType = ttPattern.getCheckType();
if (component != null && checkType != null && checkType.getType().isAssignableFrom(component.getType())) {
action = new ReplaceWithUnnamedPatternFix(pattern).asIntention();
}
}
}
if (action == null && declarationScope.getParent() instanceof PsiSwitchBlock) {
action = variable.getParent() instanceof PsiDeconstructionPattern
? quickFixFactory.createDeleteFix(parameter)
: quickFixFactory.createRenameToIgnoredFix(parameter, false);
}
else if (!(pattern instanceof PsiTypeTestPattern && pattern.getParent() instanceof PsiDeconstructionList)) {
action = quickFixFactory.createDeleteFix(parameter);
}
if (action != null) {
quickFixOptions.add(action);
}
}
}
else if ((myUnusedSymbolInspection.checkParameterExcludingHierarchy() ||
PsiUtil.isAvailable(JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES, declarationScope))
&& declarationScope instanceof PsiLambdaExpression) {
checkUnusedParameter(parameter, null);
if (message != null) {
quickFixes.add(quickFixFactory.createRenameToIgnoredFix(parameter, true));
quickFixes.add(PriorityIntentionActionWrapper.lowPriority(quickFixFactory.createSafeDeleteUnusedParameterInHierarchyFix(parameter, true)));
}
}
}
private static boolean isUsedMainOrPremainMethod(@NotNull PsiMethod method) {
if (!PsiClassImplUtil.isMainOrPremainMethod(method)) {
return false;
}
//premain
if (!"main".equals(method.getName())) {
return true;
}
if (!PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, method)) {
return true;
}
return false;
}
private void checkUnusedParameter(@NotNull PsiParameter parameter, @Nullable PsiMethod declarationMethod) {
if (!myRefCountHolder.isReferenced(parameter) && !UnusedSymbolUtil.isImplicitUsage(myProject, parameter)) {
message = JavaErrorBundle.message(parameter instanceof PsiPatternVariable ?
"pattern.variable.is.not.used" : "parameter.is.not.used", parameter.getName());
if (declarationMethod != null) {
IntentionAction assignFix = QuickFixFactory.getInstance().createAssignFieldFromParameterFix();
IntentionAction createFieldFix = QuickFixFactory.getInstance().createCreateFieldFromParameterFix();
if (!declarationMethod.isConstructor()) {
assignFix = PriorityIntentionActionWrapper.lowPriority(assignFix);
createFieldFix = PriorityIntentionActionWrapper.lowPriority(createFieldFix);
}
quickFixOptions.add(assignFix);
quickFixOptions.add(createFieldFix);
}
}
}
private void processMethod(@NotNull Project project, @NotNull PsiMethod method) {
if (UnusedSymbolUtil.isMethodUsed(myProject, myFile, method, myGlobalUsageHelper)) {
return;
}
String key;
if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
key = method.isConstructor() ? "private.constructor.is.not.used" : "private.method.is.not.used";
}
else {
key = method.isConstructor() ? "constructor.is.not.used" : "method.is.not.used";
}
int options = PsiFormatUtilBase.SHOW_TYPE | PsiFormatUtilBase.SHOW_FQ_CLASS_NAMES;
String symbolName = HighlightMessageUtil.getSymbolName(method, PsiSubstitutor.EMPTY, options);
message = JavaErrorBundle.message(key, symbolName);
QuickFixFactory factory = QuickFixFactory.getInstance();
quickFixes.add(factory.createSafeDeleteFix(method));
if (ApplicationManager.getApplication().isHeadlessEnvironment() && method.hasModifierProperty(PsiModifier.PRIVATE)) {
quickFixes.add(factory.createDeletePrivateMethodFix(method).asIntention());
}
SpecialAnnotationsUtilBase.processUnknownAnnotations(method, annoName ->
quickFixes.add(factory.createAddToDependencyInjectionAnnotationsFix(project, annoName)));
}
private void processClass(@NotNull Project project, @NotNull PsiClass aClass) {
if (UnusedSymbolUtil.isClassUsed(project, myFile, aClass, myGlobalUsageHelper)) {
return;
}
String pattern;
if (aClass.getContainingClass() != null && aClass.hasModifierProperty(PsiModifier.PRIVATE)) {
pattern = aClass.isInterface()
? "private.inner.interface.is.not.used"
: "private.inner.class.is.not.used";
}
else if (aClass.getParent() instanceof PsiDeclarationStatement) { // local class
pattern = "local.class.is.not.used";
}
else if (aClass instanceof PsiTypeParameter) {
pattern = "type.parameter.is.not.used";
}
else if (aClass.isInterface()) {
pattern = "interface.is.not.used";
}
else if (aClass.isEnum()) {
pattern = "enum.is.not.used";
}
else {
pattern = "class.is.not.used";
}
formatUnusedSymbolHighlightInfo(myProject, pattern, aClass);
}
private void formatUnusedSymbolHighlightInfo(@NotNull Project project,
@NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String pattern,
@NotNull PsiMember member) {
String symbolName = member.getName();
message = JavaErrorBundle.message(pattern, symbolName);
quickFixes.add(QuickFixFactory.getInstance().createSafeDeleteFix(member));
SpecialAnnotationsUtilBase.processUnknownAnnotations(member, annoName ->
quickFixes.add(QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName)));
}
private void processImport(@NotNull HighlightInfoHolder holder,
@NotNull PsiJavaFile javaFile,
@@ -541,6 +127,22 @@ class PostHighlightingVisitor extends JavaElementVisitor {
if (PsiUtilCore.hasErrorElementChild(importStatement)) return;
boolean isRedundant = isRedundantImport(javaFile, importStatement);
if (isRedundant) {
registerRedundantImport(holder, importStatement, unusedImportKey);
return;
}
int entryIndex = JavaCodeStyleManager.getInstance(myProject).findEntryIndex(importStatement);
if (entryIndex < myCurrentEntryIndex && myOptimizeImportsFix == null) {
// mis-sorted imports found
myOptimizeImportsFix = QuickFixFactory.getInstance().createOptimizeImportsFix(true, myFile);
}
myCurrentEntryIndex = entryIndex;
}
private boolean isRedundantImport(@NotNull PsiJavaFile javaFile, @NotNull PsiImportStatementBase importStatement) {
boolean isRedundant = myRefCountHolder.isRedundant(importStatement);
if (!isRedundant && !(importStatement instanceof PsiImportStaticStatement)) {
// check import from the same package
@@ -558,18 +160,7 @@ class PostHighlightingVisitor extends JavaElementVisitor {
}
}
}
if (isRedundant) {
registerRedundantImport(holder, importStatement, unusedImportKey);
return;
}
int entryIndex = JavaCodeStyleManager.getInstance(myProject).findEntryIndex(importStatement);
if (entryIndex < myCurrentEntryIndex && myOptimizeImportsFix == null) {
// mis-sorted imports found
myOptimizeImportsFix = QuickFixFactory.getInstance().createOptimizeImportsFix(true, myFile);
}
myCurrentEntryIndex = entryIndex;
return isRedundant;
}
private void registerRedundantImport(@NotNull HighlightInfoHolder holder,

View File

@@ -1,33 +1,61 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.unusedSymbol;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.GlobalUsageHelper;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInsight.daemon.impl.UnusedSymbolUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
import com.intellij.codeInsight.daemon.impl.analysis.LocalRefUseInfo;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceWithUnnamedPatternFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
import com.intellij.codeInspection.ex.UnfairLocalInspectionTool;
import com.intellij.codeInspection.ex.EntryPointsManagerBase;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.options.OptDropdown;
import com.intellij.codeInspection.options.OptPane;
import com.intellij.codeInspection.reference.UnusedDeclarationFixProvider;
import com.intellij.codeInspection.util.InspectionMessage;
import com.intellij.codeInspection.util.SpecialAnnotationsUtilBase;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.util.AccessModifier;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.JavaFeature;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.*;
import com.intellij.util.ObjectUtils;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.lang.annotations.Language;
import org.intellij.lang.annotations.Pattern;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static com.intellij.codeInspection.options.OptPane.*;
/**
* Local counterpart of {@link com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase}
*/
public class UnusedSymbolLocalInspection extends AbstractBaseJavaLocalInspectionTool implements UnfairLocalInspectionTool {
public final class UnusedSymbolLocalInspection extends AbstractBaseJavaLocalInspectionTool {
@NonNls public static final String SHORT_NAME = HighlightInfoType.UNUSED_SYMBOL_SHORT_NAME;
@NonNls public static final String UNUSED_PARAMETERS_SHORT_NAME = "UnusedParameters";
@NonNls public static final String UNUSED_ID = "unused";
@@ -36,17 +64,352 @@ public class UnusedSymbolLocalInspection extends AbstractBaseJavaLocalInspection
public boolean FIELD = true;
public boolean METHOD = true;
public boolean CLASS = true;
protected boolean INNER_CLASS = true;
private boolean INNER_CLASS = true;
public boolean PARAMETER = true;
public boolean REPORT_PARAMETER_FOR_PUBLIC_METHODS = true;
protected String myClassVisibility = PsiModifier.PUBLIC;
protected String myInnerClassVisibility = PsiModifier.PUBLIC;
protected String myFieldVisibility = PsiModifier.PUBLIC;
protected String myMethodVisibility = PsiModifier.PUBLIC;
protected String myParameterVisibility = PsiModifier.PUBLIC;
protected boolean myIgnoreAccessors = false;
protected boolean myCheckParameterExcludingHierarchy = false;
private String myClassVisibility = PsiModifier.PUBLIC;
private String myInnerClassVisibility = PsiModifier.PUBLIC;
private String myFieldVisibility = PsiModifier.PUBLIC;
private String myMethodVisibility = PsiModifier.PUBLIC;
private String myParameterVisibility = PsiModifier.PUBLIC;
private boolean myIgnoreAccessors = false;
private boolean myCheckParameterExcludingHierarchy = false;
@Override
public boolean runForWholeFile() {
return true;
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
PsiFile file = holder.getFile();
LocalRefUseInfo info = LocalRefUseInfo.forFile(file);
Project project = holder.getProject();
InspectionProfileImpl profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile();
UnusedDeclarationInspectionBase deadCodeInspection = ObjectUtils.tryCast(
profile.getUnwrappedTool(UnusedDeclarationInspectionBase.SHORT_NAME, file), UnusedDeclarationInspectionBase.class);
GlobalUsageHelper helper = info.getGlobalUsageHelper(file, deadCodeInspection);
return new JavaElementVisitor() {
private final QuickFixFactory fixFactory = QuickFixFactory.getInstance();
private final Map<PsiMethod, Boolean> isOverriddenOrOverrides = ConcurrentFactoryMap.createMap(
method -> {
boolean overrides = SuperMethodsSearch.search(method, null, true, false).findFirst() != null;
return overrides || OverridingMethodsSearch.search(method).findFirst() != null;
}
);
private boolean isOverriddenOrOverrides(@NotNull PsiMethod method) {
return isOverriddenOrOverrides.get(method);
}
private static boolean compareVisibilities(PsiModifierListOwner listOwner, String visibility) {
if (visibility != null) {
while (listOwner != null) {
if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(listOwner.getModifierList()), visibility) >= 0) {
return true;
}
listOwner = PsiTreeUtil.getParentOfType(listOwner, PsiModifierListOwner.class, true);
}
}
return false;
}
private void registerProblem(@NotNull PsiElement element,
@NotNull @InspectionMessage String message,
@NotNull List<IntentionAction> fixes) {
if (element instanceof PsiNameIdentifierOwner owner) {
PsiElement identifier = owner.getNameIdentifier();
if (identifier != null) {
element = identifier;
}
}
for (UnusedDeclarationFixProvider provider : UnusedDeclarationFixProvider.EP_NAME.getExtensionList()) {
IntentionAction[] additionalFixes = provider.getQuickFixes(element);
fixes = ContainerUtil.append(fixes, additionalFixes);
}
holder.registerProblem(element, message, ContainerUtil.map2Array(fixes, LocalQuickFix.EMPTY_ARRAY,
UnusedSymbolLocalInspection::toLocalQuickFix));
}
@Override
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
if (!LOCAL_VARIABLE) return;
if (variable.isUnnamed() || PsiUtil.isIgnoredName(variable.getName())) return;
if (UnusedSymbolUtil.isImplicitUsage(project, variable)) return;
if (!info.isReferenced(variable)) {
IntentionAction fix = variable instanceof PsiResourceVariable
? fixFactory.createRenameToIgnoredFix(variable, false)
: QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable);
registerProblem(variable, JavaErrorBundle.message("local.variable.is.never.used", variable.getName()), List.of(fix));
}
else if (!info.isReferencedForRead(variable) && !UnusedSymbolUtil.isImplicitRead(project, variable)) {
registerProblem(variable, JavaErrorBundle.message("local.variable.is.not.used.for.reading", variable.getName()),
List.of(QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable)));
}
else if (!variable.hasInitializer() &&
!info.isReferencedForWrite(variable) &&
!UnusedSymbolUtil.isImplicitWrite(project, variable)) {
registerProblem(variable, JavaErrorBundle.message("local.variable.is.not.assigned", variable.getName()),
List.of(fixFactory.createAddVariableInitializerFix(variable)));
}
}
@Override
public void visitField(@NotNull PsiField field) {
if (!compareVisibilities(field, getFieldVisibility())) return;
if (HighlightUtil.isSerializationImplicitlyUsedField(field)) return;
if (field.hasModifierProperty(PsiModifier.PRIVATE)) {
if (!info.isReferenced(field) && !UnusedSymbolUtil.isImplicitUsage(project, field)) {
List<IntentionAction> fixes = new ArrayList<>(suggestionsToMakeFieldUsed(field));
if (!field.hasInitializer() && !field.hasModifierProperty(PsiModifier.FINAL)) {
fixes.add(fixFactory.createCreateConstructorParameterFromFieldFix(field));
}
registerProblem(field, JavaErrorBundle.message("private.field.is.not.used", field.getName()), fixes);
return;
}
boolean readReferenced = info.isReferencedForRead(field);
if (!readReferenced && !UnusedSymbolUtil.isImplicitRead(project, field)) {
registerProblem(field, getNotUsedForReadingMessage(field), suggestionsToMakeFieldUsed(field));
return;
}
if (field.hasInitializer()) {
return;
}
boolean writeReferenced = info.isReferencedForWrite(field);
if (!writeReferenced && !UnusedSymbolUtil.isImplicitWrite(project, field)) {
List<IntentionAction> fixes = new ArrayList<>();
fixes.add(fixFactory.createCreateGetterOrSetterFix(false, true, field));
if (!field.hasModifierProperty(PsiModifier.FINAL)) {
fixes.add(fixFactory.createCreateConstructorParameterFromFieldFix(field));
}
SpecialAnnotationsUtilBase.processUnknownAnnotations(field, annoName ->
fixes.add(fixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName)));
registerProblem(field, JavaErrorBundle.message("private.field.is.not.assigned", field.getName()), fixes);
}
}
else if (!UnusedSymbolUtil.isFieldUsed(project, file, field, helper)) {
if (UnusedSymbolUtil.isImplicitWrite(project, field)) {
registerProblem(field, getNotUsedForReadingMessage(field), List.of(fixFactory.createSafeDeleteFix(field)));
}
else if (!UnusedSymbolUtil.isImplicitUsage(project, field)) {
formatUnusedSymbolHighlightInfo("field.is.not.used", field);
}
}
}
private void formatUnusedSymbolHighlightInfo(@NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String pattern,
@NotNull PsiMember member) {
List<IntentionAction> fixes = new ArrayList<>();
fixes.add(QuickFixFactory.getInstance().createSafeDeleteFix(member));
SpecialAnnotationsUtilBase.processUnknownAnnotations(member, annoName ->
fixes.add(QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName)));
registerProblem(member, JavaErrorBundle.message(pattern, member.getName()), fixes);
}
private static boolean isUsedMainOrPremainMethod(@NotNull PsiMethod method) {
if (!PsiClassImplUtil.isMainOrPremainMethod(method)) {
return false;
}
//premain
if (!"main".equals(method.getName())) {
return true;
}
return !PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, method);
}
@Override
public void visitMethod(@NotNull PsiMethod method) {
if (isIgnoreAccessors() && PropertyUtilBase.isSimplePropertyAccessor(method)) return;
if (!compareVisibilities(method, getMethodVisibility())) return;
if (UnusedSymbolUtil.isMethodUsed(project, file, method, helper)) return;
String key;
if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
key = method.isConstructor() ? "private.constructor.is.not.used" : "private.method.is.not.used";
}
else {
key = method.isConstructor() ? "constructor.is.not.used" : "method.is.not.used";
}
int options = PsiFormatUtilBase.SHOW_TYPE | PsiFormatUtilBase.SHOW_FQ_CLASS_NAMES;
String symbolName = HighlightMessageUtil.getSymbolName(method, PsiSubstitutor.EMPTY, options);
QuickFixFactory factory = QuickFixFactory.getInstance();
List<IntentionAction> fixes = new ArrayList<>();
fixes.add(factory.createSafeDeleteFix(method));
if (ApplicationManager.getApplication().isHeadlessEnvironment() && method.hasModifierProperty(PsiModifier.PRIVATE)) {
fixes.add(factory.createDeletePrivateMethodFix(method).asIntention());
}
SpecialAnnotationsUtilBase.processUnknownAnnotations(method, annoName ->
fixes.add(factory.createAddToDependencyInjectionAnnotationsFix(project, annoName)));
registerProblem(method, JavaErrorBundle.message(key, symbolName), fixes);
}
@Override
public void visitParameter(@NotNull PsiParameter parameter) {
PsiElement declarationScope = parameter.getDeclarationScope();
boolean needToProcessParameter;
if (declarationScope instanceof PsiMethod method) {
needToProcessParameter = compareVisibilities(method, getParameterVisibility());
}
else if (declarationScope instanceof PsiLambdaExpression) {
needToProcessParameter = compareVisibilities(
PsiTreeUtil.getParentOfType(declarationScope, PsiModifierListOwner.class), getParameterVisibility());
}
else {
needToProcessParameter = LOCAL_VARIABLE;
}
if (!needToProcessParameter) return;
if (parameter.isUnnamed() || PsiUtil.isIgnoredName(parameter.getName())) return;
QuickFixFactory quickFixFactory = QuickFixFactory.getInstance();
if (declarationScope instanceof PsiMethod method) {
if (PsiUtilCore.hasErrorElementChild(method)) return;
if ((method.isConstructor() ||
method.hasModifierProperty(PsiModifier.PRIVATE) ||
method.hasModifierProperty(PsiModifier.STATIC) ||
!method.hasModifierProperty(PsiModifier.ABSTRACT) &&
(!isOverriddenOrOverrides(method) || checkParameterExcludingHierarchy())) &&
!method.hasModifierProperty(PsiModifier.NATIVE) &&
!JavaHighlightUtil.isSerializationRelatedMethod(method, method.getContainingClass()) &&
!isUsedMainOrPremainMethod(method)) {
if (UnusedSymbolUtil.isInjected(project, method)) return;
String message = checkUnusedParameter(parameter);
if (message != null) {
registerProblem(
parameter, message,
ContainerUtil.append(
getFixesForUnusedParameter(method),
quickFixFactory.createRenameToIgnoredFix(parameter, true),
PriorityIntentionActionWrapper.highPriority(quickFixFactory.createSafeDeleteUnusedParameterInHierarchyFix(
parameter, checkParameterExcludingHierarchy() && isOverriddenOrOverrides(method)))));
}
}
}
else if (declarationScope instanceof PsiForeachStatement) {
String message = checkUnusedParameter(parameter);
if (message != null) {
registerProblem(parameter, message, List.of(quickFixFactory.createRenameToIgnoredFix(parameter, false)));
}
}
else if (parameter instanceof PsiPatternVariable variable) {
String message = checkUnusedParameter(parameter);
if (message != null) {
PsiPattern pattern = variable.getPattern();
IntentionAction action = null;
if (PsiUtil.isAvailable(JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES, parameter)) {
if (pattern instanceof PsiTypeTestPattern ttPattern && pattern.getParent() instanceof PsiDeconstructionList) {
PsiRecordComponent component = JavaPsiPatternUtil.getRecordComponentForPattern(pattern);
PsiTypeElement checkType = ttPattern.getCheckType();
if (component != null && checkType != null && checkType.getType().isAssignableFrom(component.getType())) {
action = new ReplaceWithUnnamedPatternFix(pattern).asIntention();
}
}
}
if (action == null && declarationScope.getParent() instanceof PsiSwitchBlock) {
action = variable.getParent() instanceof PsiDeconstructionPattern
? quickFixFactory.createDeleteFix(parameter)
: quickFixFactory.createRenameToIgnoredFix(parameter, false);
}
else if (!(pattern instanceof PsiTypeTestPattern && pattern.getParent() instanceof PsiDeconstructionList)) {
action = quickFixFactory.createDeleteFix(parameter);
}
registerProblem(parameter, message, ContainerUtil.createMaybeSingletonList(action));
}
}
else if ((checkParameterExcludingHierarchy() ||
PsiUtil.isAvailable(JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES, declarationScope))
&& declarationScope instanceof PsiLambdaExpression) {
String message = checkUnusedParameter(parameter);
if (message != null) {
registerProblem(
parameter, message,
List.of(quickFixFactory.createRenameToIgnoredFix(parameter, true),
PriorityIntentionActionWrapper.lowPriority(
quickFixFactory.createSafeDeleteUnusedParameterInHierarchyFix(parameter, true))));
}
}
}
private @Nullable @InspectionMessage String checkUnusedParameter(@NotNull PsiParameter parameter) {
if (!info.isReferenced(parameter) && !UnusedSymbolUtil.isImplicitUsage(project, parameter)) {
return JavaErrorBundle.message(parameter instanceof PsiPatternVariable ?
"pattern.variable.is.not.used" : "parameter.is.not.used", parameter.getName());
}
return null;
}
private static @NotNull List<IntentionAction> getFixesForUnusedParameter(@Nullable PsiMethod declarationMethod) {
if (declarationMethod != null) {
IntentionAction assignFix = QuickFixFactory.getInstance().createAssignFieldFromParameterFix();
IntentionAction createFieldFix = QuickFixFactory.getInstance().createCreateFieldFromParameterFix();
if (!declarationMethod.isConstructor()) {
assignFix = PriorityIntentionActionWrapper.lowPriority(assignFix);
createFieldFix = PriorityIntentionActionWrapper.lowPriority(createFieldFix);
}
return List.of(assignFix, createFieldFix);
}
return List.of();
}
@Override
public void visitClass(@NotNull PsiClass aClass) {
String acceptedVisibility = aClass.getContainingClass() == null ? getClassVisibility()
: getInnerClassVisibility();
if (!compareVisibilities(aClass, acceptedVisibility)) return;
if (UnusedSymbolUtil.isClassUsed(project, file, aClass, helper)) return;
String pattern;
if (aClass.getContainingClass() != null && aClass.hasModifierProperty(PsiModifier.PRIVATE)) {
pattern = aClass.isInterface()
? "private.inner.interface.is.not.used"
: "private.inner.class.is.not.used";
}
else if (aClass.getParent() instanceof PsiDeclarationStatement) { // local class
pattern = "local.class.is.not.used";
}
else if (aClass instanceof PsiTypeParameter) {
pattern = "type.parameter.is.not.used";
}
else if (aClass.isInterface()) {
pattern = "interface.is.not.used";
}
else if (aClass.isEnum()) {
pattern = "enum.is.not.used";
}
else {
pattern = "class.is.not.used";
}
formatUnusedSymbolHighlightInfo(pattern, aClass);
}
@NotNull
private static @NlsContexts.DetailedDescription String getNotUsedForReadingMessage(@NotNull PsiField field) {
String visibility = VisibilityUtil.getVisibilityStringToDisplay(field);
String message = JavaErrorBundle.message("field.is.not.used.for.reading", visibility, field.getName());
return StringUtil.capitalize(message);
}
private List<IntentionAction> suggestionsToMakeFieldUsed(@NotNull PsiField field) {
List<IntentionAction> quickFixes = new ArrayList<>();
SpecialAnnotationsUtilBase.processUnknownAnnotations(field, annoName ->
quickFixes.add(EntryPointsManagerBase.createAddEntryPointAnnotation(annoName).asIntention()));
quickFixes.add(QuickFixFactory.getInstance().createRemoveUnusedVariableFix(field));
quickFixes.add(fixFactory.createCreateGetterOrSetterFix(true, false, field));
quickFixes.add(fixFactory.createCreateGetterOrSetterFix(false, true, field));
quickFixes.add(fixFactory.createCreateGetterOrSetterFix(true, true, field));
return quickFixes;
}
};
}
private static @NotNull LocalQuickFix toLocalQuickFix(IntentionAction fix) {
ModCommandAction action = fix.asModCommandAction();
return action == null ? new LocalQuickFixBackedByIntentionAction(fix) :
LocalQuickFix.from(action);
}
@Override
public @NotNull OptPane getOptionsPane() {
@@ -110,27 +473,18 @@ public class UnusedSymbolLocalInspection extends AbstractBaseJavaLocalInspection
return myInnerClassVisibility;
}
public void setInnerClassVisibility(String innerClassVisibility) {
myInnerClassVisibility = innerClassVisibility;
}
@TestOnly
public void setClassVisibility(String classVisibility) {
this.myClassVisibility = classVisibility;
}
public void setFieldVisibility(String fieldVisibility) {
this.myFieldVisibility = fieldVisibility;
}
public void setMethodVisibility(String methodVisibility) {
this.myMethodVisibility = methodVisibility;
}
@TestOnly
public void setParameterVisibility(String parameterVisibility) {
REPORT_PARAMETER_FOR_PUBLIC_METHODS = PsiModifier.PUBLIC.equals(parameterVisibility);
this.myParameterVisibility = parameterVisibility;
}
@TestOnly
public void setCheckParameterExcludingHierarchy(boolean checkParameterExcludingHierarchy) {
this.myCheckParameterExcludingHierarchy = checkParameterExcludingHierarchy;
}
@@ -139,10 +493,6 @@ public class UnusedSymbolLocalInspection extends AbstractBaseJavaLocalInspection
return myIgnoreAccessors;
}
public void setIgnoreAccessors(boolean ignoreAccessors) {
myIgnoreAccessors = ignoreAccessors;
}
@Override
@NotNull
public String getGroupDisplayName() {

View File

@@ -1,4 +1,4 @@
// "Apply all 'Replace with unnamed pattern' fixes in file" "true"
// "Fix all 'Unused declaration' problems in file" "true"
public class Test {
record R(int x, int y) {}
void test(Object obj) {

View File

@@ -1,4 +1,4 @@
// "Apply all 'Replace with unnamed pattern' fixes in file" "true"
// "Fix all 'Unused declaration' problems in file" "true"
public class Test {
record R(int x, int y) {}
void test(Object obj) {

View File

@@ -134,17 +134,17 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
-f()
-D
-b()
Variable 'r' is never used.
Variable 'r' is never used
-anonymous (Runnable)
-run()
Variable 'i' is never used.
Variable 'i' is never used
-ff()
Variable 'a' is never used.
Variable 'd' is never used.
Variable 'd' is never used
Variable 'a' is never used
-foo()
Variable 'j' is never used.
Variable 'j' is never used
-main(String[])
Variable 'test' is never used.
Variable 'test' is never used
-Probable bugs
-'equals()' called on itself
-testOfflineWithInvalid
@@ -179,17 +179,17 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
-f()
-D
-b()
Variable 'r' is never used.
Variable 'r' is never used
-anonymous (Runnable)
-run()
Variable 'i' is never used.
Variable 'i' is never used
-ff()
Variable 'a' is never used.
Variable 'd' is never used.
Variable 'd' is never used
Variable 'a' is never used
-foo()
Variable 'j' is never used.
Variable 'j' is never used
-main(String[])
Variable 'test' is never used.
Variable 'test' is never used
-Probable bugs
-'equals()' called on itself
-testOfflineView
@@ -217,17 +217,17 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
-f()
-D
-b()
Variable 'r' is never used.
Variable 'r' is never used
-anonymous (Runnable)
-run()
Variable 'i' is never used.
Variable 'i' is never used
-ff()
Variable 'a' is never used.
Variable 'd' is never used.
Variable 'd' is never used
Variable 'a' is never used
-foo()
Variable 'j' is never used.
Variable 'j' is never used
-main(String[])
Variable 'test' is never used.
Variable 'test' is never used
-Probable bugs
-'equals()' called on itself
-Test
@@ -251,17 +251,17 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
-f()
-D
-b()
Variable 'r' is never used.
Variable 'r' is never used
-anonymous (Runnable)
-run()
Variable 'i' is never used.
Variable 'i' is never used
-ff()
Variable 'a' is never used.
Variable 'd' is never used.
Variable 'd' is never used
Variable 'a' is never used
-foo()
Variable 'j' is never used.
Variable 'j' is never used
-main(String[])
Variable 'test' is never used.
Variable 'test' is never used
-Probable bugs
-'equals()' called on itself
-Test

View File

@@ -798,12 +798,6 @@ public class HighlightInfo implements Segment {
List<IntentionAction> newOptions = intentionManager.getStandardIntentionOptions(key, element);
InspectionProfile profile = InspectionProjectProfileManager.getInstance(element.getProject()).getCurrentProfile();
InspectionToolWrapper<?, ?> toolWrapper = profile.getInspectionTool(key.toString(), element);
if (!(toolWrapper instanceof LocalInspectionToolWrapper)) {
HighlightDisplayKey idKey = HighlightDisplayKey.findById(key.toString());
if (idKey != null) {
toolWrapper = profile.getInspectionTool(idKey.toString(), element);
}
}
if (toolWrapper != null) {
myCanCleanup = toolWrapper.isCleanupTool();