moved PostHighlightingPass to analysis

This commit is contained in:
Alexey Kudravtsev
2014-02-10 12:32:36 +04:00
parent 9d46ee9c3f
commit 1cf0b70a21
12 changed files with 565 additions and 290 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
@@ -222,6 +223,26 @@ public abstract class QuickFixFactory {
public abstract void registerPullAsAbstractUpFixes(@NotNull PsiMethod method, @NotNull QuickFixActionRegistrar registrar);
public abstract IntentionAction createCreateAnnotationMethodFromUsageFix(PsiNameValuePair pair);
@NotNull
public abstract IntentionAction createCreateAnnotationMethodFromUsageFix(@NotNull PsiNameValuePair pair);
@NotNull
public abstract IntentionAction createOptimizeImportsFix();
public abstract void registerFixesForUnusedParameter(@NotNull PsiParameter parameter, @NotNull Object highlightInfo);
@NotNull
public abstract IntentionAction createAddToDependencyInjectionAnnotationsFix(@NotNull Project project, @NotNull String qualifiedName, @NotNull String element);
@NotNull
public abstract IntentionAction createCreateGetterOrSetterFix(boolean createGetter, boolean createSetter, @NotNull PsiField field);
@NotNull
public abstract IntentionAction createRenameToIgnoredFix(@NotNull PsiNamedElement namedElement);
@NotNull
public abstract IntentionAction createEnableOptimizeImportsOnTheFlyFix();
@NotNull
public abstract IntentionAction createSafeDeleteFix(@NotNull PsiElement element);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -16,32 +16,27 @@
package com.intellij.codeInsight.daemon.impl;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.ProblemHighlightFilter;
import com.intellij.codeInsight.daemon.impl.analysis.*;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.EmptyIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.codeInspection.*;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.SuppressionUtil;
import com.intellij.codeInspection.UnusedSymbolLocalInspectionBase;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.ex.EntryPointsManagerBase;
import com.intellij.codeInspection.reference.UnusedDeclarationFixProvider;
import com.intellij.codeInspection.unusedImport.UnusedImportLocalInspection;
import com.intellij.codeInspection.unusedParameters.UnusedParametersInspection;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection;
import com.intellij.codeInspection.util.SpecialAnnotationsUtilBase;
import com.intellij.diagnostic.AttachmentFactory;
import com.intellij.diagnostic.LogMessageEx;
import com.intellij.find.FindManager;
import com.intellij.find.findUsages.*;
import com.intellij.find.impl.FindManagerImpl;
import com.intellij.lang.Language;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
@@ -51,8 +46,7 @@ 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.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.PomNamedTarget;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
@@ -60,34 +54,35 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.source.PsiClassImpl;
import com.intellij.psi.impl.source.jsp.jspJava.JspxImportStatement;
import com.intellij.psi.jsp.JspFile;
import com.intellij.psi.jsp.JspSpiUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector;
import com.intellij.util.Processor;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.*;
import static com.intellij.psi.search.PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES;
import static com.intellij.psi.search.PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.PostHighlightingPass");
private static final Key<Long> LAST_POST_PASS_TIMESTAMP = Key.create("LAST_POST_PASS_TIMESTAMP");
private RefCountHolder myRefCountHolder;
private final PsiFile myFile;
@Nullable private final Editor myEditor;
private final boolean myUnusedImportEnabled;
private final int myStartOffset;
private final int myEndOffset;
@@ -98,22 +93,23 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
private boolean myHasMissortedImports;
private static final ImplicitUsageProvider[] ourImplicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME);
private UnusedDeclarationInspection myDeadCodeInspection;
private UnusedSymbolLocalInspection myUnusedSymbolInspection;
private UnusedSymbolLocalInspectionBase myUnusedSymbolInspection;
private HighlightDisplayKey myUnusedSymbolKey;
private boolean myDeadCodeEnabled;
private boolean myInLibrary;
private HighlightDisplayKey myDeadCodeKey;
private HighlightInfoType myDeadCodeInfoType;
private UnusedParametersInspection myUnusedParametersInspection;
PostHighlightingPass(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull Document document,
@NotNull HighlightInfoProcessor highlightInfoProcessor) {
public PostHighlightingPass(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull Document document,
@NotNull HighlightInfoProcessor highlightInfoProcessor,
boolean unusedImportEnabled) {
super(project, document, "Unused symbols", file, editor, file.getTextRange(), true, highlightInfoProcessor);
myFile = file;
myEditor = editor;
myUnusedImportEnabled = unusedImportEnabled;
myStartOffset = 0;
myEndOffset = file.getTextLength();
@@ -121,6 +117,21 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
myCurrentEntryIndex = -1;
}
static boolean isUpToDate(@NotNull PsiFile file) {
Long lastStamp = file.getUserData(LAST_POST_PASS_TIMESTAMP);
long currentStamp = PsiModificationTracker.SERVICE.getInstance(file.getProject()).getModificationCount();
return lastStamp != null && lastStamp == currentStamp || !ProblemHighlightFilter.shouldHighlightFile(file);
}
private static void markFileUpToDate(@NotNull PsiFile file) {
long lastStamp = PsiModificationTracker.SERVICE.getInstance(file.getProject()).getModificationCount();
file.putUserData(LAST_POST_PASS_TIMESTAMP, lastStamp);
}
private static boolean isInjected(@NotNull Project project, @NotNull PsiModifierListOwner modifierListOwner) {
return EntryPointsManagerBase.getInstance(project).isEntryPoint(modifierListOwner);
}
@Override
protected void collectInformationWithProgress(@NotNull final ProgressIndicator progress) {
DaemonCodeAnalyzerEx daemonCodeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject);
@@ -166,59 +177,23 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
protected void applyInformationWithProgress() {
if (myHighlights == null) return;
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, myStartOffset, myEndOffset, myHighlights, getColorsScheme(), Pass.POST_UPDATE_ALL);
PostHighlightingPassFactory.markFileUpToDate(myFile);
markFileUpToDate(myFile);
Editor editor = myEditor;
if (editor != null && timeToOptimizeImports()) {
if (editor != null) {
optimizeImportsOnTheFly(editor);
}
}
private void optimizeImportsOnTheFly(@NotNull final Editor editor) {
if (myHasRedundantImports || myHasMissortedImports) {
final OptimizeImportsFix optimizeImportsFix = new OptimizeImportsFix();
IntentionAction optimizeImportsFix = QuickFixFactory.getInstance().createOptimizeImportsFix();
if (optimizeImportsFix.isAvailable(myProject, editor, myFile) && myFile.isWritable()) {
invokeOnTheFlyImportOptimizer(new Runnable() {
@Override
public void run() {
optimizeImportsFix.invoke(myProject, editor, myFile);
}
}, myFile, editor);
optimizeImportsFix.invoke(myProject, editor, myFile);
}
}
}
public static void invokeOnTheFlyImportOptimizer(@NotNull final Runnable runnable,
@NotNull final PsiFile file,
@NotNull final Editor editor) {
final long stamp = editor.getDocument().getModificationStamp();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (file.getProject().isDisposed() || editor.isDisposed() || editor.getDocument().getModificationStamp() != stamp) return;
//no need to optimize imports on the fly during undo/redo
final UndoManager undoManager = UndoManager.getInstance(editor.getProject());
if (undoManager.isUndoInProgress() || undoManager.isRedoInProgress()) return;
PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
String beforeText = file.getText();
final long oldStamp = editor.getDocument().getModificationStamp();
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(runnable);
}
});
if (oldStamp != editor.getDocument().getModificationStamp()) {
String afterText = file.getText();
if (Comparing.strEqual(beforeText, afterText)) {
LOG.error(LogMessageEx.createEvent("Import optimizer hasn't optimized any imports", file.getViewProvider().getVirtualFile().getPath(),
AttachmentFactory.createAttachment(file.getViewProvider().getVirtualFile())));
}
}
}
});
}
// returns true if error highlight was created
private boolean collectHighlights(@NotNull Collection<PsiElement> elements,
@NotNull final List<HighlightInfo> result,
@@ -226,25 +201,16 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
ApplicationManager.getApplication().assertReadAccessAllowed();
InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
myUnusedSymbolKey = HighlightDisplayKey.find(UnusedSymbolLocalInspection.SHORT_NAME);
myUnusedSymbolKey = HighlightDisplayKey.find(UnusedSymbolLocalInspectionBase.SHORT_NAME);
boolean unusedSymbolEnabled = profile.isToolEnabled(myUnusedSymbolKey, myFile);
HighlightDisplayKey unusedImportKey = HighlightDisplayKey.find(UnusedImportLocalInspection.SHORT_NAME);
boolean unusedImportEnabled = profile.isToolEnabled(unusedImportKey, myFile);
myUnusedSymbolInspection = (UnusedSymbolLocalInspection)profile.getUnwrappedTool(UnusedSymbolLocalInspection.SHORT_NAME, myFile);
myUnusedSymbolInspection = (UnusedSymbolLocalInspectionBase)profile.getUnwrappedTool(UnusedSymbolLocalInspectionBase.SHORT_NAME, myFile);
LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || myUnusedSymbolInspection != null);
myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspection.SHORT_NAME);
myDeadCodeInspection = (UnusedDeclarationInspection)profile.getUnwrappedTool(UnusedDeclarationInspection.SHORT_NAME, myFile);
myDeadCodeEnabled = profile.isToolEnabled(myDeadCodeKey, myFile);
myUnusedParametersInspection = (UnusedParametersInspection)profile.getUnwrappedTool(UnusedParametersInspection.SHORT_NAME, myFile);
LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || myUnusedParametersInspection != null);
if (unusedImportEnabled && JspPsiUtil.isInJspFile(myFile)) {
final JspFile jspFile = JspPsiUtil.getJspFile(myFile);
if (jspFile != null) {
unusedImportEnabled = !JspSpiUtil.isIncludedOrIncludesSomething(jspFile);
}
}
HighlightDisplayKey unusedImportKey = HighlightDisplayKey.find(UnusedImportLocalInspection.SHORT_NAME);
myDeadCodeInfoType = myDeadCodeKey == null
? null
@@ -282,7 +248,7 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
}
}
}
if (unusedImportEnabled && myFile instanceof PsiJavaFile && HighlightingLevelManager.getInstance(myProject).shouldHighlight(myFile)) {
if (myUnusedImportEnabled && myFile instanceof PsiJavaFile && HighlightingLevelManager.getInstance(myProject).shouldHighlight(myFile)) {
PsiImportList importList = ((PsiJavaFile)myFile).getImportList();
if (importList != null) {
final PsiImportStatementBase[] imports = importList.getAllImportStatements();
@@ -301,7 +267,7 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
}
@Nullable
private HighlightInfo processIdentifier(PsiIdentifier identifier, ProgressIndicator progress, GlobalUsageHelper helper) {
private HighlightInfo processIdentifier(@NotNull PsiIdentifier identifier, @NotNull ProgressIndicator progress, @NotNull GlobalUsageHelper helper) {
if (SuppressionUtil.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) return null;
PsiElement parent = identifier.getParent();
if (PsiUtilCore.hasErrorElementChild(parent)) return null;
@@ -310,17 +276,17 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
return processLocalVariable((PsiLocalVariable)parent, identifier, progress);
}
if (parent instanceof PsiField && myUnusedSymbolInspection.FIELD) {
return processField((PsiField)parent, identifier, progress, helper);
return processField(myProject, (PsiField)parent, identifier, progress, helper);
}
if (parent instanceof PsiParameter && myUnusedSymbolInspection.PARAMETER) {
if (SuppressionUtil.isSuppressed(identifier, UnusedParametersInspection.SHORT_NAME)) return null;
return processParameter((PsiParameter)parent, identifier, progress);
if (SuppressionUtil.isSuppressed(identifier, UnusedSymbolLocalInspectionBase.UNUSED_PARAMETERS_SHORT_NAME)) return null;
return processParameter(myProject, (PsiParameter)parent, identifier, progress);
}
if (parent instanceof PsiMethod && myUnusedSymbolInspection.METHOD) {
return processMethod((PsiMethod)parent, identifier, progress, helper);
return processMethod(myProject, (PsiMethod)parent, identifier, progress, helper);
}
if (parent instanceof PsiClass && myUnusedSymbolInspection.CLASS) {
return processClass((PsiClass)parent, identifier, progress, helper);
return processClass(myProject, (PsiClass)parent, identifier, progress, helper);
}
return null;
}
@@ -330,30 +296,30 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
@NotNull PsiIdentifier identifier,
@NotNull ProgressIndicator progress) {
if (variable instanceof PsiResourceVariable && PsiUtil.isIgnoredName(variable.getName())) return null;
if (isImplicitUsage(variable, progress)) return null;
if (isImplicitUsage(myProject, variable, progress)) return null;
if (!myRefCountHolder.isReferenced(variable)) {
String message = JavaErrorMessages.message("local.variable.is.never.used", identifier.getText());
HighlightInfo highlightInfo = createUnusedSymbolInfo(identifier, message, HighlightInfoType.UNUSED_SYMBOL);
IntentionAction fix = variable instanceof PsiResourceVariable ? new RenameToIgnoredFix(variable) : new RemoveUnusedVariableFix(variable);
IntentionAction fix = variable instanceof PsiResourceVariable ? QuickFixFactory.getInstance().createRenameToIgnoredFix(variable) : QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable);
QuickFixAction.registerQuickFixAction(highlightInfo, fix, myUnusedSymbolKey);
return highlightInfo;
}
boolean referenced = myRefCountHolder.isReferencedForRead(variable);
if (!referenced && !isImplicitRead(variable, progress)) {
if (!referenced && !isImplicitRead(myProject, variable, progress)) {
String message = JavaErrorMessages.message("local.variable.is.not.used.for.reading", identifier.getText());
HighlightInfo highlightInfo = createUnusedSymbolInfo(identifier, message, HighlightInfoType.UNUSED_SYMBOL);
QuickFixAction.registerQuickFixAction(highlightInfo, new RemoveUnusedVariableFix(variable), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createRemoveUnusedVariableFix(variable), myUnusedSymbolKey);
return highlightInfo;
}
if (!variable.hasInitializer()) {
referenced = myRefCountHolder.isReferencedForWrite(variable);
if (!referenced && !isImplicitWrite(variable, progress)) {
if (!referenced && !isImplicitWrite(myProject, variable, progress)) {
String message = JavaErrorMessages.message("local.variable.is.not.assigned", identifier.getText());
final HighlightInfo unusedSymbolInfo = createUnusedSymbolInfo(identifier, message, HighlightInfoType.UNUSED_SYMBOL);
QuickFixAction.registerQuickFixAction(unusedSymbolInfo, new EmptyIntentionAction(UnusedSymbolLocalInspection.DISPLAY_NAME), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(unusedSymbolInfo, new EmptyIntentionAction(UnusedSymbolLocalInspectionBase.DISPLAY_NAME), myUnusedSymbolKey);
return unusedSymbolInfo;
}
}
@@ -361,8 +327,10 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
return null;
}
public static boolean isImplicitUsage(final PsiModifierListOwner element, ProgressIndicator progress) {
if (UnusedSymbolLocalInspection.isInjected(element)) return true;
public static boolean isImplicitUsage(@NotNull Project project,
@NotNull PsiModifierListOwner element,
@NotNull ProgressIndicator progress) {
if (isInjected(project, element)) return true;
for (ImplicitUsageProvider provider : ourImplicitUsageProviders) {
progress.checkCanceled();
if (provider.isImplicitUsage(element)) {
@@ -373,24 +341,26 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
return false;
}
private static boolean isImplicitRead(final PsiVariable element, ProgressIndicator progress) {
private static boolean isImplicitRead(@NotNull Project project, @NotNull PsiVariable element, @NotNull ProgressIndicator progress) {
for(ImplicitUsageProvider provider: ourImplicitUsageProviders) {
progress.checkCanceled();
if (provider.isImplicitRead(element)) {
return true;
}
}
return UnusedSymbolLocalInspection.isInjected(element);
return isInjected(project, element);
}
private static boolean isImplicitWrite(final PsiVariable element, ProgressIndicator progress) {
private static boolean isImplicitWrite(@NotNull Project project,
@NotNull PsiVariable element,
@NotNull ProgressIndicator progress) {
for(ImplicitUsageProvider provider: ourImplicitUsageProviders) {
progress.checkCanceled();
if (provider.isImplicitWrite(element)) {
return true;
}
}
return UnusedSymbolLocalInspection.isInjected(element);
return isInjected(project, element);
}
@Nullable
@@ -411,7 +381,8 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
}
@Nullable
private HighlightInfo processField(@NotNull final PsiField field,
private HighlightInfo processField(@NotNull final Project project,
@NotNull final PsiField field,
@NotNull PsiIdentifier identifier,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
@@ -419,18 +390,18 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
return null;
}
if (field.hasModifierProperty(PsiModifier.PRIVATE)) {
if (!myRefCountHolder.isReferenced(field) && !isImplicitUsage(field, progress)) {
if (!myRefCountHolder.isReferenced(field) && !isImplicitUsage(myProject, field, progress)) {
String message = JavaErrorMessages.message("private.field.is.not.used", identifier.getText());
HighlightInfo highlightInfo = suggestionsToMakeFieldUsed(field, identifier, message);
if (!field.hasInitializer()) {
QuickFixAction.registerQuickFixAction(highlightInfo, HighlightMethodUtil.getFixRange(field), new CreateConstructorParameterFromFieldFix(field));
QuickFixAction.registerQuickFixAction(highlightInfo, HighlightMethodUtil.getFixRange(field), QuickFixFactory.getInstance().createCreateConstructorParameterFromFieldFix(field));
}
return highlightInfo;
}
final boolean readReferenced = myRefCountHolder.isReferencedForRead(field);
if (!readReferenced && !isImplicitRead(field, progress)) {
if (!readReferenced && !isImplicitRead(project, field, progress)) {
String message = JavaErrorMessages.message("private.field.is.not.used.for.reading", identifier.getText());
return suggestionsToMakeFieldUsed(field, identifier, message);
}
@@ -439,44 +410,52 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
return null;
}
final boolean writeReferenced = myRefCountHolder.isReferencedForWrite(field);
if (!writeReferenced && !isImplicitWrite(field, progress)) {
if (!writeReferenced && !isImplicitWrite(project, field, progress)) {
String message = JavaErrorMessages.message("private.field.is.not.assigned", identifier.getText());
final HighlightInfo info = createUnusedSymbolInfo(identifier, message, HighlightInfoType.UNUSED_SYMBOL);
QuickFixAction.registerQuickFixAction(info, new CreateGetterOrSetterFix(false, true, field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(info, HighlightMethodUtil.getFixRange(field), new CreateConstructorParameterFromFieldFix(field));
QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createCreateGetterOrSetterFix(false, true, field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(info, HighlightMethodUtil.getFixRange(field), QuickFixFactory.getInstance().createCreateConstructorParameterFromFieldFix(
field));
SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes(field, new Processor<String>() {
@Override
public boolean process(final String annoName) {
QuickFixAction.registerQuickFixAction(info, UnusedSymbolLocalInspection.createQuickFix(annoName, "fields", field.getProject()));
QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance()
.createAddToDependencyInjectionAnnotationsFix(project, annoName, "fields"));
return true;
}
});
return info;
}
}
else if (isImplicitUsage(field, progress)) {
else if (isImplicitUsage(myProject, field, progress)) {
return null;
}
else if (isFieldUnused(field, progress, helper)) {
return formatUnusedSymbolHighlightInfo("field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType, identifier);
else if (isFieldUnused(myProject, myFile, field, progress, helper)) {
return formatUnusedSymbolHighlightInfo(project, "field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType, identifier
);
}
return null;
}
public static boolean isFieldUnused(PsiField field, ProgressIndicator progress, GlobalUsageHelper helper) {
if (helper.isLocallyUsed(field) || !weAreSureThereAreNoUsages(field, progress, helper)) {
public static boolean isFieldUnused(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull PsiField field,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
if (helper.isLocallyUsed(field) || !weAreSureThereAreNoUsages(project, containingFile, field, progress, helper)) {
return false;
}
return !(field instanceof PsiEnumConstant) || !isEnumValuesMethodUsed(field, progress, helper);
return !(field instanceof PsiEnumConstant) || !isEnumValuesMethodUsed(project, containingFile, field, progress, helper
);
}
private HighlightInfo suggestionsToMakeFieldUsed(final PsiField field, final PsiIdentifier identifier, final String message) {
private HighlightInfo suggestionsToMakeFieldUsed(@NotNull PsiField field, @NotNull PsiIdentifier identifier, @NotNull String message) {
HighlightInfo highlightInfo = createUnusedSymbolInfo(identifier, message, HighlightInfoType.UNUSED_SYMBOL);
QuickFixAction.registerQuickFixAction(highlightInfo, new RemoveUnusedVariableFix(field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, new CreateGetterOrSetterFix(true, false, field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, new CreateGetterOrSetterFix(false, true, field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, new CreateGetterOrSetterFix(true, true, field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createRemoveUnusedVariableFix(field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, false, field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createCreateGetterOrSetterFix(false, true, field), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createCreateGetterOrSetterFix(true, true, field), myUnusedSymbolKey);
return highlightInfo;
}
@@ -486,7 +465,8 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
}
@Nullable
private HighlightInfo processParameter(@NotNull PsiParameter parameter,
private HighlightInfo processParameter(@NotNull Project project,
@NotNull PsiParameter parameter,
@NotNull PsiIdentifier identifier,
@NotNull ProgressIndicator progress) {
PsiElement declarationScope = parameter.getDeclarationScope();
@@ -502,18 +482,10 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
!method.hasModifierProperty(PsiModifier.NATIVE) &&
!JavaHighlightUtil.isSerializationRelatedMethod(method, method.getContainingClass()) &&
!PsiClassImplUtil.isMainOrPremainMethod(method)) {
if (UnusedSymbolLocalInspection.isInjected(method)) return null;
if (isInjected(project, method)) return null;
HighlightInfo highlightInfo = checkUnusedParameter(parameter, identifier, progress);
if (highlightInfo != null) {
List<IntentionAction> options = new ArrayList<IntentionAction>();
options.addAll(IntentionManager.getInstance().getStandardIntentionOptions(myUnusedSymbolKey, myFile));
if (myUnusedParametersInspection != null) {
SuppressQuickFix[] batchSuppressActions = myUnusedParametersInspection.getBatchSuppressActions(parameter);
Collections.addAll(options, SuppressIntentionActionFromFix.convertBatchToSuppressIntentionActions(batchSuppressActions));
}
//need suppress from Unused Parameters but settings from Unused Symbol
QuickFixAction.registerQuickFixAction(highlightInfo, new RemoveUnusedParameterFix(parameter),
options, HighlightDisplayKey.getDisplayNameByKey(myUnusedSymbolKey));
QuickFixFactory.getInstance().registerFixesForUnusedParameter(parameter, highlightInfo);
return highlightInfo;
}
}
@@ -521,7 +493,7 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
else if (declarationScope instanceof PsiForeachStatement && !PsiUtil.isIgnoredName(parameter.getName())) {
HighlightInfo highlightInfo = checkUnusedParameter(parameter, identifier, progress);
if (highlightInfo != null) {
QuickFixAction.registerQuickFixAction(highlightInfo, new RenameToIgnoredFix(parameter), myUnusedSymbolKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter), myUnusedSymbolKey);
return highlightInfo;
}
}
@@ -533,7 +505,7 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
private HighlightInfo checkUnusedParameter(@NotNull PsiParameter parameter,
@NotNull PsiIdentifier identifier,
@NotNull ProgressIndicator progress) {
if (!myRefCountHolder.isReferenced(parameter) && !isImplicitUsage(parameter, progress)) {
if (!myRefCountHolder.isReferenced(parameter) && !isImplicitUsage(myProject, parameter, progress)) {
String message = JavaErrorMessages.message("parameter.is.not.used", identifier.getText());
return createUnusedSymbolInfo(identifier, message, HighlightInfoType.UNUSED_SYMBOL);
}
@@ -541,11 +513,12 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
}
@Nullable
private HighlightInfo processMethod(@NotNull final PsiMethod method,
private HighlightInfo processMethod(@NotNull final Project project,
@NotNull final PsiMethod method,
@NotNull PsiIdentifier identifier,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
if (isMethodReferenced(method, progress, helper)) return null;
if (isMethodReferenced(myProject, myFile, method, progress, helper)) return null;
HighlightInfoType highlightInfoType;
HighlightDisplayKey highlightDisplayKey;
String key;
@@ -562,26 +535,23 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
String symbolName = HighlightMessageUtil.getSymbolName(method, PsiSubstitutor.EMPTY);
String message = JavaErrorMessages.message(key, symbolName);
final HighlightInfo highlightInfo = createUnusedSymbolInfo(identifier, message, highlightInfoType);
QuickFixAction.registerQuickFixAction(highlightInfo, new SafeDeleteFix(method), highlightDisplayKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createSafeDeleteFix(method), highlightDisplayKey);
SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes(method, new Processor<String>() {
@Override
public boolean process(final String annoName) {
QuickFixAction
.registerQuickFixAction(highlightInfo, UnusedSymbolLocalInspection.createQuickFix(annoName, "methods", method.getProject()));
IntentionAction fix = QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, "methods");
QuickFixAction.registerQuickFixAction(highlightInfo, fix);
return true;
}
});
PsiClass containingClass = method.getContainingClass();
if (method.getReturnType() != null || containingClass != null && Comparing.strEqual(containingClass.getName(), method.getName())) {
//ignore methods with deleted return types as they are always marked as unused without any reason
ChangeSignatureGestureDetector.getInstance(myProject).dismissForElement(method);
}
return highlightInfo;
}
public static boolean isMethodReferenced(PsiMethod method,
ProgressIndicator progress,
GlobalUsageHelper helper) {
public static boolean isMethodReferenced(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull PsiMethod method,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
if (helper.isLocallyUsed(method)) return true;
boolean aPrivate = method.hasModifierProperty(PsiModifier.PRIVATE);
@@ -591,39 +561,42 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
if (isIntentionalPrivateConstructor(method, containingClass)) {
return true;
}
if (isImplicitUsage(method, progress)) {
if (isImplicitUsage(project, method, progress)) {
return true;
}
if (!helper.isCurrentFileAlreadyChecked()) {
return !weAreSureThereAreNoUsages(method, progress, helper);
return !weAreSureThereAreNoUsages(project, containingFile, method, progress, helper);
}
}
else {
//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
&& isClassUsed(containingClass, progress, helper)) {
&& isClassUsed(project, containingFile, containingClass, progress, helper)) {
return true;
}
if (isImplicitUsage(method, progress)) return true;
if (isImplicitUsage(project, method, progress)) return true;
if (method.findSuperMethods().length != 0) {
return true;
}
if (!weAreSureThereAreNoUsages(method, progress, helper)) {
if (!weAreSureThereAreNoUsages(project, containingFile, method, progress, helper)) {
return true;
}
}
return false;
}
private static boolean weAreSureThereAreNoUsages(@NotNull PsiMember member, ProgressIndicator progress, GlobalUsageHelper helper) {
private static boolean weAreSureThereAreNoUsages(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull PsiMember member,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
if (!helper.shouldCheckUsages(member)) return false;
String name = member.getName();
if (name == null) return false;
SearchScope useScope = member.getUseScope();
Project project = member.getProject();
if (useScope instanceof GlobalSearchScope) {
// some classes may have references from within XML outside dependent modules, e.g. our actions
if (member instanceof PsiClass) {
@@ -631,46 +604,67 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
}
PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(project);
PsiFile file = member.getContainingFile();
PsiFile ignoreFile = helper.isCurrentFileAlreadyChecked() ? file : null;
PsiFile ignoreFile = helper.isCurrentFileAlreadyChecked() ? containingFile : null;
PsiSearchHelper.SearchCostResult cheapEnough = searchHelper.isCheapEnoughToSearch(name, (GlobalSearchScope)useScope, ignoreFile, progress);
if (cheapEnough == TOO_MANY_OCCURRENCES) return false;
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 == ZERO_OCCURRENCES) {
if (!canBeReferencedViaWeirdNames(member)) return true;
if (cheapEnough == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES && !canBeReferencedViaWeirdNames(member, containingFile)) {
return true;
}
if (member instanceof PsiMethod) {
String propertyName = PropertyUtil.getPropertyName(member);
if (propertyName != null && file != null) {
SearchScope fileScope = file.getUseScope();
if (propertyName != null) {
SearchScope fileScope = containingFile.getUseScope();
if (fileScope instanceof GlobalSearchScope &&
searchHelper.isCheapEnoughToSearch(propertyName, (GlobalSearchScope)fileScope, ignoreFile, progress) == TOO_MANY_OCCURRENCES) {
searchHelper.isCheapEnoughToSearch(propertyName, (GlobalSearchScope)fileScope, ignoreFile, progress) ==
PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) {
return false;
}
}
}
}
FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager();
FindUsagesHandler handler = new JavaFindUsagesHandler(member, new JavaFindUsagesHandlerFactory(project));
FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions().clone();
findUsagesOptions.searchScope = useScope;
findUsagesOptions.isSearchForTextOccurrences = true;
return !findUsagesManager.isUsed(member, findUsagesOptions);
//FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager();
//FindUsagesHandler handler = new JavaFindUsagesHandler(member, new JavaFindUsagesHandlerFactory(project));
//FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions().clone();
//findUsagesOptions.searchScope = useScope;
//findUsagesOptions.isSearchForTextOccurrences = true;
//boolean old = !findUsagesManager.isUsed(member, findUsagesOptions);
//if (true) {
if (ReferencesSearch.search(member, useScope, true).findFirst() != null) return false;
return !(useScope instanceof GlobalSearchScope) || !foundUsageInText(member, (GlobalSearchScope)useScope);
//}
//return old;
}
private static boolean isEnumValuesMethodUsed(PsiMember member, ProgressIndicator progress, GlobalUsageHelper helper) {
private static boolean foundUsageInText(PsiMember member, GlobalSearchScope scope) {
PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(member.getProject());
return !helper.processUsagesInNonJavaFiles(member, member.getName(), new PsiNonJavaFileReferenceProcessor() {
@Override
public boolean process(final PsiFile psiFile, final int startOffset, final int endOffset) {
return false;
}
}, scope);
}
private static boolean isEnumValuesMethodUsed(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull PsiMember member,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
final PsiClass containingClass = member.getContainingClass();
if (containingClass == null || !(containingClass instanceof PsiClassImpl)) return true;
final PsiMethod valuesMethod = ((PsiClassImpl)containingClass).getValuesMethod();
return valuesMethod == null || isMethodReferenced(valuesMethod, progress, helper);
return valuesMethod == null || isMethodReferenced(project, containingFile, valuesMethod, progress, helper);
}
private static boolean canBeReferencedViaWeirdNames(PsiMember member) {
private static boolean canBeReferencedViaWeirdNames(@NotNull PsiMember member, @NotNull PsiFile containingFile) {
if (member instanceof PsiClass) return false;
PsiFile containingFile = member.getContainingFile();
if (!(containingFile instanceof PsiJavaFile)) return true; // Groovy field can be referenced from Java by getter
if (member instanceof PsiField) return false; //Java field cannot be referenced by anything but its name
if (member instanceof PsiMethod) {
@@ -680,11 +674,12 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
}
@Nullable
private HighlightInfo processClass(@NotNull PsiClass aClass,
private HighlightInfo processClass(@NotNull Project project,
@NotNull PsiClass aClass,
@NotNull PsiIdentifier identifier,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
if (isClassUsed(aClass, progress, helper)) return null;
if (isClassUsed(project, myFile, aClass, progress, helper)) return null;
String pattern;
HighlightDisplayKey highlightDisplayKey;
@@ -711,30 +706,39 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
highlightDisplayKey = myDeadCodeKey;
highlightInfoType = myDeadCodeInfoType;
}
return formatUnusedSymbolHighlightInfo(pattern, aClass, "classes", highlightDisplayKey, highlightInfoType, identifier);
return formatUnusedSymbolHighlightInfo(myProject, pattern, aClass, "classes", highlightDisplayKey, highlightInfoType, identifier
);
}
public static boolean isClassUsed(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) {
if (aClass == null) return true;
public static boolean isClassUsed(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull PsiClass aClass,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
Boolean result = helper.unusedClassCache.get(aClass);
if (result == null) {
result = isReallyUsed(aClass, progress, helper);
result = isReallyUsed(project, containingFile, aClass, progress, helper);
helper.unusedClassCache.put(aClass, result);
}
return result;
}
private static boolean isReallyUsed(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) {
if (isImplicitUsage(aClass, progress) || helper.isLocallyUsed(aClass)) return true;
private static boolean isReallyUsed(@NotNull Project project,
@NotNull PsiFile containingFile,
@NotNull PsiClass aClass,
@NotNull ProgressIndicator progress,
@NotNull GlobalUsageHelper helper) {
if (isImplicitUsage(project, aClass, progress) || helper.isLocallyUsed(aClass)) return true;
if (helper.isCurrentFileAlreadyChecked()) {
if (aClass.getContainingClass() != null && aClass.hasModifierProperty(PsiModifier.PRIVATE) ||
aClass.getParent() instanceof PsiDeclarationStatement ||
aClass instanceof PsiTypeParameter) return false;
}
return !weAreSureThereAreNoUsages(aClass, progress, helper);
return !weAreSureThereAreNoUsages(project, containingFile, aClass, progress, helper);
}
private static HighlightInfo formatUnusedSymbolHighlightInfo(@NotNull @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String pattern,
private static HighlightInfo formatUnusedSymbolHighlightInfo(@NotNull final Project project,
@NotNull @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String pattern,
@NotNull final PsiNameIdentifierOwner aClass,
@NotNull final String element,
@NotNull HighlightDisplayKey highlightDisplayKey,
@@ -743,12 +747,13 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
String symbolName = aClass.getName();
String message = JavaErrorMessages.message(pattern, symbolName);
final HighlightInfo highlightInfo = createUnusedSymbolInfo(identifier, message, highlightInfoType);
QuickFixAction.registerQuickFixAction(highlightInfo, new SafeDeleteFix(aClass), highlightDisplayKey);
QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createSafeDeleteFix(aClass), highlightDisplayKey);
SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes((PsiModifierListOwner)aClass, new Processor<String>() {
@Override
public boolean process(final String annoName) {
QuickFixAction
.registerQuickFixAction(highlightInfo, UnusedSymbolLocalInspection.createQuickFix(annoName, element, aClass.getProject()));
.registerQuickFixAction(highlightInfo,
QuickFixFactory.getInstance().createAddToDependencyInjectionAnnotationsFix(project, annoName, element));
return true;
}
});
@@ -758,7 +763,7 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
@Nullable
private HighlightInfo processImport(@NotNull PsiImportStatementBase importStatement, @NotNull HighlightDisplayKey unusedImportKey) {
// jsp include directive hack
if (importStatement instanceof JspxImportStatement && ((JspxImportStatement)importStatement).isForeignFileImport()) return null;
if (importStatement.isForeignFileImport()) return null;
if (PsiUtilCore.hasErrorElementChild(importStatement)) return null;
@@ -799,43 +804,12 @@ public class PostHighlightingPass extends ProgressableTextEditorHighlightingPass
HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.UNUSED_IMPORT).range(importStatement).descriptionAndTooltip(description)
.create();
QuickFixAction.registerQuickFixAction(info, new OptimizeImportsFix(), unusedImportKey);
QuickFixAction.registerQuickFixAction(info, new EnableOptimizeImportsOnTheFlyFix(), unusedImportKey);
QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createOptimizeImportsFix(), unusedImportKey);
QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createEnableOptimizeImportsOnTheFlyFix(), unusedImportKey);
myHasRedundantImports = true;
return info;
}
private boolean timeToOptimizeImports() {
if (!CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) return false;
DaemonCodeAnalyzerEx codeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject);
PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
// dont optimize out imports in JSP since it can be included in other JSP
if (file == null || !codeAnalyzer.isHighlightingAvailable(file) || !(file instanceof PsiJavaFile) || file instanceof ServerPageFile) return false;
if (!codeAnalyzer.isErrorAnalyzingFinished(file)) return false;
boolean errors = containsErrorsPreventingOptimize(file);
return !errors && DaemonListeners.canChangeFileSilently(myFile);
}
private boolean containsErrorsPreventingOptimize(@NotNull PsiFile file) {
// ignore unresolved imports errors
PsiImportList importList = ((PsiJavaFile)file).getImportList();
final TextRange importsRange = importList == null ? TextRange.EMPTY_RANGE : importList.getTextRange();
boolean hasErrorsExceptUnresolvedImports = !DaemonCodeAnalyzerEx
.processHighlights(myDocument, myProject, HighlightSeverity.ERROR, 0, myDocument.getTextLength(), new Processor<HighlightInfo>() {
@Override
public boolean process(HighlightInfo error) {
int infoStart = error.getActualStartOffset();
int infoEnd = error.getActualEndOffset();
return importsRange.containsRange(infoStart, infoEnd) && error.type.equals(HighlightInfoType.WRONG_REF);
}
});
return hasErrorsExceptUnresolvedImports;
}
private static boolean isIntentionalPrivateConstructor(@NotNull PsiMethod method, PsiClass containingClass) {
return method.isConstructor() &&
@@ -0,0 +1,53 @@
/*
* Copyright 2000-2014 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.codeInspection;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class UnusedSymbolLocalInspectionBase extends AbstractBaseJavaLocalInspectionTool implements CustomSuppressableInspectionTool {
@NonNls public static final String SHORT_NAME = HighlightInfoType.UNUSED_SYMBOL_SHORT_NAME;
@NonNls public static final String DISPLAY_NAME = HighlightInfoType.UNUSED_SYMBOL_DISPLAY_NAME;
@NonNls public static final String UNUSED_PARAMETERS_SHORT_NAME = "UnusedParameters";
public boolean LOCAL_VARIABLE = true;
public boolean FIELD = true;
public boolean METHOD = true;
public boolean CLASS = true;
public boolean PARAMETER = true;
public boolean REPORT_PARAMETER_FOR_PUBLIC_METHODS = true;
@Override
public boolean isSuppressedFor(@NotNull PsiElement element) {
return isSuppressedFor(element, this);
}
public static boolean isSuppressedFor(@NotNull PsiElement element, @NotNull LocalInspectionTool tool) {
return BaseJavaBatchLocalInspectionTool.isSuppressedFor(element, tool);
}
@Override
public SuppressIntentionAction[] getSuppressActions(final PsiElement element) {
String shortName = getShortName();
HighlightDisplayKey key = HighlightDisplayKey.find(shortName);
if (key == null) {
throw new AssertionError("HighlightDisplayKey.find(" + shortName + ") is null. Inspection: "+getClass());
}
SuppressQuickFix[] batchSuppressActions = BatchSuppressManager.SERVICE.getInstance().createBatchSuppressActions(key);
return SuppressIntentionActionFromFix.convertBatchToSuppressIntentionActions(batchSuppressActions);
}
}
@@ -19,18 +19,22 @@ import com.intellij.codeHighlighting.MainHighlightingPassFactory;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
import com.intellij.codeInsight.daemon.ProblemHighlightFilter;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightVisitorImpl;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.unusedImport.UnusedImportLocalInspection;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.JspPsiUtil;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.jsp.JspFile;
import com.intellij.psi.jsp.JspSpiUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -42,7 +46,7 @@ import java.util.Arrays;
*/
public class PostHighlightingPassFactory extends AbstractProjectComponent implements MainHighlightingPassFactory {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.PostHighlightingPassFactory");
private static final Key<Long> LAST_POST_PASS_TIMESTAMP = Key.create("LAST_POST_PASS_TIMESTAMP");
public PostHighlightingPassFactory(Project project, TextEditorHighlightingPassRegistrar highlightingPassRegistrar, HighlightVisitorImpl hvi) {
super(project);
highlightingPassRegistrar.registerTextEditorHighlightingPass(this, new int[]{Pass.UPDATE_ALL,}, null, true, Pass.POST_UPDATE_ALL);
@@ -65,26 +69,34 @@ public class PostHighlightingPassFactory extends AbstractProjectComponent implem
@Nullable
public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
if (textRange == null) {
Long lastStamp = file.getUserData(LAST_POST_PASS_TIMESTAMP);
long currentStamp = PsiModificationTracker.SERVICE.getInstance(myProject).getModificationCount();
if (lastStamp != null && lastStamp == currentStamp || !ProblemHighlightFilter.shouldHighlightFile(file)) {
return null;
}
}
if (textRange == null && PostHighlightingPass.isUpToDate(file)) return null;
return new PostHighlightingPass(myProject, file, editor, editor.getDocument(), new DefaultHighlightInfoProcessor());
return create(file, editor.getDocument(), editor, new DefaultHighlightInfoProcessor());
}
@Override
public TextEditorHighlightingPass createMainHighlightingPass(@NotNull PsiFile file,
@NotNull Document document,
@NotNull HighlightInfoProcessor highlightInfoProcessor) {
return new PostHighlightingPass(myProject, file, null, document, highlightInfoProcessor);
return create(file, document, null, highlightInfoProcessor);
}
public static void markFileUpToDate(@NotNull PsiFile file) {
long lastStamp = PsiModificationTracker.SERVICE.getInstance(file.getProject()).getModificationCount();
file.putUserData(LAST_POST_PASS_TIMESTAMP, lastStamp);
private PostHighlightingPass create(@NotNull PsiFile file,
@NotNull Document document, Editor editor,
@NotNull HighlightInfoProcessor highlightInfoProcessor) {
HighlightDisplayKey unusedImportKey = HighlightDisplayKey.find(UnusedImportLocalInspection.SHORT_NAME);
return new PostHighlightingPass(myProject, file, editor, document, highlightInfoProcessor, isUnusedImportEnabled(unusedImportKey, file));
}
private static boolean isUnusedImportEnabled(HighlightDisplayKey unusedImportKey, @NotNull PsiFile file) {
InspectionProfile profile = InspectionProjectProfileManager.getInstance(file.getProject()).getInspectionProfile();
boolean unusedImportEnabled = profile.isToolEnabled(unusedImportKey, file);
if (unusedImportEnabled && JspPsiUtil.isInJspFile(file)) {
final JspFile jspFile = JspPsiUtil.getJspFile(file);
if (jspFile != null) {
unusedImportEnabled = !JspSpiUtil.isIncludedOrIncludesSomething(jspFile);
}
}
return unusedImportEnabled;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -15,30 +15,60 @@
*/
package com.intellij.codeInsight.intention.impl.config;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.codeInsight.daemon.impl.DaemonListeners;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.analysis.IncreaseLanguageLevelFix;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplacePrimitiveWithBoxedTypeAction;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix;
import com.intellij.codeInsight.daemon.quickFix.CreateFieldOrPropertyFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.EntryPointsManagerBase;
import com.intellij.codeInspection.unusedParameters.UnusedParametersInspection;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection;
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
import com.intellij.diagnostic.AttachmentFactory;
import com.intellij.diagnostic.LogMessageEx;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PropertyMemberType;
import com.intellij.psi.util.ClassKind;
import com.intellij.psi.util.PropertyMemberType;
import com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author cdr
*/
public class QuickFixFactoryImpl extends QuickFixFactory {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.config.QuickFixFactoryImpl");
@NotNull
@Override
public LocalQuickFixAndIntentionActionOnPsiElement createModifierListFix(@NotNull PsiModifierList modifierList,
@@ -561,9 +591,177 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
PullAsAbstractUpFix.registerQuickFix(method, registrar);
}
@NotNull
@Override
public IntentionAction createCreateAnnotationMethodFromUsageFix(PsiNameValuePair pair) {
public IntentionAction createCreateAnnotationMethodFromUsageFix(@NotNull PsiNameValuePair pair) {
return new CreateAnnotationMethodFromUsageFix(pair);
}
@NotNull
@Override
public IntentionAction createOptimizeImportsFix() {
final OptimizeImportsFix fix = new OptimizeImportsFix();
return new IntentionAction() {
@NotNull
@Override
public String getText() {
return fix.getText();
}
@NotNull
@Override
public String getFamilyName() {
return fix.getFamilyName();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return timeToOptimizeImports(file) && fix.isAvailable(project, editor, file);
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
invokeOnTheFlyImportOptimizer(new Runnable() {
@Override
public void run() {
fix.invoke(project, editor, file);
}
}, file, editor);
}
@Override
public boolean startInWriteAction() {
return fix.startInWriteAction();
}
};
}
@Override
public void registerFixesForUnusedParameter(@NotNull PsiParameter parameter, @NotNull Object highlightInfo) {
Project myProject = parameter.getProject();
InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
UnusedParametersInspection unusedParametersInspection =
(UnusedParametersInspection)profile.getUnwrappedTool(UnusedSymbolLocalInspectionBase.UNUSED_PARAMETERS_SHORT_NAME, parameter);
LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || unusedParametersInspection != null);
List<IntentionAction> options = new ArrayList<IntentionAction>();
HighlightDisplayKey myUnusedSymbolKey = HighlightDisplayKey.find(UnusedSymbolLocalInspection.SHORT_NAME);
options.addAll(IntentionManager.getInstance().getStandardIntentionOptions(myUnusedSymbolKey, parameter));
if (unusedParametersInspection != null) {
SuppressQuickFix[] batchSuppressActions = unusedParametersInspection.getBatchSuppressActions(parameter);
Collections.addAll(options, SuppressIntentionActionFromFix.convertBatchToSuppressIntentionActions(batchSuppressActions));
}
//need suppress from Unused Parameters but settings from Unused Symbol
QuickFixAction.registerQuickFixAction((HighlightInfo)highlightInfo, new RemoveUnusedParameterFix(parameter),
options, HighlightDisplayKey.getDisplayNameByKey(myUnusedSymbolKey));
}
@NotNull
@Override
public IntentionAction createAddToDependencyInjectionAnnotationsFix(@NotNull Project project,
@NotNull String qualifiedName,
@NotNull String element) {
final EntryPointsManagerBase entryPointsManager = EntryPointsManagerBase.getInstance(project);
return SpecialAnnotationsUtil.createAddToSpecialAnnotationsListIntentionAction(
QuickFixBundle.message("fix.unused.symbol.injection.text", element, qualifiedName),
QuickFixBundle.message("fix.unused.symbol.injection.family"),
entryPointsManager.ADDITIONAL_ANNOTATIONS, qualifiedName);
}
@NotNull
@Override
public IntentionAction createCreateGetterOrSetterFix(boolean createGetter, boolean createSetter, @NotNull PsiField field) {
return new CreateGetterOrSetterFix(createGetter, createSetter, field);
}
@NotNull
@Override
public IntentionAction createRenameToIgnoredFix(@NotNull PsiNamedElement namedElement) {
return new RenameToIgnoredFix(namedElement);
}
@NotNull
@Override
public IntentionAction createEnableOptimizeImportsOnTheFlyFix() {
return new EnableOptimizeImportsOnTheFlyFix();
}
@NotNull
@Override
public IntentionAction createSafeDeleteFix(@NotNull PsiElement element) {
if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod)element;
PsiClass containingClass = method.getContainingClass();
if (method.getReturnType() != null || containingClass != null && Comparing.strEqual(containingClass.getName(), method.getName())) {
//ignore methods with deleted return types as they are always marked as unused without any reason
ChangeSignatureGestureDetector.getInstance(method.getProject()).dismissForElement(method);
}
}
return new SafeDeleteFix(element);
}
public static void invokeOnTheFlyImportOptimizer(@NotNull final Runnable runnable,
@NotNull final PsiFile file,
@NotNull final Editor editor) {
final long stamp = editor.getDocument().getModificationStamp();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (file.getProject().isDisposed() || editor.isDisposed() || editor.getDocument().getModificationStamp() != stamp) return;
//no need to optimize imports on the fly during undo/redo
final UndoManager undoManager = UndoManager.getInstance(editor.getProject());
if (undoManager.isUndoInProgress() || undoManager.isRedoInProgress()) return;
PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
String beforeText = file.getText();
final long oldStamp = editor.getDocument().getModificationStamp();
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(runnable);
}
});
if (oldStamp != editor.getDocument().getModificationStamp()) {
String afterText = file.getText();
if (Comparing.strEqual(beforeText, afterText)) {
LOG.error(
LogMessageEx.createEvent("Import optimizer hasn't optimized any imports", file.getViewProvider().getVirtualFile().getPath(),
AttachmentFactory.createAttachment(file.getViewProvider().getVirtualFile())));
}
}
}
});
}
private static boolean timeToOptimizeImports(@NotNull PsiFile file) {
if (!CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) return false;
DaemonCodeAnalyzerEx codeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(file.getProject());
// dont optimize out imports in JSP since it can be included in other JSP
if (file == null || !codeAnalyzer.isHighlightingAvailable(file) || !(file instanceof PsiJavaFile) || file instanceof ServerPageFile) return false;
if (!codeAnalyzer.isErrorAnalyzingFinished(file)) return false;
boolean errors = containsErrorsPreventingOptimize(file);
return !errors && DaemonListeners.canChangeFileSilently(file);
}
private static boolean containsErrorsPreventingOptimize(@NotNull PsiFile file) {
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
if (document == null) return true;
// ignore unresolved imports errors
PsiImportList importList = ((PsiJavaFile)file).getImportList();
final TextRange importsRange = importList == null ? TextRange.EMPTY_RANGE : importList.getTextRange();
boolean hasErrorsExceptUnresolvedImports = !DaemonCodeAnalyzerEx
.processHighlights(document, file.getProject(), HighlightSeverity.ERROR, 0, document.getTextLength(), new Processor<HighlightInfo>() {
@Override
public boolean process(HighlightInfo error) {
int infoStart = error.getActualStartOffset();
int infoEnd = error.getActualEndOffset();
return importsRange.containsRange(infoStart, infoEnd) && error.type.equals(HighlightInfoType.WRONG_REF);
}
});
return hasErrorsExceptUnresolvedImports;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -43,7 +43,6 @@ import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -54,8 +53,6 @@ import java.util.Collection;
import java.util.List;
public class UnusedParametersInspection extends GlobalJavaBatchInspectionTool {
@NonNls public static final String SHORT_NAME = "UnusedParameters";
@Override
@Nullable
public CommonProblemDescriptor[] checkElement(@NotNull final RefEntity refEntity,
@@ -229,7 +226,7 @@ public class UnusedParametersInspection extends GlobalJavaBatchInspectionTool {
@Override
@NotNull
public String getShortName() {
return SHORT_NAME;
return UnusedSymbolLocalInspectionBase.UNUSED_PARAMETERS_SHORT_NAME;
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -17,20 +17,14 @@
package com.intellij.codeInspection.unusedSymbol;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
import com.intellij.codeInspection.UnusedSymbolLocalInspectionBase;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.ex.EntryPointsManager;
import com.intellij.codeInspection.ex.EntryPointsManagerBase;
import com.intellij.codeInspection.ex.PairedUnfairLocalInspectionTool;
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.psi.PsiModifierListOwner;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,18 +38,7 @@ import java.awt.event.ActionListener;
* User: anna
* Date: 17-Feb-2006
*/
public class UnusedSymbolLocalInspection extends BaseJavaLocalInspectionTool implements PairedUnfairLocalInspectionTool {
@NonNls public static final String SHORT_NAME = HighlightInfoType.UNUSED_SYMBOL_SHORT_NAME;
@NonNls public static final String DISPLAY_NAME = HighlightInfoType.UNUSED_SYMBOL_DISPLAY_NAME;
public boolean LOCAL_VARIABLE = true;
public boolean FIELD = true;
public boolean METHOD = true;
public boolean CLASS = true;
public boolean PARAMETER = true;
public boolean REPORT_PARAMETER_FOR_PUBLIC_METHODS = true;
public class UnusedSymbolLocalInspection extends UnusedSymbolLocalInspectionBase implements PairedUnfairLocalInspectionTool {
@Override
@NotNull
public String getGroupDisplayName() {
@@ -154,16 +137,4 @@ public class UnusedSymbolLocalInspection extends BaseJavaLocalInspectionTool imp
public JComponent createOptionsPanel() {
return new OptionsPanel().getPanel();
}
public static IntentionAction createQuickFix(@NonNls String qualifiedName, @Nls String element, Project project) {
final EntryPointsManagerBase entryPointsManager = EntryPointsManagerBase.getInstance(project);
return SpecialAnnotationsUtil.createAddToSpecialAnnotationsListIntentionAction(
QuickFixBundle.message("fix.unused.symbol.injection.text", element, qualifiedName),
QuickFixBundle.message("fix.unused.symbol.injection.family"),
entryPointsManager.ADDITIONAL_ANNOTATIONS, qualifiedName);
}
public static boolean isInjected(final PsiModifierListOwner modifierListOwner) {
return EntryPointsManagerBase.getInstance(modifierListOwner.getProject()).isEntryPoint(modifierListOwner);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -61,4 +61,6 @@ public interface PsiImportStatementBase extends PsiElement {
*/
@Nullable
PsiElement resolve();
boolean isForeignFileImport();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -63,4 +63,9 @@ public abstract class PsiImportStatementBaseImpl extends JavaStubPsiElement<PsiI
final PsiJavaCodeReferenceElement reference = getImportReference();
return reference == null ? null : reference.resolve();
}
@Override
public boolean isForeignFileImport() {
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -22,6 +22,5 @@ import com.intellij.psi.PsiImportStatement;
* @author peter
*/
public interface JspxImportStatement extends PsiImportStatement {
boolean isForeignFileImport();
PsiFile getDeclarationFile();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2014 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.
@@ -30,16 +30,22 @@ 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.diagnostic.AttachmentFactory;
import com.intellij.diagnostic.LogMessageEx;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
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.command.undo.UndoManager;
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.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -75,6 +81,8 @@ import java.util.*;
* @author ilyas
*/
public class GroovyPostHighlightingPass extends TextEditorHighlightingPass {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.codeInspection.local.GroovyPostHighlightingPass");
private final GroovyFile myFile;
private final Editor myEditor;
private volatile Set<GrImportStatement> myUnusedImports;
@@ -133,19 +141,21 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass {
if (deadCodeEnabled &&
element instanceof GrNamedElement && element instanceof PsiModifierListOwner &&
!PostHighlightingPass.isImplicitUsage((PsiModifierListOwner)element, progress) &&
!PostHighlightingPass.isImplicitUsage(((PsiModifierListOwner)element).getProject(), (PsiModifierListOwner)element, progress) &&
!GroovySuppressableInspectionTool.isElementToolSuppressedIn(element, GroovyUnusedDeclarationInspection.SHORT_NAME)) {
PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy();
if (nameId.getNode().getElementType() == GroovyTokenTypes.mIDENT) {
String name = ((GrNamedElement)element).getName();
if (element instanceof GrTypeDefinition && !PostHighlightingPass.isClassUsed((GrTypeDefinition)element, progress, usageHelper)) {
if (element instanceof GrTypeDefinition && !PostHighlightingPass.isClassUsed(myProject,
((GrTypeDefinition)element).getContainingFile(), (GrTypeDefinition)element, progress, usageHelper
)) {
HighlightInfo highlightInfo = PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL);
QuickFixAction.registerQuickFixAction(highlightInfo, new SafeDeleteFix(element), unusedDefKey);
ContainerUtil.addIfNotNull(unusedDeclarations, highlightInfo);
}
else if (element instanceof GrMethod) {
GrMethod method = (GrMethod)element;
if (!PostHighlightingPass.isMethodReferenced(method, progress, usageHelper)) {
if (!PostHighlightingPass.isMethodReferenced(method.getProject(), method.getContainingFile(), method, progress, usageHelper)) {
String message = (method.isConstructor() ? "Constructor" : "Method") + " " + name + " is unused";
HighlightInfo highlightInfo = PostHighlightingPass.createUnusedSymbolInfo(nameId, message, HighlightInfoType.UNUSED_SYMBOL);
QuickFixAction.registerQuickFixAction(highlightInfo, new SafeDeleteFix(method), unusedDefKey);
@@ -222,7 +232,7 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass {
}
private static boolean isFieldUnused(GrField field, ProgressIndicator progress, GlobalUsageHelper usageHelper) {
if (!PostHighlightingPass.isFieldUnused(field, progress, usageHelper)) return false;
if (!PostHighlightingPass.isFieldUnused(field.getProject(), field.getContainingFile(), field, progress, usageHelper)) return false;
final GrAccessorMethod[] getters = field.getGetters();
final GrAccessorMethod setter = field.getSetter();
@@ -300,7 +310,7 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass {
final Runnable optimize = myOptimizeRunnable;
if (optimize != null && timeToOptimizeImports()) {
PostHighlightingPass.invokeOnTheFlyImportOptimizer(new Runnable() {
invokeOnTheFlyImportOptimizer(new Runnable() {
@Override
public void run() {
optimize.run();
@@ -309,6 +319,39 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass {
}
}
public static void invokeOnTheFlyImportOptimizer(@NotNull final Runnable runnable,
@NotNull final PsiFile file,
@NotNull final Editor editor) {
final long stamp = editor.getDocument().getModificationStamp();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (file.getProject().isDisposed() || editor.isDisposed() || editor.getDocument().getModificationStamp() != stamp) return;
//no need to optimize imports on the fly during undo/redo
final UndoManager undoManager = UndoManager.getInstance(editor.getProject());
if (undoManager.isUndoInProgress() || undoManager.isRedoInProgress()) return;
PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
String beforeText = file.getText();
final long oldStamp = editor.getDocument().getModificationStamp();
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(runnable);
}
});
if (oldStamp != editor.getDocument().getModificationStamp()) {
String afterText = file.getText();
if (Comparing.strEqual(beforeText, afterText)) {
LOG.error(
LogMessageEx.createEvent("Import optimizer hasn't optimized any imports", file.getViewProvider().getVirtualFile().getPath(),
AttachmentFactory.createAttachment(file.getViewProvider().getVirtualFile())));
}
}
}
});
}
private static TextRange calculateRangeToUse(GrImportStatement unusedImport) {
final TextRange range = unusedImport.getTextRange();