diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java new file mode 100644 index 000000000000..18a363133c01 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java @@ -0,0 +1,40 @@ +/* + * 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 com.intellij.codeInsight.daemon.impl; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiNamedElement; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author peter + */ +public abstract class GlobalUsageHelper { + final Map unusedClassCache = new HashMap(); + + public abstract boolean shouldCheckUsages(@NotNull PsiMember member); + public boolean isLocallyUsed(@NotNull PsiNamedElement member) { + return false; + } + + public boolean shouldIgnoreUsagesInCurrentFile() { + return false; + } +} 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 6672bd5c45bf..74f7f6c5d949 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 @@ -82,7 +82,6 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector; import com.intellij.util.Processor; import gnu.trove.THashSet; -import gnu.trove.TObjectIntHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.PropertyKey; @@ -252,13 +251,28 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { myDeadCodeInfoType = myDeadCodeKey == null ? null : new HighlightInfoType.HighlightInfoTypeImpl(profile.getErrorLevel(myDeadCodeKey, myFile).getSeverity(), HighlightInfoType.UNUSED_SYMBOL.getAttributesKey()); + GlobalUsageHelper helper = new GlobalUsageHelper() { + @Override + public boolean shouldCheckUsages(@NotNull PsiMember member) { + if (myInLibrary) return false; + if (!myDeadCodeEnabled) return false; + if (myDeadCodeInspection.isEntryPoint(member)) return false; + return true; + } + + @Override + public boolean isLocallyUsed(@NotNull PsiNamedElement member) { + return myRefCountHolder.isReferenced(myFile); + } + }; + boolean errorFound = false; if (unusedSymbolEnabled) { for (PsiElement element : elements) { progress.checkCanceled(); if (element instanceof PsiIdentifier) { PsiIdentifier identifier = (PsiIdentifier)element; - HighlightInfo info = processIdentifier(identifier, progress); + HighlightInfo info = processIdentifier(identifier, progress, helper); if (info != null) { errorFound |= info.getSeverity() == HighlightSeverity.ERROR; result.add(info); @@ -285,7 +299,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processIdentifier(PsiIdentifier identifier, ProgressIndicator progress) { + private HighlightInfo processIdentifier(PsiIdentifier identifier, ProgressIndicator progress, GlobalUsageHelper helper) { if (InspectionManagerEx.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) return null; PsiElement parent = identifier.getParent(); if (PsiUtilCore.hasErrorElementChild(parent)) return null; @@ -294,17 +308,17 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return processLocalVariable((PsiLocalVariable)parent, progress); } if (parent instanceof PsiField && myUnusedSymbolInspection.FIELD) { - return processField((PsiField)parent, identifier, progress); + return processField((PsiField)parent, identifier, progress, helper); } if (parent instanceof PsiParameter && myUnusedSymbolInspection.PARAMETER) { if (InspectionManagerEx.isSuppressed(identifier, UnusedParametersInspection.SHORT_NAME)) return null; return processParameter((PsiParameter)parent, progress); } if (parent instanceof PsiMethod && myUnusedSymbolInspection.METHOD) { - return processMethod((PsiMethod)parent, progress); + return processMethod((PsiMethod)parent, progress, helper); } if (parent instanceof PsiClass && myUnusedSymbolInspection.CLASS) { - return processClass((PsiClass)parent, progress); + return processClass((PsiClass)parent, progress, helper); } return null; } @@ -389,7 +403,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processField(final PsiField field, final PsiIdentifier identifier, ProgressIndicator progress) { + private HighlightInfo processField(final PsiField field, final PsiIdentifier identifier, ProgressIndicator progress, GlobalUsageHelper helper) { if (field.hasModifierProperty(PsiModifier.PRIVATE)) { if (!myRefCountHolder.isReferenced(field) && !isImplicitUsage(field, progress)) { if (HighlightUtil.isSerializationImplicitlyUsedField(field)) { @@ -433,8 +447,8 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { else if (isImplicitUsage(field, progress)) { return null; } - else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress)) { - if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress)) { + else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress, helper)) { + if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress, helper)) { return null; } return formatUnusedSymbolHighlightInfo("field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType); @@ -509,10 +523,10 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processMethod(final PsiMethod method, ProgressIndicator progress) { + private HighlightInfo processMethod(final PsiMethod method, ProgressIndicator progress, GlobalUsageHelper helper) { boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE); PsiClass containingClass = method.getContainingClass(); - if (isMethodReferenced(method, progress, isPrivate, containingClass)) return null; + if (isMethodReferenced(method, progress, isPrivate, containingClass, helper)) return null; HighlightInfoType highlightInfoType; HighlightDisplayKey highlightDisplayKey; String key; @@ -545,8 +559,12 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return highlightInfo; } - private boolean isMethodReferenced(PsiMethod method, ProgressIndicator progress, boolean aPrivate, PsiClass containingClass) { - if (myRefCountHolder.isReferenced(method)) return true; + private static boolean isMethodReferenced(PsiMethod method, + ProgressIndicator progress, + boolean aPrivate, + PsiClass containingClass, + GlobalUsageHelper helper) { + if (helper.isLocallyUsed(method)) return true; if (HighlightMethodUtil.isSerializationRelatedMethod(method, containingClass)) return true; if (aPrivate) { @@ -561,7 +579,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { //class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too if (containingClass != null && method.isConstructor() && containingClass.getConstructors().length == 1 - && isClassUnused(containingClass, progress) == USED) { + && isClassUsed(containingClass, progress, helper)) { return true; } if (isImplicitUsage(method, progress)) return true; @@ -569,22 +587,17 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { if (method.findSuperMethods().length != 0) { return true; } - if (!weAreSureThereAreNoUsages(method, progress)) { + if (!weAreSureThereAreNoUsages(method, progress, helper)) { return true; } } return false; } - private boolean weAreSureThereAreNoUsages(PsiMember member, ProgressIndicator progress) { - if (myInLibrary) return false; - if (!myDeadCodeEnabled) return false; - if (myDeadCodeInspection.isEntryPoint(member)) return false; + private static boolean weAreSureThereAreNoUsages(PsiMember member, ProgressIndicator progress, GlobalUsageHelper helper) { + if (!helper.shouldCheckUsages(member)) return false; - return isGloballyUnused(member, progress, myFile, member.getName()); - } - - public static boolean isGloballyUnused(PsiMember member, ProgressIndicator progress, @Nullable PsiFile fileToIgnoreOccurrencesIn, String name) { + String name = member.getName(); if (name == null) return false; SearchScope useScope = member.getUseScope(); if (!(useScope instanceof GlobalSearchScope)) return false; @@ -593,7 +606,9 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { Project project = member.getProject(); if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(project).uniteWith(scope); - PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress); + PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, + helper.shouldIgnoreUsagesInCurrentFile() ? member.getContainingFile() : null, + progress); if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return false; //search usages if it cheap @@ -608,13 +623,13 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return !findUsagesManager.isUsed(member, findUsagesOptions); } - private boolean isEnumValuesMethodUsed(PsiMember member, ProgressIndicator progress) { + private static boolean isEnumValuesMethodUsed(PsiMember member, ProgressIndicator progress, GlobalUsageHelper helper) { final PsiClassImpl containingClass = (PsiClassImpl)member.getContainingClass(); if (containingClass == null) return true; final PsiMethod valuesMethod = containingClass.getValuesMethod(); if (valuesMethod == null) return true; boolean isPrivate = valuesMethod.hasModifierProperty(PsiModifier.PRIVATE); - return isMethodReferenced(valuesMethod, progress, isPrivate, containingClass); + return isMethodReferenced(valuesMethod, progress, isPrivate, containingClass, helper); } private static boolean canBeReferencedViaWeirdNames(PsiMember member) { @@ -629,9 +644,8 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processClass(PsiClass aClass, ProgressIndicator progress) { - int usage = isClassUnused(aClass, progress); - if (usage == USED) return null; + private HighlightInfo processClass(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) { + if (isClassUsed(aClass, progress, helper)) return null; String pattern; HighlightDisplayKey highlightDisplayKey; @@ -661,27 +675,22 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return formatUnusedSymbolHighlightInfo(pattern, aClass, "classes", highlightDisplayKey, highlightInfoType); } - private static final int USED = 1; - private static final int UNUSED_LOCALLY = 2; - private static final int UNUSED_GLOBALLY = 3; - private final TObjectIntHashMap unusedClassCache = new TObjectIntHashMap(); - private int isClassUnused(PsiClass aClass, ProgressIndicator progress) { - if (aClass == null) return USED; - int result = unusedClassCache.get(aClass); - if (result == 0) { - result = isReallyUnused(aClass, progress); - unusedClassCache.put(aClass, result); + public static boolean isClassUsed(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) { + if (aClass == null) return true; + Boolean result = helper.unusedClassCache.get(aClass); + if (result == null) { + result = !isReallyUsed(aClass, progress, helper); + helper.unusedClassCache.put(aClass, result); } return result; } - private int isReallyUnused(PsiClass aClass, ProgressIndicator progress) { - if (isImplicitUsage(aClass, progress) || myRefCountHolder.isReferenced(aClass)) return USED; + private static boolean isReallyUsed(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) { + if (isImplicitUsage(aClass, progress) || helper.isLocallyUsed(aClass)) return true; if (aClass.getContainingClass() != null && aClass.hasModifierProperty(PsiModifier.PRIVATE) || aClass.getParent() instanceof PsiDeclarationStatement || - aClass instanceof PsiTypeParameter) return UNUSED_LOCALLY; - if (weAreSureThereAreNoUsages(aClass, progress)) return UNUSED_GLOBALLY; - return USED; + aClass instanceof PsiTypeParameter) return false; + return !weAreSureThereAreNoUsages(aClass, progress, helper); } private static HighlightInfo formatUnusedSymbolHighlightInfo(@PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String pattern, diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index ea024e4cc37e..5f90199da46c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -24,6 +24,7 @@ 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.codeInspection.deadCode.UnusedDeclarationInspection; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.AnnotationSession; @@ -40,6 +41,7 @@ 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.PsiMember; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; @@ -76,14 +78,20 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { } public void doCollectInformation(final ProgressIndicator progress) { - InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + final 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 UnusedDeclarationInspection deadCodeInspection = (UnusedDeclarationInspection)profile.getInspectionTool(UnusedDeclarationInspection.SHORT_NAME, myFile); + final GlobalUsageHelper usageHelper = new GlobalUsageHelper() { + @Override + public boolean shouldCheckUsages(@NotNull PsiMember member) { + return deadCodeInspection == null || !deadCodeInspection.isEntryPoint(member); + } + }; final List unusedDeclarations = new ArrayList(); final Set unusedImports = new HashSet(GroovyImportOptimizer.getValidImportStatements(myFile)); @@ -103,7 +111,7 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { if (deadCodeEnabled && element instanceof GrNamedElement && !PostHighlightingPass.isImplicitUsage((GrNamedElement)element, progress)) { PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy(); String name = ((GrNamedElement)element).getName(); - if (element instanceof GrTypeDefinition && PostHighlightingPass.isGloballyUnused((GrTypeDefinition)element, progress, null, name)) { + if (element instanceof GrTypeDefinition && PostHighlightingPass.isClassUsed((GrTypeDefinition)element, progress, usageHelper)) { unusedDeclarations.add( PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); }