diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/UnusedSymbolUtil.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/UnusedSymbolUtil.java index 1c7e4cf98..a56d8f191 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/UnusedSymbolUtil.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/UnusedSymbolUtil.java @@ -23,6 +23,7 @@ import com.intellij.java.language.impl.psi.impl.source.PsiClassImpl; import com.intellij.java.language.psi.*; import com.intellij.java.language.psi.util.PropertyUtil; +import consulo.annotation.DeprecationInfo; import consulo.annotation.access.RequiredReadAction; import consulo.application.progress.ProgressIndicator; import consulo.application.progress.ProgressManager; @@ -40,6 +41,7 @@ import consulo.language.psi.resolve.RefResolveService; import consulo.language.psi.scope.GlobalSearchScope; import consulo.language.psi.search.PsiSearchHelper; +import consulo.localize.LocalizeValue; import consulo.project.Project; import consulo.usage.UsageInfo; import jakarta.annotation.Nonnull; @@ -87,28 +89,37 @@ public static boolean isImplicitWrite(@Nonnull Project project, @Nonnull PsiVari }) || isInjected(project, element); } - @Nullable + @Nonnull @RequiredReadAction - public static HighlightInfo createUnusedSymbolInfo( + public static HighlightInfo.Builder createUnusedSymbolInfo( @Nonnull PsiElement element, - @Nonnull String message, + @Nonnull LocalizeValue message, @Nonnull HighlightInfoType highlightInfoType ) { - HighlightInfo info = HighlightInfo.newHighlightInfo(highlightInfoType) + HighlightInfo.Builder hlBuilder = HighlightInfo.newHighlightInfo(highlightInfoType) .range(element) - .descriptionAndTooltip(message).create(); - - if (info == null) { - return null; //filtered out - } + .descriptionAndTooltip(message); - for (UnusedDeclarationFixProvider provider : UnusedDeclarationFixProvider.EP_NAME.getExtensionList()) { + element.getApplication().getExtensionPoint(UnusedDeclarationFixProvider.class).forEach(provider -> { IntentionAction[] fixes = provider.getQuickFixes(element); for (IntentionAction fix : fixes) { - info.registerFix(fix, null, null, null, null); + hlBuilder.registerFix(fix); } - } - return info; + }); + + return hlBuilder; + } + + @Deprecated + @DeprecationInfo("Use variant with LocalizeValue") + @Nullable + @RequiredReadAction + public static HighlightInfo createUnusedSymbolInfo( + @Nonnull PsiElement element, + @Nonnull String message, + @Nonnull HighlightInfoType highlightInfoType + ) { + return createUnusedSymbolInfo(element, LocalizeValue.of(message), highlightInfoType).create(); } @RequiredReadAction diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java index 571470444..f9e9ee57f 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java @@ -26,7 +26,6 @@ import com.intellij.java.analysis.impl.codeInspection.unusedSymbol.UnusedSymbolLocalInspectionBase; import com.intellij.java.analysis.impl.codeInspection.util.SpecialAnnotationsUtilBase; import com.intellij.java.indexing.search.searches.OverridingMethodsSearch; -import com.intellij.java.language.impl.codeInsight.daemon.JavaErrorBundle; import com.intellij.java.language.impl.psi.impl.PsiClassImplUtil; import com.intellij.java.language.psi.*; import com.intellij.java.language.psi.codeStyle.JavaCodeStyleManager; @@ -41,6 +40,7 @@ import consulo.disposer.Disposable; import consulo.disposer.Disposer; import consulo.document.Document; +import consulo.java.language.impl.localize.JavaErrorLocalize; import consulo.language.Language; import consulo.language.editor.DaemonCodeAnalyzer; import consulo.language.editor.FileStatusMap; @@ -53,7 +53,6 @@ import consulo.language.editor.inspection.scheme.InspectionProjectProfileManager; import consulo.language.editor.intention.EmptyIntentionAction; import consulo.language.editor.intention.IntentionAction; -import consulo.language.editor.intention.QuickFixAction; import consulo.language.editor.rawHighlight.HighlightDisplayKey; import consulo.language.editor.rawHighlight.HighlightInfo; import consulo.language.editor.rawHighlight.HighlightInfoHolder; @@ -68,588 +67,585 @@ import consulo.project.Project; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import org.jetbrains.annotations.PropertyKey; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; public class PostHighlightingVisitor { - private static final Logger LOG = Logger.getInstance(PostHighlightingVisitor.class); - private final RefCountHolder myRefCountHolder; - @Nonnull - private final Project myProject; - private final PsiFile myFile; - @Nonnull - private final Document myDocument; - - private boolean myHasRedundantImports; - private int myCurrentEntryIndex; - private boolean myHasMissortedImports; - private final UnusedSymbolLocalInspectionBase myUnusedSymbolInspection; - private final HighlightDisplayKey myDeadCodeKey; - private final HighlightInfoType myDeadCodeInfoType; - private final UnusedDeclarationInspectionBase myDeadCodeInspection; - private final UnusedDeclarationInspectionState myDeadCodeState; - - private void optimizeImportsOnTheFlyLater(@Nonnull final ProgressIndicator progress) { - if ((myHasRedundantImports || myHasMissortedImports) && !progress.isCanceled()) { - // schedule optimise action at the time of session disposal, which is after all applyInformation() calls - Disposable invokeFixLater = () -> { - // later because should invoke when highlighting is finished - myProject.getUIAccess().give(() -> { - if (!myFile.isValid() || !myFile.isWritable()) { - return; - } - IntentionAction optimizeImportsFix = QuickFixFactory.getInstance().createOptimizeImportsFix(true); - if (optimizeImportsFix.isAvailable(myProject, null, myFile)) { - optimizeImportsFix.invoke(myProject, null, myFile); - } - }); - }; - try { - Disposer.register((Disposable)progress, invokeFixLater); - } - catch (Exception ignored) { - // suppress "parent already has been disposed" exception here - } - if (progress.isCanceled()) { - Disposer.dispose(invokeFixLater); - Disposer.dispose((Disposable)progress); - progress.checkCanceled(); - } - } - } - - @RequiredReadAction - public PostHighlightingVisitor( - @Nonnull PsiFile file, - @Nonnull Document document, - @Nonnull RefCountHolder refCountHolder - ) throws ProcessCanceledException { - myProject = file.getProject(); - myFile = file; - myDocument = document; - - myCurrentEntryIndex = -1; - - myRefCountHolder = refCountHolder; - - myProject.getApplication().assertReadAccessAllowed(); - - InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); - - myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME); - - myDeadCodeInspection = (UnusedDeclarationInspectionBase)profile.getUnwrappedTool(UnusedDeclarationInspectionBase.SHORT_NAME, myFile); - myDeadCodeState = profile.getToolState(UnusedDeclarationInspectionBase.SHORT_NAME, myFile); - - LOG.assertTrue(myDeadCodeInspection != null); - - myUnusedSymbolInspection = myDeadCodeInspection != null ? myDeadCodeInspection.getSharedLocalInspectionTool() : null; - - myDeadCodeInfoType = myDeadCodeKey == null ? HighlightInfoType.UNUSED_SYMBOL - : new HighlightInfoType.HighlightInfoTypeImpl( - profile.getErrorLevel(myDeadCodeKey, myFile).getSeverity(), - HighlightInfoType.UNUSED_SYMBOL.getAttributesKey() - ); - } - - @RequiredReadAction - public void collectHighlights(@Nonnull HighlightInfoHolder result, @Nonnull ProgressIndicator progress) { - DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject); - FileStatusMap fileStatusMap = daemonCodeAnalyzer.getFileStatusMap(); - InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); - - boolean unusedSymbolEnabled = profile.isToolEnabled(myDeadCodeKey, myFile); - GlobalUsageHelper globalUsageHelper = myRefCountHolder.getGlobalUsageHelper(myFile, myDeadCodeInspection, myDeadCodeState); - - boolean errorFound = false; - - if (unusedSymbolEnabled) { - final FileViewProvider viewProvider = myFile.getViewProvider(); - final Set relevantLanguages = viewProvider.getLanguages(); - for (Language language : relevantLanguages) { - progress.checkCanceled(); - PsiElement psiRoot = viewProvider.getPsi(language); - if (!HighlightingLevelManager.getInstance(myProject).shouldInspect(psiRoot)) { - continue; - } - List elements = CollectHighlightsUtil.getElementsInRange(psiRoot, 0, myFile.getTextLength()); - for (PsiElement element : elements) { - progress.checkCanceled(); - if (element instanceof PsiIdentifier identifier) { - HighlightInfo info = processIdentifier(identifier, progress, globalUsageHelper); - if (info != null) { - errorFound |= info.getSeverity() == HighlightSeverity.ERROR; - result.add(info); + private static final Logger LOG = Logger.getInstance(PostHighlightingVisitor.class); + private final RefCountHolder myRefCountHolder; + @Nonnull + private final Project myProject; + private final PsiFile myFile; + @Nonnull + private final Document myDocument; + + private boolean myHasRedundantImports; + private int myCurrentEntryIndex; + private boolean myHasMissortedImports; + private final UnusedSymbolLocalInspectionBase myUnusedSymbolInspection; + private final HighlightDisplayKey myDeadCodeKey; + private final HighlightInfoType myDeadCodeInfoType; + private final UnusedDeclarationInspectionBase myDeadCodeInspection; + private final UnusedDeclarationInspectionState myDeadCodeState; + + private void optimizeImportsOnTheFlyLater(@Nonnull ProgressIndicator progress) { + if ((myHasRedundantImports || myHasMissortedImports) && !progress.isCanceled()) { + // schedule optimise action at the time of session disposal, which is after all applyInformation() calls + Disposable invokeFixLater = () -> { + // later because should invoke when highlighting is finished + myProject.getUIAccess().give(() -> { + if (!myFile.isValid() || !myFile.isWritable()) { + return; + } + IntentionAction optimizeImportsFix = QuickFixFactory.getInstance().createOptimizeImportsFix(true); + if (optimizeImportsFix.isAvailable(myProject, null, myFile)) { + optimizeImportsFix.invoke(myProject, null, myFile); + } + }); + }; + try { + Disposer.register((Disposable)progress, invokeFixLater); + } + catch (Exception ignored) { + // suppress "parent already has been disposed" exception here + } + if (progress.isCanceled()) { + Disposer.dispose(invokeFixLater); + Disposer.dispose((Disposable)progress); + progress.checkCanceled(); } - } } - } } - HighlightDisplayKey unusedImportKey = HighlightDisplayKey.find(UnusedImportLocalInspection.SHORT_NAME); - if (isUnusedImportEnabled(unusedImportKey)) { - PsiImportList importList = ((PsiJavaFile)myFile).getImportList(); - if (importList != null) { - final PsiImportStatementBase[] imports = importList.getAllImportStatements(); - for (PsiImportStatementBase statement : imports) { - progress.checkCanceled(); - final HighlightInfo info = processImport(statement, unusedImportKey); - if (info != null) { - errorFound |= info.getSeverity() == HighlightSeverity.ERROR; - result.add(info); - } - } - } - } + @RequiredReadAction + public PostHighlightingVisitor( + @Nonnull PsiFile file, + @Nonnull Document document, + @Nonnull RefCountHolder refCountHolder + ) throws ProcessCanceledException { + myProject = file.getProject(); + myFile = file; + myDocument = document; + + myCurrentEntryIndex = -1; + + myRefCountHolder = refCountHolder; + + myProject.getApplication().assertReadAccessAllowed(); + + InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + + myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME); + + myDeadCodeInspection = + (UnusedDeclarationInspectionBase)profile.getUnwrappedTool(UnusedDeclarationInspectionBase.SHORT_NAME, myFile); + myDeadCodeState = profile.getToolState(UnusedDeclarationInspectionBase.SHORT_NAME, myFile); + + LOG.assertTrue(myDeadCodeInspection != null); + + myUnusedSymbolInspection = myDeadCodeInspection != null ? myDeadCodeInspection.getSharedLocalInspectionTool() : null; + + myDeadCodeInfoType = myDeadCodeKey == null + ? HighlightInfoType.UNUSED_SYMBOL + : new HighlightInfoType.HighlightInfoTypeImpl( + profile.getErrorLevel(myDeadCodeKey, myFile).getSeverity(), + HighlightInfoType.UNUSED_SYMBOL.getAttributesKey() + ); + } + + @RequiredReadAction + public void collectHighlights(@Nonnull HighlightInfoHolder result, @Nonnull ProgressIndicator progress) { + DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject); + FileStatusMap fileStatusMap = daemonCodeAnalyzer.getFileStatusMap(); + InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + + boolean unusedSymbolEnabled = profile.isToolEnabled(myDeadCodeKey, myFile); + GlobalUsageHelper globalUsageHelper = myRefCountHolder.getGlobalUsageHelper(myFile, myDeadCodeInspection, myDeadCodeState); + + boolean errorFound = false; + + if (unusedSymbolEnabled) { + FileViewProvider viewProvider = myFile.getViewProvider(); + Set relevantLanguages = viewProvider.getLanguages(); + for (Language language : relevantLanguages) { + progress.checkCanceled(); + PsiElement psiRoot = viewProvider.getPsi(language); + if (!HighlightingLevelManager.getInstance(myProject).shouldInspect(psiRoot)) { + continue; + } + List elements = CollectHighlightsUtil.getElementsInRange(psiRoot, 0, myFile.getTextLength()); + for (PsiElement element : elements) { + progress.checkCanceled(); + if (element instanceof PsiIdentifier identifier) { + HighlightInfo info = processIdentifier(identifier, progress, globalUsageHelper); + if (info != null) { + errorFound |= info.getSeverity() == HighlightSeverity.ERROR; + result.add(info); + } + } + } + } + } - if (errorFound) { - fileStatusMap.setErrorFoundFlag(myProject, myDocument, true); - } + HighlightDisplayKey unusedImportKey = HighlightDisplayKey.find(UnusedImportLocalInspection.SHORT_NAME); + if (isUnusedImportEnabled(unusedImportKey)) { + PsiImportList importList = ((PsiJavaFile)myFile).getImportList(); + if (importList != null) { + PsiImportStatementBase[] imports = importList.getAllImportStatements(); + for (PsiImportStatementBase statement : imports) { + progress.checkCanceled(); + HighlightInfo info = processImport(statement, unusedImportKey); + if (info != null) { + errorFound |= info.getSeverity() == HighlightSeverity.ERROR; + result.add(info); + } + } + } + } - optimizeImportsOnTheFlyLater(progress); - } + if (errorFound) { + fileStatusMap.setErrorFoundFlag(myProject, myDocument, true); + } - private boolean isUnusedImportEnabled(HighlightDisplayKey unusedImportKey) { - InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); - if (profile.isToolEnabled(unusedImportKey, myFile) && myFile instanceof PsiJavaFile - && HighlightingLevelManager.getInstance(myProject).shouldInspect(myFile)) { - return true; + optimizeImportsOnTheFlyLater(progress); } - return myProject.getExtensionPoint(ImplicitUsageProvider.class).findFirstSafe( - implicitUsageProvider -> implicitUsageProvider instanceof JavaImplicitUsageProvider javaImplicitUsageProvider - && javaImplicitUsageProvider.isUnusedImportEnabled(myFile) - ) != null; - } - - @Nullable - @RequiredReadAction - private HighlightInfo processIdentifier( - @Nonnull PsiIdentifier identifier, - @Nonnull ProgressIndicator progress, - @Nonnull GlobalUsageHelper helper - ) { - PsiElement parent = identifier.getParent(); - if (!(parent instanceof PsiVariable || parent instanceof PsiMember)) { - return null; - } + private boolean isUnusedImportEnabled(HighlightDisplayKey unusedImportKey) { + InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + //noinspection SimplifiableIfStatement + if (profile.isToolEnabled(unusedImportKey, myFile) && myFile instanceof PsiJavaFile + && HighlightingLevelManager.getInstance(myProject).shouldInspect(myFile)) { + return true; + } - if (SuppressionUtil.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) { - return null; + return myProject.getExtensionPoint(ImplicitUsageProvider.class).anyMatchSafe( + implicitUsageProvider -> implicitUsageProvider instanceof JavaImplicitUsageProvider javaImplicitUsageProvider + && javaImplicitUsageProvider.isUnusedImportEnabled(myFile) + ); } - if (parent instanceof PsiLocalVariable localVariable && myUnusedSymbolInspection.LOCAL_VARIABLE) { - return processLocalVariable(localVariable, identifier, progress); - } - if (parent instanceof PsiField field && compareVisibilities(field, myUnusedSymbolInspection.getFieldVisibility())) { - return processField(myProject, field, identifier, progress, helper); - } - if (parent instanceof PsiParameter parameter) { - final PsiElement declarationScope = parameter.getDeclarationScope(); - if (declarationScope instanceof PsiMethod method - ? compareVisibilities(method, myUnusedSymbolInspection.getParameterVisibility()) : myUnusedSymbolInspection.LOCAL_VARIABLE) { - if (SuppressionUtil.isSuppressed(identifier, UnusedSymbolLocalInspectionBase.UNUSED_PARAMETERS_SHORT_NAME)) { - return null; - } - return processParameter(myProject, parameter, identifier, progress); - } - } - if (parent instanceof PsiMethod method) { - if (myUnusedSymbolInspection.isIgnoreAccessors() && PropertyUtil.isSimplePropertyAccessor(method)) { + @Nullable + @RequiredReadAction + private HighlightInfo processIdentifier( + @Nonnull PsiIdentifier identifier, + @Nonnull ProgressIndicator progress, + @Nonnull GlobalUsageHelper helper + ) { + PsiElement parent = identifier.getParent(); + if (!(parent instanceof PsiVariable || parent instanceof PsiMember)) { + return null; + } + + if (SuppressionUtil.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) { + return null; + } + + if (parent instanceof PsiLocalVariable localVariable && myUnusedSymbolInspection.LOCAL_VARIABLE) { + return processLocalVariable(localVariable, identifier, progress); + } + if (parent instanceof PsiField field && compareVisibilities(field, myUnusedSymbolInspection.getFieldVisibility())) { + return processField(myProject, field, identifier, progress, helper); + } + if (parent instanceof PsiParameter parameter) { + PsiElement declarationScope = parameter.getDeclarationScope(); + if (declarationScope instanceof PsiMethod method + ? compareVisibilities(method, myUnusedSymbolInspection.getParameterVisibility()) + : myUnusedSymbolInspection.LOCAL_VARIABLE) { + if (SuppressionUtil.isSuppressed(identifier, UnusedSymbolLocalInspectionBase.UNUSED_PARAMETERS_SHORT_NAME)) { + return null; + } + return processParameter(myProject, parameter, identifier, progress); + } + } + if (parent instanceof PsiMethod method) { + if (myUnusedSymbolInspection.isIgnoreAccessors() && PropertyUtil.isSimplePropertyAccessor(method)) { + return null; + } + if (compareVisibilities(method, myUnusedSymbolInspection.getMethodVisibility())) { + return processMethod(myProject, method, identifier, progress, helper); + } + } + if (parent instanceof PsiClass psiClass) { + String acceptedVisibility = psiClass.getContainingClass() == null + ? myUnusedSymbolInspection.getClassVisibility() + : myUnusedSymbolInspection.getInnerClassVisibility(); + if (compareVisibilities(psiClass, acceptedVisibility)) { + return processClass(myProject, psiClass, identifier, progress, helper); + } + } return null; - } - if (compareVisibilities(method, myUnusedSymbolInspection.getMethodVisibility())) { - return processMethod(myProject, method, identifier, progress, helper); - } - } - if (parent instanceof PsiClass psiClass) { - final String acceptedVisibility = psiClass.getContainingClass() == null - ? myUnusedSymbolInspection.getClassVisibility() : myUnusedSymbolInspection.getInnerClassVisibility(); - if (compareVisibilities(psiClass, acceptedVisibility)) { - return processClass(myProject, psiClass, identifier, progress, helper); - } } - return null; - } - private static boolean compareVisibilities(PsiModifierListOwner listOwner, final String visibility) { - if (visibility != null) { - while (listOwner != null) { - if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(listOwner.getModifierList()), visibility) >= 0) { - return true; + 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; + } + + @Nullable + @RequiredReadAction + private HighlightInfo processLocalVariable( + @Nonnull PsiLocalVariable variable, + @Nonnull PsiIdentifier identifier, + @Nonnull ProgressIndicator progress + ) { + if (variable instanceof PsiResourceVariable && PsiUtil.isIgnoredName(variable.getName())) { + return null; + } + if (UnusedSymbolUtil.isImplicitUsage(myProject, variable, progress)) { + return null; } - listOwner = PsiTreeUtil.getParentOfType(listOwner, PsiModifierListOwner.class, true); - } - } - return false; - } - - @Nullable - @RequiredReadAction - private HighlightInfo processLocalVariable( - @Nonnull PsiLocalVariable variable, - @Nonnull PsiIdentifier identifier, - @Nonnull ProgressIndicator progress - ) { - if (variable instanceof PsiResourceVariable && PsiUtil.isIgnoredName(variable.getName())) { - return null; - } - if (UnusedSymbolUtil.isImplicitUsage(myProject, variable, progress)) { - return null; - } - if (!myRefCountHolder.isReferenced(variable)) { - String message = JavaErrorBundle.message("local.variable.is.never.used", identifier.getText()); - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - IntentionAction fix = variable instanceof PsiResourceVariable - ? QuickFixFactory.getInstance().createRenameToIgnoredFix(variable) - : QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable); - QuickFixAction.registerQuickFixAction(highlightInfo, fix, myDeadCodeKey); - return highlightInfo; - } + if (!myRefCountHolder.isReferenced(variable)) { + LocalizeValue message = JavaErrorLocalize.localVariableIsNeverUsed(identifier.getText()); + IntentionAction fix = variable instanceof PsiResourceVariable + ? QuickFixFactory.getInstance().createRenameToIgnoredFix(variable) + : QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable); + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType) + .registerFix(fix, null, null, null, myDeadCodeKey) + .create(); + } - boolean referenced = myRefCountHolder.isReferencedForRead(variable); - if (!referenced && !UnusedSymbolUtil.isImplicitRead(myProject, variable, progress)) { - String message = JavaErrorBundle.message("local.variable.is.not.used.for.reading", identifier.getText()); - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable), - myDeadCodeKey - ); - return highlightInfo; - } + boolean referenced = myRefCountHolder.isReferencedForRead(variable); + if (!referenced && !UnusedSymbolUtil.isImplicitRead(myProject, variable, progress)) { + LocalizeValue message = JavaErrorLocalize.localVariableIsNotUsedForReading(identifier.getText()); + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType) + .registerFix(QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable), null, null, null, myDeadCodeKey) + .create(); + } - if (!variable.hasInitializer()) { - referenced = myRefCountHolder.isReferencedForWrite(variable); - if (!referenced && !UnusedSymbolUtil.isImplicitWrite(myProject, variable, progress)) { - String message = JavaErrorBundle.message("local.variable.is.not.assigned", identifier.getText()); - final HighlightInfo unusedSymbolInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction( - unusedSymbolInfo, - new EmptyIntentionAction(UnusedSymbolLocalInspectionBase.DISPLAY_NAME), - myDeadCodeKey - ); - return unusedSymbolInfo; - } - } + if (!variable.hasInitializer()) { + referenced = myRefCountHolder.isReferencedForWrite(variable); + if (!referenced && !UnusedSymbolUtil.isImplicitWrite(myProject, variable, progress)) { + LocalizeValue message = JavaErrorLocalize.localVariableIsNotAssigned(identifier.getText()); + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType) + .registerFix(new EmptyIntentionAction(UnusedSymbolLocalInspectionBase.DISPLAY_NAME), null, null, null, myDeadCodeKey) + .create(); + } + } - return null; - } - - @Nullable - @RequiredReadAction - private HighlightInfo processField( - @Nonnull final Project project, - @Nonnull final PsiField field, - @Nonnull PsiIdentifier identifier, - @Nonnull ProgressIndicator progress, - @Nonnull GlobalUsageHelper helper - ) { - if (HighlightUtil.isSerializationImplicitlyUsedField(field)) { - return null; - } - if (field.hasModifierProperty(PsiModifier.PRIVATE)) { - final QuickFixFactory quickFixFactory = QuickFixFactory.getInstance(); - if (!myRefCountHolder.isReferenced(field) && !UnusedSymbolUtil.isImplicitUsage(myProject, field, progress)) { - String message = JavaErrorBundle.message("private.field.is.not.used", identifier.getText()); - - HighlightInfo highlightInfo = suggestionsToMakeFieldUsed(field, identifier, message); - if (!field.hasInitializer() && !field.hasModifierProperty(PsiModifier.FINAL)) { - QuickFixAction.registerQuickFixAction( - highlightInfo, - HighlightMethodUtil.getFixRange(field), - quickFixFactory.createCreateConstructorParameterFromFieldFix(field) - ); - } - return highlightInfo; - } - - final boolean readReferenced = myRefCountHolder.isReferencedForRead(field); - if (!readReferenced && !UnusedSymbolUtil.isImplicitRead(project, field, progress)) { - String message = JavaErrorBundle.message("private.field.is.not.used.for.reading", identifier.getText()); - return suggestionsToMakeFieldUsed(field, identifier, message); - } - - if (field.hasInitializer()) { return null; - } - final boolean writeReferenced = myRefCountHolder.isReferencedForWrite(field); - if (!writeReferenced && !UnusedSymbolUtil.isImplicitWrite(project, field, progress)) { - String message = JavaErrorBundle.message("private.field.is.not.assigned", identifier.getText()); - final HighlightInfo info = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - - QuickFixAction.registerQuickFixAction( - info, - quickFixFactory.createCreateGetterOrSetterFix(false, true, field), - myDeadCodeKey - ); - if (!field.hasModifierProperty(PsiModifier.FINAL)) { - QuickFixAction.registerQuickFixAction( - info, - HighlightMethodUtil.getFixRange(field), - quickFixFactory.createCreateConstructorParameterFromFieldFix(field) - ); - } - SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes(field, annoName -> - { - QuickFixAction.registerQuickFixAction(info, quickFixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName)); - return true; - }); - return info; - } - } - else if (UnusedSymbolUtil.isImplicitUsage(myProject, field, progress) - && !UnusedSymbolUtil.isImplicitWrite(myProject, field, progress)) { - return null; - } - else if (UnusedSymbolUtil.isFieldUnused(myProject, myFile, field, progress, helper)) { - if (UnusedSymbolUtil.isImplicitWrite(myProject, field, progress)) { - String message = JavaErrorBundle.message("private.field.is.not.used.for.reading", identifier.getText()); - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createSafeDeleteFix(field), myDeadCodeKey); - return highlightInfo; - } - return formatUnusedSymbolHighlightInfo(project, "field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType, identifier); } - return null; - } - - private HighlightInfo suggestionsToMakeFieldUsed(@Nonnull PsiField field, @Nonnull PsiIdentifier identifier, @Nonnull String message) { - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createRemoveUnusedVariableFix(field), myDeadCodeKey); - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, false, field), - myDeadCodeKey - ); - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createCreateGetterOrSetterFix(false, true, field), - myDeadCodeKey - ); - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, true, field), - myDeadCodeKey - ); - return highlightInfo; - } - - private final Map isOverriddenOrOverrides = ConcurrentFactoryMap.createMap( - method -> - { - boolean overrides = SuperMethodsSearch.search(method, null, true, false).findFirst() != null; - return overrides || OverridingMethodsSearch.search(method).findFirst() != null; - } - ); - - private boolean isOverriddenOrOverrides(@Nonnull PsiMethod method) { - return isOverriddenOrOverrides.get(method); - } - - @Nullable - private HighlightInfo processParameter( - @Nonnull Project project, - @Nonnull PsiParameter parameter, - @Nonnull PsiIdentifier identifier, - @Nonnull ProgressIndicator progress - ) { - PsiElement declarationScope = parameter.getDeclarationScope(); - if (declarationScope instanceof PsiMethod method) { - if (PsiUtilCore.hasErrorElementChild(method)) { + + @Nullable + @RequiredReadAction + private HighlightInfo processField( + @Nonnull Project project, + @Nonnull PsiField field, + @Nonnull PsiIdentifier identifier, + @Nonnull ProgressIndicator progress, + @Nonnull GlobalUsageHelper helper + ) { + if (HighlightUtil.isSerializationImplicitlyUsedField(field)) { + return null; + } + if (field.isPrivate()) { + QuickFixFactory quickFixFactory = QuickFixFactory.getInstance(); + if (!myRefCountHolder.isReferenced(field) && !UnusedSymbolUtil.isImplicitUsage(myProject, field, progress)) { + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsed(identifier.getText()); + + HighlightInfo.Builder hlBuilder = suggestionsToMakeFieldUsed(field, identifier, message); + if (!field.hasInitializer() && !field.isFinal()) { + hlBuilder.registerFix( + quickFixFactory.createCreateConstructorParameterFromFieldFix(field), + HighlightMethodUtil.getFixRange(field) + ); + } + return hlBuilder.create(); + } + + boolean readReferenced = myRefCountHolder.isReferencedForRead(field); + if (!readReferenced && !UnusedSymbolUtil.isImplicitRead(project, field, progress)) { + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsedForReading(identifier.getText()); + return suggestionsToMakeFieldUsed(field, identifier, message).create(); + } + + if (field.hasInitializer()) { + return null; + } + boolean writeReferenced = myRefCountHolder.isReferencedForWrite(field); + if (!writeReferenced && !UnusedSymbolUtil.isImplicitWrite(project, field, progress)) { + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotAssigned(identifier.getText()); + HighlightInfo.Builder hlBuilder = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); + + hlBuilder.registerFix(quickFixFactory.createCreateGetterOrSetterFix(false, true, field), null, null, null, myDeadCodeKey); + if (!field.isFinal()) { + hlBuilder.registerFix( + quickFixFactory.createCreateConstructorParameterFromFieldFix(field), + HighlightMethodUtil.getFixRange(field) + ); + } + SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( + field, + annoName -> { + hlBuilder.registerFix(quickFixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName)); + return true; + } + ); + return hlBuilder.create(); + } + } + else if (UnusedSymbolUtil.isImplicitUsage(myProject, field, progress) + && !UnusedSymbolUtil.isImplicitWrite(myProject, field, progress)) { + return null; + } + else if (UnusedSymbolUtil.isFieldUnused(myProject, myFile, field, progress, helper)) { + if (UnusedSymbolUtil.isImplicitWrite(myProject, field, progress)) { + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsedForReading(identifier.getText()); + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType) + .registerFix(QuickFixFactory.getInstance().createSafeDeleteFix(field), null, null, null, myDeadCodeKey) + .create(); + } + return formatUnusedSymbolHighlightInfo( + project, + JavaErrorLocalize::fieldIsNotUsed, + field, + "fields", + myDeadCodeKey, + myDeadCodeInfoType, + identifier + ).create(); + } return null; - } - if ((method.isConstructor() || method.hasModifierProperty(PsiModifier.PRIVATE) || method.hasModifierProperty(PsiModifier.STATIC) || !method - .hasModifierProperty(PsiModifier.ABSTRACT) && - !isOverriddenOrOverrides(method)) && !method.hasModifierProperty(PsiModifier.NATIVE) && !JavaHighlightUtil.isSerializationRelatedMethod( - method, - method.getContainingClass()) && - !PsiClassImplUtil.isMainOrPremainMethod(method)) { - if (UnusedSymbolUtil.isInjected(project, method)) { - return null; - } - HighlightInfo highlightInfo = checkUnusedParameter(parameter, identifier, progress); - if (highlightInfo != null) { - QuickFixFactory.getInstance().registerFixesForUnusedParameter(parameter, highlightInfo); - return highlightInfo; - } - } - } - else if (declarationScope instanceof PsiForeachStatement && !PsiUtil.isIgnoredName(parameter.getName())) { - HighlightInfo highlightInfo = checkUnusedParameter(parameter, identifier, progress); - if (highlightInfo != null) { - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter), - myDeadCodeKey - ); - return highlightInfo; - } } - return null; - } - - @Nullable - @RequiredReadAction - private HighlightInfo checkUnusedParameter( - @Nonnull PsiParameter parameter, - @Nonnull PsiIdentifier identifier, - @Nonnull ProgressIndicator progress - ) { - if (!myRefCountHolder.isReferenced(parameter) && !UnusedSymbolUtil.isImplicitUsage(myProject, parameter, progress)) { - String message = JavaErrorBundle.message("parameter.is.not.used", identifier.getText()); - return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - } - return null; - } - - @Nullable - private HighlightInfo processMethod( - @Nonnull final Project project, - @Nonnull final PsiMethod method, - @Nonnull PsiIdentifier identifier, - @Nonnull ProgressIndicator progress, - @Nonnull GlobalUsageHelper helper - ) { - if (UnusedSymbolUtil.isMethodReferenced(myProject, myFile, method, progress, helper)) { - return null; - } - 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"; - } - LocalizeValue symbolName = HighlightMessageUtil.getSymbolName(method, PsiSubstitutor.EMPTY); - String message = JavaErrorBundle.message(key, symbolName); - final HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createSafeDeleteFix(method), myDeadCodeKey); - SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes(method, annoName -> - { - IntentionAction fix = QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, "methods"); - QuickFixAction.registerQuickFixAction(highlightInfo, fix); - return true; + @Nonnull + @RequiredReadAction + private HighlightInfo.Builder suggestionsToMakeFieldUsed( + @Nonnull PsiField field, + @Nonnull PsiIdentifier identifier, + @Nonnull LocalizeValue message + ) { + QuickFixFactory fixFactory = QuickFixFactory.getInstance(); + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType) + .registerFix(fixFactory.createRemoveUnusedVariableFix(field), null, null, null, myDeadCodeKey) + .registerFix(fixFactory.createCreateGetterOrSetterFix(true, false, field), null, null, null, myDeadCodeKey) + .registerFix(fixFactory.createCreateGetterOrSetterFix(false, true, field), null, null, null, myDeadCodeKey) + .registerFix(fixFactory.createCreateGetterOrSetterFix(true, true, field), null, null, null, myDeadCodeKey); + } + + private final Map isOverriddenOrOverrides = ConcurrentFactoryMap.createMap(method -> { + boolean overrides = SuperMethodsSearch.search(method, null, true, false).findFirst() != null; + return overrides || OverridingMethodsSearch.search(method).findFirst() != null; }); - return highlightInfo; - } - - @Nullable - @RequiredReadAction - private HighlightInfo processClass( - @Nonnull Project project, - @Nonnull PsiClass aClass, - @Nonnull PsiIdentifier identifier, - @Nonnull ProgressIndicator progress, - @Nonnull GlobalUsageHelper helper - ) { - if (UnusedSymbolUtil.isClassUsed(project, myFile, aClass, progress, helper)) { - return null; - } - 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 { - pattern = "class.is.not.used"; - } - return formatUnusedSymbolHighlightInfo(myProject, pattern, aClass, "classes", myDeadCodeKey, myDeadCodeInfoType, identifier); - } - - @RequiredReadAction - private static HighlightInfo formatUnusedSymbolHighlightInfo( - @Nonnull final Project project, - @Nonnull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String pattern, - @Nonnull final PsiNameIdentifierOwner aClass, - @Nonnull final String element, - HighlightDisplayKey highlightDisplayKey, - @Nonnull HighlightInfoType highlightInfoType, - @Nonnull PsiElement identifier - ) { - String symbolName = aClass.getName(); - String message = JavaErrorBundle.message(pattern, symbolName); - final HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, highlightInfoType); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createSafeDeleteFix(aClass), highlightDisplayKey); - SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes((PsiModifierListOwner)aClass, annoName -> - { - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, element) - ); - return true; - }); - return highlightInfo; - } - - @Nullable - @RequiredReadAction - private HighlightInfo processImport(@Nonnull PsiImportStatementBase importStatement, @Nonnull HighlightDisplayKey unusedImportKey) { - // jsp include directive hack - if (importStatement.isForeignFileImport()) { - return null; + private boolean isOverriddenOrOverrides(@Nonnull PsiMethod method) { + return isOverriddenOrOverrides.get(method); + } + + @Nullable + @RequiredReadAction + private HighlightInfo processParameter( + @Nonnull Project project, + @Nonnull PsiParameter parameter, + @Nonnull PsiIdentifier identifier, + @Nonnull ProgressIndicator progress + ) { + PsiElement declarationScope = parameter.getDeclarationScope(); + if (declarationScope instanceof PsiMethod method) { + if (PsiUtilCore.hasErrorElementChild(method)) { + return null; + } + if ((method.isConstructor() || method.isPrivate() || method.isStatic() + || !method.isAbstract() && !isOverriddenOrOverrides(method)) + && !method.hasModifierProperty(PsiModifier.NATIVE) + && !JavaHighlightUtil.isSerializationRelatedMethod(method, method.getContainingClass()) + && !PsiClassImplUtil.isMainOrPremainMethod(method)) { + if (UnusedSymbolUtil.isInjected(project, method)) { + return null; + } + HighlightInfo.Builder hlBuilder = checkUnusedParameter(parameter, identifier, progress); + if (hlBuilder != null) { + HighlightInfo highlightInfo = hlBuilder.create(); + if (highlightInfo != null) { + QuickFixFactory.getInstance().registerFixesForUnusedParameter(parameter, highlightInfo); + return highlightInfo; + } + } + } + } + else if (declarationScope instanceof PsiForeachStatement && !PsiUtil.isIgnoredName(parameter.getName())) { + HighlightInfo.Builder hlBuilder = checkUnusedParameter(parameter, identifier, progress); + if (hlBuilder != null) { + return hlBuilder + .registerFix(QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter), null, null, null, myDeadCodeKey) + .create(); + } + } + + return null; } - if (PsiUtilCore.hasErrorElementChild(importStatement)) { - return null; + @Nullable + @RequiredReadAction + private HighlightInfo.Builder checkUnusedParameter( + @Nonnull PsiParameter parameter, + @Nonnull PsiIdentifier identifier, + @Nonnull ProgressIndicator progress + ) { + if (!myRefCountHolder.isReferenced(parameter) && !UnusedSymbolUtil.isImplicitUsage(myProject, parameter, progress)) { + LocalizeValue message = JavaErrorLocalize.parameterIsNotUsed(identifier.getText()); + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); + } + return null; } - boolean isRedundant = myRefCountHolder.isRedundant(importStatement); - if (!isRedundant && !(importStatement instanceof PsiImportStaticStatement)) { - //check import from same package - String packageName = ((PsiClassOwner)importStatement.getContainingFile()).getPackageName(); - PsiJavaCodeReferenceElement reference = importStatement.getImportReference(); - PsiElement resolved = reference == null ? null : reference.resolve(); - if (resolved instanceof PsiPackage psiPackage) { - isRedundant = packageName.equals(psiPackage.getQualifiedName()); - } - else if (resolved instanceof PsiClass psiClass && !importStatement.isOnDemand()) { - String qName = psiClass.getQualifiedName(); - if (qName != null) { - String name = ((PomNamedTarget)resolved).getName(); - isRedundant = qName.equals(packageName + '.' + name); - } - } + @Nullable + @RequiredReadAction + private HighlightInfo processMethod( + @Nonnull Project project, + @Nonnull PsiMethod method, + @Nonnull PsiIdentifier identifier, + @Nonnull ProgressIndicator progress, + @Nonnull GlobalUsageHelper helper + ) { + if (UnusedSymbolUtil.isMethodReferenced(myProject, myFile, method, progress, helper)) { + return null; + } + Function key; + if (method.isPrivate()) { + key = method.isConstructor() ? JavaErrorLocalize::privateConstructorIsNotUsed : JavaErrorLocalize::privateMethodIsNotUsed; + } + else { + key = method.isConstructor() ? JavaErrorLocalize::constructorIsNotUsed : JavaErrorLocalize::methodIsNotUsed; + } + LocalizeValue symbolName = HighlightMessageUtil.getSymbolName(method, PsiSubstitutor.EMPTY); + LocalizeValue message = key.apply(symbolName); + HighlightInfo.Builder hlBuilder = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); + hlBuilder.registerFix(QuickFixFactory.getInstance().createSafeDeleteFix(method), null, null, null, myDeadCodeKey); + SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( + method, + annoName -> { + hlBuilder.registerFix(QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, "methods")); + return true; + } + ); + return hlBuilder.create(); + } + + @Nullable + @RequiredReadAction + private HighlightInfo processClass( + @Nonnull Project project, + @Nonnull PsiClass aClass, + @Nonnull PsiIdentifier identifier, + @Nonnull ProgressIndicator progress, + @Nonnull GlobalUsageHelper helper + ) { + if (UnusedSymbolUtil.isClassUsed(project, myFile, aClass, progress, helper)) { + return null; + } + + Function pattern; + if (aClass.getContainingClass() != null && aClass.isPrivate()) { + pattern = aClass.isInterface() + ? JavaErrorLocalize::privateInnerInterfaceIsNotUsed + : JavaErrorLocalize::privateInnerClassIsNotUsed; + } + else if (aClass.getParent() instanceof PsiDeclarationStatement) { // local class + pattern = JavaErrorLocalize::localClassIsNotUsed; + } + else if (aClass instanceof PsiTypeParameter) { + pattern = JavaErrorLocalize::typeParameterIsNotUsed; + } + else { + pattern = JavaErrorLocalize::classIsNotUsed; + } + return formatUnusedSymbolHighlightInfo(myProject, pattern, aClass, "classes", myDeadCodeKey, myDeadCodeInfoType, identifier) + .create(); + } + + @RequiredReadAction + private static HighlightInfo.Builder formatUnusedSymbolHighlightInfo( + @Nonnull Project project, + @Nonnull Function pattern, + @Nonnull PsiNameIdentifierOwner aClass, + @Nonnull String element, + HighlightDisplayKey highlightDisplayKey, + @Nonnull HighlightInfoType highlightInfoType, + @Nonnull PsiElement identifier + ) { + String symbolName = aClass.getName(); + LocalizeValue message = pattern.apply(symbolName); + HighlightInfo.Builder hlBuilder = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, highlightInfoType); + QuickFixFactory fixFactory = QuickFixFactory.getInstance(); + hlBuilder.registerFix(fixFactory.createSafeDeleteFix(aClass), null, null, null, highlightDisplayKey); + SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( + (PsiModifierListOwner)aClass, + annoName -> { + hlBuilder.registerFix(fixFactory.createAddToDependencyInjectionAnnotationsFix(project, annoName, element)); + return true; + } + ); + return hlBuilder; } - if (isRedundant) { - return registerRedundantImport(importStatement, unusedImportKey); + @Nullable + @RequiredReadAction + private HighlightInfo processImport(@Nonnull PsiImportStatementBase importStatement, @Nonnull HighlightDisplayKey unusedImportKey) { + // jsp include directive hack + if (importStatement.isForeignFileImport()) { + return null; + } + + if (PsiUtilCore.hasErrorElementChild(importStatement)) { + return null; + } + + boolean isRedundant = myRefCountHolder.isRedundant(importStatement); + if (!isRedundant && !(importStatement instanceof PsiImportStaticStatement)) { + //check import from same package + String packageName = ((PsiClassOwner)importStatement.getContainingFile()).getPackageName(); + PsiJavaCodeReferenceElement reference = importStatement.getImportReference(); + PsiElement resolved = reference == null ? null : reference.resolve(); + if (resolved instanceof PsiPackage psiPackage) { + isRedundant = packageName.equals(psiPackage.getQualifiedName()); + } + else if (resolved instanceof PsiClass psiClass && !importStatement.isOnDemand()) { + String qName = psiClass.getQualifiedName(); + if (qName != null) { + String name = ((PomNamedTarget)resolved).getName(); + isRedundant = qName.equals(packageName + '.' + name); + } + } + } + + if (isRedundant) { + return registerRedundantImport(importStatement, unusedImportKey); + } + + int entryIndex = JavaCodeStyleManager.getInstance(myProject).findEntryIndex(importStatement); + if (entryIndex < myCurrentEntryIndex) { + myHasMissortedImports = true; + } + myCurrentEntryIndex = entryIndex; + + return null; } - int entryIndex = JavaCodeStyleManager.getInstance(myProject).findEntryIndex(importStatement); - if (entryIndex < myCurrentEntryIndex) { - myHasMissortedImports = true; + @RequiredReadAction + private HighlightInfo registerRedundantImport( + @Nonnull PsiImportStatementBase importStatement, + @Nonnull HighlightDisplayKey unusedImportKey + ) { + HighlightInfo info = HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.UNUSED_IMPORT) + .range(importStatement) + .descriptionAndTooltip(InspectionLocalize.unusedImportStatement()) + .registerFix(QuickFixFactory.getInstance().createOptimizeImportsFix(false), null, null, null, unusedImportKey) + .registerFix(QuickFixFactory.getInstance().createEnableOptimizeImportsOnTheFlyFix(), null, null, null, unusedImportKey) + .create(); + + myHasRedundantImports = true; + + return info; } - myCurrentEntryIndex = entryIndex; - - return null; - } - - @RequiredReadAction - private HighlightInfo registerRedundantImport( - @Nonnull PsiImportStatementBase importStatement, - @Nonnull HighlightDisplayKey unusedImportKey - ) { - String description = InspectionLocalize.unusedImportStatement().get(); - HighlightInfo info = HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.UNUSED_IMPORT) - .range(importStatement) - .descriptionAndTooltip(description) - .create(); - - QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createOptimizeImportsFix(false), unusedImportKey); - QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createEnableOptimizeImportsOnTheFlyFix(), unusedImportKey); - myHasRedundantImports = true; - return info; - } } diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/performance/ClassInitializerMayBeStaticInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/performance/ClassInitializerMayBeStaticInspection.java index 48b1238bf..3aeb313af 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/performance/ClassInitializerMayBeStaticInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/performance/ClassInitializerMayBeStaticInspection.java @@ -26,8 +26,9 @@ import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.localize.InspectionGadgetsLocalize; import consulo.annotation.component.ExtensionImpl; +import consulo.application.Application; +import consulo.component.extension.ExtensionPoint; import consulo.java.analysis.codeInspection.CantBeStaticCondition; -import consulo.java.analysis.codeInspection.JavaExtensionPoints; import consulo.language.psi.PsiElement; import jakarta.annotation.Nonnull; import org.jetbrains.annotations.Nls; @@ -76,11 +77,12 @@ public void visitClassInitializer(PsiClassInitializer initializer) { if (containingClass == null) { return; } - for (CantBeStaticCondition addin : JavaExtensionPoints.CANT_BE_STATIC_EP_NAME.getExtensions()) { - if (addin.cantBeStatic(initializer)) { - return; - } + ExtensionPoint cantBeStaticEP = + initializer.getApplication().getExtensionPoint(CantBeStaticCondition.class); + if (cantBeStaticEP.anyMatchSafe(addin -> addin.cantBeStatic(initializer))) { + return; } + PsiElement scope = containingClass.getScope(); if (!(scope instanceof PsiJavaFile) && !containingClass.isStatic()) { return; diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/performance/MethodMayBeStaticInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/performance/MethodMayBeStaticInspection.java index 92e842471..a545fe4de 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/performance/MethodMayBeStaticInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/performance/MethodMayBeStaticInspection.java @@ -27,9 +27,9 @@ import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.application.util.query.Query; +import consulo.component.extension.ExtensionPoint; import consulo.deadCodeNotWorking.impl.MultipleCheckboxOptionsPanel; import consulo.java.analysis.codeInspection.CantBeStaticCondition; -import consulo.java.analysis.codeInspection.JavaExtensionPoints; import consulo.language.psi.PsiElement; import consulo.util.lang.ref.SimpleReference; import jakarta.annotation.Nonnull; @@ -98,10 +98,9 @@ public void visitMethod(@Nonnull PsiMethod method) { if (containingClass == null) { return; } - for (CantBeStaticCondition addin : JavaExtensionPoints.CANT_BE_STATIC_EP_NAME.getExtensions()) { - if (addin.cantBeStatic(method)) { - return; - } + ExtensionPoint cantBeStaticEP = method.getApplication().getExtensionPoint(CantBeStaticCondition.class); + if (cantBeStaticEP.anyMatchSafe(addin -> addin.cantBeStatic(method))) { + return; } PsiElement scope = containingClass.getScope(); if (!(scope instanceof PsiJavaFile) && !containingClass.isStatic()) {