EA-87767 - assert: ComponentManagerImpl.getPicoContainer
[idea/community.git] / platform / analysis-impl / src / com / intellij / codeInspection / ex / GlobalInspectionContextBase.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.intellij.codeInspection.ex;
18
19 import com.intellij.analysis.AnalysisScope;
20 import com.intellij.codeInspection.*;
21 import com.intellij.codeInspection.lang.GlobalInspectionContextExtension;
22 import com.intellij.codeInspection.lang.InspectionExtensionsFactory;
23 import com.intellij.codeInspection.reference.*;
24 import com.intellij.openapi.application.Application;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.openapi.extensions.Extensions;
28 import com.intellij.openapi.progress.*;
29 import com.intellij.openapi.progress.util.ProgressWrapper;
30 import com.intellij.openapi.project.DumbService;
31 import com.intellij.openapi.project.IndexNotReadyException;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.util.Computable;
34 import com.intellij.openapi.util.Key;
35 import com.intellij.openapi.util.UserDataHolderBase;
36 import com.intellij.profile.Profile;
37 import com.intellij.profile.codeInspection.InspectionProfileManager;
38 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
39 import com.intellij.psi.*;
40 import com.intellij.psi.search.LocalSearchScope;
41 import com.intellij.psi.search.scope.packageSet.NamedScope;
42 import com.intellij.util.containers.ContainerUtil;
43 import com.intellij.util.containers.HashMap;
44 import gnu.trove.THashMap;
45 import gnu.trove.THashSet;
46 import gnu.trove.TObjectHashingStrategy;
47 import org.jetbrains.annotations.NonNls;
48 import org.jetbrains.annotations.NotNull;
49 import org.jetbrains.annotations.Nullable;
50
51 import java.util.*;
52
53 public class GlobalInspectionContextBase extends UserDataHolderBase implements GlobalInspectionContext {
54   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl");
55   private static final TObjectHashingStrategy<Tools> TOOLS_HASHING_STRATEGY = new TObjectHashingStrategy<Tools>() {
56     @Override
57     public int computeHashCode(Tools object) {
58       return object.getShortName().hashCode();
59     }
60
61     @Override
62     public boolean equals(Tools o1, Tools o2) {
63       return o1.getShortName().equals(o2.getShortName());
64     }
65   };
66
67   private RefManager myRefManager;
68
69   private AnalysisScope myCurrentScope;
70   @NotNull
71   private final Project myProject;
72   private final List<JobDescriptor> myJobDescriptors = new ArrayList<>();
73
74   private final StdJobDescriptors myStdJobDescriptors = new StdJobDescriptors();
75   protected ProgressIndicator myProgressIndicator = new EmptyProgressIndicator();
76
77   private InspectionProfile myExternalProfile;
78
79   protected final Map<Key, GlobalInspectionContextExtension> myExtensions = new HashMap<>();
80
81   protected final Map<String, Tools> myTools = new THashMap<>();
82
83   @NonNls public static final String LOCAL_TOOL_ATTRIBUTE = "is_local_tool";
84
85   public GlobalInspectionContextBase(@NotNull Project project) {
86     myProject = project;
87
88     for (InspectionExtensionsFactory factory : Extensions.getExtensions(InspectionExtensionsFactory.EP_NAME)) {
89       final GlobalInspectionContextExtension extension = factory.createGlobalInspectionContextExtension();
90       myExtensions.put(extension.getID(), extension);
91     }
92   }
93
94   public AnalysisScope getCurrentScope() {
95     return myCurrentScope;
96   }
97
98   @Override
99   @NotNull
100   public Project getProject() {
101     return myProject;
102   }
103
104   @Override
105   public <T> T getExtension(@NotNull final Key<T> key) {
106     //noinspection unchecked
107     return (T)myExtensions.get(key);
108   }
109
110   public InspectionProfile getCurrentProfile() {
111     if (myExternalProfile != null) return myExternalProfile;
112     String currentProfile = ((InspectionManagerBase)InspectionManager.getInstance(myProject)).getCurrentProfile();
113     final InspectionProjectProfileManager inspectionProfileManager = InspectionProjectProfileManager.getInstance(myProject);
114     Profile profile = inspectionProfileManager.getProfile(currentProfile, false);
115     if (profile == null) {
116       profile = InspectionProfileManager.getInstance().getProfile(currentProfile);
117       if (profile != null) return (InspectionProfile)profile;
118
119       final String[] availableProfileNames = inspectionProfileManager.getAvailableProfileNames();
120       if (availableProfileNames.length == 0) {
121         //can't be
122         return null;
123       }
124       profile = inspectionProfileManager.getProfile(availableProfileNames[0]);
125     }
126     return (InspectionProfile)profile;
127   }
128
129   @Override
130   public boolean isSuppressed(@NotNull RefEntity entity, @NotNull String id) {
131     return entity instanceof RefElementImpl && ((RefElementImpl)entity).isSuppressed(id);
132   }
133
134   @Override
135   public boolean shouldCheck(@NotNull RefEntity entity, @NotNull GlobalInspectionTool tool) {
136     return !(entity instanceof RefElementImpl) || isToCheckMember((RefElementImpl)entity, tool);
137   }
138
139   @Override
140   public boolean isSuppressed(@NotNull PsiElement element, @NotNull String id) {
141     final RefManagerImpl refManager = (RefManagerImpl)getRefManager();
142     if (refManager.isDeclarationsFound()) {
143       final RefElement refElement = refManager.getReference(element);
144       return refElement instanceof RefElementImpl && ((RefElementImpl)refElement).isSuppressed(id);
145     }
146     return SuppressionUtil.isSuppressed(element, id);
147   }
148
149
150   void cleanupTools() {
151     myProgressIndicator.cancel();
152     for (GlobalInspectionContextExtension extension : myExtensions.values()) {
153       extension.cleanup();
154     }
155
156     for (Tools tools : myTools.values()) {
157       for (ScopeToolState state : tools.getTools()) {
158         InspectionToolWrapper toolWrapper = state.getTool();
159         toolWrapper.cleanup(myProject);
160       }
161     }
162     myTools.clear();
163
164     EntryPointsManager entryPointsManager = EntryPointsManager.getInstance(getProject());
165     if (entryPointsManager != null) {
166       entryPointsManager.cleanup();
167     }
168
169     if (myRefManager != null) {
170       ((RefManagerImpl)myRefManager).cleanup();
171       myRefManager = null;
172       if (myCurrentScope != null){
173         myCurrentScope.invalidate();
174         myCurrentScope = null;
175       }
176     }
177     myJobDescriptors.clear();
178   }
179
180   public void setCurrentScope(@NotNull AnalysisScope currentScope) {
181     myCurrentScope = currentScope;
182   }
183
184   public void doInspections(@NotNull final AnalysisScope scope) {
185     if (!GlobalInspectionContextUtil.canRunInspections(myProject, true)) return;
186
187     cleanup();
188
189     ApplicationManager.getApplication().invokeLater(() -> {
190       myCurrentScope = scope;
191       launchInspections(scope);
192     }, myProject.getDisposed());
193   }
194
195
196   @Override
197   @NotNull
198   public RefManager getRefManager() {
199     if (myRefManager == null) {
200       myRefManager = ApplicationManager.getApplication().runReadAction(new Computable<RefManagerImpl>() {
201         @Override
202         public RefManagerImpl compute() {
203           return new RefManagerImpl(myProject, myCurrentScope, GlobalInspectionContextBase.this);
204         }
205       });
206     }
207     return myRefManager;
208   }
209
210   public boolean isToCheckMember(@NotNull RefElement owner, @NotNull InspectionProfileEntry tool) {
211     return isToCheckFile(((RefElementImpl)owner).getContainingFile(), tool) && !((RefElementImpl)owner).isSuppressed(tool.getShortName(), tool.getAlternativeID());
212   }
213
214   public boolean isToCheckFile(PsiFile file, @NotNull InspectionProfileEntry tool) {
215     final Tools tools = myTools.get(tool.getShortName());
216     if (tools != null && file != null) {
217       for (ScopeToolState state : tools.getTools()) {
218         final NamedScope namedScope = state.getScope(file.getProject());
219         if (namedScope == null || namedScope.getValue().contains(file, getCurrentProfile().getProfileManager().getScopesManager())) {
220           return state.isEnabled() && state.getTool().getTool() == tool;
221         }
222       }
223     }
224     return false;
225   }
226
227   protected void launchInspections(@NotNull final AnalysisScope scope) {
228     ApplicationManager.getApplication().assertIsDispatchThread();
229     PsiDocumentManager.getInstance(myProject).commitAllDocuments();
230
231     LOG.info("Code inspection started");
232     ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), InspectionsBundle.message("inspection.progress.title"), true,
233                                                               createOption()) {
234       @Override
235       public void run(@NotNull ProgressIndicator indicator) {
236         performInspectionsWithProgress(scope, false, false);
237       }
238
239       @Override
240       public void onSuccess() {
241         notifyInspectionsFinished(scope);
242       }
243     });
244   }
245
246   @NotNull
247   protected PerformInBackgroundOption createOption() {
248     return new PerformInBackgroundOption(){
249       @Override
250       public boolean shouldStartInBackground() {
251         return true;
252       }
253
254       @Override
255       public void processSentToBackground() {
256
257       }
258     };
259   }
260
261   protected void notifyInspectionsFinished(AnalysisScope scope) {
262   }
263
264   public void performInspectionsWithProgress(@NotNull final AnalysisScope scope, final boolean runGlobalToolsOnly, final boolean isOfflineInspections) {
265     myProgressIndicator = ProgressManager.getInstance().getProgressIndicator();
266     if (myProgressIndicator == null) {
267       throw new IllegalStateException("Inspections must be run under progress");
268     }
269     final PsiManager psiManager = PsiManager.getInstance(myProject);
270     //init manager in read action
271     RefManagerImpl refManager = (RefManagerImpl)getRefManager();
272     try {
273       psiManager.startBatchFilesProcessingMode();
274       refManager.inspectionReadActionStarted();
275       getStdJobDescriptors().BUILD_GRAPH.setTotalAmount(scope.getFileCount());
276       getStdJobDescriptors().LOCAL_ANALYSIS.setTotalAmount(scope.getFileCount());
277       getStdJobDescriptors().FIND_EXTERNAL_USAGES.setTotalAmount(0);
278       //to override current progress in order to hide useless messages/%
279       ProgressManager.getInstance().executeProcessUnderProgress(() -> runTools(scope, runGlobalToolsOnly, isOfflineInspections), ProgressWrapper.wrap(myProgressIndicator));
280     }
281     catch (ProcessCanceledException e) {
282       cleanup();
283       throw e;
284     }
285     catch (IndexNotReadyException e) {
286       cleanup();
287       DumbService.getInstance(myProject).showDumbModeNotification("Usage search is not available until indices are ready");
288       throw new ProcessCanceledException();
289     }
290     catch (Throwable e) {
291       LOG.error(e);
292     }
293     finally {
294       refManager.inspectionReadActionFinished();
295       psiManager.finishBatchFilesProcessingMode();
296     }
297   }
298
299   protected void runTools(@NotNull AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections) {
300   }
301
302
303   public void initializeTools(@NotNull List<Tools> outGlobalTools,
304                               @NotNull List<Tools> outLocalTools,
305                               @NotNull List<Tools> outGlobalSimpleTools) {
306     final List<Tools> usedTools = getUsedTools();
307     for (Tools currentTools : usedTools) {
308       final String shortName = currentTools.getShortName();
309       myTools.put(shortName, currentTools);
310       InspectionToolWrapper toolWrapper = currentTools.getTool();
311       classifyTool(outGlobalTools, outLocalTools, outGlobalSimpleTools, currentTools, toolWrapper);
312
313       for (ScopeToolState state : currentTools.getTools()) {
314         state.getTool().initialize(this);
315       }
316
317       JobDescriptor[] jobDescriptors = toolWrapper.getJobDescriptors(this);
318       for (JobDescriptor jobDescriptor : jobDescriptors) {
319         appendJobDescriptor(jobDescriptor);
320       }
321     }
322     for (GlobalInspectionContextExtension extension : myExtensions.values()) {
323       extension.performPreRunActivities(outGlobalTools, outLocalTools, this);
324     }
325   }
326
327   @NotNull
328   protected List<Tools> getUsedTools() {
329     InspectionProfileImpl profile = (InspectionProfileImpl)getCurrentProfile();
330     List<Tools> tools = profile.getAllEnabledInspectionTools(myProject);
331     Set<InspectionToolWrapper> dependentTools = new LinkedHashSet<>();
332     for (Tools tool : tools) {
333       profile.collectDependentInspections(tool.getTool(), dependentTools, getProject());
334     }
335
336     if (dependentTools.isEmpty()) {
337       return tools;
338     }
339     Set<Tools> set = new THashSet<>(tools, TOOLS_HASHING_STRATEGY);
340     set.addAll(ContainerUtil.map(dependentTools, toolWrapper -> new ToolsImpl(toolWrapper, toolWrapper.getDefaultLevel(), true, true)));
341     return new ArrayList<>(set);
342   }
343
344   private static void classifyTool(@NotNull List<Tools> outGlobalTools,
345                                    @NotNull List<Tools> outLocalTools,
346                                    @NotNull List<Tools> outGlobalSimpleTools,
347                                    @NotNull Tools currentTools,
348                                    @NotNull InspectionToolWrapper toolWrapper) {
349     if (toolWrapper instanceof LocalInspectionToolWrapper) {
350       outLocalTools.add(currentTools);
351     }
352     else if (toolWrapper instanceof GlobalInspectionToolWrapper) {
353       if (toolWrapper.getTool() instanceof GlobalSimpleInspectionTool) {
354         outGlobalSimpleTools.add(currentTools);
355       }
356       else if (toolWrapper.getTool() instanceof GlobalInspectionTool) {
357         outGlobalTools.add(currentTools);
358       }
359       else {
360         throw new RuntimeException("unknown global tool " + toolWrapper);
361       }
362     }
363     else {
364       throw new RuntimeException("unknown tool " + toolWrapper);
365     }
366   }
367
368   @NotNull
369   public Map<String, Tools> getTools() {
370     return myTools;
371   }
372
373   private void appendJobDescriptor(@NotNull JobDescriptor job) {
374     if (!myJobDescriptors.contains(job)) {
375       myJobDescriptors.add(job);
376       job.setDoneAmount(0);
377     }
378   }
379
380   public void codeCleanup(@NotNull AnalysisScope scope,
381                           @NotNull InspectionProfile profile,
382                           @Nullable String commandName,
383                           @Nullable Runnable postRunnable,
384                           final boolean modal) {}
385
386   public static void codeCleanup(@NotNull Project project, @NotNull AnalysisScope scope, @Nullable Runnable runnable) {
387     GlobalInspectionContextBase globalContext = (GlobalInspectionContextBase)InspectionManager.getInstance(project).createNewGlobalContext(false);
388     final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile();
389     globalContext.codeCleanup(scope, profile, null, runnable, false);
390   }
391
392   public static void cleanupElements(@NotNull final Project project, @Nullable final Runnable runnable, @NotNull PsiElement... scope) {
393     final List<SmartPsiElementPointer<PsiElement>> elements = new ArrayList<>();
394     final SmartPointerManager manager = SmartPointerManager.getInstance(project);
395     for (PsiElement element : scope) {
396       elements.add(manager.createSmartPsiElementPointer(element));
397     }
398
399     cleanupElements(project, runnable, elements);
400   }
401
402   public static void cleanupElements(@NotNull final Project project,
403                                      @Nullable final Runnable runnable,
404                                      final List<SmartPsiElementPointer<PsiElement>> elements) {
405     Runnable cleanupRunnable = () -> {
406       final List<PsiElement> psiElements = new ArrayList<>();
407       for (SmartPsiElementPointer<PsiElement> element : elements) {
408         PsiElement psiElement = element.getElement();
409         if (psiElement != null && psiElement.isPhysical()) {
410           psiElements.add(psiElement);
411         }
412       }
413       if (psiElements.isEmpty()) {
414         return;
415       }
416       GlobalInspectionContextBase globalContext = (GlobalInspectionContextBase)InspectionManager.getInstance(project).createNewGlobalContext(false);
417       final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile();
418       AnalysisScope analysisScope = new AnalysisScope(new LocalSearchScope(psiElements.toArray(new PsiElement[psiElements.size()])), project);
419       globalContext.codeCleanup(analysisScope, profile, null, runnable, true);
420     };
421
422     Application application = ApplicationManager.getApplication();
423     if (application.isWriteAccessAllowed() && !application.isUnitTestMode()) {
424       application.invokeLater(cleanupRunnable);
425     }
426     else {
427       cleanupRunnable.run();
428     }
429   }
430
431   public void close(boolean noSuspisiousCodeFound) {
432     cleanup();
433   }
434
435   @Override
436   public void cleanup() {
437     cleanupTools();
438   }
439
440   @Override
441   public void incrementJobDoneAmount(@NotNull JobDescriptor job, @NotNull String message) {
442     if (myProgressIndicator == null) return;
443
444     ProgressManager.checkCanceled();
445
446     int old = job.getDoneAmount();
447     job.setDoneAmount(old + 1);
448
449     float totalProgress = getTotalProgress();
450
451     myProgressIndicator.setFraction(totalProgress);
452     myProgressIndicator.setText(job.getDisplayName() + " " + message);
453   }
454
455   private float getTotalProgress() {
456     int totalDone = 0;
457     int totalTotal = 0;
458     for (JobDescriptor jobDescriptor : myJobDescriptors) {
459       totalDone += jobDescriptor.getDoneAmount();
460       totalTotal += jobDescriptor.getTotalAmount();
461     }
462     return totalTotal == 0 ? 1 : 1.0f * totalDone / totalTotal;
463   }
464
465   public void setExternalProfile(InspectionProfile profile) {
466     myExternalProfile = profile;
467   }
468
469   @Override
470   @NotNull
471   public StdJobDescriptors getStdJobDescriptors() {
472     return myStdJobDescriptors;
473   }
474 }