highlight globally unused groovy classes (IDEA-75803)

This commit is contained in:
peter
2012-01-31 18:30:24 +01:00
parent 3ba0a4231d
commit 2db14dd95b
5 changed files with 95 additions and 37 deletions
@@ -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) {
+4
View File
@@ -382,6 +382,10 @@
<copyPastePreProcessor implementation="org.jetbrains.plugins.groovy.lang.editor.GroovyLiteralCopyPasteProcessor"/>
<copyPastePostProcessor implementation="org.jetbrains.plugins.groovy.lang.editor.GroovyReferenceCopyPasteProcessor"/>
<specialTool shortName="GroovyUnusedDeclaration" displayName="Unused declaration"
groupName="Declaration Redundancy" enabledByDefault="true" level="WARNING"
implementationClass="org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection"/>
<localInspection language="Groovy" groupPath="Groovy" shortName="SecondUnsafeCall" bundle="org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle"
key="second.unsafe.call" groupName="Probable bugs" enabledByDefault="true" level="WARNING"
implementationClass="org.jetbrains.plugins.groovy.codeInspection.secondUnsafeCall.SecondUnsafeCallInspection"/>
@@ -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;
}
}
@@ -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<GrImportStatement> myUnusedImports;
private volatile Runnable myOptimizeRunnable;
private volatile List<HighlightInfo> 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<HighlightInfo> unusedDeclarations = new ArrayList<HighlightInfo>();
final Set<GrImportStatement> unusedImports = new HashSet<GrImportStatement>(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<HighlightInfo> infos = new ArrayList<HighlightInfo>(myUnusedImports.size());
List<HighlightInfo> infos = new ArrayList<HighlightInfo>(myUnusedDeclarations);
for (GrImportStatement unusedImport : myUnusedImports) {
Annotation annotation = annotationHolder.createWarningAnnotation(unusedImport, GroovyInspectionBundle.message("unused.import"));
annotation.setHighlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL);
@@ -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