private void doAnalyze()

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;
  }