From 7cba359bedace0acac0a44f5aaa0b23ffb26afff Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 11 Jun 2025 21:23:23 +0300 Subject: [PATCH 1/4] Reformatting PostHighlightingVisitor. --- .../analysis/PostHighlightingVisitor.java | 1084 +++++++++-------- 1 file changed, 555 insertions(+), 529 deletions(-) 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..820aaab72 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 @@ -75,581 +75,607 @@ import java.util.Set; 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 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(); } - } } - } } - 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; - if (errorFound) { - fileStatusMap.setErrorFoundFlag(myProject, myDocument, true); - } + myCurrentEntryIndex = -1; - optimizeImportsOnTheFlyLater(progress); - } + myRefCountHolder = refCountHolder; - 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; - } + myProject.getApplication().assertReadAccessAllowed(); - 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; - } + InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); - if (SuppressionUtil.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) { - return null; - } + myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME); - if (parent instanceof PsiLocalVariable localVariable && myUnusedSymbolInspection.LOCAL_VARIABLE) { - return processLocalVariable(localVariable, identifier, progress); + 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() + ); } - if (parent instanceof PsiField field && compareVisibilities(field, myUnusedSymbolInspection.getFieldVisibility())) { - return processField(myProject, field, identifier, progress, helper); + + @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); + } + } + } + } + } + + 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); + } + } + } + } + + if (errorFound) { + fileStatusMap.setErrorFoundFlag(myProject, myDocument, true); + } + + optimizeImportsOnTheFlyLater(progress); } - 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); - } + + 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; + } + + return myProject.getExtensionPoint(ImplicitUsageProvider.class).anyMatchSafe( + implicitUsageProvider -> implicitUsageProvider instanceof JavaImplicitUsageProvider javaImplicitUsageProvider + && javaImplicitUsageProvider.isUnusedImportEnabled(myFile) + ); } - 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) { + 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)) { + 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; - } - 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, final 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; + } + + 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; + } + + 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; } - 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 (!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; + } + } + + return null; } - 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; + @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; } - 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); + private HighlightInfo suggestionsToMakeFieldUsed(@Nonnull PsiField field, @Nonnull PsiIdentifier identifier, @Nonnull String message) { + HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); QuickFixAction.registerQuickFixAction( - unusedSymbolInfo, - new EmptyIntentionAction(UnusedSymbolLocalInspectionBase.DISPLAY_NAME), - myDeadCodeKey + highlightInfo, + QuickFixFactory.getInstance().createRemoveUnusedVariableFix(field), + myDeadCodeKey + ); + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, false, field), + myDeadCodeKey ); - return unusedSymbolInfo; - } + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createCreateGetterOrSetterFix(false, true, field), + myDeadCodeKey + ); + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, true, field), + myDeadCodeKey + ); + return highlightInfo; } - 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) - ); + 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)) { + 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 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); - } + return null; + } - if (field.hasInitializer()) { + @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; - } - 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) - ); + @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; } - SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes(field, annoName -> + 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 -> { - QuickFixAction.registerQuickFixAction(info, quickFixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName)); - return true; + IntentionAction fix = QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, "methods"); + QuickFixAction.registerQuickFixAction(highlightInfo, fix); + 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)) { - 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) { + + @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().createRenameToIgnoredFix(parameter), - myDeadCodeKey + highlightInfo, + QuickFixFactory.getInstance().createSafeDeleteFix(aClass), + highlightDisplayKey + ); + SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( + (PsiModifierListOwner)aClass, + annoName -> { + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, element) + ); + return true; + } ); 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; - }); - 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; - } + @Nullable + @RequiredReadAction + private HighlightInfo processImport(@Nonnull PsiImportStatementBase importStatement, @Nonnull HighlightDisplayKey unusedImportKey) { + // jsp include directive hack + if (importStatement.isForeignFileImport()) { + 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; - } + if (PsiUtilCore.hasErrorElementChild(importStatement)) { + 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); + } + } + } - 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); + } - 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 + ) { + 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; } - 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; - } } From 5751f1ac7d20566f107177e2c572f2d96f981e6d Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 11 Jun 2025 21:37:16 +0300 Subject: [PATCH 2/4] Refactoring PostHighlightingVisitor. --- .../analysis/PostHighlightingVisitor.java | 144 ++++++++++-------- 1 file changed, 81 insertions(+), 63 deletions(-) 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 820aaab72..5f18892df 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 @@ -41,6 +41,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; @@ -73,6 +74,7 @@ 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); @@ -92,7 +94,7 @@ public class PostHighlightingVisitor { private final UnusedDeclarationInspectionBase myDeadCodeInspection; private final UnusedDeclarationInspectionState myDeadCodeState; - private void optimizeImportsOnTheFlyLater(@Nonnull final ProgressIndicator progress) { + 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 = () -> { @@ -149,11 +151,12 @@ public PostHighlightingVisitor( myUnusedSymbolInspection = myDeadCodeInspection != null ? myDeadCodeInspection.getSharedLocalInspectionTool() : null; - myDeadCodeInfoType = myDeadCodeKey == null ? HighlightInfoType.UNUSED_SYMBOL + myDeadCodeInfoType = myDeadCodeKey == null + ? HighlightInfoType.UNUSED_SYMBOL : new HighlightInfoType.HighlightInfoTypeImpl( - profile.getErrorLevel(myDeadCodeKey, myFile).getSeverity(), - HighlightInfoType.UNUSED_SYMBOL.getAttributesKey() - ); + profile.getErrorLevel(myDeadCodeKey, myFile).getSeverity(), + HighlightInfoType.UNUSED_SYMBOL.getAttributesKey() + ); } @RequiredReadAction @@ -168,8 +171,8 @@ public void collectHighlights(@Nonnull HighlightInfoHolder result, @Nonnull Prog boolean errorFound = false; if (unusedSymbolEnabled) { - final FileViewProvider viewProvider = myFile.getViewProvider(); - final Set relevantLanguages = viewProvider.getLanguages(); + FileViewProvider viewProvider = myFile.getViewProvider(); + Set relevantLanguages = viewProvider.getLanguages(); for (Language language : relevantLanguages) { progress.checkCanceled(); PsiElement psiRoot = viewProvider.getPsi(language); @@ -194,10 +197,10 @@ public void collectHighlights(@Nonnull HighlightInfoHolder result, @Nonnull Prog if (isUnusedImportEnabled(unusedImportKey)) { PsiImportList importList = ((PsiJavaFile)myFile).getImportList(); if (importList != null) { - final PsiImportStatementBase[] imports = importList.getAllImportStatements(); + PsiImportStatementBase[] imports = importList.getAllImportStatements(); for (PsiImportStatementBase statement : imports) { progress.checkCanceled(); - final HighlightInfo info = processImport(statement, unusedImportKey); + HighlightInfo info = processImport(statement, unusedImportKey); if (info != null) { errorFound |= info.getSeverity() == HighlightSeverity.ERROR; result.add(info); @@ -215,8 +218,8 @@ public void collectHighlights(@Nonnull HighlightInfoHolder result, @Nonnull Prog private boolean isUnusedImportEnabled(HighlightDisplayKey unusedImportKey) { InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); - //noinspection SimplifiableIfStatement - if (profile.isToolEnabled(unusedImportKey, myFile) && myFile instanceof PsiJavaFile + //noinspection SimplifiableIfStatement + if (profile.isToolEnabled(unusedImportKey, myFile) && myFile instanceof PsiJavaFile && HighlightingLevelManager.getInstance(myProject).shouldInspect(myFile)) { return true; } @@ -250,15 +253,15 @@ private HighlightInfo processIdentifier( return processField(myProject, field, identifier, progress, helper); } if (parent instanceof PsiParameter parameter) { - final PsiElement declarationScope = parameter.getDeclarationScope(); + 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 (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)) { @@ -269,7 +272,7 @@ private HighlightInfo processIdentifier( } } if (parent instanceof PsiClass psiClass) { - final String acceptedVisibility = psiClass.getContainingClass() == null + String acceptedVisibility = psiClass.getContainingClass() == null ? myUnusedSymbolInspection.getClassVisibility() : myUnusedSymbolInspection.getInnerClassVisibility(); if (compareVisibilities(psiClass, acceptedVisibility)) { @@ -279,7 +282,7 @@ private HighlightInfo processIdentifier( return null; } - private static boolean compareVisibilities(PsiModifierListOwner listOwner, final String visibility) { + private static boolean compareVisibilities(PsiModifierListOwner listOwner, String visibility) { if (visibility != null) { while (listOwner != null) { if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(listOwner.getModifierList()), visibility) >= 0) { @@ -306,7 +309,7 @@ private HighlightInfo processLocalVariable( } if (!myRefCountHolder.isReferenced(variable)) { - String message = JavaErrorBundle.message("local.variable.is.never.used", identifier.getText()); + LocalizeValue message = JavaErrorLocalize.localVariableIsNeverUsed(identifier.getText()); HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); IntentionAction fix = variable instanceof PsiResourceVariable ? QuickFixFactory.getInstance().createRenameToIgnoredFix(variable) @@ -317,7 +320,7 @@ private HighlightInfo processLocalVariable( 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()); + LocalizeValue message = JavaErrorLocalize.localVariableIsNotUsedForReading(identifier.getText()); HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); QuickFixAction.registerQuickFixAction( highlightInfo, @@ -330,8 +333,8 @@ private HighlightInfo processLocalVariable( 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); + LocalizeValue message = JavaErrorLocalize.localVariableIsNotAssigned(identifier.getText()); + HighlightInfo unusedSymbolInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); QuickFixAction.registerQuickFixAction( unusedSymbolInfo, new EmptyIntentionAction(UnusedSymbolLocalInspectionBase.DISPLAY_NAME), @@ -347,8 +350,8 @@ private HighlightInfo processLocalVariable( @Nullable @RequiredReadAction private HighlightInfo processField( - @Nonnull final Project project, - @Nonnull final PsiField field, + @Nonnull Project project, + @Nonnull PsiField field, @Nonnull PsiIdentifier identifier, @Nonnull ProgressIndicator progress, @Nonnull GlobalUsageHelper helper @@ -356,13 +359,13 @@ private HighlightInfo processField( if (HighlightUtil.isSerializationImplicitlyUsedField(field)) { return null; } - if (field.hasModifierProperty(PsiModifier.PRIVATE)) { - final QuickFixFactory quickFixFactory = QuickFixFactory.getInstance(); + if (field.isPrivate()) { + QuickFixFactory quickFixFactory = QuickFixFactory.getInstance(); if (!myRefCountHolder.isReferenced(field) && !UnusedSymbolUtil.isImplicitUsage(myProject, field, progress)) { - String message = JavaErrorBundle.message("private.field.is.not.used", identifier.getText()); + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsed(identifier.getText()); HighlightInfo highlightInfo = suggestionsToMakeFieldUsed(field, identifier, message); - if (!field.hasInitializer() && !field.hasModifierProperty(PsiModifier.FINAL)) { + if (!field.hasInitializer() && !field.isFinal()) { QuickFixAction.registerQuickFixAction( highlightInfo, HighlightMethodUtil.getFixRange(field), @@ -372,37 +375,42 @@ private HighlightInfo processField( return highlightInfo; } - final boolean readReferenced = myRefCountHolder.isReferencedForRead(field); + 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()); + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsedForReading(identifier.getText()); return suggestionsToMakeFieldUsed(field, identifier, message); } if (field.hasInitializer()) { return null; } - final boolean writeReferenced = myRefCountHolder.isReferencedForWrite(field); + 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); + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotAssigned(identifier.getText()); + HighlightInfo info = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); QuickFixAction.registerQuickFixAction( info, quickFixFactory.createCreateGetterOrSetterFix(false, true, field), myDeadCodeKey ); - if (!field.hasModifierProperty(PsiModifier.FINAL)) { + if (!field.isFinal()) { QuickFixAction.registerQuickFixAction( info, HighlightMethodUtil.getFixRange(field), quickFixFactory.createCreateConstructorParameterFromFieldFix(field) ); } - SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes(field, annoName -> - { - QuickFixAction.registerQuickFixAction(info, quickFixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName)); - return true; - }); + SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( + field, + annoName -> { + QuickFixAction.registerQuickFixAction( + info, + quickFixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName) + ); + return true; + } + ); return info; } } @@ -412,7 +420,7 @@ else if (UnusedSymbolUtil.isImplicitUsage(myProject, field, progress) } 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()); + LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsedForReading(identifier.getText()); HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); QuickFixAction.registerQuickFixAction( highlightInfo, @@ -434,7 +442,12 @@ else if (UnusedSymbolUtil.isFieldUnused(myProject, myFile, field, progress, help return null; } - private HighlightInfo suggestionsToMakeFieldUsed(@Nonnull PsiField field, @Nonnull PsiIdentifier identifier, @Nonnull String message) { + @RequiredReadAction + private HighlightInfo suggestionsToMakeFieldUsed( + @Nonnull PsiField field, + @Nonnull PsiIdentifier identifier, + @Nonnull LocalizeValue message + ) { HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); QuickFixAction.registerQuickFixAction( highlightInfo, @@ -469,6 +482,7 @@ private boolean isOverriddenOrOverrides(@Nonnull PsiMethod method) { } @Nullable + @RequiredReadAction private HighlightInfo processParameter( @Nonnull Project project, @Nonnull PsiParameter parameter, @@ -480,8 +494,8 @@ private HighlightInfo processParameter( if (PsiUtilCore.hasErrorElementChild(method)) { return null; } - if ((method.isConstructor() || method.hasModifierProperty(PsiModifier.PRIVATE) || method.hasModifierProperty(PsiModifier.STATIC) - || !method.hasModifierProperty(PsiModifier.ABSTRACT) && !isOverriddenOrOverrides(method)) + if ((method.isConstructor() || method.isPrivate() || method.isStatic() + || !method.isAbstract() && !isOverriddenOrOverrides(method)) && !method.hasModifierProperty(PsiModifier.NATIVE) && !JavaHighlightUtil.isSerializationRelatedMethod(method, method.getContainingClass()) && !PsiClassImplUtil.isMainOrPremainMethod(method)) { @@ -518,16 +532,17 @@ private HighlightInfo checkUnusedParameter( @Nonnull ProgressIndicator progress ) { if (!myRefCountHolder.isReferenced(parameter) && !UnusedSymbolUtil.isImplicitUsage(myProject, parameter, progress)) { - String message = JavaErrorBundle.message("parameter.is.not.used", identifier.getText()); + LocalizeValue message = JavaErrorLocalize.parameterIsNotUsed(identifier.getText()); return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); } return null; } @Nullable + @RequiredReadAction private HighlightInfo processMethod( - @Nonnull final Project project, - @Nonnull final PsiMethod method, + @Nonnull Project project, + @Nonnull PsiMethod method, @Nonnull PsiIdentifier identifier, @Nonnull ProgressIndicator progress, @Nonnull GlobalUsageHelper helper @@ -535,23 +550,26 @@ private HighlightInfo processMethod( 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"; + Function key; + if (method.isPrivate()) { + key = method.isConstructor() ? JavaErrorLocalize::privateConstructorIsNotUsed : JavaErrorLocalize::privateMethodIsNotUsed; } else { - key = method.isConstructor() ? "constructor.is.not.used" : "method.is.not.used"; + key = method.isConstructor() ? JavaErrorLocalize::constructorIsNotUsed : JavaErrorLocalize::methodIsNotUsed; } LocalizeValue symbolName = HighlightMessageUtil.getSymbolName(method, PsiSubstitutor.EMPTY); - String message = JavaErrorBundle.message(key, symbolName); - final HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); + LocalizeValue message = key.apply(symbolName); + 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; - }); + SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( + method, + annoName -> { + IntentionAction fix = + QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, "methods"); + QuickFixAction.registerQuickFixAction(highlightInfo, fix); + return true; + } + ); return highlightInfo; } @@ -569,7 +587,7 @@ private HighlightInfo processClass( } String pattern; - if (aClass.getContainingClass() != null && aClass.hasModifierProperty(PsiModifier.PRIVATE)) { + if (aClass.getContainingClass() != null && aClass.isPrivate()) { pattern = aClass.isInterface() ? "private.inner.interface.is.not.used" : "private.inner.class.is.not.used"; } else if (aClass.getParent() instanceof PsiDeclarationStatement) { // local class @@ -586,17 +604,17 @@ else if (aClass instanceof PsiTypeParameter) { @RequiredReadAction private static HighlightInfo formatUnusedSymbolHighlightInfo( - @Nonnull final Project project, + @Nonnull Project project, @Nonnull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String pattern, - @Nonnull final PsiNameIdentifierOwner aClass, - @Nonnull final String element, + @Nonnull PsiNameIdentifierOwner aClass, + @Nonnull 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); + HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, highlightInfoType); QuickFixAction.registerQuickFixAction( highlightInfo, QuickFixFactory.getInstance().createSafeDeleteFix(aClass), From fc7815391f0d37a8f51f954d33d4b79f333c5e7f Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 11 Jun 2025 22:17:31 +0300 Subject: [PATCH 3/4] Thorough refactoring of PostHighlightingVisitor. --- .../analysis/PostHighlightingVisitor.java | 194 +++++++----------- 1 file changed, 73 insertions(+), 121 deletions(-) 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 5f18892df..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; @@ -54,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; @@ -69,7 +67,6 @@ 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; @@ -310,37 +307,29 @@ private HighlightInfo processLocalVariable( if (!myRefCountHolder.isReferenced(variable)) { LocalizeValue message = JavaErrorLocalize.localVariableIsNeverUsed(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; + 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)) { LocalizeValue message = JavaErrorLocalize.localVariableIsNotUsedForReading(identifier.getText()); - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable), - myDeadCodeKey - ); - return highlightInfo; + 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)) { LocalizeValue message = JavaErrorLocalize.localVariableIsNotAssigned(identifier.getText()); - HighlightInfo unusedSymbolInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction( - unusedSymbolInfo, - new EmptyIntentionAction(UnusedSymbolLocalInspectionBase.DISPLAY_NAME), - myDeadCodeKey - ); - return unusedSymbolInfo; + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType) + .registerFix(new EmptyIntentionAction(UnusedSymbolLocalInspectionBase.DISPLAY_NAME), null, null, null, myDeadCodeKey) + .create(); } } @@ -364,21 +353,20 @@ private HighlightInfo processField( if (!myRefCountHolder.isReferenced(field) && !UnusedSymbolUtil.isImplicitUsage(myProject, field, progress)) { LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsed(identifier.getText()); - HighlightInfo highlightInfo = suggestionsToMakeFieldUsed(field, identifier, message); + HighlightInfo.Builder hlBuilder = suggestionsToMakeFieldUsed(field, identifier, message); if (!field.hasInitializer() && !field.isFinal()) { - QuickFixAction.registerQuickFixAction( - highlightInfo, - HighlightMethodUtil.getFixRange(field), - quickFixFactory.createCreateConstructorParameterFromFieldFix(field) + hlBuilder.registerFix( + quickFixFactory.createCreateConstructorParameterFromFieldFix(field), + HighlightMethodUtil.getFixRange(field) ); } - return highlightInfo; + 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); + return suggestionsToMakeFieldUsed(field, identifier, message).create(); } if (field.hasInitializer()) { @@ -387,31 +375,23 @@ private HighlightInfo processField( boolean writeReferenced = myRefCountHolder.isReferencedForWrite(field); if (!writeReferenced && !UnusedSymbolUtil.isImplicitWrite(project, field, progress)) { LocalizeValue message = JavaErrorLocalize.privateFieldIsNotAssigned(identifier.getText()); - HighlightInfo info = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); + HighlightInfo.Builder hlBuilder = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction( - info, - quickFixFactory.createCreateGetterOrSetterFix(false, true, field), - myDeadCodeKey - ); + hlBuilder.registerFix(quickFixFactory.createCreateGetterOrSetterFix(false, true, field), null, null, null, myDeadCodeKey); if (!field.isFinal()) { - QuickFixAction.registerQuickFixAction( - info, - HighlightMethodUtil.getFixRange(field), - quickFixFactory.createCreateConstructorParameterFromFieldFix(field) + hlBuilder.registerFix( + quickFixFactory.createCreateConstructorParameterFromFieldFix(field), + HighlightMethodUtil.getFixRange(field) ); } SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( field, annoName -> { - QuickFixAction.registerQuickFixAction( - info, - quickFixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName) - ); + hlBuilder.registerFix(quickFixFactory.createAddToImplicitlyWrittenFieldsFix(project, annoName)); return true; } ); - return info; + return hlBuilder.create(); } } else if (UnusedSymbolUtil.isImplicitUsage(myProject, field, progress) @@ -421,55 +401,36 @@ else if (UnusedSymbolUtil.isImplicitUsage(myProject, field, progress) else if (UnusedSymbolUtil.isFieldUnused(myProject, myFile, field, progress, helper)) { if (UnusedSymbolUtil.isImplicitWrite(myProject, field, progress)) { LocalizeValue message = JavaErrorLocalize.privateFieldIsNotUsedForReading(identifier.getText()); - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createSafeDeleteFix(field), - myDeadCodeKey - ); - return highlightInfo; + return UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType) + .registerFix(QuickFixFactory.getInstance().createSafeDeleteFix(field), null, null, null, myDeadCodeKey) + .create(); } return formatUnusedSymbolHighlightInfo( project, - "field.is.not.used", + JavaErrorLocalize::fieldIsNotUsed, field, "fields", myDeadCodeKey, myDeadCodeInfoType, identifier - ); + ).create(); } return null; } + @Nonnull @RequiredReadAction - private HighlightInfo suggestionsToMakeFieldUsed( + private HighlightInfo.Builder suggestionsToMakeFieldUsed( @Nonnull PsiField field, @Nonnull PsiIdentifier identifier, @Nonnull LocalizeValue 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; + 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 -> { @@ -502,22 +463,22 @@ private HighlightInfo processParameter( if (UnusedSymbolUtil.isInjected(project, method)) { return null; } - HighlightInfo highlightInfo = checkUnusedParameter(parameter, identifier, progress); - if (highlightInfo != null) { - QuickFixFactory.getInstance().registerFixesForUnusedParameter(parameter, highlightInfo); - return highlightInfo; + 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 highlightInfo = checkUnusedParameter(parameter, identifier, progress); - if (highlightInfo != null) { - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter), - myDeadCodeKey - ); - return highlightInfo; + HighlightInfo.Builder hlBuilder = checkUnusedParameter(parameter, identifier, progress); + if (hlBuilder != null) { + return hlBuilder + .registerFix(QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter), null, null, null, myDeadCodeKey) + .create(); } } @@ -526,7 +487,7 @@ else if (declarationScope instanceof PsiForeachStatement && !PsiUtil.isIgnoredNa @Nullable @RequiredReadAction - private HighlightInfo checkUnusedParameter( + private HighlightInfo.Builder checkUnusedParameter( @Nonnull PsiParameter parameter, @Nonnull PsiIdentifier identifier, @Nonnull ProgressIndicator progress @@ -559,18 +520,16 @@ private HighlightInfo processMethod( } LocalizeValue symbolName = HighlightMessageUtil.getSymbolName(method, PsiSubstitutor.EMPTY); LocalizeValue message = key.apply(symbolName); - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createSafeDeleteFix(method), myDeadCodeKey); + HighlightInfo.Builder hlBuilder = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, myDeadCodeInfoType); + hlBuilder.registerFix(QuickFixFactory.getInstance().createSafeDeleteFix(method), null, null, null, myDeadCodeKey); SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( method, annoName -> { - IntentionAction fix = - QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, "methods"); - QuickFixAction.registerQuickFixAction(highlightInfo, fix); + hlBuilder.registerFix(QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, "methods")); return true; } ); - return highlightInfo; + return hlBuilder.create(); } @Nullable @@ -586,26 +545,29 @@ private HighlightInfo processClass( return null; } - String pattern; + Function pattern; if (aClass.getContainingClass() != null && aClass.isPrivate()) { - pattern = aClass.isInterface() ? "private.inner.interface.is.not.used" : "private.inner.class.is.not.used"; + pattern = aClass.isInterface() + ? JavaErrorLocalize::privateInnerInterfaceIsNotUsed + : JavaErrorLocalize::privateInnerClassIsNotUsed; } else if (aClass.getParent() instanceof PsiDeclarationStatement) { // local class - pattern = "local.class.is.not.used"; + pattern = JavaErrorLocalize::localClassIsNotUsed; } else if (aClass instanceof PsiTypeParameter) { - pattern = "type.parameter.is.not.used"; + pattern = JavaErrorLocalize::typeParameterIsNotUsed; } else { - pattern = "class.is.not.used"; + pattern = JavaErrorLocalize::classIsNotUsed; } - return formatUnusedSymbolHighlightInfo(myProject, pattern, aClass, "classes", myDeadCodeKey, myDeadCodeInfoType, identifier); + return formatUnusedSymbolHighlightInfo(myProject, pattern, aClass, "classes", myDeadCodeKey, myDeadCodeInfoType, identifier) + .create(); } @RequiredReadAction - private static HighlightInfo formatUnusedSymbolHighlightInfo( + private static HighlightInfo.Builder formatUnusedSymbolHighlightInfo( @Nonnull Project project, - @Nonnull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String pattern, + @Nonnull Function pattern, @Nonnull PsiNameIdentifierOwner aClass, @Nonnull String element, HighlightDisplayKey highlightDisplayKey, @@ -613,24 +575,18 @@ private static HighlightInfo formatUnusedSymbolHighlightInfo( @Nonnull PsiElement identifier ) { String symbolName = aClass.getName(); - String message = JavaErrorBundle.message(pattern, symbolName); - HighlightInfo highlightInfo = UnusedSymbolUtil.createUnusedSymbolInfo(identifier, message, highlightInfoType); - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createSafeDeleteFix(aClass), - highlightDisplayKey - ); + 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 -> { - QuickFixAction.registerQuickFixAction( - highlightInfo, - QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, element) - ); + hlBuilder.registerFix(fixFactory.createAddToDependencyInjectionAnnotationsFix(project, annoName, element)); return true; } ); - return highlightInfo; + return hlBuilder; } @Nullable @@ -681,19 +637,15 @@ 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) + .descriptionAndTooltip(InspectionLocalize.unusedImportStatement()) + .registerFix(QuickFixFactory.getInstance().createOptimizeImportsFix(false), null, null, null, unusedImportKey) + .registerFix(QuickFixFactory.getInstance().createEnableOptimizeImportsOnTheFlyFix(), null, null, null, unusedImportKey) .create(); - QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createOptimizeImportsFix(false), unusedImportKey); - QuickFixAction.registerQuickFixAction( - info, - QuickFixFactory.getInstance().createEnableOptimizeImportsOnTheFlyFix(), - unusedImportKey - ); myHasRedundantImports = true; + return info; } } From 94787bb66fbb47acfe2d0ddf6c6188d1a6fe849a Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 11 Jun 2025 22:19:28 +0300 Subject: [PATCH 4/4] Replacing EP_NAME with getExtensionPoint (part 4). --- .../daemon/impl/UnusedSymbolUtil.java | 37 ++++++++++++------- ...ClassInitializerMayBeStaticInspection.java | 12 +++--- .../MethodMayBeStaticInspection.java | 9 ++--- 3 files changed, 35 insertions(+), 23 deletions(-) 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/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()) {