/* * Copyright 2000-2013 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.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.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 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 tools, @NotNull final PsiFile file, @NotNull final InspectionManager iManager, final boolean isOnTheFly, boolean failFastOnAcquireReadAction, @NotNull final ProgressIndicator indicator) { final Map> problemDescriptors = inspectEx(tools, 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 tools, @NotNull final PsiFile file, @NotNull final InspectionManager iManager, final boolean isOnTheFly, boolean failFastOnAcquireReadAction, @NotNull final ProgressIndicator indicator) { if (tools.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); boolean result = JobLauncher.getInstance().invokeConcurrentlyUnderProgress( tools, indicator, failFastOnAcquireReadAction, new Processor() { @Override public boolean process(final LocalInspectionTool tool) { ProblemsHolder holder = new ProblemsHolder(iManager, file, isOnTheFly); createVisitorAndAcceptElements(tool, holder, isOnTheFly, session, elements, null); 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(); if (element != null) { return !SuppressionUtil.inspectionResultSuppressed(element, tool); } return true; } })); } return true; } }); 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) { LocalInspectionTool localTool = ((LocalInspectionToolWrapper)toolWrapper).getTool(); return inspect(Collections.singletonList(localTool), 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); } } } }