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
.codeInspection
;
18 import com
.intellij
.analysis
.AnalysisScope
;
19 import com
.intellij
.codeInsight
.daemon
.GroupNames
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.RemoveSuppressWarningAction
;
21 import com
.intellij
.codeInspection
.ex
.*;
22 import com
.intellij
.codeInspection
.reference
.RefClass
;
23 import com
.intellij
.codeInspection
.reference
.RefElement
;
24 import com
.intellij
.codeInspection
.reference
.RefJavaVisitor
;
25 import com
.intellij
.codeInspection
.reference
.RefManagerImpl
;
26 import com
.intellij
.openapi
.diagnostic
.Logger
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.util
.text
.StringUtil
;
29 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
30 import com
.intellij
.psi
.*;
31 import com
.intellij
.psi
.util
.PsiTreeUtil
;
32 import com
.intellij
.util
.containers
.BidirectionalMap
;
33 import gnu
.trove
.THashMap
;
34 import gnu
.trove
.THashSet
;
35 import org
.jetbrains
.annotations
.NonNls
;
36 import org
.jetbrains
.annotations
.NotNull
;
37 import org
.jetbrains
.annotations
.Nullable
;
39 import java
.util
.ArrayList
;
40 import java
.util
.Collection
;
41 import java
.util
.List
;
47 public class RedundantSuppressInspection
extends GlobalInspectionTool
{
48 private BidirectionalMap
<String
, QuickFix
> myQuickFixes
= null;
49 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInspection.RedundantSuppressInspection");
52 public String
getGroupDisplayName() {
53 return GroupNames
.DECLARATION_REDUNDANCY
;
57 public String
getDisplayName() {
58 return InspectionsBundle
.message("inspection.redundant.suppression.name");
63 public String
getShortName() {
64 return "RedundantSuppression";
68 public void runInspection(final AnalysisScope scope
,
69 final InspectionManager manager
,
70 final GlobalInspectionContext globalContext
,
71 final ProblemDescriptionsProcessor problemDescriptionsProcessor
) {
72 globalContext
.getRefManager().iterate(new RefJavaVisitor() {
73 @Override public void visitClass(RefClass refClass
) {
74 if (!globalContext
.shouldCheck(refClass
, RedundantSuppressInspection
.this)) return;
75 CommonProblemDescriptor
[] descriptors
= checkElement(refClass
, manager
, globalContext
.getProject());
76 if (descriptors
!= null) {
77 for (CommonProblemDescriptor descriptor
: descriptors
) {
78 if (descriptor
instanceof ProblemDescriptor
) {
79 final PsiElement psiElement
= ((ProblemDescriptor
)descriptor
).getPsiElement();
80 final PsiMember member
= PsiTreeUtil
.getParentOfType(psiElement
, PsiMember
.class);
81 final RefElement refElement
= globalContext
.getRefManager().getReference(member
);
82 if (refElement
!= null) {
83 problemDescriptionsProcessor
.addProblemElement(refElement
, descriptor
);
87 problemDescriptionsProcessor
.addProblemElement(refClass
, descriptor
);
95 private CommonProblemDescriptor
[] checkElement(RefClass refEntity
, InspectionManager manager
, final Project project
) {
96 final PsiElement psiElement
= refEntity
.getElement();
97 final Map
<PsiElement
, Collection
<String
>> suppressedScopes
= new THashMap
<PsiElement
, Collection
<String
>>();
98 psiElement
.accept(new JavaRecursiveElementWalkingVisitor() {
99 @Override public void visitModifierList(PsiModifierList list
) {
100 super.visitModifierList(list
);
101 final PsiElement parent
= list
.getParent();
102 if (parent
instanceof PsiModifierListOwner
&& !(parent
instanceof PsiClass
)) {
103 checkElement(parent
);
107 @Override public void visitComment(PsiComment comment
) {
108 checkElement(comment
);
111 @Override public void visitClass(PsiClass aClass
) {
112 if (aClass
== psiElement
) {
113 super.visitClass(aClass
);
114 checkElement(aClass
);
119 private void checkElement(final PsiElement owner
) {
120 String idsString
= SuppressManager
.getInstance().getSuppressedInspectionIdsIn(owner
);
121 if (idsString
!= null && idsString
.length() != 0) {
122 List
<String
> ids
= StringUtil
.split(idsString
, ",");
123 Collection
<String
> suppressed
= suppressedScopes
.get(owner
);
124 if (suppressed
== null) {
128 for (String id
: ids
) {
129 if (!suppressed
.contains(id
)) {
134 suppressedScopes
.put(owner
, suppressed
);
139 if (suppressedScopes
.values().isEmpty()) return null;
140 // have to visit all file from scratch since inspections can be written in any perversive way including checkFile() overriding
141 final ModifiableModel model
= InspectionProjectProfileManager
.getInstance(manager
.getProject()).getInspectionProfile().getModifiableModel();
142 InspectionProfileWrapper profile
= new InspectionProfileWrapper((InspectionProfile
)model
);
143 profile
.init(manager
.getProject());
144 Collection
<InspectionTool
> suppressedTools
= new THashSet
<InspectionTool
>();
146 InspectionTool
[] tools
= profile
.getInspectionTools(psiElement
);
147 for (Collection
<String
> ids
: suppressedScopes
.values()) {
148 for (String id
: ids
) {
149 String shortName
= id
.trim();
150 for (InspectionTool tool
: tools
) {
151 if (tool
instanceof LocalInspectionToolWrapper
&& ((LocalInspectionToolWrapper
)tool
).getTool().getID().equals(shortName
) || tool
.getShortName().equals(shortName
)) {
152 suppressedTools
.add(tool
);
158 final AnalysisScope scope
= new AnalysisScope(psiElement
.getContainingFile());
159 final InspectionManagerEx inspectionManagerEx
= ((InspectionManagerEx
)InspectionManager
.getInstance(project
));
160 GlobalInspectionContextImpl globalContext
= inspectionManagerEx
.createNewGlobalContext(false);
161 globalContext
.setCurrentScope(scope
);
162 final RefManagerImpl refManager
= ((RefManagerImpl
)globalContext
.getRefManager());
163 refManager
.inspectionReadActionStarted();
164 final List
<ProblemDescriptor
> result
;
166 result
= new ArrayList
<ProblemDescriptor
>();
167 for (InspectionTool tool
: suppressedTools
) {
168 String toolId
= tool
instanceof LocalInspectionToolWrapper ?
((LocalInspectionToolWrapper
)tool
).getTool().getID() : tool
.getShortName();
169 tool
.initialize(globalContext
);
170 Collection
<CommonProblemDescriptor
> descriptors
;
171 if (tool
instanceof LocalInspectionToolWrapper
) {
172 LocalInspectionToolWrapper local
= (LocalInspectionToolWrapper
)tool
;
173 if (local
.getTool() instanceof UnfairLocalInspectionTool
) continue; //cant't work with passes other than LocalInspectionPass
174 local
.processFile(psiElement
.getContainingFile(), false, manager
);
175 descriptors
= local
.getProblemDescriptors();
177 else if (tool
instanceof GlobalInspectionToolWrapper
) {
178 GlobalInspectionToolWrapper global
= (GlobalInspectionToolWrapper
)tool
;
179 if (global
.getTool().isGraphNeeded()) {
180 refManager
.findAllDeclarations();
182 global
.processFile(scope
, manager
, globalContext
, false);
183 descriptors
= global
.getProblemDescriptors();
188 for (PsiElement suppressedScope
: suppressedScopes
.keySet()) {
189 Collection
<String
> suppressedIds
= suppressedScopes
.get(suppressedScope
);
190 if (!suppressedIds
.contains(toolId
)) continue;
191 boolean hasErrorInsideSuppressedScope
= false;
192 for (CommonProblemDescriptor descriptor
: descriptors
) {
193 if (!(descriptor
instanceof ProblemDescriptor
)) continue;
194 PsiElement element
= ((ProblemDescriptor
)descriptor
).getPsiElement();
195 if (element
== null) continue;
196 PsiElement annotation
= SuppressManager
.getInstance().getElementToolSuppressedIn(element
, toolId
);
197 if (annotation
!= null && PsiTreeUtil
.isAncestor(suppressedScope
, annotation
, false)) {
198 hasErrorInsideSuppressedScope
= true;
202 if (!hasErrorInsideSuppressedScope
) {
204 String problemLine
= null;
205 if (suppressedScope
instanceof PsiMember
) {
206 psiMember
= (PsiMember
)suppressedScope
;
208 psiMember
= PsiTreeUtil
.getParentOfType(suppressedScope
, PsiDocCommentOwner
.class);
209 final PsiStatement statement
= PsiTreeUtil
.getNextSiblingOfType(suppressedScope
, PsiStatement
.class);
210 problemLine
= statement
!= null ? statement
.getText() : null;
212 if (psiMember
!= null && psiMember
.isValid()) {
213 String description
= InspectionsBundle
.message("inspection.redundant.suppression.description");
214 if (myQuickFixes
== null) myQuickFixes
= new BidirectionalMap
<String
, QuickFix
>();
215 final String key
= toolId
+ (problemLine
!= null ?
";" + problemLine
: "");
216 QuickFix fix
= myQuickFixes
.get(key
);
218 fix
= new RemoveSuppressWarningAction(toolId
, problemLine
);
219 myQuickFixes
.put(key
, fix
);
221 PsiElement identifier
= null;
222 if (psiMember
instanceof PsiMethod
) {
223 identifier
= ((PsiMethod
)psiMember
).getNameIdentifier();
224 } else if (psiMember
instanceof PsiField
) {
225 identifier
= ((PsiField
)psiMember
).getNameIdentifier();
226 } else if (psiMember
instanceof PsiClass
) {
227 identifier
= ((PsiClass
)psiMember
).getNameIdentifier();
229 if (identifier
== null) {
230 identifier
= psiMember
;
232 result
.add(manager
.createProblemDescriptor(identifier
, description
, (LocalQuickFix
)fix
, ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
,
240 refManager
.inspectionReadActionFinished();
241 globalContext
.close(true);
243 return result
.toArray(new ProblemDescriptor
[result
.size()]);
248 public QuickFix
getQuickFix(final String hint
) {
249 return myQuickFixes
!= null ? myQuickFixes
.get(hint
) : new RemoveSuppressWarningAction(hint
);
254 public String
getHint(final QuickFix fix
) {
255 if (myQuickFixes
!= null) {
256 final List
<String
> list
= myQuickFixes
.getKeysByValue(fix
);
258 LOG
.assertTrue(list
.size() == 1);
265 public boolean isEnabledByDefault() {