From 2db14dd95b674a582a1bf9f8d64728d9de278fe8 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 19:24:59 +0100 Subject: [PATCH] highlight globally unused groovy classes (IDEA-75803) --- .../daemon/impl/PostHighlightingPass.java | 45 +++++++------------ plugins/groovy/src/META-INF/plugin.xml | 4 ++ .../GroovyUnusedDeclarationInspection.java | 42 +++++++++++++++++ ...s.java => GroovyPostHighlightingPass.java} | 39 +++++++++++++--- .../local/GroovyUnusedImportsPassFactory.java | 2 +- 5 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java rename plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/{GroovyUnusedImportPass.java => GroovyPostHighlightingPass.java} (80%) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 5a4292878996..48b7f0ee5b25 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -378,7 +378,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return UnusedSymbolLocalInspection.isInjected(element); } - private static HighlightInfo createUnusedSymbolInfo(PsiElement element, String message, final HighlightInfoType highlightInfoType) { + public static HighlightInfo createUnusedSymbolInfo(PsiElement element, String message, final HighlightInfoType highlightInfoType) { HighlightInfo info = HighlightInfo.createHighlightInfo(highlightInfoType, element, message); UnusedDeclarationFixProvider[] fixProviders = Extensions.getExtensions(UnusedDeclarationFixProvider.EP_NAME); for (UnusedDeclarationFixProvider provider : fixProviders) { @@ -436,6 +436,9 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return null; } else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress)) { + if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress)) { + return null; + } return formatUnusedSymbolHighlightInfo("field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType); } return null; @@ -580,49 +583,31 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { if (!myDeadCodeEnabled) return false; if (myDeadCodeInspection.isEntryPoint(member)) return false; - String name = member.getName(); + return isGloballyUnused(member, progress, myFile, member.getName()); + } + + public static boolean isGloballyUnused(PsiMember member, ProgressIndicator progress, @Nullable PsiFile fileToIgnoreOccurrencesIn, String name) { if (name == null) return false; SearchScope useScope = member.getUseScope(); if (!(useScope instanceof GlobalSearchScope)) return false; GlobalSearchScope scope = (GlobalSearchScope)useScope; // some classes may have references from within XML outside dependent modules, e.g. our actions - if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(myProject).uniteWith(scope); + Project project = member.getProject(); + if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(project).uniteWith(scope); - PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(myFile.getProject()) - .isCheapEnoughToSearch(name, scope, myFile, progress); + PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress); if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return false; //search usages if it cheap //if count is 0 there is no usages since we've called myRefCountHolder.isReferenced() before if (cheapEnough == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES) { - if (member instanceof PsiEnumConstant) { - return !isEnumValuesMethodUsed(member, progress); - } if (!canBeReferencedViaWeirdNames(member)) return true; } - FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(myProject)).getFindUsagesManager(); - FindUsagesOptions findUsagesOptions; - if (member instanceof PsiClass) { - findUsagesOptions = new JavaClassFindUsagesOptions(myProject); - } - else if (member instanceof PsiMethod) { - findUsagesOptions = new JavaMethodFindUsagesOptions(myProject); - } - else if (member instanceof PsiField) { - findUsagesOptions = new JavaVariableFindUsagesOptions(myProject); - } - else { - LOG.error("unknown member: " + member); - return false; - } + FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager(); + FindUsagesHandler handler = new JavaFindUsagesHandler(member, new JavaFindUsagesHandlerFactory(project)); + FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions(); findUsagesOptions.searchScope = scope; - - boolean used = findUsagesManager.isUsed(member, findUsagesOptions); - - if (!used && member instanceof PsiEnumConstant) { - return !isEnumValuesMethodUsed(member, progress); - } - return !used; + return !findUsagesManager.isUsed(member, findUsagesOptions); } private boolean isEnumValuesMethodUsed(PsiMember member, ProgressIndicator progress) { diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 9a1384fa312a..63f176ae2810 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -382,6 +382,10 @@ + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java new file mode 100644 index 000000000000..3b953f67d90c --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.codeInspection; + +import com.intellij.analysis.AnalysisScope; +import com.intellij.codeInspection.GlobalInspectionContext; +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ex.DescriptorProviderInspection; +import com.intellij.codeInspection.ex.JobDescriptor; +import com.intellij.codeInspection.ex.UnfairLocalInspectionTool; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class GroovyUnusedDeclarationInspection extends DescriptorProviderInspection implements UnfairLocalInspectionTool { + public static final String SHORT_NAME = "GroovyUnusedDeclaration"; + + @Override + public void runInspection(@NotNull AnalysisScope scope, @NotNull InspectionManager manager) { + } + + @NotNull + @Override + public JobDescriptor[] getJobDescriptors(GlobalInspectionContext globalInspectionContext) { + return JobDescriptor.EMPTY_ARRAY; + } + +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java similarity index 80% rename from plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportPass.java rename to plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index 818f4badb3d3..d3f3b5f40330 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -19,8 +19,10 @@ package org.jetbrains.plugins.groovy.codeInspection.local; import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.daemon.impl.*; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; @@ -28,24 +30,28 @@ import com.intellij.lang.annotation.AnnotationSession; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; +import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection; import org.jetbrains.plugins.groovy.lang.editor.GroovyImportOptimizer; +import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import java.util.ArrayList; @@ -56,20 +62,30 @@ import java.util.Set; /** * @author ilyas */ -public class GroovyUnusedImportPass extends TextEditorHighlightingPass { +public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { private final GroovyFile myFile; private final Editor myEditor; - public static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.codeInspection.local.GroovyUnusedImportsPass"); private volatile Set myUnusedImports; private volatile Runnable myOptimizeRunnable; + private volatile List myUnusedDeclarations; - public GroovyUnusedImportPass(GroovyFile file, Editor editor) { + public GroovyPostHighlightingPass(GroovyFile file, Editor editor) { super(file.getProject(), editor.getDocument(), true); myFile = file; myEditor = editor; } - public void doCollectInformation(ProgressIndicator progress) { + public void doCollectInformation(final ProgressIndicator progress) { + InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + final boolean deadCodeEnabled = profile.isToolEnabled(HighlightDisplayKey.find(GroovyUnusedDeclarationInspection.SHORT_NAME), myFile); + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + VirtualFile virtualFile = myFile.getViewProvider().getVirtualFile(); + if (!fileIndex.isInContent(virtualFile)) { + return; + } + + + final List unusedDeclarations = new ArrayList(); final Set unusedImports = new HashSet(GroovyImportOptimizer.getValidImportStatements(myFile)); myFile.accept(new PsiRecursiveElementWalkingVisitor() { @Override @@ -83,10 +99,21 @@ public class GroovyUnusedImportPass extends TextEditorHighlightingPass { } } } + + if (deadCodeEnabled && element instanceof GrNamedElement) { + PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy(); + String name = ((GrNamedElement)element).getName(); + if (element instanceof GrTypeDefinition && PostHighlightingPass.isGloballyUnused((GrTypeDefinition)element, progress, null, name)) { + unusedDeclarations.add( + PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); + } + } + super.visitElement(element); } }); myUnusedImports = unusedImports; + myUnusedDeclarations = unusedDeclarations; if (!unusedImports.isEmpty() && CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) { final VirtualFile vfile = myFile.getVirtualFile(); if (vfile != null && ProjectRootManager.getInstance(myFile.getProject()).getFileIndex().isInSource(vfile)) { @@ -143,7 +170,7 @@ public class GroovyUnusedImportPass extends TextEditorHighlightingPass { public void doApplyInformationToEditor() { AnnotationHolder annotationHolder = new AnnotationHolderImpl(new AnnotationSession(myFile)); - List infos = new ArrayList(myUnusedImports.size()); + List infos = new ArrayList(myUnusedDeclarations); for (GrImportStatement unusedImport : myUnusedImports) { Annotation annotation = annotationHolder.createWarningAnnotation(unusedImport, GroovyInspectionBundle.message("unused.import")); annotation.setHighlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java index 2ea820e498b7..593564cf1bd0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java @@ -42,7 +42,7 @@ public class GroovyUnusedImportsPassFactory extends AbstractProjectComponent imp @Nullable public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) { if (!(file instanceof GroovyFile)) return null; - return new GroovyUnusedImportPass((GroovyFile)file, editor); + return new GroovyPostHighlightingPass((GroovyFile)file, editor); } @NonNls -- 2.23.3