/* * 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.analysis.AnalysisScope; import com.intellij.codeInsight.daemon.impl.Divider; import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper; import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; import com.intellij.codeInspection.reference.RefElement; import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.reference.RefManagerImpl; import com.intellij.codeInspection.reference.RefVisitor; import com.intellij.concurrency.JobLauncher; import com.intellij.lang.Language; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.containers.SmartHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class InspectionEngine { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.InspectionEngine"); @NotNull public static PsiElementVisitor createVisitorAndAcceptElements(@NotNull LocalInspectionTool tool, @NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session, @NotNull List elements, @Nullable Collection languages) { PsiElementVisitor visitor = tool.buildVisitor(holder, isOnTheFly, session); //noinspection ConstantConditions if(visitor == null) { LOG.error("Tool " + tool + " must not return null from the buildVisitor() method"); } assert !(visitor instanceof PsiRecursiveElementVisitor || visitor instanceof PsiRecursiveElementWalkingVisitor) : "The visitor returned from LocalInspectionTool.buildVisitor() must not be recursive. "+tool; tool.inspectionStarted(session, isOnTheFly); acceptElements(elements, visitor, languages); return visitor; } public static void acceptElements(@NotNull List elements, @NotNull PsiElementVisitor elementVisitor, @Nullable Collection languages) { //noinspection ForLoopReplaceableByForEach for (int i = 0, elementsSize = elements.size(); i < elementsSize; i++) { PsiElement element = elements.get(i); if (languages == null || languages.contains(element.getLanguage().getID())) { element.accept(elementVisitor); } ProgressManager.checkCanceled(); } } @NotNull public static List inspect(@NotNull final List toolWrappers, @NotNull final PsiFile file, @NotNull final InspectionManager iManager, final boolean isOnTheFly, boolean failFastOnAcquireReadAction, @NotNull final ProgressIndicator indicator) { final Map> problemDescriptors = inspectEx(toolWrappers, file, iManager, isOnTheFly, failFastOnAcquireReadAction, indicator); final List result = new ArrayList(); for (List group : problemDescriptors.values()) { result.addAll(group); } return result; } // public accessibility for Upsource @NotNull public static Map> inspectEx(@NotNull final List toolWrappers, @NotNull final PsiFile file, @NotNull final InspectionManager iManager, final boolean isOnTheFly, boolean failFastOnAcquireReadAction, @NotNull final ProgressIndicator indicator) { if (toolWrappers.isEmpty()) return Collections.emptyMap(); final Map> resultDescriptors = new ConcurrentHashMap>(); final List elements = new ArrayList(); TextRange range = file.getTextRange(); final LocalInspectionToolSession session = new LocalInspectionToolSession(file, range.getStartOffset(), range.getEndOffset()); Divider.divideInsideAndOutside(file, range.getStartOffset(), range.getEndOffset(), range, elements, new ArrayList(), Collections.emptyList(), Collections.emptyList(), true, Condition.TRUE); MultiMap toolToLanguages = getToolsForElements(toolWrappers, DumbService.isDumb(file.getProject()), elements, Collections.emptyList()); List>> entries = new ArrayList>>(toolToLanguages.entrySet()); Processor>> processor = new Processor>>() { @Override public boolean process(final Map.Entry> entry) { ProblemsHolder holder = new ProblemsHolder(iManager, file, isOnTheFly); final LocalInspectionTool tool = entry.getKey().getTool(); Collection languages = entry.getValue(); createVisitorAndAcceptElements(tool, holder, isOnTheFly, session, elements, languages); tool.inspectionFinished(session, holder); if (holder.hasResults()) { resultDescriptors.put(tool.getShortName(), ContainerUtil.filter(holder.getResults(), new Condition() { @Override public boolean value(ProblemDescriptor descriptor) { PsiElement element = descriptor.getPsiElement(); return element == null || !SuppressionUtil.inspectionResultSuppressed(element, tool); } })); } return true; } }; JobLauncher.getInstance().invokeConcurrentlyUnderProgress(entries, indicator, failFastOnAcquireReadAction, processor); return resultDescriptors; } @NotNull public static List runInspectionOnFile(@NotNull final PsiFile file, @NotNull InspectionToolWrapper toolWrapper, @NotNull final GlobalInspectionContext inspectionContext) { final InspectionManager inspectionManager = InspectionManager.getInstance(file.getProject()); toolWrapper.initialize(inspectionContext); RefManagerImpl refManager = (RefManagerImpl)inspectionContext.getRefManager(); refManager.inspectionReadActionStarted(); try { if (toolWrapper instanceof LocalInspectionToolWrapper) { return inspect(Collections.singletonList((LocalInspectionToolWrapper)toolWrapper), file, inspectionManager, false, false, new EmptyProgressIndicator()); } if (toolWrapper instanceof GlobalInspectionToolWrapper) { final GlobalInspectionTool globalTool = ((GlobalInspectionToolWrapper)toolWrapper).getTool(); final List descriptors = new ArrayList(); if (globalTool instanceof GlobalSimpleInspectionTool) { GlobalSimpleInspectionTool simpleTool = (GlobalSimpleInspectionTool)globalTool; ProblemsHolder problemsHolder = new ProblemsHolder(inspectionManager, file, false); ProblemDescriptionsProcessor collectProcessor = new ProblemDescriptionsProcessor() { @Nullable @Override public CommonProblemDescriptor[] getDescriptions(@NotNull RefEntity refEntity) { return descriptors.toArray(new CommonProblemDescriptor[descriptors.size()]); } @Override public void ignoreElement(@NotNull RefEntity refEntity) { throw new RuntimeException(); } @Override public void addProblemElement(@Nullable RefEntity refEntity, @NotNull CommonProblemDescriptor... commonProblemDescriptors) { if (!(refEntity instanceof RefElement)) return; PsiElement element = ((RefElement)refEntity).getElement(); convertToProblemDescriptors(element, commonProblemDescriptors, descriptors); } @Override public RefEntity getElement(@NotNull CommonProblemDescriptor descriptor) { throw new RuntimeException(); } }; simpleTool.checkFile(file, inspectionManager, problemsHolder, inspectionContext, collectProcessor); return descriptors; } RefElement fileRef = refManager.getReference(file); final AnalysisScope scope = new AnalysisScope(file); fileRef.accept(new RefVisitor(){ @Override public void visitElement(@NotNull RefEntity elem) { CommonProblemDescriptor[] elemDescriptors = globalTool.checkElement(elem, scope, inspectionManager, inspectionContext); if (descriptors != null) { convertToProblemDescriptors(file, elemDescriptors, descriptors); } for (RefEntity child : elem.getChildren()) { child.accept(this); } } }); return descriptors; } } finally { refManager.inspectionReadActionFinished(); toolWrapper.cleanup(file.getProject()); inspectionContext.cleanup(); } return Collections.emptyList(); } private static void convertToProblemDescriptors(PsiElement element, CommonProblemDescriptor[] commonProblemDescriptors, List descriptors) { for (CommonProblemDescriptor common : commonProblemDescriptors) { if (common instanceof ProblemDescriptor) { descriptors.add((ProblemDescriptor)common); } else { ProblemDescriptorBase base = new ProblemDescriptorBase(element, element, common.getDescriptionTemplate(), (LocalQuickFix[])common.getFixes(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false, null, false, false); descriptors.add(base); } } } @NotNull public static MultiMap getToolsForElements(@NotNull List toolWrappers, boolean checkDumbAwareness, @NotNull List inside, @NotNull List outside) { Set languages = new SmartHashSet(); Map langIds = new SmartHashMap(); Set dialects = new SmartHashSet(); calculateDialects(inside, languages, langIds, dialects); calculateDialects(outside, languages, langIds, dialects); MultiMap toolToLanguages = new MultiMap() { @NotNull @Override protected Collection createCollection() { return new SmartHashSet(); } @NotNull @Override protected Collection createEmptyCollection() { return Collections.emptySet(); } }; for (T wrapper : toolWrappers) { ProgressManager.checkCanceled(); String language = wrapper.getLanguage(); if (language == null) { InspectionProfileEntry tool = wrapper.getTool(); if (!checkDumbAwareness || tool instanceof DumbAware) { toolToLanguages.put(wrapper, null); } continue; } Language lang = langIds.get(language); if (lang != null) { InspectionProfileEntry tool = wrapper.getTool(); if (!checkDumbAwareness || tool instanceof DumbAware) { toolToLanguages.putValue(wrapper, language); if (wrapper.applyToDialects()) { for (Language dialect : lang.getDialects()) { toolToLanguages.putValue(wrapper, dialect.getID()); } } } } else if (wrapper.applyToDialects() && dialects.contains(language)) { InspectionProfileEntry tool = wrapper.getTool(); if (!checkDumbAwareness || tool instanceof DumbAware) { toolToLanguages.putValue(wrapper, language); } } } return toolToLanguages; } private static void calculateDialects(@NotNull List inside, @NotNull Set languages, @NotNull Map langIds, @NotNull Set dialects) { for (PsiElement element : inside) { Language language = element.getLanguage(); if (languages.add(language)) { langIds.put(language.getID(), language); for (Language dialect : language.getDialects()) { dialects.add(dialect.getID()); } } } } }