Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / RedundantSuppressInspection.java
blobc4bc71b35a30926d8658bda2405c513ba91d1f10
1 /*
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;
42 import java.util.Map;
44 /**
45 * @author cdr
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");
51 @NotNull
52 public String getGroupDisplayName() {
53 return GroupNames.DECLARATION_REDUNDANCY;
56 @NotNull
57 public String getDisplayName() {
58 return InspectionsBundle.message("inspection.redundant.suppression.name");
61 @NotNull
62 @NonNls
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);
84 continue;
87 problemDescriptionsProcessor.addProblemElement(refClass, descriptor);
91 });
94 @Nullable
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) {
125 suppressed = ids;
127 else {
128 for (String id : ids) {
129 if (!suppressed.contains(id)) {
130 suppressed.add(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;
165 try {
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();
185 else {
186 continue;
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;
199 break;
202 if (!hasErrorInsideSuppressedScope) {
203 PsiMember psiMember;
204 String problemLine = null;
205 if (suppressedScope instanceof PsiMember) {
206 psiMember = (PsiMember)suppressedScope;
207 } else {
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);
217 if (fix == null) {
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,
233 false));
239 finally {
240 refManager.inspectionReadActionFinished();
241 globalContext.close(true);
243 return result.toArray(new ProblemDescriptor[result.size()]);
247 @Nullable
248 public QuickFix getQuickFix(final String hint) {
249 return myQuickFixes != null ? myQuickFixes.get(hint) : new RemoveSuppressWarningAction(hint);
253 @Nullable
254 public String getHint(final QuickFix fix) {
255 if (myQuickFixes != null) {
256 final List<String> list = myQuickFixes.getKeysByValue(fix);
257 if (list != null) {
258 LOG.assertTrue(list.size() == 1);
259 return list.get(0);
262 return null;
265 public boolean isEnabledByDefault() {
266 return false;