2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.codeInsight
.daemon
.impl
;
18 import com
.intellij
.codeHighlighting
.Pass
;
19 import com
.intellij
.codeHighlighting
.TextEditorHighlightingPass
;
20 import com
.intellij
.codeInsight
.daemon
.DaemonCodeAnalyzer
;
21 import com
.intellij
.codeInsight
.daemon
.HighlightDisplayKey
;
22 import com
.intellij
.codeInsight
.daemon
.ImplicitUsageProvider
;
23 import com
.intellij
.codeInsight
.daemon
.JavaErrorMessages
;
24 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightLevelUtil
;
25 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightMessageUtil
;
26 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightMethodUtil
;
27 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightUtil
;
28 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.*;
29 import com
.intellij
.codeInsight
.intention
.EmptyIntentionAction
;
30 import com
.intellij
.codeInspection
.InspectionProfile
;
31 import com
.intellij
.codeInspection
.InspectionsBundle
;
32 import com
.intellij
.codeInspection
.deadCode
.DeadCodeInspection
;
33 import com
.intellij
.codeInspection
.ex
.InspectionManagerEx
;
34 import com
.intellij
.codeInspection
.ex
.LocalInspectionToolWrapper
;
35 import com
.intellij
.codeInspection
.unusedImport
.UnusedImportLocalInspection
;
36 import com
.intellij
.codeInspection
.unusedSymbol
.UnusedSymbolLocalInspection
;
37 import com
.intellij
.codeInspection
.util
.SpecialAnnotationsUtil
;
38 import com
.intellij
.lang
.Language
;
39 import com
.intellij
.lang
.annotation
.HighlightSeverity
;
40 import com
.intellij
.openapi
.application
.ApplicationManager
;
41 import com
.intellij
.openapi
.command
.CommandProcessor
;
42 import com
.intellij
.openapi
.diagnostic
.Logger
;
43 import com
.intellij
.openapi
.editor
.Document
;
44 import com
.intellij
.openapi
.editor
.Editor
;
45 import com
.intellij
.openapi
.extensions
.Extensions
;
46 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
47 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
48 import com
.intellij
.openapi
.progress
.ProgressManager
;
49 import com
.intellij
.openapi
.project
.Project
;
50 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
51 import com
.intellij
.openapi
.roots
.ProjectFileIndex
;
52 import com
.intellij
.openapi
.vfs
.VirtualFile
;
53 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
54 import com
.intellij
.psi
.*;
55 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
56 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
57 import com
.intellij
.psi
.impl
.PsiClassImplUtil
;
58 import com
.intellij
.psi
.impl
.PsiManagerEx
;
59 import com
.intellij
.psi
.impl
.cache
.CacheManager
;
60 import com
.intellij
.psi
.impl
.source
.jsp
.jspJava
.JspxImportStatement
;
61 import com
.intellij
.psi
.jsp
.JspFile
;
62 import com
.intellij
.psi
.jsp
.JspSpiUtil
;
63 import com
.intellij
.psi
.search
.GlobalSearchScope
;
64 import com
.intellij
.psi
.search
.SearchScope
;
65 import com
.intellij
.psi
.search
.UsageSearchContext
;
66 import com
.intellij
.psi
.search
.searches
.MethodReferencesSearch
;
67 import com
.intellij
.psi
.search
.searches
.OverridingMethodsSearch
;
68 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
69 import com
.intellij
.psi
.search
.searches
.SuperMethodsSearch
;
70 import com
.intellij
.psi
.util
.PropertyUtil
;
71 import com
.intellij
.psi
.util
.PsiUtilBase
;
72 import com
.intellij
.util
.Processor
;
73 import com
.intellij
.util
.Query
;
74 import gnu
.trove
.THashMap
;
75 import gnu
.trove
.THashSet
;
76 import org
.jetbrains
.annotations
.NotNull
;
77 import org
.jetbrains
.annotations
.Nullable
;
78 import org
.jetbrains
.annotations
.PropertyKey
;
79 import org
.jetbrains
.annotations
.TestOnly
;
83 public class PostHighlightingPass
extends TextEditorHighlightingPass
{
84 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.daemon.impl.PostHighlightingPass");
85 private RefCountHolder myRefCountHolder
;
86 private final PsiFile myFile
;
87 @Nullable private final Editor myEditor
;
88 private final int myStartOffset
;
89 private final int myEndOffset
;
91 private Collection
<HighlightInfo
> myHighlights
;
92 private boolean myHasRedundantImports
;
93 private final JavaCodeStyleManager myStyleManager
;
94 private int myCurentEntryIndex
;
95 private boolean myHasMissortedImports
;
96 private final ImplicitUsageProvider
[] myImplicitUsageProviders
;
97 private DeadCodeInspection myDeadCodeInspection
;
98 private UnusedSymbolLocalInspection myUnusedSymbolInspection
;
99 private HighlightDisplayKey myUnusedSymbolKey
;
100 private boolean myDeadCodeEnabled
;
101 private boolean myInLibrary
;
103 private PostHighlightingPass(@NotNull Project project
,
104 @NotNull PsiFile file
,
105 @Nullable Editor editor
,
106 @NotNull Document document
,
109 super(project
, document
, true);
112 myStartOffset
= startOffset
;
113 myEndOffset
= endOffset
;
115 myStyleManager
= JavaCodeStyleManager
.getInstance(myProject
);
116 myCurentEntryIndex
= -1;
118 myImplicitUsageProviders
= Extensions
.getExtensions(ImplicitUsageProvider
.EP_NAME
);
121 PostHighlightingPass(@NotNull Project project
, @NotNull PsiFile file
, @NotNull Editor editor
, int startOffset
, int endOffset
) {
122 this(project
, file
, editor
, editor
.getDocument(), startOffset
, endOffset
);
125 public PostHighlightingPass(@NotNull Project project
, @NotNull PsiFile file
, @NotNull Document document
, int startOffset
, int endOffset
) {
126 this(project
, file
, null, document
, startOffset
, endOffset
);
129 public void doCollectInformation(ProgressIndicator progress
) {
130 DaemonCodeAnalyzer daemonCodeAnalyzer
= DaemonCodeAnalyzer
.getInstance(myProject
);
131 final FileStatusMap fileStatusMap
= ((DaemonCodeAnalyzerImpl
)daemonCodeAnalyzer
).getFileStatusMap();
132 final List
<HighlightInfo
> highlights
= new ArrayList
<HighlightInfo
>();
133 final FileViewProvider viewProvider
= myFile
.getViewProvider();
134 final Set
<Language
> relevantLanguages
= viewProvider
.getLanguages();
135 final Set
<PsiElement
> elementSet
= new THashSet
<PsiElement
>();
136 for (Language language
: relevantLanguages
) {
137 PsiElement psiRoot
= viewProvider
.getPsi(language
);
138 if (!HighlightLevelUtil
.shouldHighlight(psiRoot
)) continue;
139 List
<PsiElement
> elements
= CollectHighlightsUtil
.getElementsInRange(psiRoot
, myStartOffset
, myEndOffset
);
140 elementSet
.addAll(elements
);
143 ProjectFileIndex fileIndex
= ProjectRootManager
.getInstance(myProject
).getFileIndex();
144 VirtualFile virtualFile
= viewProvider
.getVirtualFile();
145 myInLibrary
= fileIndex
.isInLibraryClasses(virtualFile
) || fileIndex
.isInLibrarySource(virtualFile
);
147 myRefCountHolder
= RefCountHolder
.getInstance(myFile
);
148 if (!myRefCountHolder
.retrieveUnusedReferencesInfo(new Runnable() {
150 collectHighlights(elementSet
, highlights
);
151 myHighlights
= highlights
;
152 for (HighlightInfo info
: highlights
) {
153 if (info
.getSeverity() == HighlightSeverity
.ERROR
) {
154 fileStatusMap
.setErrorFoundFlag(myDocument
, true);
160 // we must be sure GHP will restart
161 fileStatusMap
.markFileScopeDirty(getDocument(), Pass
.UPDATE_ALL
);
162 GeneralHighlightingPass
.cancelAndRestartDaemonLater(progress
, myProject
, this);
166 public void doApplyInformationToEditor() {
167 if (myHighlights
== null) return;
168 UpdateHighlightersUtil
.setHighlightersToEditor(myProject
, myDocument
, myStartOffset
, myEndOffset
, myHighlights
, Pass
.POST_UPDATE_ALL
);
170 DaemonCodeAnalyzer daemonCodeAnalyzer
= DaemonCodeAnalyzer
.getInstance(myProject
);
171 ((DaemonCodeAnalyzerImpl
)daemonCodeAnalyzer
).getFileStatusMap().markFileUpToDate(myDocument
, myFile
, getId());
173 if (timeToOptimizeImports() && myEditor
!= null) {
174 optimizeImportsOnTheFly();
178 private void optimizeImportsOnTheFly() {
179 if (myHasRedundantImports
|| myHasMissortedImports
) {
180 ApplicationManager
.getApplication().invokeLater(new Runnable() {
182 CommandProcessor
.getInstance().runUndoTransparentAction(new Runnable() {
184 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
186 OptimizeImportsFix optimizeImportsFix
= new OptimizeImportsFix();
187 if (optimizeImportsFix
.isAvailable(myProject
, myEditor
, myFile
) && myFile
.isWritable()) {
188 PsiDocumentManager
.getInstance(myProject
).commitAllDocuments();
189 optimizeImportsFix
.invoke(myProject
, myEditor
, myFile
);
201 public Collection
<HighlightInfo
> getHighlights() {
205 private void collectHighlights(Collection
<PsiElement
> elements
, final List
<HighlightInfo
> result
) throws ProcessCanceledException
{
206 ApplicationManager
.getApplication().assertReadAccessAllowed();
208 InspectionProfile profile
= InspectionProjectProfileManager
.getInstance(myProject
).getInspectionProfile();
209 myUnusedSymbolKey
= HighlightDisplayKey
.find(UnusedSymbolLocalInspection
.SHORT_NAME
);
210 boolean unusedSymbolEnabled
= profile
.isToolEnabled(myUnusedSymbolKey
, myFile
);
211 HighlightDisplayKey unusedImportKey
= HighlightDisplayKey
.find(UnusedImportLocalInspection
.SHORT_NAME
);
212 boolean unusedImportEnabled
= profile
.isToolEnabled(unusedImportKey
, myFile
);
213 LocalInspectionToolWrapper unusedSymbolTool
= (LocalInspectionToolWrapper
)profile
.getInspectionTool(UnusedSymbolLocalInspection
.SHORT_NAME
,
215 myUnusedSymbolInspection
= unusedSymbolTool
== null ?
null : (UnusedSymbolLocalInspection
)unusedSymbolTool
.getTool();
216 LOG
.assertTrue(ApplicationManager
.getApplication().isUnitTestMode() || myUnusedSymbolInspection
!= null);
218 HighlightDisplayKey deadCodeKey
= HighlightDisplayKey
.find(DeadCodeInspection
.SHORT_NAME
);
219 myDeadCodeInspection
= (DeadCodeInspection
)profile
.getInspectionTool(DeadCodeInspection
.SHORT_NAME
, myFile
);
220 myDeadCodeEnabled
= profile
.isToolEnabled(deadCodeKey
, myFile
);
221 if (unusedImportEnabled
&& JspPsiUtil
.isInJspFile(myFile
)) {
222 final JspFile jspFile
= JspPsiUtil
.getJspFile(myFile
);
223 if (jspFile
!= null) {
224 unusedImportEnabled
= !JspSpiUtil
.isIncludedOrIncludesSomething(jspFile
);
228 if (!unusedSymbolEnabled
&& !unusedImportEnabled
) {
231 for (PsiElement element
: elements
) {
232 ProgressManager
.getInstance().checkCanceled();
234 if (unusedSymbolEnabled
&& element
instanceof PsiIdentifier
) {
235 PsiIdentifier identifier
= (PsiIdentifier
)element
;
236 HighlightInfo info
= processIdentifier(identifier
);
241 else if (unusedImportEnabled
&& element
instanceof PsiImportList
) {
242 final PsiImportStatementBase
[] imports
= ((PsiImportList
)element
).getAllImportStatements();
243 for (PsiImportStatementBase statement
: imports
) {
244 ProgressManager
.getInstance().checkCanceled();
245 final HighlightInfo info
= processImport(statement
, unusedImportKey
);
255 private HighlightInfo
processIdentifier(PsiIdentifier identifier
) {
256 if (InspectionManagerEx
.inspectionResultSuppressed(identifier
, myUnusedSymbolInspection
)) return null;
257 PsiElement parent
= identifier
.getParent();
258 if (PsiUtilBase
.hasErrorElementChild(parent
)) return null;
261 if (parent
instanceof PsiLocalVariable
&& myUnusedSymbolInspection
.LOCAL_VARIABLE
) {
262 info
= processLocalVariable((PsiLocalVariable
)parent
);
264 else if (parent
instanceof PsiField
&& myUnusedSymbolInspection
.FIELD
) {
265 final PsiField psiField
= (PsiField
)parent
;
266 info
= processField(psiField
, identifier
);
268 else if (parent
instanceof PsiParameter
&& myUnusedSymbolInspection
.PARAMETER
) {
269 info
= processParameter((PsiParameter
)parent
);
271 else if (parent
instanceof PsiMethod
&& myUnusedSymbolInspection
.METHOD
) {
272 info
= processMethod((PsiMethod
)parent
);
274 else if (parent
instanceof PsiClass
&& identifier
.equals(((PsiClass
)parent
).getNameIdentifier()) && myUnusedSymbolInspection
.CLASS
) {
275 info
= processClass((PsiClass
)parent
);
285 private HighlightInfo
processLocalVariable(PsiLocalVariable variable
) {
286 PsiIdentifier identifier
= variable
.getNameIdentifier();
287 if (identifier
== null) return null;
288 if (isImplicitUsage(variable
)) return null;
289 if (!myRefCountHolder
.isReferenced(variable
)) {
290 String message
= JavaErrorMessages
.message("local.variable.is.never.used", identifier
.getText());
291 HighlightInfo highlightInfo
= createUnusedSymbolInfo(identifier
, message
);
292 QuickFixAction
.registerQuickFixAction(highlightInfo
, new RemoveUnusedVariableFix(variable
), myUnusedSymbolKey
);
293 return highlightInfo
;
296 boolean referenced
= myRefCountHolder
.isReferencedForRead(variable
);
297 if (!referenced
&& !isImplicitRead(variable
)) {
298 String message
= JavaErrorMessages
.message("local.variable.is.not.used.for.reading", identifier
.getText());
299 HighlightInfo highlightInfo
= createUnusedSymbolInfo(identifier
, message
);
300 QuickFixAction
.registerQuickFixAction(highlightInfo
, new RemoveUnusedVariableFix(variable
), myUnusedSymbolKey
);
301 return highlightInfo
;
304 if (!variable
.hasInitializer()) {
305 referenced
= myRefCountHolder
.isReferencedForWrite(variable
);
306 if (!referenced
&& !isImplicitWrite(variable
)) {
307 String message
= JavaErrorMessages
.message("local.variable.is.not.assigned", identifier
.getText());
308 final HighlightInfo unusedSymbolInfo
= createUnusedSymbolInfo(identifier
, message
);
309 QuickFixAction
.registerQuickFixAction(unusedSymbolInfo
, new EmptyIntentionAction(UnusedSymbolLocalInspection
.DISPLAY_NAME
), myUnusedSymbolKey
);
310 return unusedSymbolInfo
;
318 private boolean isImplicitUsage(final PsiModifierListOwner element
) {
319 if (UnusedSymbolLocalInspection
.isInjected(element
, myUnusedSymbolInspection
)) return true;
320 for (ImplicitUsageProvider provider
: myImplicitUsageProviders
) {
321 ProgressManager
.getInstance().checkCanceled();
322 if (provider
.isImplicitUsage(element
)) {
330 private boolean isImplicitRead(final PsiVariable element
) {
331 for(ImplicitUsageProvider provider
: myImplicitUsageProviders
) {
332 ProgressManager
.getInstance().checkCanceled();
333 if (provider
.isImplicitRead(element
)) {
340 private boolean isImplicitWrite(final PsiVariable element
) {
341 for(ImplicitUsageProvider provider
: myImplicitUsageProviders
) {
342 ProgressManager
.getInstance().checkCanceled();
343 if (provider
.isImplicitWrite(element
)) {
350 private static HighlightInfo
createUnusedSymbolInfo(PsiElement element
, String message
) {
351 return HighlightInfo
.createHighlightInfo(HighlightInfoType
.UNUSED_SYMBOL
, element
, message
);
355 private HighlightInfo
processField(final PsiField field
, final PsiIdentifier identifier
) {
356 if (isImplicitUsage(field
)) return null;
357 if (field
.hasModifierProperty(PsiModifier
.PRIVATE
)) {
358 if (!myRefCountHolder
.isReferenced(field
)) {
359 if (HighlightUtil
.isSerializationImplicitlyUsedField(field
)) {
362 String message
= JavaErrorMessages
.message("private.field.is.not.used", identifier
.getText());
364 HighlightInfo highlightInfo
= suggestionsToMakeFieldUsed(field
, identifier
, message
);
365 QuickFixAction
.registerQuickFixAction(highlightInfo
, HighlightMethodUtil
.getFixRange(field
), new CreateConstructorParameterFromFieldFix(field
), null);
366 return highlightInfo
;
369 final boolean readReferenced
= myRefCountHolder
.isReferencedForRead(field
);
370 if (!readReferenced
&& !isImplicitRead(field
)) {
371 String message
= JavaErrorMessages
.message("private.field.is.not.used.for.reading", identifier
.getText());
372 return suggestionsToMakeFieldUsed(field
, identifier
, message
);
375 if (field
.hasInitializer()) {
378 final boolean writeReferenced
= myRefCountHolder
.isReferencedForWrite(field
);
379 if (!writeReferenced
&& !isImplicitWrite(field
)) {
380 String message
= JavaErrorMessages
.message("private.field.is.not.assigned", identifier
.getText());
381 final HighlightInfo info
= createUnusedSymbolInfo(identifier
, message
);
383 QuickFixAction
.registerQuickFixAction(info
, new CreateGetterOrSetterFix(false, true, field
), myUnusedSymbolKey
);
384 QuickFixAction
.registerQuickFixAction(info
, HighlightMethodUtil
.getFixRange(field
), new CreateConstructorParameterFromFieldFix(field
), null);
385 SpecialAnnotationsUtil
.createAddToSpecialAnnotationFixes(field
, new Processor
<String
>() {
386 public boolean process(final String annoName
) {
387 QuickFixAction
.registerQuickFixAction(info
, myUnusedSymbolInspection
.createQuickFix(annoName
, "fields"));
394 else if (!myRefCountHolder
.isReferenced(field
) && weAreSureThereAreNoUsages(field
)) {
395 return formatUnusedSymbolHighlightInfo("field.is.not.used", field
, "fields");
400 private HighlightInfo
suggestionsToMakeFieldUsed(final PsiField field
, final PsiIdentifier identifier
, final String message
) {
401 HighlightInfo highlightInfo
= createUnusedSymbolInfo(identifier
, message
);
402 QuickFixAction
.registerQuickFixAction(highlightInfo
, new RemoveUnusedVariableFix(field
), myUnusedSymbolKey
);
403 QuickFixAction
.registerQuickFixAction(highlightInfo
, new CreateGetterOrSetterFix(true, false, field
), myUnusedSymbolKey
);
404 QuickFixAction
.registerQuickFixAction(highlightInfo
, new CreateGetterOrSetterFix(false, true, field
), myUnusedSymbolKey
);
405 QuickFixAction
.registerQuickFixAction(highlightInfo
, new CreateGetterOrSetterFix(true, true, field
), myUnusedSymbolKey
);
406 return highlightInfo
;
409 private static boolean isOverriddenOrOverrides(PsiMethod method
) {
410 boolean overrides
= SuperMethodsSearch
.search(method
, null, true, false).findFirst() != null;
411 return overrides
|| OverridingMethodsSearch
.search(method
).findFirst() != null;
415 private HighlightInfo
processParameter(PsiParameter parameter
) {
416 PsiElement declarationScope
= parameter
.getDeclarationScope();
417 if (declarationScope
instanceof PsiMethod
) {
418 PsiMethod method
= (PsiMethod
)declarationScope
;
419 if (PsiUtilBase
.hasErrorElementChild(method
)) return null;
420 if ((method
.isConstructor() ||
421 method
.hasModifierProperty(PsiModifier
.PRIVATE
) ||
422 method
.hasModifierProperty(PsiModifier
.STATIC
) ||
423 !method
.hasModifierProperty(PsiModifier
.ABSTRACT
) &&
424 myUnusedSymbolInspection
.REPORT_PARAMETER_FOR_PUBLIC_METHODS
&&
425 !isOverriddenOrOverrides(method
)) &&
426 !method
.hasModifierProperty(PsiModifier
.NATIVE
) &&
427 !HighlightMethodUtil
.isSerializationRelatedMethod(method
, method
.getContainingClass()) &&
428 !PsiClassImplUtil
.isMainMethod(method
)) {
429 HighlightInfo highlightInfo
= checkUnusedParameter(parameter
);
430 if (highlightInfo
!= null) {
431 QuickFixAction
.registerQuickFixAction(highlightInfo
, new RemoveUnusedParameterFix(parameter
), myUnusedSymbolKey
);
432 return highlightInfo
;
436 else if (declarationScope
instanceof PsiForeachStatement
) {
437 HighlightInfo highlightInfo
= checkUnusedParameter(parameter
);
438 if (highlightInfo
!= null) {
439 QuickFixAction
.registerQuickFixAction(highlightInfo
, new EmptyIntentionAction(UnusedSymbolLocalInspection
.DISPLAY_NAME
), myUnusedSymbolKey
);
440 return highlightInfo
;
448 private HighlightInfo
checkUnusedParameter(final PsiParameter parameter
) {
449 if (!myRefCountHolder
.isReferenced(parameter
) && !isImplicitUsage(parameter
)) {
450 PsiIdentifier identifier
= parameter
.getNameIdentifier();
451 assert identifier
!= null;
452 String message
= JavaErrorMessages
.message("parameter.is.not.used", identifier
.getText());
453 return createUnusedSymbolInfo(identifier
, message
);
459 private HighlightInfo
processMethod(final PsiMethod method
) {
460 if (myRefCountHolder
.isReferenced(method
)) return null;
461 boolean isPrivate
= method
.hasModifierProperty(PsiModifier
.PRIVATE
);
462 PsiClass containingClass
= method
.getContainingClass();
464 if (HighlightMethodUtil
.isSerializationRelatedMethod(method
, containingClass
) ||
465 isIntentionalPrivateConstructor(method
, containingClass
)) {
468 if (isImplicitUsage(method
)) {
473 //class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too
474 if (containingClass
!= null && method
.isConstructor() && containingClass
.getConstructors().length
== 1 && !isClassUnused(containingClass
)) return null;
475 if (isImplicitUsage(method
)) return null;
477 if (method
.findSuperMethods().length
!= 0 || !weAreSureThereAreNoUsages(method
)) {
481 String key
= isPrivate
482 ? method
.isConstructor() ?
"private.constructor.is.not.used" : "private.method.is.not.used"
483 : method
.isConstructor() ?
"constructor.is.not.used" : "method.is.not.used";
484 String symbolName
= HighlightMessageUtil
.getSymbolName(method
, PsiSubstitutor
.EMPTY
);
485 String message
= JavaErrorMessages
.message(key
, symbolName
);
486 PsiIdentifier identifier
= method
.getNameIdentifier();
487 final HighlightInfo highlightInfo
= createUnusedSymbolInfo(identifier
, message
);
488 QuickFixAction
.registerQuickFixAction(highlightInfo
, new SafeDeleteFix(method
), myUnusedSymbolKey
);
489 SpecialAnnotationsUtil
.createAddToSpecialAnnotationFixes(method
, new Processor
<String
>() {
490 public boolean process(final String annoName
) {
491 QuickFixAction
.registerQuickFixAction(highlightInfo
, myUnusedSymbolInspection
.createQuickFix(annoName
, "methods"));
495 return highlightInfo
;
498 private boolean weAreSureThereAreNoUsages(PsiMember member
) {
499 if (myInLibrary
) return false;
500 if (!myDeadCodeEnabled
) return false;
501 if (myDeadCodeInspection
.isEntryPoint(member
)) return false;
503 String name
= member
.getName();
504 if (name
== null) return false;
505 SearchScope useScope
= member
.getUseScope();
506 if (!(useScope
instanceof GlobalSearchScope
)) return false;
507 final int[] count
= {0};
508 GlobalSearchScope scope
= (GlobalSearchScope
)useScope
;
509 // some classes may have references from within XML outside dependent modules, e.g. our actions
510 if (member
instanceof PsiClass
) scope
= scope
.uniteWith(GlobalSearchScope
.projectScope(myProject
));
512 CacheManager cacheManager
= ((PsiManagerEx
)myFile
.getManager()).getCacheManager();
513 if (!cacheManager
.processFilesWithWord(new Processor
<PsiFile
>() {
514 public boolean process(PsiFile file
) {
515 if (file
== myFile
) return true;
517 return count
[0] <= 10;
519 }, name
, UsageSearchContext
.ANY
, scope
, true)) return false;
521 //search usages if it cheap
522 //if count is 0 there is no usages since we've called myRefCountHolder.isReferenced() before
523 if (count
[0] == 0 && !canbeReferencedViaWeirdNames(member
)) return true;
525 Query
<PsiReference
> query
= member
instanceof PsiMethod
526 ? MethodReferencesSearch
.search((PsiMethod
)member
, scope
, false)
527 : ReferencesSearch
.search(member
, scope
, true);
528 return query
.findFirst() == null;
531 private static boolean canbeReferencedViaWeirdNames(PsiMember member
) {
532 if (member
instanceof PsiClass
) return false;
533 PsiFile containingFile
= member
.getContainingFile();
534 if (!(containingFile
instanceof PsiJavaFile
)) return true; // Groovy field can be referenced from Java by getter
535 if (member
instanceof PsiField
) return false; //Java field cannot be referenced by anything but its name
536 if (member
instanceof PsiMethod
) {
537 return PropertyUtil
.isSimplePropertyAccessor((PsiMethod
)member
); //Java accessors can be referenced by field name from Groovy
543 private HighlightInfo
processClass(PsiClass aClass
) {
544 if (!isClassUnused(aClass
)) return null;
545 String element
= "classes";
546 if (aClass
.getContainingClass() != null && aClass
.hasModifierProperty(PsiModifier
.PRIVATE
)) {
547 String pattern
= aClass
.isInterface()
548 ?
"private.inner.interface.is.not.used"
549 : "private.inner.class.is.not.used";
550 return formatUnusedSymbolHighlightInfo(pattern
, aClass
, element
);
552 if (aClass
.getParent() instanceof PsiDeclarationStatement
) { // local class
553 return formatUnusedSymbolHighlightInfo("local.class.is.not.used", aClass
, element
);
555 if (aClass
instanceof PsiTypeParameter
) {
556 return formatUnusedSymbolHighlightInfo("type.parameter.is.not.used", aClass
, element
);
558 return formatUnusedSymbolHighlightInfo("class.is.not.used", aClass
, element
);
561 private final Map
<PsiClass
, Boolean
> unusedClassCache
= new THashMap
<PsiClass
, Boolean
>();
562 private boolean isClassUnused(PsiClass aClass
) {
563 if (aClass
== null) return false;
564 Boolean result
= unusedClassCache
.get(aClass
);
565 if (result
== null) {
566 result
= isReallyUnused(aClass
);
567 unusedClassCache
.put(aClass
, result
);
572 private boolean isReallyUnused(PsiClass aClass
) {
573 if (isImplicitUsage(aClass
) || myRefCountHolder
.isReferenced(aClass
)) return false;
574 return aClass
.getContainingClass() != null && aClass
.hasModifierProperty(PsiModifier
.PRIVATE
) ||
575 aClass
.getParent() instanceof PsiDeclarationStatement
||
576 aClass
instanceof PsiTypeParameter
||
577 weAreSureThereAreNoUsages(aClass
);
580 private HighlightInfo
formatUnusedSymbolHighlightInfo(@PropertyKey(resourceBundle
= JavaErrorMessages
.BUNDLE
) String pattern
,
581 PsiNameIdentifierOwner aClass
, final String element
) {
582 String symbolName
= aClass
.getName();
583 String message
= JavaErrorMessages
.message(pattern
, symbolName
);
584 PsiElement identifier
= aClass
.getNameIdentifier();
585 final HighlightInfo highlightInfo
= createUnusedSymbolInfo(identifier
, message
);
586 QuickFixAction
.registerQuickFixAction(highlightInfo
, new SafeDeleteFix(aClass
), myUnusedSymbolKey
);
587 SpecialAnnotationsUtil
.createAddToSpecialAnnotationFixes((PsiModifierListOwner
)aClass
, new Processor
<String
>() {
588 public boolean process(final String annoName
) {
589 QuickFixAction
.registerQuickFixAction(highlightInfo
, myUnusedSymbolInspection
.createQuickFix(annoName
, element
));
593 return highlightInfo
;
597 private HighlightInfo
processImport(PsiImportStatementBase importStatement
, HighlightDisplayKey unusedImportKey
) {
598 // jsp include directive hack
599 if (importStatement
instanceof JspxImportStatement
&& ((JspxImportStatement
)importStatement
).isForeignFileImport()) return null;
601 if (PsiUtilBase
.hasErrorElementChild(importStatement
)) return null;
603 boolean isRedundant
= myRefCountHolder
.isRedundant(importStatement
);
604 if (!isRedundant
&& !(importStatement
instanceof PsiImportStaticStatement
)) {
605 //check import from same package
606 String packageName
= ((PsiClassOwner
)importStatement
.getContainingFile()).getPackageName();
607 PsiJavaCodeReferenceElement reference
= importStatement
.getImportReference();
608 PsiElement resolved
= reference
== null ?
null : reference
.resolve();
609 if (resolved
instanceof PsiPackage
) {
610 isRedundant
= packageName
.equals(((PsiPackage
)resolved
).getQualifiedName());
612 else if (resolved
instanceof PsiClass
&& !importStatement
.isOnDemand()) {
613 String qName
= ((PsiClass
)resolved
).getQualifiedName();
615 String name
= ((PsiClass
)resolved
).getName();
616 isRedundant
= qName
.equals(packageName
+ '.' + name
);
622 return registerRedundantImport(importStatement
, unusedImportKey
);
625 int entryIndex
= myStyleManager
.findEntryIndex(importStatement
);
626 if (entryIndex
< myCurentEntryIndex
) {
627 myHasMissortedImports
= true;
629 myCurentEntryIndex
= entryIndex
;
634 private HighlightInfo
registerRedundantImport(PsiImportStatementBase importStatement
, HighlightDisplayKey unusedImportKey
) {
635 HighlightInfo info
= HighlightInfo
.createHighlightInfo(JavaHightlightInfoTypes
.UNUSED_IMPORT
, importStatement
, InspectionsBundle
.message("unused.import.statement"));
637 QuickFixAction
.registerQuickFixAction(info
, new OptimizeImportsFix(), unusedImportKey
);
638 QuickFixAction
.registerQuickFixAction(info
, new EnableOptimizeImportsOnTheFlyFix(), unusedImportKey
);
639 myHasRedundantImports
= true;
643 private boolean timeToOptimizeImports() {
644 if (!CodeStyleSettingsManager
.getSettings(myProject
).OPTIMIZE_IMPORTS_ON_THE_FLY
) return false;
646 DaemonCodeAnalyzerImpl codeAnalyzer
= (DaemonCodeAnalyzerImpl
)DaemonCodeAnalyzer
.getInstance(myProject
);
647 PsiFile file
= PsiDocumentManager
.getInstance(myProject
).getPsiFile(myDocument
);
648 // dont optimize out imports in JSP since it can be included in other JSP
649 if (file
== null || !codeAnalyzer
.isHighlightingAvailable(file
) || !(file
instanceof PsiJavaFile
) || file
instanceof JspFile
) return false;
651 if (!codeAnalyzer
.isErrorAnalyzingFinished(file
)) return false;
652 List
<HighlightInfo
> errors
= DaemonCodeAnalyzerImpl
.getHighlights(myDocument
, HighlightSeverity
.ERROR
, myProject
);
654 return errors
.isEmpty() && codeAnalyzer
.canChangeFileSilently(myFile
);
657 private static boolean isIntentionalPrivateConstructor(PsiMethod method
, PsiClass containingClass
) {
658 return method
.isConstructor() &&
659 method
.getParameterList().getParametersCount() == 0 &&
660 containingClass
!= null &&
661 containingClass
.getConstructors().length
== 1;