introduce GlobalUsageHelper
authorpeter <peter@jetbrains.com>
Mon, 30 Jan 2012 19:15:09 +0000 (20:15 +0100)
committerpeter <peter@jetbrains.com>
Tue, 31 Jan 2012 17:30:24 +0000 (18:30 +0100)
java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java [new file with mode: 0644]
java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java
plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java

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 (file)
index 0000000..18a3631
--- /dev/null
@@ -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<PsiClass,Boolean> unusedClassCache = new HashMap<PsiClass, Boolean>();
+
+  public abstract boolean shouldCheckUsages(@NotNull PsiMember member);
+  public boolean isLocallyUsed(@NotNull PsiNamedElement member) {
+    return false;
+  }
+
+  public boolean shouldIgnoreUsagesInCurrentFile() {
+    return false;
+  }
+}
index 6672bd5c45bf2a9571dba0fdae7955cd7f8fc7cc..74f7f6c5d949cd57bbd9eab91db98f65e4422d20 100644 (file)
@@ -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;
-
-    return isGloballyUnused(member, progress, myFile, member.getName());
-  }
+  private static boolean weAreSureThereAreNoUsages(PsiMember member, ProgressIndicator progress, GlobalUsageHelper helper) {
+    if (!helper.shouldCheckUsages(member)) return false;
 
-  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<PsiClass> unusedClassCache = new TObjectIntHashMap<PsiClass>();
-  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,
index ea024e4cc37e70784b9b2e8efd5a1ae92aee7008..5f90199da46ccbc2dbd572b6725466555162377e 100644 (file)
@@ -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<HighlightInfo> unusedDeclarations = new ArrayList<HighlightInfo>();
     final Set<GrImportStatement> unusedImports = new HashSet<GrImportStatement>(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));
           }