in lint/src/com/android/tools/idea/lint/common/LintGlobalInspectionContext.java [100:359]
private void doAnalyze(@NotNull List<Tools> globalTools,
@NotNull List<Tools> localTools,
@NotNull final GlobalInspectionContext context) {
ThreadingAssertions.assertBackgroundThread();
final Project project = context.getProject();
LintIdeSupport ideSupport = LintIdeSupport.get();
if (!ideSupport.canAnalyze(project)) {
return;
}
// If none of the active inspections are Lint checks, then do not run Lint.
// TODO: In theory, this is not ideal, as we don't discover third party Lint checks until Lint
// runs for the first time, and the user might be trying to only run third party checks (all
// built-in checks disabled). But in practice, this is very unlikely.
if (globalTools.stream().noneMatch(it -> it.getShortName().startsWith(LINT_INSPECTION_PREFIX))) {
return;
}
if (!(context instanceof GlobalInspectionContextBase)) {
LOG.error("Reached doAnalyze with a context that is not a GlobalInspectionContextBase: " + context.getClass());
return;
}
GlobalInspectionContextBase contextImpl = (GlobalInspectionContextBase)context;
// A previous version of doAnalyze was (at various points, sometimes via other functions) using
// the project's "current" inspection profile, but this is a bad idea, as this can be different
// from the global inspection context's inspection profile. See b/380216450.
InspectionProfileImpl profile = contextImpl.getCurrentProfile();
Set<Issue> enabledIssues;
Set<Issue> disabledIssues;
{
InspectionProfileIssues issuesFromProfile = LintExternalAnnotator.Companion.getIssuesFromInspectionProfile(profile);
enabledIssues = issuesFromProfile.getEnabledIssues();
disabledIssues = issuesFromProfile.getDisabledIssues();
}
if (enabledIssues.isEmpty()) {
return;
}
long startTime = System.currentTimeMillis();
final Map<Issue, Map<File, List<LintProblemData>>> problemMap = new HashMap<>();
AnalysisScope scope = contextImpl.getCurrentScope();
if (scope == null) {
scope = context.getRefManager().getScope();
}
if (scope == null) {
return;
}
if (globalTools.size() == 1 && enabledIssues.size() == 1) {
// The user is probably running a single Lint inspection by name. Setting disabledIssues to
// null indicates that we ONLY want the enabledIssues to be enabled (even if disabled by the
// Gradle config) and we don't want all third party issues to be implicitly enabled.
disabledIssues = null;
}
LintBatchResult lintResult = new LintBatchResult(project, problemMap, scope, enabledIssues, disabledIssues);
final LintIdeClient client = ideSupport.createBatchClient(lintResult);
EnumSet<Scope> lintScope;
if (!LintIdeClient.SUPPORT_CLASS_FILES) {
lintScope = EnumSet.copyOf(Scope.ALL);
// Can't run class file based checks
lintScope.remove(Scope.CLASS_FILE);
lintScope.remove(Scope.ALL_CLASS_FILES);
lintScope.remove(Scope.JAVA_LIBRARIES);
}
else {
lintScope = Scope.ALL;
}
List<VirtualFile> files = null;
final List<Module> modules = new ArrayList<>();
int scopeType = scope.getScopeType();
switch (scopeType) {
case AnalysisScope.MODULE: {
SearchScope searchScope = ReadAction.compute(scope::toSearchScope);
if (searchScope instanceof ModuleWithDependenciesScope) {
ModuleWithDependenciesScope s = (ModuleWithDependenciesScope)searchScope;
if (!s.isSearchInLibraries()) {
modules.add(s.getModule());
}
}
break;
}
case AnalysisScope.FILE:
case AnalysisScope.VIRTUAL_FILES:
case AnalysisScope.UNCOMMITTED_FILES: {
files = new ArrayList<>();
SearchScope searchScope = ReadAction.compute(scope::toSearchScope);
if (searchScope instanceof LocalSearchScope) {
final LocalSearchScope localSearchScope = (LocalSearchScope)searchScope;
final PsiElement[] elements = localSearchScope.getScope();
final List<VirtualFile> finalFiles = files;
ApplicationManager.getApplication().runReadAction(() -> {
for (PsiElement element : elements) {
if (element instanceof PsiFile) { // should be the case since scope type is FILE
Module module = ModuleUtilCore.findModuleForPsiElement(element);
if (module != null && !modules.contains(module)) {
modules.add(module);
}
VirtualFile virtualFile = ((PsiFile)element).getVirtualFile();
if (virtualFile != null) {
if (!(virtualFile instanceof LightVirtualFile)) { // such as translations editor
finalFiles.add(virtualFile);
}
}
}
}
});
}
else {
final List<VirtualFile> finalList = files;
scope.accept(new PsiElementVisitor() {
@Override
public void visitFile(PsiFile file) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
finalList.add(virtualFile);
}
}
});
}
if (files.isEmpty()) {
files = null;
}
else {
// Lint will compute it lazily based on actual files in the request
lintScope = null;
}
break;
}
case AnalysisScope.PROJECT: {
modules.addAll(Arrays.asList(ModuleManager.getInstance(project).getModules()));
break;
}
case AnalysisScope.CUSTOM:
case AnalysisScope.MODULES:
case AnalysisScope.DIRECTORY: {
// Handled by the getNarrowedComplementaryScope case below
break;
}
case AnalysisScope.INVALID:
break;
default:
LOG.warn("Unexpected inspection scope " + scope + ", " + scopeType);
}
if (modules.isEmpty()) {
for (Module module : ModuleManager.getInstance(project).getModules()) {
AnalysisScope currentScope = scope;
ReadAction.run(() -> {
if (currentScope.containsModule(module)) {
modules.add(module);
}
});
}
if (modules.isEmpty() && files != null) {
for (VirtualFile file : files) {
Module module = ModuleUtilCore.findModuleForFile(file, project);
if (module != null && !modules.contains(module)) {
modules.add(module);
}
}
}
if (modules.isEmpty()) {
AnalysisScope scopeRef = scope; // Need effectively final reference to permit capture by lambda.
AnalysisScope narrowed = ReadAction.compute(() -> scopeRef.getNarrowedComplementaryScope(project));
for (Module module : ModuleManager.getInstance(project).getModules()) {
if (narrowed.containsModule(module)) {
modules.add(module);
}
}
}
}
LintRequest request = new LintIdeRequest(client, project, files, modules, files != null && files.size() == 1);
request.setScope(lintScope);
final LintDriver lint = client.createDriver(request);
// Baseline analysis?
myBaseline = null;
Module severityModule = null;
for (Module module : modules) {
if (severityModule == null) {
if (ideSupport.getSeverityOverrides(module) != null) {
severityModule = module;
}
}
File baselineFile = ideSupport.getBaselineFile(client, module);
if (baselineFile != null && !AbstractBaselineInspection.ourSkipBaselineNextRun) {
if (!baselineFile.isAbsolute()) {
String path = module.getProject().getBasePath();
if (path != null) {
baselineFile = new File(FileUtil.toSystemDependentName(path), baselineFile.getPath());
}
}
myBaseline = new LintBaseline(client, baselineFile);
lint.setBaseline(myBaseline);
if (!baselineFile.isFile()) {
myBaseline.setWriteOnClose(true);
}
else if (AbstractBaselineInspection.ourUpdateBaselineNextRun) {
myBaseline.setRemoveFixed(true);
myBaseline.setWriteOnClose(true);
}
break;
}
}
lint.analyze();
// In analyze(), LintDriver's registry is updated to include all discovered third party issues.
// We can now access these issues. Below, we make sure they are registered.
List<Issue> thirdPartyIssues = lint.getRegistry().getIssues().stream().filter(issue -> issue.getRegistry() instanceof JarFileIssueRegistry).toList();
// Ensure all third party issues are registered in the global inspection context's profile.
AndroidLintInspectionBase.ensureInspectionsRegistered(project, thirdPartyIssues, profile);
// Some or all third party issues may still not have a corresponding tool in globalTools
// (probably just the issues that were newly registered above, but to be safe, we check
// regardless, otherwise there could be missing results). We add those that are missing,
// otherwise the inspection results won't include these issues until the next time "inspect
// code" is run with the same inspection profile.
// Note: Only add tools that come from (are registered with) the global inspection context's
// profile, otherwise there will be exceptions when viewing the results (b/380216450).
// Third party inspection names that we might need to add.
Set<String> thirdPartyInspectionsToAdd = new LinkedHashSet<>(thirdPartyIssues.size());
for (Issue issue : thirdPartyIssues) {
String inspectionShortName = LINT_INSPECTION_PREFIX + issue.getId();
thirdPartyInspectionsToAdd.add(inspectionShortName);
}
// Remove those that are already present in globalTools.
for (Tools tool : globalTools) {
thirdPartyInspectionsToAdd.remove(tool.getShortName());
}
// Add the missing tools.
for (String inspectionShortName : thirdPartyInspectionsToAdd) {
ToolsImpl tool = profile.getToolsOrNull(inspectionShortName, project);
if (tool == null) continue;
globalTools.add(tool);
}
AbstractBaselineInspection.clearNextRunState();
lint.setAnalysisStartTime(startTime);
ideSupport.logSession(lint, severityModule, lintResult);
myResults = problemMap;
}