Update to latest UAST (1.3.70)

This CL updates lint to the latest version of UAST from the 1.3.70
branch in the Kotlin repository. With this we pick up some very
important bug fixes, such as the one for KT-32031; prior to this all
suspend methods were completely missing from the AST. There's now
a new passing test for this scenario.

This also moves UAST over to platform 193, which required some big
changes in the setup code. It's no longer possible to subclass the
application environment, so instead, lint now has a UastEnvironment
class modeled after KotlinEnvironment in the Kotlin compiler, which
attempts to delegate as much as possible over to KotlinEnvironment.

Finally, the new PSI environment also required the Kotlin type
resolution to happen up front before anyone attempts to call into any
of the application services; this was for example done by some
resource detectors (like DeprecationDetector) to do inheritance
lookups on classes referenced in XML files. This required splitting up
the UAST visitors into two halves: first the setup part, run prior to
any XML processing, and then the actual visitor part, which still
needs to happen after XML processing.

There are some changes to the shape of the UAST; for example, some
lambda expressions in Java went from this:
  ULambdaExpression
    UCallExpression
      UIdentifier
to this:
  ULambdaExpression
    UBlockExpression
      UReturnExpression
        UCallExpression
          UIdentifier

This required adjusting some lint checks.

Also updated the code to rewrite a number of now deprecated
constructs, such as UastContext and UAnnotated#annotations, to the
recommended replacements.

Test: Many unit tests added and updated
Fixes: 135168818
Fixes: 143972324
Fixes: 141622949
Fixes: 140328642
Fixes: 142952555
Change-Id: I0ba284f52e62015af95aff8c5e83539b5a888e81
diff --git a/bazel/src/com/android/tools/binaries/BazelLintWrapper.java b/bazel/src/com/android/tools/binaries/BazelLintWrapper.java
index 548e0a6..d7adc63 100644
--- a/bazel/src/com/android/tools/binaries/BazelLintWrapper.java
+++ b/bazel/src/com/android/tools/binaries/BazelLintWrapper.java
@@ -205,7 +205,7 @@
                     + "Do not add anything new to the baseline file; suppress new issues with "
                     + "@SuppressWarnings in Java or @Suppress in Kotlin.\n\n"
                     + ""
-                    + "If you want to regenerate the file, run `blaze test %1$s` and find the new file in %2$s. "
+                    + "If you want to regenerate the file, run `bazel test %1$s` and find the new file in %2$s. "
                     + "You can also find the regenerated file in your presubmit results for the %1$s target, "
                     + "under the Artifacts tab, in Archives/undeclared_outputs.zip";
 
diff --git a/lint/cli/src/main/java/com/android/tools/lint/KotlinLintAnalyzerFacade.kt b/lint/cli/src/main/java/com/android/tools/lint/KotlinLintAnalyzerFacade.kt
index f5fb80a..ed1458c 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/KotlinLintAnalyzerFacade.kt
+++ b/lint/cli/src/main/java/com/android/tools/lint/KotlinLintAnalyzerFacade.kt
@@ -172,6 +172,8 @@
             javaSourceRoots
         )
 
+        compilerConfiguration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
+
         // Add support for Kotlin script files (.kts) if not already there.
         if (ScriptDefinitionProvider.getInstance(project) == null) {
             compilerConfiguration.add(
@@ -253,7 +255,7 @@
             rootsIndex,
             packagePartProviders,
             SingleJavaFileRootsIndex(singleJavaFileRoots),
-            compilerConfiguration.getBoolean(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING)
+            usePsiClassFilesReading = true
         )
 
         performanceManager?.notifyCompilerInitialized()
@@ -294,12 +296,13 @@
     }
 
     private fun findJarRoot(file: File): VirtualFile? {
-        val fileSystem = LintCoreApplicationEnvironment.get().jarFileSystem
+        val fileSystem = UastEnvironment.applicationEnvironment?.jarFileSystem
+            ?: return null
         return fileSystem.findFileByPath(file.path + URLUtil.JAR_SEPARATOR)
     }
 
     private fun findLocalFile(path: String): VirtualFile? =
-        LintCoreApplicationEnvironment.get().localFileSystem.findFileByPath(path)
+        UastEnvironment.applicationEnvironment?.localFileSystem?.findFileByPath(path)
 
     private fun findLocalFile(root: JvmContentRoot): VirtualFile? {
         val file = findLocalFile(root.file.absolutePath)
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java b/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
deleted file mode 100644
index 8df9ebb..0000000
--- a/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
+++ /dev/null
@@ -1,1780 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * 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.android.tools.lint;
-
-import static com.android.SdkConstants.DOT_JAVA;
-import static com.android.SdkConstants.DOT_KT;
-import static com.android.SdkConstants.DOT_KTS;
-import static com.android.SdkConstants.DOT_SRCJAR;
-import static com.android.SdkConstants.FN_R_CLASS_JAR;
-import static com.android.SdkConstants.VALUE_NONE;
-import static com.android.manifmerger.MergingReport.MergedManifestKind.MERGED;
-import static com.android.tools.lint.LintCliFlags.ERRNO_APPLIED_SUGGESTIONS;
-import static com.android.tools.lint.LintCliFlags.ERRNO_CREATED_BASELINE;
-import static com.android.tools.lint.LintCliFlags.ERRNO_ERRORS;
-import static com.android.tools.lint.LintCliFlags.ERRNO_SUCCESS;
-import static com.android.tools.lint.client.api.LintBaseline.VARIANT_ALL;
-import static com.android.tools.lint.client.api.LintBaseline.VARIANT_FATAL;
-import static com.android.utils.CharSequences.indexOf;
-
-import com.android.Version;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.ApiVersion;
-import com.android.builder.model.JavaCompileOptions;
-import com.android.builder.model.ProductFlavor;
-import com.android.builder.model.SourceProvider;
-import com.android.builder.model.Variant;
-import com.android.ide.common.gradle.model.IdeAndroidProject;
-import com.android.ide.common.repository.GradleVersion;
-import com.android.manifmerger.ManifestMerger2;
-import com.android.manifmerger.ManifestMerger2.Invoker.Feature;
-import com.android.manifmerger.ManifestMerger2.MergeType;
-import com.android.manifmerger.MergingReport;
-import com.android.manifmerger.XmlDocument;
-import com.android.sdklib.IAndroidTarget;
-import com.android.tools.lint.checks.HardcodedValuesDetector;
-import com.android.tools.lint.checks.WrongThreadInterproceduralDetector;
-import com.android.tools.lint.client.api.Configuration;
-import com.android.tools.lint.client.api.DefaultConfiguration;
-import com.android.tools.lint.client.api.GradleVisitor;
-import com.android.tools.lint.client.api.IssueRegistry;
-import com.android.tools.lint.client.api.LintBaseline;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.client.api.LintDriver;
-import com.android.tools.lint.client.api.LintListener;
-import com.android.tools.lint.client.api.LintRequest;
-import com.android.tools.lint.client.api.UastParser;
-import com.android.tools.lint.client.api.XmlParser;
-import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.JavaContext;
-import com.android.tools.lint.detector.api.Lint;
-import com.android.tools.lint.detector.api.LintFix;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.Position;
-import com.android.tools.lint.detector.api.Project;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.TextFormat;
-import com.android.tools.lint.helpers.DefaultUastParser;
-import com.android.utils.CharSequences;
-import com.android.utils.StdLogger;
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.intellij.codeInsight.ExternalAnnotationsManager;
-import com.intellij.mock.MockProject;
-import com.intellij.openapi.Disposable;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.roots.LanguageLevelProjectExtension;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.vfs.StandardFileSystems;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.pom.java.LanguageLevel;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.PsiManager;
-import com.intellij.psi.impl.file.impl.JavaFileManager;
-import com.intellij.util.io.URLUtil;
-import com.intellij.util.lang.UrlClassLoader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.xml.parsers.ParserConfigurationException;
-import org.jetbrains.kotlin.cli.common.CommonCompilerPerformanceManager;
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCliJavaFileManagerImpl;
-import org.jetbrains.kotlin.cli.jvm.index.JavaRoot;
-import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndexImpl;
-import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex;
-import org.jetbrains.kotlin.util.PerformanceCounter;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-/**
- * Lint client for command line usage. Supports the flags in {@link LintCliFlags}, and offers text,
- * HTML and XML reporting, etc.
- *
- * <p>Minimal example:
- *
- * <pre>
- * // files is a list of java.io.Files, typically a directory containing
- * // lint projects or direct references to project root directories
- * IssueRegistry registry = new BuiltinIssueRegistry();
- * LintCliFlags flags = new LintCliFlags();
- * LintCliClient client = new LintCliClient(flags);
- * int exitCode = client.run(registry, files);
- * </pre>
- *
- * <p><b>NOTE: This is not a public or final API; if you rely on this be prepared to adjust your
- * code for the next tools release.</b>
- */
-@Beta
-public class LintCliClient extends LintClient {
-    protected final List<Warning> warnings = new ArrayList<>();
-    protected boolean hasErrors;
-    protected int errorCount;
-    protected int warningCount;
-    protected IssueRegistry registry;
-    protected LintDriver driver;
-    protected final LintCliFlags flags;
-    private Configuration configuration;
-    private boolean validatedIds;
-    private LintCliKotlinPerformanceManager kotlinPerformanceManager;
-
-    public LintCliClient(@NonNull String clientName) {
-        super(clientName);
-        flags = new LintCliFlags();
-        TextReporter reporter =
-                new TextReporter(this, flags, new PrintWriter(System.out, true), false);
-        flags.getReporters().add(reporter);
-        initialize();
-    }
-
-    /** @deprecated Specify client explicitly by calling {@link LintCliClient(String)} */
-    @Deprecated
-    public LintCliClient() {
-        this(CLIENT_UNIT_TESTS);
-    }
-
-    public LintCliClient(@NonNull LintCliFlags flags, @NonNull String clientName) {
-        super(clientName);
-        this.flags = flags;
-        initialize();
-    }
-
-    @Nullable protected DefaultConfiguration overrideConfiguration;
-
-    // Environment variable, system property and internal system property used to tell lint to
-    // override the configuration
-    private static final String LINT_OVERRIDE_CONFIGURATION_ENV_VAR = "LINT_OVERRIDE_CONFIGURATION";
-    private static final String LINT_CONFIGURATION_OVERRIDE_PROP = "lint.configuration.override";
-
-    private void initialize() {
-        String configuration = System.getenv(LINT_OVERRIDE_CONFIGURATION_ENV_VAR);
-        if (configuration == null) {
-            configuration = System.getProperty(LINT_CONFIGURATION_OVERRIDE_PROP);
-        }
-        if (configuration != null) {
-            File file = new File(configuration);
-            if (file.exists()) {
-                overrideConfiguration = createConfigurationFromFile(file);
-                System.out.println("Overriding configuration from " + file);
-            } else {
-                log(
-                        Severity.ERROR,
-                        null,
-                        "Configuration override requested but does not exist: " + file);
-            }
-        }
-    }
-
-    @NonNull
-    protected String getBaselineVariantName() {
-        if (flags.isFatalOnly()) {
-            return VARIANT_FATAL;
-        }
-
-        Collection<Project> projects = driver.getProjects();
-        for (Project project : projects) {
-            Variant variant = project.getCurrentVariant();
-            if (variant != null) {
-                return variant.getName();
-            }
-        }
-
-        return VARIANT_ALL;
-    }
-
-    /**
-     * Runs the static analysis command line driver. You need to add at least one error reporter to
-     * the command line flags.
-     */
-    public int run(@NonNull IssueRegistry registry, @NonNull List<File> files) throws IOException {
-        long startTime = System.currentTimeMillis();
-
-        assert !flags.getReporters().isEmpty();
-        this.registry = registry;
-
-        String kotlinPerfReport = System.getenv("KOTLIN_PERF_REPORT");
-        if (kotlinPerfReport != null && !kotlinPerfReport.isEmpty()) {
-            kotlinPerformanceManager = new LintCliKotlinPerformanceManager(kotlinPerfReport);
-        }
-
-        LintRequest lintRequest = createLintRequest(files);
-        driver = createDriver(registry, lintRequest);
-        driver.setAnalysisStartTime(startTime);
-
-        addProgressPrinter();
-        validateIssueIds();
-
-        driver.analyze();
-
-        if (kotlinPerformanceManager != null) {
-            kotlinPerformanceManager.report(lintRequest);
-        }
-
-        Collections.sort(warnings);
-
-        LintBaseline baseline = driver.getBaseline();
-        LintStats stats = LintStats.Companion.create(warnings, baseline);
-
-        for (Reporter reporter : flags.getReporters()) {
-            reporter.write(stats, warnings);
-        }
-
-        Collection<Project> projects = lintRequest.getProjects();
-        if (projects == null) {
-            projects = getKnownProjects();
-        }
-        if (!projects.isEmpty()) {
-            LintBatchAnalytics analytics = new LintBatchAnalytics();
-            analytics.logSession(registry, flags, driver, projects, warnings);
-        }
-
-        if (flags.isAutoFix()) {
-            boolean statistics = !flags.isQuiet();
-            LintFixPerformer performer = new LintFixPerformer(this, statistics);
-            boolean fixed = performer.fix(warnings);
-            if (fixed && LintClient.Companion.isGradle()) {
-                String message =
-                        ""
-                                + "One or more issues were fixed in the source code.\n"
-                                + "Aborting the build since the edits to the source files were "
-                                + "performed **after** compilation, so the outputs do not "
-                                + "contain the fixes. Re-run the build.";
-                System.err.println(message);
-                return ERRNO_APPLIED_SUGGESTIONS;
-            }
-        }
-
-        File baselineFile = flags.getBaselineFile();
-        if (baselineFile != null && baseline != null) {
-            emitBaselineDiagnostics(baseline, baselineFile, stats);
-        }
-
-        if (baselineFile != null && !baselineFile.exists() && flags.isWriteBaselineIfMissing()) {
-            File dir = baselineFile.getParentFile();
-            boolean ok = true;
-            if (dir != null && !dir.isDirectory()) {
-                ok = dir.mkdirs();
-            }
-            if (!ok) {
-                System.err.println("Couldn't create baseline folder " + dir);
-            } else {
-                XmlReporter reporter = Reporter.createXmlReporter(this, baselineFile, true, false);
-                reporter.setBaselineAttributes(this, getBaselineVariantName());
-                reporter.write(stats, warnings);
-                System.err.println(getBaselineCreationMessage(baselineFile));
-                return ERRNO_CREATED_BASELINE;
-            }
-        } else if (baseline != null
-                && baseline.getWriteOnClose()
-                && baseline.getFixedCount() > 0
-                && flags.isRemoveFixedBaselineIssues()) {
-            baseline.close();
-            return ERRNO_CREATED_BASELINE;
-        } else if (baseline != null && flags.isUpdateBaseline()) {
-            baseline.close();
-            return ERRNO_CREATED_BASELINE;
-        }
-
-        return flags.isSetExitCode() ? (hasErrors ? ERRNO_ERRORS : ERRNO_SUCCESS) : ERRNO_SUCCESS;
-    }
-
-    @SuppressWarnings("MethodMayBeStatic")
-    public String getBaselineCreationMessage(@NonNull File baselineFile) {
-        String message =
-                ""
-                        + "Created baseline file "
-                        + baselineFile
-                        + "\n"
-                        + "\n"
-                        + "Also breaking the build in case this was not intentional. If you\n"
-                        + "deliberately created the baseline file, re-run the build and this\n"
-                        + "time it should succeed without warnings.\n"
-                        + "\n"
-                        + "If not, investigate the baseline path in the lintOptions config\n"
-                        + "or verify that the baseline file has been checked into version\n"
-                        + "control.\n";
-
-        if (LintClient.Companion.isGradle()) {
-            message +=
-                    ""
-                            + "\n"
-                            + "You can run lint with -Dlint.baselines.continue=true\n"
-                            + "if you want to create many missing baselines in one go.";
-        }
-
-        return message;
-    }
-
-    public void emitBaselineDiagnostics(
-            @NonNull LintBaseline baseline, @NonNull File baselineFile, LintStats stats) {
-        boolean hasConsoleOutput = false;
-        for (Reporter reporter : flags.getReporters()) {
-            if (reporter instanceof TextReporter && ((TextReporter) reporter).isWriteToConsole()) {
-                hasConsoleOutput = true;
-                break;
-            }
-        }
-
-        if (!flags.isQuiet() && !hasConsoleOutput) {
-            if (stats.getBaselineErrorCount() > 0 || stats.getBaselineWarningCount() > 0) {
-                if (errorCount == 0 && warningCount == 1) {
-                    // the warning is the warning about baseline issues having been filtered
-                    // out, don't list this as "1 warning"
-                    System.out.print("Lint found no new issues");
-                } else {
-                    System.out.print(
-                            String.format(
-                                    "Lint found %1$s",
-                                    Lint.describeCounts(
-                                            errorCount,
-                                            Math.max(0, warningCount - 1),
-                                            true,
-                                            false)));
-                    if (stats.getAutoFixedCount() > 0) {
-                        System.out.print(
-                                String.format(
-                                        " (%1$s of these were automatically fixed)",
-                                        stats.getAutoFixedCount()));
-                    }
-                }
-                System.out.print(
-                        String.format(
-                                " (%1$s filtered by baseline %2$s)",
-                                Lint.describeCounts(
-                                        stats.getBaselineErrorCount(),
-                                        stats.getBaselineWarningCount(),
-                                        true,
-                                        true),
-                                baselineFile.getName()));
-            } else {
-                System.out.print(
-                        String.format(
-                                "Lint found %1$s",
-                                Lint.describeCounts(errorCount, warningCount, true, false)));
-            }
-            System.out.println();
-            if (stats.getBaselineFixedCount() > 0) {
-                System.out.println(
-                        String.format(
-                                "\n%1$d errors/warnings were listed in the "
-                                        + "baseline file (%2$s) but not found in the project; perhaps they have "
-                                        + "been fixed?",
-                                stats.getBaselineFixedCount(), baselineFile));
-            }
-
-            String checkVariant = getBaselineVariantName();
-            String creationVariant = baseline.getAttribute("variant");
-            if (creationVariant != null && !creationVariant.equals(checkVariant)) {
-                System.out.println(
-                        "\nNote: The baseline was created using a different target/variant than it was checked against.");
-                System.out.println("Creation variant: " + getTargetName(creationVariant));
-                System.out.println("Current variant: " + getTargetName(checkVariant));
-            }
-
-            // TODO: If the versions don't match, emit some additional diagnostic hints, such as
-            // the possibility that newer versions of lint have newer checks not included in
-            // older ones, have existing checks that cover more areas, etc.
-            if (stats.getBaselineFixedCount() > 0) {
-                String checkVersion = getClientDisplayRevision();
-                String checkClient = LintClient.Companion.getClientName();
-                String creationVersion = baseline.getAttribute("version");
-                String creationClient = baseline.getAttribute("client");
-                if (checkClient.equals(creationClient)
-                        && creationVersion != null
-                        && checkVersion != null
-                        && !creationVersion.equals(checkVersion)) {
-                    GradleVersion created = GradleVersion.tryParse(creationVersion);
-                    GradleVersion current = GradleVersion.tryParse(checkVersion);
-                    if (created != null && current != null && created.compareTo(current) > 0) {
-                        System.out.println(
-                                "\nNote: The baseline was created with a newer version of "
-                                        + checkClient
-                                        + " ("
-                                        + creationVersion
-                                        + ") than the current version ("
-                                        + checkVersion
-                                        + ")");
-                        System.out.println(
-                                "This means that some of the issues marked as fixed in the baseline may not actually be fixed, but may ");
-                        System.out.println(
-                                "be new issues uncovered by the more recent version of lint.");
-                    }
-                }
-            }
-        }
-    }
-
-    protected static String getTargetName(@NonNull String baselineVariantName) {
-        if (LintClient.isGradle()) {
-            if (VARIANT_ALL.equals(baselineVariantName)) {
-                return "lint";
-            } else if (VARIANT_FATAL.equals(baselineVariantName)) {
-                return "lintVitalRelease";
-            }
-        }
-        return baselineVariantName;
-    }
-
-    protected void validateIssueIds() {
-        driver.addLintListener(
-                (driver, type, project, context) -> {
-                    if (type == LintListener.EventType.SCANNING_PROJECT && !validatedIds) {
-                        // Make sure all the id's are valid once the driver is all set up and
-                        // ready to run (such that custom rules are available in the registry etc)
-                        validateIssueIds(project);
-                    }
-                });
-    }
-
-    @NonNull
-    protected LintDriver createDriver(
-            @NonNull IssueRegistry registry, @NonNull LintRequest request) {
-        this.registry = registry;
-
-        driver = new LintDriver(registry, this, request);
-        driver.setAbbreviating(!flags.isShowEverything());
-        driver.setCheckTestSources(flags.isCheckTestSources());
-        driver.setIgnoreTestSources(flags.isIgnoreTestSources());
-        driver.setCheckGeneratedSources(flags.isCheckGeneratedSources());
-        driver.setFatalOnlyMode(flags.isFatalOnly());
-        driver.setCheckDependencies(flags.isCheckDependencies());
-        driver.setAllowSuppress(flags.getAllowSuppress());
-
-        File baselineFile = flags.getBaselineFile();
-        if (baselineFile != null) {
-            LintBaseline baseline = new LintBaseline(this, baselineFile);
-            driver.setBaseline(baseline);
-            if (flags.isRemoveFixedBaselineIssues()) {
-                baseline.setWriteOnClose(true);
-                baseline.setRemoveFixed(true);
-            } else if (flags.isUpdateBaseline()) {
-                baseline.setWriteOnClose(true);
-            }
-        }
-
-        return driver;
-    }
-
-    protected void addProgressPrinter() {
-        if (!flags.isQuiet()) {
-            driver.addLintListener(new ProgressPrinter());
-        }
-    }
-
-    /** Creates a lint request */
-    @NonNull
-    protected LintRequest createLintRequest(@NonNull List<File> files) {
-        return new LintRequest(this, files);
-    }
-
-    @Override
-    public void log(
-            @NonNull Severity severity,
-            @Nullable Throwable exception,
-            @Nullable String format,
-            @Nullable Object... args) {
-        System.out.flush();
-        if (!flags.isQuiet()) {
-            // Place the error message on a line of its own since we're printing '.' etc
-            // with newlines during analysis
-            System.err.println();
-        }
-        if (format != null) {
-            System.err.println(String.format(format, args));
-        }
-        if (exception != null) {
-            exception.printStackTrace();
-        }
-    }
-
-    @NonNull
-    @Override
-    public XmlParser getXmlParser() {
-        return new LintCliXmlParser(this);
-    }
-
-    @NonNull
-    @Override
-    public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
-        if (overrideConfiguration != null) {
-            return overrideConfiguration;
-        }
-
-        return new CliConfiguration(getConfiguration(), project, flags.isFatalOnly());
-    }
-
-    /** File content cache */
-    private final Map<File, CharSequence> mFileContents = new HashMap<>(100);
-
-    /** Read the contents of the given file, possibly cached */
-    private CharSequence getContents(File file) {
-        return mFileContents.computeIfAbsent(file, k -> readFile(file));
-    }
-
-    @NonNull
-    @Override
-    public UastParser getUastParser(@Nullable Project project) {
-        return new LintCliUastParser(project);
-    }
-
-    @NonNull
-    @Override
-    public GradleVisitor getGradleVisitor() {
-        return new GradleVisitor();
-    }
-
-    @Override
-    public void report(
-            @NonNull Context context,
-            @NonNull Issue issue,
-            @NonNull Severity severity,
-            @NonNull Location location,
-            @NonNull String message,
-            @NonNull TextFormat format,
-            @Nullable LintFix fix) {
-        assert context.isEnabled(issue) || issue.getCategory() == Category.LINT;
-
-        if (severity.isError()) {
-            hasErrors = true;
-            errorCount++;
-        } else if (severity == Severity.WARNING) { // Don't count informational as a warning
-            warningCount++;
-        }
-
-        // Store the message in the raw format internally such that we can
-        // convert it to text for the text reporter, HTML for the HTML reporter
-        // and so on.
-        message = format.convertTo(message, TextFormat.RAW);
-        Warning warning = new Warning(issue, message, severity, context.getProject());
-        warnings.add(warning);
-
-        //noinspection ConstantConditions
-        if (location == null) {
-            // Misbehaving third party lint rules
-            log(Severity.ERROR, null, "No location provided for issue " + issue);
-            return;
-        }
-
-        warning.location = location;
-        File file = location.getFile();
-        warning.file = file;
-        warning.path = getDisplayPath(context.getProject(), file);
-        warning.quickfixData = fix;
-
-        Position startPosition = location.getStart();
-        if (startPosition != null) {
-            int line = startPosition.getLine();
-            warning.line = line;
-            warning.offset = startPosition.getOffset();
-            Position endPosition = location.getEnd();
-            if (endPosition != null) {
-                warning.endOffset = endPosition.getOffset();
-            }
-            if (line >= 0) {
-                if (context.file == location.getFile()) {
-                    warning.fileContents = context.getContents();
-                }
-                if (warning.fileContents == null) {
-                    warning.fileContents = getContents(location.getFile());
-                }
-
-                if (flags.isShowSourceLines()) {
-                    // Compute error line contents
-                    warning.errorLine = getLine(warning.fileContents, line);
-                    if (warning.errorLine != null) {
-                        // Replace tabs with spaces such that the column
-                        // marker (^) lines up properly:
-                        warning.errorLine = warning.errorLine.replace('\t', ' ');
-                        int column = startPosition.getColumn();
-                        if (column < 0) {
-                            column = 0;
-                            for (int i = 0; i < warning.errorLine.length(); i++, column++) {
-                                if (!Character.isWhitespace(warning.errorLine.charAt(i))) {
-                                    break;
-                                }
-                            }
-                        }
-                        StringBuilder sb = new StringBuilder(100);
-                        sb.append(warning.errorLine);
-                        sb.append('\n');
-                        for (int i = 0; i < column; i++) {
-                            sb.append(' ');
-                        }
-
-                        boolean displayCaret = true;
-                        if (endPosition != null) {
-                            int endLine = endPosition.getLine();
-                            int endColumn = endPosition.getColumn();
-                            if (endLine == line && endColumn > column) {
-                                for (int i = column; i < endColumn; i++) {
-                                    sb.append('~');
-                                }
-                                displayCaret = false;
-                            }
-                        }
-
-                        if (displayCaret) {
-                            sb.append('^');
-                        }
-                        sb.append('\n');
-                        warning.errorLine = sb.toString();
-                    }
-                }
-            }
-        }
-    }
-
-    /** Look up the contents of the given line */
-    static String getLine(CharSequence contents, int line) {
-        int index = getLineOffset(contents, line);
-        if (index != -1) {
-            return getLineOfOffset(contents, index);
-        } else {
-            return null;
-        }
-    }
-
-    static String getLineOfOffset(CharSequence contents, int offset) {
-        int end = indexOf(contents, '\n', offset);
-        if (end == -1) {
-            end = indexOf(contents, '\r', offset);
-        } else if (end > 0 && contents.charAt(end - 1) == '\r') {
-            end--;
-        }
-        return contents.subSequence(offset, end != -1 ? end : contents.length()).toString();
-    }
-
-    /** Look up the contents of the given line */
-    static int getLineOffset(CharSequence contents, int line) {
-        int index = 0;
-        for (int i = 0; i < line; i++) {
-            index = indexOf(contents, '\n', index);
-            if (index == -1) {
-                return -1;
-            }
-            index++;
-        }
-
-        return index;
-    }
-
-    @NonNull
-    @Override
-    public CharSequence readFile(@NonNull File file) {
-        CharSequence contents;
-        try {
-            contents = Lint.getEncodedString(this, file, false);
-        } catch (IOException e) {
-            contents = "";
-        }
-
-        String path = file.getPath();
-        if ((path.endsWith(DOT_JAVA) || path.endsWith(DOT_KT) || path.endsWith(DOT_KTS))
-                && CharSequences.indexOf(contents, '\r') != -1) {
-            // Offsets in these files will be relative to PSI's text offsets (which may
-            // have converted line offsets); make sure we use the same offsets.
-            // (Can't just do this on Windows; what matters is whether the file contains
-            // CRLF's.)
-            VirtualFile vFile = StandardFileSystems.local().findFileByPath(path);
-            if (vFile != null) {
-                com.intellij.openapi.project.Project project = getIdeaProject();
-                if (project != null) {
-                    PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile);
-                    if (psiFile != null) {
-                        contents = psiFile.getText();
-                    }
-                }
-            }
-        }
-
-        return contents;
-    }
-
-    boolean isCheckingSpecificIssues() {
-        return flags.getExactCheckedIds() != null;
-    }
-
-    private Map<Project, ClassPathInfo> mProjectInfo;
-
-    @Override
-    @NonNull
-    protected ClassPathInfo getClassPath(@NonNull Project project) {
-        ClassPathInfo classPath = super.getClassPath(project);
-
-        List<File> sources = flags.getSourcesOverride();
-        List<File> classes = flags.getClassesOverride();
-        List<File> libraries = flags.getLibrariesOverride();
-        if (classes == null && sources == null && libraries == null) {
-            return classPath;
-        }
-
-        ClassPathInfo info;
-        if (mProjectInfo == null) {
-            mProjectInfo = Maps.newHashMap();
-            info = null;
-        } else {
-            info = mProjectInfo.get(project);
-        }
-
-        if (info == null) {
-            if (sources == null) {
-                sources = classPath.getSourceFolders();
-            }
-            if (classes == null) {
-                classes = classPath.getClassFolders();
-            }
-            if (libraries == null) {
-                libraries = classPath.getLibraries(true);
-            }
-
-            info =
-                    new ClassPathInfo(
-                            sources,
-                            classes,
-                            libraries,
-                            classPath.getLibraries(false),
-                            classPath.getTestSourceFolders(),
-                            classPath.getTestLibraries(),
-                            classPath.getGeneratedFolders());
-            mProjectInfo.put(project, info);
-        }
-
-        return info;
-    }
-
-    @NonNull
-    @Override
-    public List<File> getResourceFolders(@NonNull Project project) {
-        List<File> resources = flags.getResourcesOverride();
-        if (resources == null) {
-            return super.getResourceFolders(project);
-        }
-
-        return resources;
-    }
-
-    /**
-     * Consult the lint.xml file, but override with the --enable and --disable flags supplied on the
-     * command line
-     */
-    protected class CliConfiguration extends DefaultConfiguration {
-        private final boolean mFatalOnly;
-
-        protected CliConfiguration(
-                @NonNull Configuration parent, @NonNull Project project, boolean fatalOnly) {
-            super(LintCliClient.this, project, parent);
-            mFatalOnly = fatalOnly;
-        }
-
-        protected CliConfiguration(File lintFile, boolean fatalOnly) {
-            super(LintCliClient.this, null, null, lintFile);
-            mFatalOnly = fatalOnly;
-        }
-
-        protected CliConfiguration(
-                @NonNull File lintFile,
-                @Nullable Configuration parent,
-                @Nullable Project project,
-                boolean fatalOnly) {
-            super(LintCliClient.this, project, parent, lintFile);
-            mFatalOnly = fatalOnly;
-        }
-
-        @NonNull
-        @Override
-        public Severity getSeverity(@NonNull Issue issue) {
-            Severity severity = computeSeverity(issue);
-
-            if (mFatalOnly && severity != Severity.FATAL) {
-                return Severity.IGNORE;
-            }
-
-            if (flags.isWarningsAsErrors() && severity == Severity.WARNING) {
-                if (issue == IssueRegistry.BASELINE) {
-                    // Don't promote the baseline informational issue
-                    // (number of issues promoted) to error
-                    return severity;
-                }
-                severity = Severity.ERROR;
-            }
-
-            if (flags.isIgnoreWarnings() && severity == Severity.WARNING) {
-                severity = Severity.IGNORE;
-            }
-
-            return severity;
-        }
-
-        @NonNull
-        @Override
-        protected Severity getDefaultSeverity(@NonNull Issue issue) {
-            if (flags.isCheckAllWarnings()) {
-                // Exclude the interprocedural check from the "enable all warnings" flag;
-                // it's much slower and still triggers various bugs in UAST that can affect
-                // other checks.
-                if (issue == WrongThreadInterproceduralDetector.ISSUE) {
-                    return super.getDefaultSeverity(issue);
-                }
-
-                return issue.getDefaultSeverity();
-            }
-
-            return super.getDefaultSeverity(issue);
-        }
-
-        private Severity computeSeverity(@NonNull Issue issue) {
-            Severity severity = super.getSeverity(issue);
-
-            // Issue not allowed to be suppressed?
-            if (issue.getSuppressNames() != null && !flags.getAllowSuppress()) {
-                return getDefaultSeverity(issue);
-            }
-
-            String id = issue.getId();
-            Set<String> suppress = flags.getSuppressedIds();
-            if (suppress.contains(id)) {
-                return Severity.IGNORE;
-            }
-
-            Set<Category> disabledCategories = flags.getDisabledCategories();
-            if (disabledCategories != null) {
-                Category category = issue.getCategory();
-                if (disabledCategories.contains(category)
-                        || category.getParent() != null
-                                && disabledCategories.contains(category.getParent())) {
-                    return Severity.IGNORE;
-                }
-            }
-
-            Severity manual = flags.getSeverityOverrides().get(id);
-            if (manual != null) {
-                if (this.severity != null
-                        && (this.severity.containsKey(id)
-                                || this.severity.containsKey(VALUE_ALL))) {
-                    // Ambiguity! We have a specific severity override provided
-                    // via lint options for the main app module, but a local lint.xml
-                    // file in the library (not a lintOptions definition) which also
-                    // specifies severity for the same issue.
-                    //
-                    // Who should win? Should the intent from the main app module
-                    // win, such that you have a global way to say "this is the severity
-                    // I want during this lint run?". Or should the library-local definition
-                    // win, to say "there's a local problem in this library; I need to
-                    // change things here?".
-                    //
-                    // Both are plausible, so for now I'm going with a middle ground: local
-                    // definitions should be used to turn of issues that don't work right.
-                    // Therefore, we'll take the minimum of the two severities!
-                    return Severity.min(severity, manual);
-                }
-                return manual;
-            }
-
-            Set<String> enabled = flags.getEnabledIds();
-            Set<String> exact = flags.getExactCheckedIds();
-            Set<Category> enabledCategories = flags.getEnabledCategories();
-            Set<Category> exactCategories = flags.getExactCategories();
-            Category category = issue.getCategory();
-
-            if (exact != null) {
-                if (exact.contains(id)) {
-                    return getVisibleSeverity(issue, severity);
-                } else if (category != Category.LINT) {
-                    return Severity.IGNORE;
-                }
-            }
-
-            if (exactCategories != null) {
-                if (exactCategories.contains(category)
-                        || category.getParent() != null
-                                && exactCategories.contains(category.getParent())) {
-                    return getVisibleSeverity(issue, severity);
-                } else if (category != Category.LINT
-                        || flags.getDisabledCategories() != null
-                                && flags.getDisabledCategories().contains(Category.LINT)) {
-                    return Severity.IGNORE;
-                }
-            }
-            if (enabled.contains(id)
-                    || enabledCategories != null
-                            && (enabledCategories.contains(category)
-                                    || category.getParent() != null
-                                            && enabledCategories.contains(category.getParent()))) {
-                return getVisibleSeverity(issue, severity);
-            }
-
-            return severity;
-        }
-
-        /** Returns the given severity, but if not visible, use the default */
-        private Severity getVisibleSeverity(@NonNull Issue issue, Severity severity) {
-            // Overriding default
-            // Detectors shouldn't be returning ignore as a default severity,
-            // but in case they do, force it up to warning here to ensure that
-            // it's run
-            if (severity == Severity.IGNORE) {
-                severity = issue.getDefaultSeverity();
-                if (severity == Severity.IGNORE) {
-                    severity = Severity.WARNING;
-                }
-            }
-
-            return severity;
-        }
-    }
-
-    @NonNull
-    @Override
-    protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-        Project project = super.createProject(dir, referenceDir);
-
-        String compileSdkVersion = flags.getCompileSdkVersionOverride();
-        if (compileSdkVersion != null) {
-            project.setBuildTargetHash(compileSdkVersion);
-        }
-
-        project.setIdeaProject(ideaProject);
-
-        return project;
-    }
-
-    /**
-     * Checks that any id's specified by id refer to valid, known, issues. This typically can't be
-     * done right away (in for example the Gradle code which handles DSL references to strings, or
-     * in the command line parser for the lint command) because the full set of valid id's is not
-     * known until lint actually starts running and for example gathers custom rules from all AAR
-     * dependencies reachable from libraries, etc.
-     */
-    private void validateIssueIds(@Nullable Project project) {
-        if (driver != null) {
-            IssueRegistry registry = driver.getRegistry();
-            if (!registry.isIssueId(HardcodedValuesDetector.ISSUE.getId())) {
-                // This should not be necessary, but there have been some strange
-                // reports where lint has reported some well known builtin issues
-                // to not exist:
-                //
-                //   Error: Unknown issue id "DuplicateDefinition" [LintError]
-                //   Error: Unknown issue id "GradleIdeError" [LintError]
-                //   Error: Unknown issue id "InvalidPackage" [LintError]
-                //   Error: Unknown issue id "JavascriptInterface" [LintError]
-                //   ...
-                //
-                // It's not clear how this can happen, though it's probably related
-                // to using 3rd party lint rules (where lint will create new composite
-                // issue registries to wrap the various additional issues) - but
-                // we definitely don't want to validate issue id's if we can't find
-                // well known issues.
-                return;
-            }
-            validatedIds = true;
-            validateIssueIds(project, registry, flags.getExactCheckedIds());
-            validateIssueIds(project, registry, flags.getEnabledIds());
-            validateIssueIds(project, registry, flags.getSuppressedIds());
-            validateIssueIds(project, registry, flags.getSeverityOverrides().keySet());
-
-            if (project != null) {
-                Configuration configuration = project.getConfiguration(driver);
-                configuration.validateIssueIds(this, driver, project, registry);
-            }
-        }
-    }
-
-    private void validateIssueIds(
-            @Nullable Project project,
-            @NonNull IssueRegistry registry,
-            @Nullable Collection<String> ids) {
-        if (ids != null) {
-            for (String id : ids) {
-                if (registry.getIssue(id) == null) {
-                    reportNonExistingIssueId(project, id);
-                }
-            }
-        }
-    }
-
-    protected void reportNonExistingIssueId(@Nullable Project project, @NonNull String id) {
-        String message = String.format("Unknown issue id \"%1$s\"", id);
-
-        if (driver != null && project != null && !isSuppressed(IssueRegistry.LINT_ERROR)) {
-            Location location = Lint.guessGradleLocation(this, project.getDir(), id);
-            LintClient.Companion.report(
-                    this,
-                    IssueRegistry.LINT_ERROR,
-                    message,
-                    driver,
-                    project,
-                    location,
-                    LintFix.create().data(id));
-
-        } else {
-            log(Severity.ERROR, null, "Lint: %1$s", message);
-        }
-    }
-
-    private static class ProgressPrinter implements LintListener {
-        @Override
-        public void update(
-                @NonNull LintDriver lint,
-                @NonNull EventType type,
-                @Nullable Project project,
-                @Nullable Context context) {
-            switch (type) {
-                case SCANNING_PROJECT:
-                    {
-                        String name = context != null ? context.getProject().getName() : "?";
-                        if (lint.getPhase() > 1) {
-                            System.out.print(
-                                    String.format(
-                                            "\nScanning %1$s (Phase %2$d): ",
-                                            name, lint.getPhase()));
-                        } else {
-                            System.out.print(String.format("\nScanning %1$s: ", name));
-                        }
-                        break;
-                    }
-                case SCANNING_LIBRARY_PROJECT:
-                    {
-                        String name = context != null ? context.getProject().getName() : "?";
-                        System.out.print(String.format("\n         - %1$s: ", name));
-                        break;
-                    }
-                case SCANNING_FILE:
-                    System.out.print('.');
-                    break;
-                case NEW_PHASE:
-                    // Ignored for now: printing status as part of next project's status
-                    break;
-                case CANCELED:
-                case COMPLETED:
-                    System.out.println();
-                    break;
-                case REGISTERED_PROJECT:
-                case STARTING:
-                    // Ignored for now
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Given a file, it produces a cleaned up path from the file. This will clean up the path such
-     * that
-     *
-     * <ul>
-     *   <li>{@code foo/./bar} becomes {@code foo/bar}
-     *   <li>{@code foo/bar/../baz} becomes {@code foo/baz}
-     * </ul>
-     *
-     * Unlike {@link java.io.File#getCanonicalPath()} however, it will <b>not</b> attempt to make
-     * the file canonical, such as expanding symlinks and network mounts.
-     *
-     * @param file the file to compute a clean path for
-     * @return the cleaned up path
-     */
-    @VisibleForTesting
-    @NonNull
-    static String getCleanPath(@NonNull File file) {
-        String path = file.getPath();
-        StringBuilder sb = new StringBuilder(path.length());
-
-        if (path.startsWith(File.separator)) {
-            sb.append(File.separator);
-        }
-        elementLoop:
-        for (String element : Splitter.on(File.separatorChar).omitEmptyStrings().split(path)) {
-            if (element.equals(".")) {
-                continue;
-            } else if (element.equals("..")) {
-                if (sb.length() > 0) {
-                    for (int i = sb.length() - 1; i >= 0; i--) {
-                        char c = sb.charAt(i);
-                        if (c == File.separatorChar) {
-                            sb.setLength(i == 0 ? 1 : i);
-                            continue elementLoop;
-                        }
-                    }
-                    sb.setLength(0);
-                    continue;
-                }
-            }
-
-            if (sb.length() > 1) {
-                sb.append(File.separatorChar);
-            } else if (sb.length() > 0 && sb.charAt(0) != File.separatorChar) {
-                sb.append(File.separatorChar);
-            }
-            sb.append(element);
-        }
-        if (path.endsWith(File.separator)
-                && sb.length() > 0
-                && sb.charAt(sb.length() - 1) != File.separatorChar) {
-            sb.append(File.separator);
-        }
-
-        return sb.toString();
-    }
-
-    @NonNull
-    String getDisplayPath(@NonNull Project project, @NonNull File file) {
-        return getDisplayPath(project, file, flags.isFullPath());
-    }
-
-    @NonNull
-    String getDisplayPath(@NonNull Project project, @NonNull File file, boolean fullPath) {
-        String path = file.getPath();
-        if (!fullPath && path.startsWith(project.getReferenceDir().getPath())) {
-            int chop = project.getReferenceDir().getPath().length();
-            if (path.length() > chop && path.charAt(chop) == File.separatorChar) {
-                chop++;
-            }
-            path = path.substring(chop);
-            if (path.isEmpty()) {
-                path = file.getName();
-            }
-        } else if (fullPath) {
-            path = getCleanPath(file.getAbsoluteFile());
-        } else if (file.isAbsolute() && file.exists()) {
-            path = getRelativePath(project.getReferenceDir(), file);
-            if (path == null) {
-                path = file.getPath();
-            }
-        }
-
-        return path;
-    }
-
-    /** Returns whether all warnings are enabled, including those disabled by default */
-    boolean isAllEnabled() {
-        return flags.isCheckAllWarnings();
-    }
-
-    /** Returns the issue registry used by this client */
-    public IssueRegistry getRegistry() {
-        return registry;
-    }
-
-    /** Returns the driver running the lint checks */
-    LintDriver getDriver() {
-        return driver;
-    }
-
-    private static Set<File> sAlreadyWarned;
-
-    /** Returns the configuration used by this client */
-    protected Configuration getConfiguration() {
-        if (configuration == null) {
-            if (overrideConfiguration != null) {
-                configuration = overrideConfiguration;
-                return configuration;
-            }
-
-            File configFile = flags.getDefaultConfiguration();
-            if (configFile != null) {
-                if (!configFile.exists()) {
-                    if (sAlreadyWarned == null || !sAlreadyWarned.contains(configFile)) {
-                        log(
-                                Severity.ERROR,
-                                null,
-                                "Warning: Configuration file %1$s does not exist",
-                                configFile);
-                    }
-                    if (sAlreadyWarned == null) {
-                        sAlreadyWarned = Sets.newHashSet();
-                    }
-                    sAlreadyWarned.add(configFile);
-                }
-                configuration = createConfigurationFromFile(configFile);
-            }
-        }
-
-        return configuration;
-    }
-
-    /** Returns true if the given issue has been explicitly disabled */
-    boolean isSuppressed(Issue issue) {
-        Set<Category> disabledCategories = flags.getDisabledCategories();
-        if (disabledCategories != null) {
-            Category category = issue.getCategory();
-            if (disabledCategories.contains(category)
-                    || category.getParent() != null
-                            && disabledCategories.contains(category.getParent())) {
-                return true;
-            }
-        }
-        return flags.getSuppressedIds().contains(issue.getId());
-    }
-
-    public DefaultConfiguration createConfigurationFromFile(File file) {
-        return new CliConfiguration(file, flags.isFatalOnly());
-    }
-
-    @Nullable LintCoreProjectEnvironment projectEnvironment;
-    @Nullable private com.intellij.openapi.project.Project ideaProject;
-    @Nullable private Disposable projectDisposer;
-
-    @Nullable
-    public com.intellij.openapi.project.Project getIdeaProject() {
-        return ideaProject;
-    }
-
-    @Nullable
-    public LintCoreProjectEnvironment getProjectEnvironment() {
-        return projectEnvironment;
-    }
-
-    @Override
-    public void initializeProjects(@NonNull Collection<? extends Project> knownProjects) {
-        // Initialize the associated idea project to use
-
-        LintCoreApplicationEnvironment appEnv = LintCoreApplicationEnvironment.get();
-        Disposable parentDisposable = Disposer.newDisposable();
-        projectDisposer = parentDisposable;
-
-        LintCoreProjectEnvironment projectEnvironment =
-                LintCoreProjectEnvironment.create(parentDisposable, appEnv);
-        this.projectEnvironment = projectEnvironment;
-        MockProject mockProject = projectEnvironment.getProject();
-        ideaProject = mockProject;
-
-        boolean includeTests = !flags.isIgnoreTestSources();
-
-        // knownProject only lists root projects, not dependencies
-        Set<Project> allProjects = Sets.newIdentityHashSet();
-        for (Project project : knownProjects) {
-            allProjects.add(project);
-            allProjects.addAll(project.getAllLibraries());
-        }
-
-        List<File> files = new ArrayList<>(50);
-
-        for (Project project : allProjects) {
-            // Note that there could be duplicates here since we're including multiple library
-            // dependencies that could have the same dependencies (e.g. lib1 and lib2 both
-            // referencing guava.jar)
-            files.addAll(project.getJavaSourceFolders());
-            if (includeTests) {
-                files.addAll(project.getTestSourceFolders());
-            }
-            files.addAll(project.getGeneratedSourceFolders());
-            files.addAll(project.getJavaLibraries(true));
-            if (includeTests) {
-                files.addAll(project.getTestLibraries());
-            }
-
-            // Don't include all class folders:
-            //  files.addAll(project.getJavaClassFolders());
-            // These are the outputs from the sources and generated sources, which we will
-            // parse directly with PSI/UAST anyway. Including them here leads lint to do
-            // a lot more work (e.g. when resolving symbols it looks at both .java and .class
-            // matches).
-            // However, we *do* need them for libraries; otherwise, type resolution into
-            // compiled libraries will not work; see
-            // https://issuetracker.google.com/72032121
-            if (project.isLibrary()) {
-                files.addAll(project.getJavaClassFolders());
-            } else if (project.isGradleProject()) { // As of 3.4, R.java is in a special jar file
-                for (File f : project.getJavaClassFolders()) {
-                    if (f.getName().equals(FN_R_CLASS_JAR)) {
-                        files.add(f);
-                    }
-                }
-            }
-        }
-
-        addBootClassPath(knownProjects, files);
-
-        projectEnvironment.registerPaths(files);
-
-        LanguageLevel maxLevel = LanguageLevel.JDK_1_7;
-        for (Project project : knownProjects) {
-            IdeAndroidProject model = project.getGradleProjectModel();
-            if (model != null) {
-                JavaCompileOptions javaCompileOptions = model.getJavaCompileOptions();
-                String sourceCompatibility = javaCompileOptions.getSourceCompatibility();
-                LanguageLevel level = LanguageLevel.parse(sourceCompatibility);
-                if (level != null && maxLevel.isLessThan(level)) {
-                    maxLevel = level;
-                }
-            }
-        }
-
-        com.intellij.openapi.project.Project ideaProject = getIdeaProject();
-        if (ideaProject != null) {
-            LanguageLevelProjectExtension languageLevelProjectExtension =
-                    ideaProject.getComponent(LanguageLevelProjectExtension.class);
-            if (languageLevelProjectExtension != null) {
-                languageLevelProjectExtension.setLanguageLevel(maxLevel);
-            }
-        }
-
-        KotlinCliJavaFileManagerImpl manager =
-                (KotlinCliJavaFileManagerImpl)
-                        ServiceManager.getService(mockProject, JavaFileManager.class);
-        List<JavaRoot> roots = new ArrayList<>();
-        for (File file : files) {
-            // IntelliJ's framework requires absolute path to JARs, name resolution might fail
-            // otherwise. We defensively ensure all paths are absolute.
-            Preconditions.checkState(
-                    file.isAbsolute(),
-                    "Relative Path found: %s. All paths should be absolute.",
-                    file.getPath());
-
-            if (file.isDirectory()) {
-                VirtualFile vFile = StandardFileSystems.local().findFileByPath(file.getPath());
-                if (vFile != null) {
-                    roots.add(new JavaRoot(vFile, JavaRoot.RootType.SOURCE, null));
-                }
-            } else {
-                String pathString = file.getPath();
-                if (pathString.endsWith(DOT_SRCJAR)) {
-                    // Mount as source files
-                    VirtualFile root =
-                            StandardFileSystems.jar()
-                                    .findFileByPath(pathString + URLUtil.JAR_SEPARATOR);
-                    if (root != null) {
-                        roots.add(new JavaRoot(root, JavaRoot.RootType.SOURCE, null));
-                    }
-                }
-            }
-        }
-
-        JvmDependenciesIndexImpl index = new JvmDependenciesIndexImpl(roots);
-        manager.initialize(
-                index,
-                Collections.emptyList(),
-                new SingleJavaFileRootsIndex(Collections.emptyList()),
-                false);
-
-        for (Project project : allProjects) {
-            project.setIdeaProject(ideaProject);
-        }
-
-        super.initializeProjects(knownProjects);
-    }
-
-    protected boolean addBootClassPath(
-            @NonNull Collection<? extends Project> knownProjects, List<File> files) {
-        IAndroidTarget buildTarget = null;
-        boolean isAndroid = false;
-        for (Project project : knownProjects) {
-            if (project.isAndroidProject()) {
-                isAndroid = true;
-            }
-
-            IAndroidTarget t = project.getBuildTarget();
-            if (t != null) {
-                if (buildTarget == null) {
-                    buildTarget = t;
-                } else if (buildTarget.getVersion().compareTo(t.getVersion()) > 0) {
-                    buildTarget = t;
-                }
-            }
-        }
-
-        if (buildTarget != null) {
-            File file = buildTarget.getFile(IAndroidTarget.ANDROID_JAR);
-            if (file != null) {
-                files.add(file);
-                return true;
-            }
-        }
-
-        if (!isAndroid) {
-            // Non android project, e.g. perhaps a pure Kotlin project
-
-            // Gradle doesn't let you configure separate SDKs; it runs the Gradle
-            // daemon on the JDK that should be used for compilation so look up the
-            // current environment:
-            String javaHome = System.getProperty("java.home");
-            if (javaHome == null) {
-                javaHome = System.getenv("JAVA_HOME");
-            }
-            if (javaHome != null) {
-                File rt = new File(javaHome, "lib" + File.separator + "rt.jar");
-                if (rt.exists()) {
-                    files.add(rt);
-                    return true;
-                }
-                rt = new File(javaHome, "jre" + File.separator + "lib" + File.separator + "rt.jar");
-                if (rt.exists()) {
-                    files.add(rt);
-                    return true;
-                }
-            }
-
-            String cp = System.getProperty("sun.boot.class.path");
-            if (cp != null) {
-                Splitter.on(File.pathSeparatorChar)
-                        .split(cp)
-                        .forEach(
-                                (path) -> {
-                                    File file = new File(path);
-                                    if (file.exists()) {
-                                        files.add(file);
-                                    }
-                                });
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public void disposeProjects(@NonNull Collection<? extends Project> knownProjects) {
-        if (projectDisposer != null) {
-            Disposer.dispose(projectDisposer);
-            LintCoreApplicationEnvironment.clearAccessorCache();
-        }
-        ideaProject = null;
-        projectDisposer = null;
-
-        super.disposeProjects(knownProjects);
-    }
-
-    public boolean isOverridingConfiguration() {
-        return overrideConfiguration != null;
-    }
-
-    /** Synchronizes any options specified in lint.xml with the {@link LintCliFlags} object */
-    public void syncConfigOptions() {
-        Configuration configuration = getConfiguration();
-        if (configuration instanceof DefaultConfiguration) {
-            DefaultConfiguration config = (DefaultConfiguration) configuration;
-            Boolean checkAllWarnings = config.getCheckAllWarnings();
-            if (checkAllWarnings != null) {
-                flags.setCheckAllWarnings(checkAllWarnings);
-            }
-            Boolean ignoreWarnings = config.getIgnoreWarnings();
-            if (ignoreWarnings != null) {
-                flags.setIgnoreWarnings(ignoreWarnings);
-            }
-            Boolean warningsAsErrors = config.getWarningsAsErrors();
-            if (warningsAsErrors != null) {
-                flags.setWarningsAsErrors(warningsAsErrors);
-            }
-            Boolean fatalOnly = config.getFatalOnly();
-            if (fatalOnly != null) {
-                flags.setFatalOnly(fatalOnly);
-            }
-            Boolean checkTestSources = config.getCheckTestSources();
-            if (checkTestSources != null) {
-                flags.setCheckTestSources(checkTestSources);
-            }
-            Boolean ignoreTestSources = config.getIgnoreTestSources();
-            if (ignoreTestSources != null) {
-                flags.setIgnoreTestSources(ignoreTestSources);
-            }
-            Boolean checkGeneratedSources = config.getCheckGeneratedSources();
-            if (checkGeneratedSources != null) {
-                flags.setCheckGeneratedSources(checkGeneratedSources);
-            }
-            Boolean checkDependencies = config.getCheckDependencies();
-            if (checkDependencies != null) {
-                flags.setCheckDependencies(checkDependencies);
-            }
-            Boolean explainIssues = config.getExplainIssues();
-            if (explainIssues != null) {
-                flags.setExplainIssues(explainIssues);
-            }
-            Boolean removeFixedBaselineIssues = config.getRemoveFixedBaselineIssues();
-            if (removeFixedBaselineIssues != null) {
-                flags.setRemovedFixedBaselineIssues(removeFixedBaselineIssues);
-            }
-            Boolean abortOnError = config.getAbortOnError();
-            if (abortOnError != null) {
-                flags.setSetExitCode(abortOnError);
-            }
-            File baselineFile = config.getBaselineFile();
-            if (baselineFile != null) {
-                flags.setBaselineFile(
-                        baselineFile.getPath().equals(VALUE_NONE) ? null : baselineFile);
-            }
-            Boolean applySuggestions = config.getApplySuggestions();
-            if (applySuggestions != null && applySuggestions) {
-                flags.setAutoFix(true);
-            }
-        }
-    }
-
-    @Override
-    @Nullable
-    public String getClientRevision() {
-        String plugin = Version.TOOLS_VERSION;
-        if (plugin != null) {
-            return plugin;
-        }
-
-        return "unknown";
-    }
-
-    @NonNull
-    public LintCliFlags getFlags() {
-        return flags;
-    }
-
-    public boolean haveErrors() {
-        return errorCount > 0;
-    }
-
-    @VisibleForTesting
-    public void reset() {
-        warnings.clear();
-        errorCount = 0;
-        warningCount = 0;
-
-        getProjectDirs().clear();
-        getDirToProject().clear();
-    }
-
-    @NonNull
-    @Override
-    public ClassLoader createUrlClassLoader(@NonNull URL[] urls, @NonNull ClassLoader parent) {
-        return UrlClassLoader.build().parent(parent).urls(urls).get();
-    }
-
-    @Nullable
-    @Override
-    public Document getMergedManifest(@NonNull Project project) {
-        List<File> manifests = Lists.newArrayList();
-        for (Project dependency : project.getAllLibraries()) {
-            manifests.addAll(dependency.getManifestFiles());
-        }
-
-        File injectedFile = new File("injected-from-gradle");
-        StringBuilder injectedXml = new StringBuilder();
-        if (project.getGradleProjectModel() != null && project.getCurrentVariant() != null) {
-            ProductFlavor mergedFlavor = project.getCurrentVariant().getMergedFlavor();
-            ApiVersion targetSdkVersion = mergedFlavor.getTargetSdkVersion();
-            ApiVersion minSdkVersion = mergedFlavor.getMinSdkVersion();
-            if (targetSdkVersion != null || minSdkVersion != null) {
-                injectedXml.append(
-                        ""
-                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
-                                + "    package=\"${packageName}\">\n"
-                                + "    <uses-sdk");
-                if (minSdkVersion != null) {
-                    injectedXml
-                            .append(" android:minSdkVersion=\"")
-                            .append(minSdkVersion.getApiString())
-                            .append("\"");
-                }
-                if (targetSdkVersion != null) {
-                    injectedXml
-                            .append(" android:targetSdkVersion=\"")
-                            .append(targetSdkVersion.getApiString())
-                            .append("\"");
-                }
-                injectedXml.append(" />\n</manifest>\n");
-                manifests.add(injectedFile);
-            }
-        }
-
-        File mainManifest = null;
-
-        if (project.getGradleProjectModel() != null && project.getCurrentVariant() != null) {
-            for (SourceProvider provider :
-                    Lint.getSourceProviders(
-                            project.getGradleProjectModel(), project.getCurrentVariant())) {
-                File manifestFile = provider.getManifestFile();
-                if (manifestFile.exists()) { // model returns path whether or not it exists
-                    if (mainManifest == null) {
-                        mainManifest = manifestFile;
-                    } else {
-                        manifests.add(manifestFile);
-                    }
-                }
-            }
-            if (mainManifest == null) {
-                return null;
-            }
-        } else {
-            List<File> projectManifests;
-            projectManifests = project.getManifestFiles();
-            if (projectManifests.isEmpty()) {
-                return null;
-            }
-            mainManifest = projectManifests.get(0);
-            for (int i = 1; i < projectManifests.size(); i++) {
-                manifests.add(projectManifests.get(i));
-            }
-        }
-
-        if (manifests.isEmpty()) {
-            // Only the main manifest: that's easy
-            try {
-                Document document = getXmlParser().parseXml(mainManifest);
-                if (document != null) {
-                    resolveMergeManifestSources(document, mainManifest);
-                }
-                return document;
-            } catch (IOException | SAXException | ParserConfigurationException e) {
-                log(Severity.WARNING, e, "Could not parse %1$s", mainManifest);
-            }
-
-            return null;
-        }
-
-        try {
-            StdLogger logger = new StdLogger(StdLogger.Level.INFO);
-            MergeType type = project.isLibrary() ? MergeType.LIBRARY : MergeType.APPLICATION;
-            MergingReport mergeReport =
-                    ManifestMerger2.newMerger(mainManifest, logger, type)
-                            .withFeatures(
-                                    // TODO: How do we get the *opposite* of EXTRACT_FQCNS:
-                                    // ensure that all names are made fully qualified?
-                                    Feature.SKIP_BLAME,
-                                    Feature.SKIP_XML_STRING,
-                                    Feature.NO_PLACEHOLDER_REPLACEMENT)
-                            .addLibraryManifests(manifests.toArray(new File[0]))
-                            .withFileStreamProvider(
-                                    new ManifestMerger2.FileStreamProvider() {
-                                        @Override
-                                        protected InputStream getInputStream(@NonNull File file)
-                                                throws FileNotFoundException {
-                                            if (injectedFile.equals(file)) {
-                                                return CharSequences.getInputStream(
-                                                        injectedXml.toString());
-                                            }
-                                            CharSequence text = readFile(file);
-                                            // TODO: Avoid having to convert back and forth
-                                            return CharSequences.getInputStream(text);
-                                        }
-                                    })
-                            .merge();
-
-            XmlDocument xmlDocument = mergeReport.getMergedXmlDocument(MERGED);
-            if (xmlDocument != null) {
-                Document document = xmlDocument.getXml();
-                if (document != null) {
-                    resolveMergeManifestSources(document, mergeReport.getActions());
-                    return document;
-                }
-            } else {
-                log(Severity.WARNING, null, mergeReport.getReportString());
-            }
-        } catch (ManifestMerger2.MergeFailureException e) {
-            log(Severity.ERROR, e, "Couldn't parse merged manifest");
-        }
-
-        return super.getMergedManifest(project);
-    }
-
-    protected class LintCliUastParser extends DefaultUastParser {
-
-        public LintCliUastParser(Project project) {
-            //noinspection ConstantConditions
-            super(project, ideaProject);
-        }
-
-        @Override
-        public boolean prepare(
-                @NonNull List<? extends JavaContext> contexts,
-                @NonNull List<? extends JavaContext> testContexts) {
-            // If we're using Kotlin, ensure we initialize the bridge
-            List<File> kotlinFiles = new ArrayList<>();
-            for (JavaContext context : contexts) {
-                String path = context.file.getPath();
-                if (path.endsWith(DOT_KT) || path.endsWith(DOT_KTS)) {
-                    kotlinFiles.add(context.file);
-                }
-            }
-            for (JavaContext context : testContexts) {
-                String path = context.file.getPath();
-                if (path.endsWith(DOT_KT) || path.endsWith(DOT_KTS)) {
-                    kotlinFiles.add(context.file);
-                }
-            }
-
-            // We unconditionally invoke the KotlinLintAnalyzerFacade, even
-            // if kotlinFiles is empty -- without this, the machinery in
-            // the project (such as the CliLightClassGenerationSupport and
-            // the CoreFileManager) will throw exceptions at runtime even
-            // for plain class lookup
-            MockProject project = (MockProject) ideaProject;
-            if (project != null && projectEnvironment != null) {
-                List<File> paths = projectEnvironment.getPaths();
-                new KotlinLintAnalyzerFacade(kotlinPerformanceManager)
-                        .analyze(kotlinFiles, paths, project);
-            }
-
-            boolean ok = super.prepare(contexts, testContexts);
-
-            if (project == null || contexts.isEmpty() && testContexts.isEmpty()) {
-                return ok;
-            }
-
-            // Now that we have a project context, ensure that the annotations manager
-            // is up to date
-            if (ideaProject != null) {
-                LintExternalAnnotationsManager annotationsManager =
-                        (LintExternalAnnotationsManager)
-                                ExternalAnnotationsManager.getInstance(ideaProject);
-                annotationsManager.updateAnnotationRoots(LintCliClient.this);
-            }
-
-            return ok;
-        }
-    }
-
-    private static class LintCliKotlinPerformanceManager extends CommonCompilerPerformanceManager {
-
-        private final String perfReportName;
-
-        public LintCliKotlinPerformanceManager(String perfReportName) {
-            super("Lint CLI");
-            this.perfReportName = perfReportName;
-            enableCollectingPerformanceStatistics();
-            PerformanceCounter.Companion.resetAllCounters();
-        }
-
-        public void report(@NonNull LintRequest request) {
-            notifyCompilationFinished();
-
-            StringBuilder sb = new StringBuilder(perfReportName);
-            if (request.getProjects() != null) {
-                for (Project project : request.getProjects()) {
-                    sb.append('-');
-                    sb.append(project.getName());
-                    if (project.getCurrentVariant() != null) {
-                        sb.append(project.getCurrentVariant().getName());
-                    }
-                }
-            }
-            sb.append(".txt");
-
-            dumpPerformanceReport(new File(sb.toString()));
-        }
-    }
-}
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.kt b/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.kt
new file mode 100644
index 0000000..14f37c4
--- /dev/null
+++ b/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.kt
@@ -0,0 +1,1515 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.android.tools.lint
+
+import com.android.SdkConstants
+import com.android.SdkConstants.DOT_JAVA
+import com.android.SdkConstants.DOT_KT
+import com.android.SdkConstants.DOT_KTS
+import com.android.SdkConstants.VALUE_TRUE
+import com.android.Version
+import com.android.ide.common.repository.GradleVersion
+import com.android.manifmerger.ManifestMerger2
+import com.android.manifmerger.ManifestMerger2.FileStreamProvider
+import com.android.manifmerger.ManifestMerger2.MergeFailureException
+import com.android.manifmerger.MergingReport.MergedManifestKind
+import com.android.sdklib.IAndroidTarget
+import com.android.tools.lint.LintCliFlags.ERRNO_APPLIED_SUGGESTIONS
+import com.android.tools.lint.LintCliFlags.ERRNO_CREATED_BASELINE
+import com.android.tools.lint.LintCliFlags.ERRNO_ERRORS
+import com.android.tools.lint.LintCliFlags.ERRNO_SUCCESS
+import com.android.tools.lint.LintStats.Companion.create
+import com.android.tools.lint.UastEnvironment.Companion.create
+import com.android.tools.lint.checks.HardcodedValuesDetector
+import com.android.tools.lint.checks.WrongThreadInterproceduralDetector
+import com.android.tools.lint.client.api.Configuration
+import com.android.tools.lint.client.api.DefaultConfiguration
+import com.android.tools.lint.client.api.GradleVisitor
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.LintBaseline
+import com.android.tools.lint.client.api.LintClient
+import com.android.tools.lint.client.api.LintDriver
+import com.android.tools.lint.client.api.LintListener
+import com.android.tools.lint.client.api.LintRequest
+import com.android.tools.lint.client.api.UastParser
+import com.android.tools.lint.client.api.XmlParser
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Project
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.Severity.Companion.min
+import com.android.tools.lint.detector.api.TextFormat
+import com.android.tools.lint.detector.api.describeCounts
+import com.android.tools.lint.detector.api.getEncodedString
+import com.android.tools.lint.detector.api.getSourceProviders
+import com.android.tools.lint.detector.api.guessGradleLocation
+import com.android.tools.lint.helpers.DefaultUastParser
+import com.android.utils.CharSequences
+import com.android.utils.StdLogger
+import com.google.common.annotations.Beta
+import com.google.common.annotations.VisibleForTesting
+import com.google.common.base.Splitter
+import com.google.common.collect.Lists
+import com.google.common.collect.Sets
+import com.intellij.codeInsight.ExternalAnnotationsManager
+import com.intellij.core.JavaCoreProjectEnvironment
+import com.intellij.mock.MockProject
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.components.ServiceManager
+import com.intellij.openapi.roots.LanguageLevelProjectExtension
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.vfs.StandardFileSystems
+import com.intellij.pom.java.LanguageLevel
+import com.intellij.psi.PsiManager
+import com.intellij.psi.impl.file.impl.JavaFileManager
+import com.intellij.util.io.URLUtil
+import com.intellij.util.lang.UrlClassLoader
+import org.jetbrains.jps.model.java.impl.JavaSdkUtil
+import org.jetbrains.kotlin.cli.common.CommonCompilerPerformanceManager
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCliJavaFileManagerImpl
+import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
+import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndexImpl
+import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex
+import org.jetbrains.kotlin.util.PerformanceCounter.Companion.resetAllCounters
+import org.w3c.dom.Document
+import org.xml.sax.SAXException
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.IOException
+import java.io.InputStream
+import java.io.PrintWriter
+import java.net.URL
+import java.util.ArrayList
+import java.util.HashMap
+import javax.xml.parsers.ParserConfigurationException
+import kotlin.math.max
+
+/**
+ * Lint client for command line usage. Supports the flags in [LintCliFlags], and offers text,
+ * HTML and XML reporting, etc.
+ *
+ * Minimal example:
+ *
+ * <pre>
+ * // files is a list of java.io.Files, typically a directory containing
+ * // lint projects or direct references to project root directories
+ * IssueRegistry registry = new BuiltinIssueRegistry();
+ * LintCliFlags flags = new LintCliFlags();
+ * LintCliClient client = new LintCliClient(flags);
+ * int exitCode = client.run(registry, files);
+ * </pre>
+ *
+ * **NOTE: This is not a public or final API; if you rely on this be prepared to adjust your
+ * code for the next tools release.**
+ */
+@Beta
+open class LintCliClient : LintClient {
+    constructor(clientName: String) : super(clientName) {
+        flags = LintCliFlags()
+        @Suppress("LeakingThis")
+        val reporter =
+            TextReporter(this, flags, PrintWriter(System.out, true), false)
+        flags.reporters.add(reporter)
+        initialize()
+    }
+
+    @Deprecated("Specify client explicitly by calling {@link LintCliClient(String)} ")
+    constructor() : this(CLIENT_UNIT_TESTS)
+
+    constructor(flags: LintCliFlags, clientName: String) : super(clientName) {
+        this.flags = flags
+        initialize()
+    }
+
+    /** Returns the issue registry used by this client  */
+    open var registry: IssueRegistry? = null
+        protected set
+
+    /** Returns the driver running the lint checks  */
+    lateinit var driver: LintDriver
+        protected set
+
+    /** Returns the configuration used by this client  */
+    var configuration: Configuration? = null
+        get() {
+            if (field == null) {
+                if (overrideConfiguration != null) {
+                    field = overrideConfiguration
+                    return field
+                }
+                val configFile = flags.defaultConfiguration
+                if (configFile != null) {
+                    if (!configFile.exists()) {
+                        val warned = ourAlreadyWarned ?: run {
+                            val new = mutableSetOf<File>()
+                            ourAlreadyWarned = new
+                            new
+                        }
+                        if (!warned.contains(configFile)) {
+                            log(
+                                Severity.ERROR,
+                                null,
+                                "Warning: Configuration file %1\$s does not exist",
+                                configFile
+                            )
+                            warned.add(configFile)
+                        }
+                    }
+                    field = createConfigurationFromFile(configFile)
+                }
+            }
+            return field
+        }
+        private set
+
+    /** Flags configuring the lint runs */
+    val flags: LintCliFlags
+
+    private var validatedIds = false
+    private var kotlinPerformanceManager: LintCliKotlinPerformanceManager? = null
+    protected var overrideConfiguration: DefaultConfiguration? = null
+    var uastEnvironment: UastEnvironment? = null
+    var ideaProject: com.intellij.openapi.project.Project? = null
+        private set
+    private var projectDisposer: Disposable? = null
+    protected val warnings: MutableList<Warning> = ArrayList()
+    private var hasErrors = false
+    protected var errorCount = 0
+    protected var warningCount = 0
+
+    private fun initialize() {
+        var configuration = System.getenv(LINT_OVERRIDE_CONFIGURATION_ENV_VAR)
+        if (configuration == null) {
+            configuration = System.getProperty(LINT_CONFIGURATION_OVERRIDE_PROP)
+        }
+        if (configuration != null) {
+            val file = File(configuration)
+            if (file.exists()) {
+                overrideConfiguration = createConfigurationFromFile(file)
+                println("Overriding configuration from $file")
+            } else {
+                log(
+                    Severity.ERROR,
+                    null,
+                    "Configuration override requested but does not exist: $file"
+                )
+            }
+        }
+    }
+
+    protected open val baselineVariantName: String
+        get() {
+            if (flags.isFatalOnly) {
+                return LintBaseline.VARIANT_FATAL
+            }
+            val projects: Collection<Project> = driver.projects
+            for (project in projects) {
+                val variant = project.currentVariant
+                if (variant != null) {
+                    return variant.name
+                }
+            }
+            return LintBaseline.VARIANT_ALL
+        }
+
+    /**
+     * Runs the static analysis command line driver. You need to add at least one error reporter to
+     * the command line flags.
+     */
+    @Throws(IOException::class)
+    fun run(registry: IssueRegistry, files: List<File>): Int {
+        val startTime = System.currentTimeMillis()
+        assert(flags.reporters.isNotEmpty())
+        this.registry = registry
+        val kotlinPerfReport = System.getenv("KOTLIN_PERF_REPORT")
+        if (kotlinPerfReport != null && kotlinPerfReport.isNotEmpty()) {
+            kotlinPerformanceManager = LintCliKotlinPerformanceManager(kotlinPerfReport)
+        }
+        val lintRequest = createLintRequest(files)
+        driver = createDriver(registry, lintRequest)
+        driver.analysisStartTime = startTime
+        addProgressPrinter()
+        validateIssueIds()
+        driver.analyze()
+        kotlinPerformanceManager?.report(lintRequest)
+        warnings.sort()
+        val baseline = driver.baseline
+        val stats = create(warnings, baseline)
+        for (reporter in flags.reporters) {
+            reporter.write(stats, warnings)
+        }
+        var projects: Collection<Project>? = lintRequest.getProjects()
+        if (projects == null) {
+            projects = knownProjects
+        }
+        if (!projects.isEmpty()) {
+            val analytics = LintBatchAnalytics()
+            analytics.logSession(registry, flags, driver, projects, warnings)
+        }
+        if (flags.isAutoFix) {
+            val statistics = !flags.isQuiet
+            val performer = LintFixPerformer(this, statistics)
+            val fixed = performer.fix(warnings)
+            if (fixed && isGradle) {
+                val message =
+                    """
+                       One or more issues were fixed in the source code.
+                       Aborting the build since the edits to the source files were performed **after** compilation, so the outputs do not contain the fixes. Re-run the build.
+                   """.trimIndent()
+                System.err.println(message)
+                return ERRNO_APPLIED_SUGGESTIONS
+            }
+        }
+        val baselineFile = flags.baselineFile
+        if (baselineFile != null && baseline != null) {
+            emitBaselineDiagnostics(baseline, baselineFile, stats)
+        }
+        if (baselineFile != null && !baselineFile.exists() && flags.isWriteBaselineIfMissing) {
+            val dir = baselineFile.parentFile
+            var ok = true
+            if (dir != null && !dir.isDirectory) {
+                ok = dir.mkdirs()
+            }
+            if (!ok) {
+                System.err.println("Couldn't create baseline folder $dir")
+            } else {
+                val reporter = Reporter.createXmlReporter(this, baselineFile, true, false)
+                reporter.setBaselineAttributes(this, baselineVariantName)
+                reporter.write(stats, warnings)
+                System.err.println(getBaselineCreationMessage(baselineFile))
+                return ERRNO_CREATED_BASELINE
+            }
+        } else if (baseline != null &&
+            baseline.writeOnClose &&
+            baseline.fixedCount > 0 &&
+            flags.isRemoveFixedBaselineIssues
+        ) {
+            baseline.close()
+            return ERRNO_CREATED_BASELINE
+        } else if (baseline != null && flags.isUpdateBaseline) {
+            baseline.close()
+            return ERRNO_CREATED_BASELINE
+        }
+        return if (flags.isSetExitCode) if (hasErrors) ERRNO_ERRORS else ERRNO_SUCCESS else ERRNO_SUCCESS
+    }
+
+    fun getBaselineCreationMessage(baselineFile: File): String {
+        val summary = "Created baseline file $baselineFile"
+
+        if (continueAfterBaseLineCreated()) {
+            return summary
+        }
+
+        val gradlePostScript = if (isGradle) """
+            |You can run lint with -Dlint.baselines.continue=true
+            |if you want to create many missing baselines in one go.
+            """ else ""
+
+        return """
+            |$summary
+            |
+            |Also breaking the build in case this was not intentional. If you
+            |deliberately created the baseline file, re-run the build and this
+            |time it should succeed without warnings.
+            |
+            |If not, investigate the baseline path in the lintOptions config
+            |or verify that the baseline file has been checked into version
+            |control.
+            |$gradlePostScript
+            """.trimMargin()
+    }
+
+    fun emitBaselineDiagnostics(baseline: LintBaseline, baselineFile: File, stats: LintStats) {
+        var hasConsoleOutput = false
+        for (reporter in flags.reporters) {
+            if (reporter is TextReporter && reporter.isWriteToConsole) {
+                hasConsoleOutput = true
+                break
+            }
+        }
+        if (!flags.isQuiet && !hasConsoleOutput) {
+            if (stats.baselineErrorCount > 0 || stats.baselineWarningCount > 0) {
+                if (errorCount == 0 && warningCount == 1) {
+                    // the warning is the warning about baseline issues having been filtered
+                    // out, don't list this as "1 warning"
+                    print("Lint found no new issues")
+                } else {
+                    val count = describeCounts(
+                        errorCount,
+                        max(0, warningCount - 1),
+                        comma = true,
+                        capitalize = false
+                    )
+                    print("Lint found $count")
+                    if (stats.autoFixedCount > 0) {
+                        print(" (${stats.autoFixedCount} of these were automatically fixed)")
+                    }
+                }
+                val count = describeCounts(
+                    stats.baselineErrorCount,
+                    stats.baselineWarningCount,
+                    comma = true,
+                    capitalize = true
+                )
+                print(" ($count filtered by baseline ${baselineFile.name})")
+            } else {
+                val count = describeCounts(
+                    errorCount,
+                    warningCount, comma = true, capitalize = false
+                )
+                print("Lint found $count")
+            }
+            println()
+            if (stats.baselineFixedCount > 0) {
+                println(
+                    "" +
+                            "\n${stats.baselineFixedCount} errors/warnings were listed in the " +
+                            "baseline file ($baselineFile) but not found in the project; " +
+                            "perhaps they have been fixed?"
+                )
+            }
+            val checkVariant = baselineVariantName
+            val creationVariant = baseline.getAttribute("variant")
+            if (creationVariant != null && creationVariant != checkVariant) {
+                println("\nNote: The baseline was created using a different target/variant than it was checked against.")
+                println("Creation variant: " + getTargetName(creationVariant))
+                println("Current variant: " + getTargetName(checkVariant))
+            }
+            // TODO: If the versions don't match, emit some additional diagnostic hints, such as
+            // the possibility that newer versions of lint have newer checks not included in
+            // older ones, have existing checks that cover more areas, etc.
+            if (stats.baselineFixedCount > 0) {
+                val checkVersion = getClientDisplayRevision()
+                val checkClient = clientName
+                val creationVersion = baseline.getAttribute("version")
+                val creationClient = baseline.getAttribute("client")
+                if (checkClient == creationClient && creationVersion != null && checkVersion != null && creationVersion != checkVersion) {
+                    val created = GradleVersion.tryParse(creationVersion)
+                    val current = GradleVersion.tryParse(checkVersion)
+                    if (created != null && current != null && created > current) {
+                        println(
+                            """
+                            Note: The baseline was created with a newer version of $checkClient ($creationVersion) than the current version ($checkVersion)
+                            This means that some of the issues marked as fixed in the baseline may not actually be fixed, but may
+                            be new issues uncovered by the more recent version of lint.
+                            """.trimIndent()
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    protected fun validateIssueIds() {
+        driver.addLintListener(object : LintListener {
+            override fun update(
+                driver: LintDriver,
+                type: LintListener.EventType,
+                project: Project?,
+                context: Context?
+            ) {
+                if (type === LintListener.EventType.SCANNING_PROJECT && !validatedIds) {
+                    // Make sure all the id's are valid once the driver is all set up and
+                    // ready to run (such that custom rules are available in the registry etc)
+                    validateIssueIds(project)
+                }
+            }
+        })
+    }
+
+    protected open fun createDriver(registry: IssueRegistry, request: LintRequest): LintDriver {
+        this.registry = registry
+        val driver = LintDriver(registry, this, request)
+        driver.isAbbreviating = !flags.isShowEverything
+        driver.checkTestSources = flags.isCheckTestSources
+        driver.ignoreTestSources = flags.isIgnoreTestSources
+        driver.checkGeneratedSources = flags.isCheckGeneratedSources
+        driver.fatalOnlyMode = flags.isFatalOnly
+        driver.checkDependencies = flags.isCheckDependencies
+        driver.allowSuppress = flags.allowSuppress
+        val baselineFile = flags.baselineFile
+        if (baselineFile != null) {
+            val baseline = LintBaseline(this, baselineFile)
+            driver.baseline = baseline
+            if (flags.isRemoveFixedBaselineIssues) {
+                baseline.writeOnClose = true
+                baseline.removeFixed = true
+            } else if (flags.isUpdateBaseline) {
+                baseline.writeOnClose = true
+            }
+        }
+        this.driver = driver
+        return driver
+    }
+
+    protected open fun addProgressPrinter() {
+        if (!flags.isQuiet) {
+            driver.addLintListener(ProgressPrinter())
+        }
+    }
+
+    /** Creates a lint request  */
+    protected open fun createLintRequest(files: List<File>): LintRequest {
+        return LintRequest(this, files)
+    }
+
+    override fun log(severity: Severity, exception: Throwable?, format: String?, vararg args: Any) {
+        System.out.flush()
+        if (!flags.isQuiet) {
+            // Place the error message on a line of its own since we're printing '.' etc
+            // with newlines during analysis
+            System.err.println()
+        }
+        if (format != null) {
+            System.err.println(String.format(format, *args))
+        }
+        exception?.printStackTrace()
+    }
+
+    override val xmlParser: XmlParser
+        get() = LintCliXmlParser(this)
+
+    override fun getConfiguration(project: Project, driver: LintDriver?): Configuration {
+        return overrideConfiguration ?: CliConfiguration(configuration, project, flags.isFatalOnly)
+    }
+
+    /** File content cache  */
+    private val fileContents: MutableMap<File, CharSequence> = HashMap(100)
+
+    /** Read the contents of the given file, possibly cached  */
+    private fun getContents(file: File): CharSequence {
+        return fileContents.computeIfAbsent(file) { readFile(file) }
+    }
+
+    override fun getUastParser(project: Project?): UastParser = LintCliUastParser(project)
+
+    override fun getGradleVisitor(): GradleVisitor = GradleVisitor()
+
+    override fun report(
+        context: Context,
+        issue: Issue,
+        severity: Severity,
+        location: Location,
+        message: String,
+        format: TextFormat,
+        fix: LintFix?
+    ) {
+        assert(context.isEnabled(issue) || issue.category === Category.LINT)
+        if (severity.isError) {
+            hasErrors = true
+            errorCount++
+        } else if (severity === Severity.WARNING) { // Don't count informational as a warning
+            warningCount++
+        }
+        // Store the message in the raw format internally such that we can
+        // convert it to text for the text reporter, HTML for the HTML reporter
+        // and so on.
+        val rawMessage = format.convertTo(message, TextFormat.RAW)
+        val warning = Warning(issue, rawMessage, severity, context.project)
+        warnings.add(warning)
+        warning.location = location
+        val file = location.file
+        warning.file = file
+        warning.path = getDisplayPath(context.project, file)
+        warning.quickfixData = fix
+        val startPosition = location.start
+        if (startPosition != null) {
+            val line = startPosition.line
+            warning.line = line
+            warning.offset = startPosition.offset
+            val endPosition = location.end
+            if (endPosition != null) {
+                warning.endOffset = endPosition.offset
+            }
+            if (line >= 0) {
+                if (context.file === location.file) {
+                    warning.fileContents = context.getContents()
+                }
+                if (warning.fileContents == null) {
+                    warning.fileContents = getContents(location.file)
+                }
+                if (flags.isShowSourceLines) {
+                    // Compute error line contents
+                    warning.errorLine = getLine(warning.fileContents, line)
+                    if (warning.errorLine != null) {
+                        // Replace tabs with spaces such that the column
+                        // marker (^) lines up properly:
+                        warning.errorLine = warning.errorLine.replace('\t', ' ')
+                        var column = startPosition.column
+                        if (column < 0) {
+                            column = 0
+                            var i = 0
+                            while (i < warning.errorLine.length) {
+                                if (!Character.isWhitespace(warning.errorLine[i])) {
+                                    break
+                                }
+                                i++
+                                column++
+                            }
+                        }
+                        val sb = StringBuilder(100)
+                        sb.append(warning.errorLine)
+                        sb.append('\n')
+                        for (i in 0 until column) {
+                            sb.append(' ')
+                        }
+                        var displayCaret = true
+                        if (endPosition != null) {
+                            val endLine = endPosition.line
+                            val endColumn = endPosition.column
+                            if (endLine == line && endColumn > column) {
+                                for (i in column until endColumn) {
+                                    sb.append('~')
+                                }
+                                displayCaret = false
+                            }
+                        }
+                        if (displayCaret) {
+                            sb.append('^')
+                        }
+                        sb.append('\n')
+                        warning.errorLine = sb.toString()
+                    }
+                }
+            }
+        }
+    }
+
+    override fun readFile(file: File): CharSequence {
+        val contents = try {
+            getEncodedString(this, file, false)
+        } catch (e: IOException) {
+            ""
+        }
+        val path = file.path
+        if ((path.endsWith(DOT_JAVA) ||
+                    path.endsWith(DOT_KT) ||
+                    path.endsWith(DOT_KTS)) &&
+            CharSequences.indexOf(contents, '\r') != -1
+        ) {
+            // Offsets in these files will be relative to PSI's text offsets (which may
+            // have converted line offsets); make sure we use the same offsets.
+            // (Can't just do this on Windows; what matters is whether the file contains
+            // CRLF's.)
+            val vFile = StandardFileSystems.local().findFileByPath(path)
+            if (vFile != null) {
+                val project = ideaProject
+                if (project != null) {
+                    val psiFile = PsiManager.getInstance(project).findFile(vFile)
+                    if (psiFile != null) {
+                        return psiFile.text
+                    }
+                }
+            }
+        }
+        return contents
+    }
+
+    val isCheckingSpecificIssues: Boolean
+        get() = flags.exactCheckedIds != null
+
+    private var projectInfoMap: MutableMap<Project, ClassPathInfo>? = null
+
+    override fun getClassPath(project: Project): ClassPathInfo {
+        val classPath = super.getClassPath(project)
+        val sources = flags.sourcesOverride
+        val classes = flags.classesOverride
+        val libraries = flags.librariesOverride
+        if (classes == null && sources == null && libraries == null) {
+            return classPath
+        }
+        return projectInfoMap?.get(project) ?: run {
+            val info = ClassPathInfo(
+                sources ?: classPath.sourceFolders,
+                classes ?: classPath.classFolders,
+                libraries ?: classPath.getLibraries(true),
+                classPath.getLibraries(false),
+                classPath.testSourceFolders,
+                classPath.testLibraries,
+                classPath.generatedFolders
+            )
+            val map = projectInfoMap ?: run {
+                val new = HashMap<Project, ClassPathInfo>()
+                projectInfoMap = new
+                new
+            }
+            map[project] = info
+            info
+        }
+    }
+
+    override fun getResourceFolders(project: Project): List<File> {
+        return flags.resourcesOverride ?: return super.getResourceFolders(project)
+    }
+
+    /**
+     * Consult the lint.xml file, but override with the --enable and --disable flags supplied on the
+     * command line
+     */
+    protected open inner class CliConfiguration : DefaultConfiguration {
+        private val fatalOnly: Boolean
+
+        constructor(
+            parent: Configuration?,
+            project: Project,
+            fatalOnly: Boolean
+        ) : super(this@LintCliClient, project, parent) {
+            this.fatalOnly = fatalOnly
+        }
+
+        constructor(lintFile: File, fatalOnly: Boolean) :
+                super(this@LintCliClient, null, null, lintFile) {
+            this.fatalOnly = fatalOnly
+        }
+
+        protected constructor(
+            lintFile: File,
+            parent: Configuration?,
+            project: Project?,
+            fatalOnly: Boolean
+        ) : super(this@LintCliClient, project, parent, lintFile) {
+            this.fatalOnly = fatalOnly
+        }
+
+        override fun getSeverity(issue: Issue): Severity {
+            var severity = computeSeverity(issue)
+            if (fatalOnly && severity !== Severity.FATAL) {
+                return Severity.IGNORE
+            }
+            if (flags.isWarningsAsErrors && severity === Severity.WARNING) {
+                if (issue === IssueRegistry.BASELINE) {
+                    // Don't promote the baseline informational issue
+                    // (number of issues promoted) to error
+                    return severity
+                }
+                severity = Severity.ERROR
+            }
+            if (flags.isIgnoreWarnings && severity === Severity.WARNING) {
+                severity = Severity.IGNORE
+            }
+            return severity
+        }
+
+        override fun getDefaultSeverity(issue: Issue): Severity {
+            return if (flags.isCheckAllWarnings) {
+                // Exclude the interprocedural check from the "enable all warnings" flag;
+                // it's much slower and still triggers various bugs in UAST that can affect
+                // other checks.
+                if (issue === WrongThreadInterproceduralDetector.ISSUE) {
+                    super.getDefaultSeverity(issue)
+                } else issue.defaultSeverity
+            } else super.getDefaultSeverity(issue)
+        }
+
+        private fun computeSeverity(issue: Issue): Severity {
+            val severity = super.getSeverity(issue)
+            // Issue not allowed to be suppressed?
+            if (issue.suppressNames != null && !flags.allowSuppress) {
+                return getDefaultSeverity(issue)
+            }
+            val id = issue.id
+            val suppress = flags.suppressedIds
+            if (suppress.contains(id)) {
+                return Severity.IGNORE
+            }
+            val disabledCategories = flags.disabledCategories
+            if (disabledCategories != null) {
+                val category = issue.category
+                if (disabledCategories.contains(category) ||
+                    category.parent != null && disabledCategories.contains(category.parent)
+                ) {
+                    return Severity.IGNORE
+                }
+            }
+            val manual = flags.severityOverrides[id]
+            if (manual != null) {
+                return if (this.severity != null &&
+                    (this.severity.containsKey(id) || this.severity.containsKey(VALUE_ALL))
+                ) {
+                    // Ambiguity! We have a specific severity override provided
+                    // via lint options for the main app module, but a local lint.xml
+                    // file in the library (not a lintOptions definition) which also
+                    // specifies severity for the same issue.
+                    //
+                    // Who should win? Should the intent from the main app module
+                    // win, such that you have a global way to say "this is the severity
+                    // I want during this lint run?". Or should the library-local definition
+                    // win, to say "there's a local problem in this library; I need to
+                    // change things here?".
+                    //
+                    // Both are plausible, so for now I'm going with a middle ground: local
+                    // definitions should be used to turn of issues that don't work right.
+                    // Therefore, we'll take the minimum of the two severities!
+                    min(severity, manual)
+                } else manual
+            }
+            val enabled = flags.enabledIds
+            val exact = flags.exactCheckedIds
+            val enabledCategories = flags.enabledCategories
+            val exactCategories = flags.exactCategories
+            val category = issue.category
+            if (exact != null) {
+                if (exact.contains(id)) {
+                    return getVisibleSeverity(issue, severity)
+                } else if (category !== Category.LINT) {
+                    return Severity.IGNORE
+                }
+            }
+            if (exactCategories != null) {
+                if (exactCategories.contains(category) ||
+                    category.parent != null && exactCategories.contains(category.parent)
+                ) {
+                    return getVisibleSeverity(issue, severity)
+                } else if (category !== Category.LINT ||
+                    flags.disabledCategories?.contains(Category.LINT) == true
+                ) {
+                    return Severity.IGNORE
+                }
+            }
+            return if (enabled.contains(id) ||
+                enabledCategories != null && (enabledCategories.contains(category) ||
+                        category.parent != null && enabledCategories.contains(category.parent))
+            ) {
+                getVisibleSeverity(issue, severity)
+            } else severity
+        }
+
+        /** Returns the given severity, but if not visible, use the default  */
+        private fun getVisibleSeverity(issue: Issue, severity: Severity): Severity {
+            // Overriding default
+            // Detectors shouldn't be returning ignore as a default severity,
+            // but in case they do, force it up to warning here to ensure that
+            // it's run
+            var visibleSeverity = severity
+            if (visibleSeverity === Severity.IGNORE) {
+                visibleSeverity = issue.defaultSeverity
+                if (visibleSeverity === Severity.IGNORE) {
+                    visibleSeverity = Severity.WARNING
+                }
+            }
+            return visibleSeverity
+        }
+    }
+
+    override fun createProject(dir: File, referenceDir: File): Project {
+        val project = super.createProject(dir, referenceDir)
+        val compileSdkVersion = flags.compileSdkVersionOverride
+        if (compileSdkVersion != null) {
+            project.buildTargetHash = compileSdkVersion
+        }
+        project.ideaProject = ideaProject
+        return project
+    }
+
+    /**
+     * Checks that any id's specified by id refer to valid, known, issues. This typically can't be
+     * done right away (in for example the Gradle code which handles DSL references to strings, or
+     * in the command line parser for the lint command) because the full set of valid id's is not
+     * known until lint actually starts running and for example gathers custom rules from all AAR
+     * dependencies reachable from libraries, etc.
+     */
+    private fun validateIssueIds(project: Project?) {
+        if (::driver.isInitialized) {
+            val registry = driver.registry
+            if (!registry.isIssueId(HardcodedValuesDetector.ISSUE.id)) {
+                // This should not be necessary, but there have been some strange
+                // reports where lint has reported some well known builtin issues
+                // to not exist:
+                //
+                //   Error: Unknown issue id "DuplicateDefinition" [LintError]
+                //   Error: Unknown issue id "GradleIdeError" [LintError]
+                //   Error: Unknown issue id "InvalidPackage" [LintError]
+                //   Error: Unknown issue id "JavascriptInterface" [LintError]
+                //   ...
+                //
+                // It's not clear how this can happen, though it's probably related
+                // to using 3rd party lint rules (where lint will create new composite
+                // issue registries to wrap the various additional issues) - but
+                // we definitely don't want to validate issue id's if we can't find
+                // well known issues.
+                return
+            }
+            validatedIds = true
+            validateIssueIds(project, registry, flags.exactCheckedIds)
+            validateIssueIds(project, registry, flags.enabledIds)
+            validateIssueIds(project, registry, flags.suppressedIds)
+            validateIssueIds(project, registry, flags.severityOverrides.keys)
+            if (project != null) {
+                val configuration = project.getConfiguration(driver)
+                configuration.validateIssueIds(this, driver, project, registry)
+            }
+        }
+    }
+
+    private fun validateIssueIds(
+        project: Project?,
+        registry: IssueRegistry,
+        ids: Collection<String>?
+    ) {
+        if (ids != null) {
+            for (id in ids) {
+                if (registry.getIssue(id) == null) {
+                    reportNonExistingIssueId(project, id)
+                }
+            }
+        }
+    }
+
+    private fun reportNonExistingIssueId(project: Project?, id: String) {
+        val message = "Unknown issue id \"$id\""
+        if (::driver.isInitialized && project != null && !isSuppressed(IssueRegistry.LINT_ERROR)) {
+            val location = guessGradleLocation(this, project.dir, id)
+            report(
+                this,
+                IssueRegistry.LINT_ERROR,
+                message,
+                driver,
+                project,
+                location,
+                LintFix.create().data(id)
+            )
+        } else {
+            log(Severity.ERROR, null, "Lint: %1\$s", message)
+        }
+    }
+
+    private class ProgressPrinter : LintListener {
+        override fun update(
+            driver: LintDriver,
+            type: LintListener.EventType,
+            project: Project?,
+            context: Context?
+        ) {
+            when (type) {
+                LintListener.EventType.SCANNING_PROJECT -> {
+                    val name = context?.project?.name ?: "?"
+                    if (driver.phase > 1) {
+                        print("\nScanning $name (Phase ${driver.phase}): ")
+                    } else {
+                        print("\nScanning $name: ")
+                    }
+                }
+                LintListener.EventType.SCANNING_LIBRARY_PROJECT -> {
+                    val name = context?.project?.name ?: "?"
+                    print("\n         - $name: ")
+                }
+                LintListener.EventType.SCANNING_FILE -> print('.')
+                LintListener.EventType.NEW_PHASE -> {
+                }
+                LintListener.EventType.CANCELED, LintListener.EventType.COMPLETED -> println()
+                LintListener.EventType.REGISTERED_PROJECT, LintListener.EventType.STARTING -> {
+                }
+            }
+        }
+    }
+
+    fun getDisplayPath(project: Project, file: File): String {
+        return getDisplayPath(project, file, flags.isFullPath)
+    }
+
+    fun getDisplayPath(project: Project, file: File, fullPath: Boolean): String {
+        var path = file.path
+        if (!fullPath && path.startsWith(project.referenceDir.path)) {
+            var chop = project.referenceDir.path.length
+            if (path.length > chop && path[chop] == File.separatorChar) {
+                chop++
+            }
+            path = path.substring(chop)
+            if (path.isEmpty()) {
+                path = file.name
+            }
+        } else if (fullPath) {
+            path = getCleanPath(file.absoluteFile)
+        } else if (file.isAbsolute && file.exists()) {
+            path = getRelativePath(project.referenceDir, file) ?: file.path
+        }
+        return path
+    }
+
+    /** Returns whether all warnings are enabled, including those disabled by default  */
+    val isAllEnabled: Boolean
+        get() = flags.isCheckAllWarnings
+
+    /** Returns true if the given issue has been explicitly disabled  */
+    fun isSuppressed(issue: Issue): Boolean {
+        val disabledCategories = flags.disabledCategories
+        if (disabledCategories != null) {
+            val category = issue.category
+            if (disabledCategories.contains(category) || category.parent != null && disabledCategories.contains(
+                    category.parent
+                )
+            ) {
+                return true
+            }
+        }
+        return flags.suppressedIds.contains(issue.id)
+    }
+
+    fun createConfigurationFromFile(file: File): DefaultConfiguration {
+        return CliConfiguration(file, flags.isFatalOnly)
+    }
+
+    val projectEnvironment: JavaCoreProjectEnvironment?
+        get() = uastEnvironment?.projectEnvironment
+
+    public override fun initializeProjects(knownProjects: Collection<Project>) {
+        // Initialize the associated idea project to use
+        val includeTests = !flags.isIgnoreTestSources
+        // knownProject only lists root projects, not dependencies
+        val allProjects = Sets.newIdentityHashSet<Project>()
+        for (project in knownProjects) {
+            allProjects.add(project)
+            allProjects.addAll(project.allLibraries)
+        }
+        val files: MutableList<File> = ArrayList(50)
+        for (project in allProjects) {
+            // Note that there could be duplicates here since we're including multiple library
+            // dependencies that could have the same dependencies (e.g. lib1 and lib2 both
+            // referencing guava.jar)
+            files.addAll(project.javaSourceFolders)
+            if (includeTests) {
+                files.addAll(project.testSourceFolders)
+            }
+            files.addAll(project.generatedSourceFolders)
+            files.addAll(project.getJavaLibraries(true))
+            if (includeTests) {
+                files.addAll(project.testLibraries)
+            }
+            // Don't include all class folders:
+            //  files.addAll(project.getJavaClassFolders());
+            // These are the outputs from the sources and generated sources, which we will
+            // parse directly with PSI/UAST anyway. Including them here leads lint to do
+            // a lot more work (e.g. when resolving symbols it looks at both .java and .class
+            // matches).
+            // However, we *do* need them for libraries; otherwise, type resolution into
+            // compiled libraries will not work; see
+            // https://issuetracker.google.com/72032121
+            if (project.isLibrary) {
+                files.addAll(project.javaClassFolders)
+            } else if (project.isGradleProject) {
+                // As of 3.4, R.java is in a special jar file
+                for (f in project.javaClassFolders) {
+                    if (f.name == SdkConstants.FN_R_CLASS_JAR) {
+                        files.add(f)
+                    }
+                }
+            }
+        }
+        addBootClassPath(knownProjects, files)
+        var maxLevel = LanguageLevel.JDK_1_7
+        for (project in knownProjects) {
+            val model = project.gradleProjectModel
+            if (model != null) {
+                val javaCompileOptions = model.javaCompileOptions
+                val sourceCompatibility = javaCompileOptions.sourceCompatibility
+                val level = LanguageLevel.parse(sourceCompatibility)
+                if (level != null && maxLevel.isLessThan(level)) {
+                    maxLevel = level
+                }
+            }
+        }
+        val parentDisposable = Disposer.newDisposable()
+        projectDisposer = parentDisposable
+        val environment = create(parentDisposable)
+        uastEnvironment = environment
+        val projectEnvironment = environment.projectEnvironment
+        val mockProject = projectEnvironment.project
+        ideaProject = mockProject
+        val languageLevelProjectExtension =
+            mockProject.getComponent(LanguageLevelProjectExtension::class.java)
+        if (languageLevelProjectExtension != null) {
+            languageLevelProjectExtension.languageLevel = maxLevel
+        }
+        projectEnvironment.registerPaths(files)
+        val manager = ServiceManager.getService(
+            mockProject,
+            JavaFileManager::class.java
+        ) as KotlinCliJavaFileManagerImpl
+        val roots: MutableList<JavaRoot> = ArrayList()
+        for (file in files) {
+            // IntelliJ's framework requires absolute path to JARs, name resolution might fail
+            // otherwise. We defensively ensure all paths are absolute.
+            require(file.isAbsolute) {
+                "Relative Path found: ${file.path}. All paths should be absolute."
+            }
+            if (file.isDirectory) {
+                val vFile = StandardFileSystems.local().findFileByPath(file.path)
+                if (vFile != null) {
+                    roots.add(JavaRoot(vFile, JavaRoot.RootType.SOURCE, null))
+                }
+            } else {
+                val pathString = file.path
+                if (pathString.endsWith(SdkConstants.DOT_SRCJAR)) {
+                    // Mount as source files
+                    val root =
+                        StandardFileSystems.jar().findFileByPath(pathString + URLUtil.JAR_SEPARATOR)
+                    if (root != null) {
+                        roots.add(JavaRoot(root, JavaRoot.RootType.SOURCE, null))
+                    }
+                }
+            }
+        }
+        val index = JvmDependenciesIndexImpl(roots)
+        manager.initialize(index, emptyList(), SingleJavaFileRootsIndex(emptyList()), false)
+        for (project in allProjects) {
+            project.ideaProject = ideaProject
+        }
+        super.initializeProjects(knownProjects)
+    }
+
+    protected open fun addBootClassPath(
+        knownProjects: Collection<Project>,
+        files: MutableList<File>
+    ): Boolean {
+        var buildTarget: IAndroidTarget? = null
+        var isAndroid = false
+        for (project in knownProjects) {
+            if (project.isAndroidProject) {
+                isAndroid = true
+            }
+            val t = project.buildTarget
+            if (t != null) {
+                if (buildTarget == null) {
+                    buildTarget = t
+                } else if (buildTarget.version > t.version) {
+                    buildTarget = t
+                }
+            }
+        }
+        if (buildTarget != null) {
+            val file: File? = buildTarget.getFile(IAndroidTarget.ANDROID_JAR)
+            if (file != null) {
+                // because we're partially mocking it in some tests
+                files.add(file)
+                return true
+            }
+        }
+        if (!isAndroid) {
+            // Non android project, e.g. perhaps a pure Kotlin project
+            // Gradle doesn't let you configure separate SDKs; it runs the Gradle
+            // daemon on the JDK that should be used for compilation so look up the
+            // current environment:
+            var javaHome = System.getProperty("java.home")
+            if (javaHome == null) {
+                javaHome = System.getenv("JAVA_HOME")
+            }
+            if (javaHome != null) { // but java.home should always be set...
+                val home = File(javaHome)
+                val isJre = home.name == "jre"
+                val roots = JavaSdkUtil.getJdkClassesRoots(home, isJre)
+                for (root in roots) {
+                    if (root.exists()) {
+                        files.add(root)
+                    }
+                }
+                return true
+            }
+        }
+        return false
+    }
+
+    public override fun disposeProjects(knownProjects: Collection<Project>) {
+        projectDisposer?.let { Disposer.dispose(it) }
+        ideaProject = null
+        projectDisposer = null
+        super.disposeProjects(knownProjects)
+    }
+
+    val isOverridingConfiguration: Boolean
+        get() = overrideConfiguration != null
+
+    /** Synchronizes any options specified in lint.xml with the [LintCliFlags] object  */
+    fun syncConfigOptions() {
+        val configuration = configuration
+        if (configuration is DefaultConfiguration) {
+            val config = configuration
+            val checkAllWarnings = config.checkAllWarnings
+            if (checkAllWarnings != null) {
+                flags.isCheckAllWarnings = checkAllWarnings
+            }
+            val ignoreWarnings = config.ignoreWarnings
+            if (ignoreWarnings != null) {
+                flags.isIgnoreWarnings = ignoreWarnings
+            }
+            val warningsAsErrors = config.warningsAsErrors
+            if (warningsAsErrors != null) {
+                flags.isWarningsAsErrors = warningsAsErrors
+            }
+            val fatalOnly = config.fatalOnly
+            if (fatalOnly != null) {
+                flags.isFatalOnly = fatalOnly
+            }
+            val checkTestSources = config.checkTestSources
+            if (checkTestSources != null) {
+                flags.isCheckTestSources = checkTestSources
+            }
+            val ignoreTestSources = config.ignoreTestSources
+            if (ignoreTestSources != null) {
+                flags.isIgnoreTestSources = ignoreTestSources
+            }
+            val checkGeneratedSources = config.checkGeneratedSources
+            if (checkGeneratedSources != null) {
+                flags.isCheckGeneratedSources = checkGeneratedSources
+            }
+            val checkDependencies = config.checkDependencies
+            if (checkDependencies != null) {
+                flags.isCheckDependencies = checkDependencies
+            }
+            val explainIssues = config.explainIssues
+            if (explainIssues != null) {
+                flags.isExplainIssues = explainIssues
+            }
+            val removeFixedBaselineIssues = config.removeFixedBaselineIssues
+            if (removeFixedBaselineIssues != null) {
+                flags.setRemovedFixedBaselineIssues(removeFixedBaselineIssues)
+            }
+            val abortOnError = config.abortOnError
+            if (abortOnError != null) {
+                flags.isSetExitCode = abortOnError
+            }
+            val baselineFile = config.baselineFile
+            if (baselineFile != null) {
+                flags.baselineFile =
+                    if (baselineFile.path == SdkConstants.VALUE_NONE) null else baselineFile
+            }
+            val applySuggestions = config.applySuggestions
+            if (applySuggestions != null && applySuggestions) {
+                flags.isAutoFix = true
+            }
+        }
+    }
+
+    override fun getClientRevision(): String? {
+        val plugin = Version.TOOLS_VERSION
+        return plugin ?: "unknown"
+    }
+
+    fun haveErrors(): Boolean {
+        return errorCount > 0
+    }
+
+    @VisibleForTesting
+    open fun reset() {
+        warnings.clear()
+        errorCount = 0
+        warningCount = 0
+        projectDirs.clear()
+        dirToProject.clear()
+    }
+
+    override fun createUrlClassLoader(urls: Array<URL>, parent: ClassLoader): ClassLoader {
+        return UrlClassLoader.build().parent(parent).urls(*urls).get()
+    }
+
+    override fun getMergedManifest(project: Project): Document? {
+        val manifests: MutableList<File> = Lists.newArrayList()
+        for (dependency in project.allLibraries) {
+            manifests.addAll(dependency.manifestFiles)
+        }
+        val injectedFile = File("injected-from-gradle")
+        val injectedXml = StringBuilder()
+        if (project.gradleProjectModel != null && project.currentVariant != null) {
+            val mergedFlavor = project.currentVariant!!.mergedFlavor
+            val targetSdkVersion = mergedFlavor.targetSdkVersion
+            val minSdkVersion = mergedFlavor.minSdkVersion
+            if (targetSdkVersion != null || minSdkVersion != null) {
+                injectedXml.append(
+                    "" +
+                            "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                            "    package=\"\${packageName}\">\n" +
+                            "    <uses-sdk"
+                )
+                if (minSdkVersion != null) {
+                    injectedXml.append(" android:minSdkVersion=\"").append(minSdkVersion.apiString)
+                        .append("\"")
+                }
+                if (targetSdkVersion != null) {
+                    injectedXml.append(" android:targetSdkVersion=\"")
+                        .append(targetSdkVersion.apiString).append("\"")
+                }
+                injectedXml.append(" />\n</manifest>\n")
+                manifests.add(injectedFile)
+            }
+        }
+        var mainManifest: File? = null
+        if (project.gradleProjectModel != null && project.currentVariant != null) {
+            for (provider in getSourceProviders(
+                project.gradleProjectModel!!,
+                project.currentVariant!!
+            )) {
+                val manifestFile = provider.manifestFile
+                if (manifestFile.exists()) { // model returns path whether or not it exists
+                    if (mainManifest == null) {
+                        mainManifest = manifestFile
+                    } else {
+                        manifests.add(manifestFile)
+                    }
+                }
+            }
+            if (mainManifest == null) {
+                return null
+            }
+        } else {
+            val projectManifests = project.manifestFiles
+            if (projectManifests.isEmpty()) {
+                return null
+            }
+            mainManifest = projectManifests[0]
+            for (i in 1 until projectManifests.size) {
+                manifests.add(projectManifests[i])
+            }
+        }
+        if (mainManifest == null) {
+            return null
+        }
+        if (manifests.isEmpty()) {
+            // Only the main manifest: that's easy
+            try {
+                val document = xmlParser.parseXml(mainManifest)
+                document?.let { resolveMergeManifestSources(it, mainManifest) }
+                return document
+            } catch (e: IOException) {
+                log(Severity.WARNING, e, "Could not parse %1\$s", mainManifest)
+            } catch (e: SAXException) {
+                log(Severity.WARNING, e, "Could not parse %1\$s", mainManifest)
+            } catch (e: ParserConfigurationException) {
+                log(Severity.WARNING, e, "Could not parse %1\$s", mainManifest)
+            }
+            return null
+        }
+        try {
+            val logger = StdLogger(StdLogger.Level.INFO)
+            val type =
+                if (project.isLibrary) ManifestMerger2.MergeType.LIBRARY else ManifestMerger2.MergeType.APPLICATION
+            val mergeReport = ManifestMerger2.newMerger(mainManifest, logger, type).withFeatures(
+                // TODO: How do we get the *opposite* of EXTRACT_FQCNS:
+                // ensure that all names are made fully qualified?
+                ManifestMerger2.Invoker.Feature.SKIP_BLAME,
+                ManifestMerger2.Invoker.Feature.SKIP_XML_STRING,
+                ManifestMerger2.Invoker.Feature.NO_PLACEHOLDER_REPLACEMENT
+            ).addLibraryManifests(*manifests.toTypedArray())
+                .withFileStreamProvider(object : FileStreamProvider() {
+                    @Throws(FileNotFoundException::class)
+                    override fun getInputStream(file: File): InputStream {
+                        if (injectedFile == file) {
+                            return CharSequences.getInputStream(injectedXml.toString())
+                        }
+                        val text = readFile(file)
+                        // TODO: Avoid having to convert back and forth
+                        return CharSequences.getInputStream(text)
+                    }
+                }).merge()
+            val xmlDocument = mergeReport.getMergedXmlDocument(MergedManifestKind.MERGED)
+            if (xmlDocument != null) {
+                val document = xmlDocument.xml
+                if (document != null) {
+                    resolveMergeManifestSources(document, mergeReport.actions)
+                    return document
+                }
+            } else {
+                log(Severity.WARNING, null, mergeReport.reportString)
+            }
+        } catch (e: MergeFailureException) {
+            log(Severity.ERROR, e, "Couldn't parse merged manifest")
+        }
+        return super.getMergedManifest(project)
+    }
+
+    protected open inner class LintCliUastParser(project: Project?) :
+        DefaultUastParser(project, ideaProject!!) {
+        override fun prepare(
+            contexts: List<JavaContext>,
+            testContexts: List<JavaContext>
+        ): Boolean {
+            // If we're using Kotlin, ensure we initialize the bridge
+            val kotlinFiles: MutableList<File> = ArrayList()
+            for (context in contexts) {
+                val path = context.file.path
+                if (path.endsWith(DOT_KT) || path.endsWith(DOT_KTS)) {
+                    kotlinFiles.add(context.file)
+                }
+            }
+            for (context in testContexts) {
+                val path = context.file.path
+                if (path.endsWith(DOT_KT) || path.endsWith(DOT_KTS)) {
+                    kotlinFiles.add(context.file)
+                }
+            }
+            // We unconditionally invoke the KotlinLintAnalyzerFacade, even
+            // if kotlinFiles is empty -- without this, the machinery in
+            // the project (such as the CliLightClassGenerationSupport and
+            // the CoreFileManager) will throw exceptions at runtime even
+            // for plain class lookup
+            val project = ideaProject as? MockProject
+            val environment = uastEnvironment
+            if (project != null && environment != null) {
+                val paths = environment.projectEnvironment.paths
+                KotlinLintAnalyzerFacade(kotlinPerformanceManager).analyze(
+                    kotlinFiles,
+                    paths,
+                    project
+                )
+            }
+            val ok = super.prepare(contexts, testContexts)
+            if (project == null || contexts.isEmpty() && testContexts.isEmpty()) {
+                return ok
+            }
+            // Now that we have a project context, ensure that the annotations manager
+            // is up to date
+            val annotationsManager =
+                ExternalAnnotationsManager.getInstance(project) as LintExternalAnnotationsManager
+            annotationsManager.updateAnnotationRoots(this@LintCliClient)
+            return ok
+        }
+    }
+
+    private class LintCliKotlinPerformanceManager(private val perfReportName: String) :
+        CommonCompilerPerformanceManager("Lint CLI") {
+        fun report(request: LintRequest) {
+            notifyCompilationFinished()
+            val sb = StringBuilder(perfReportName)
+            val projects = request.getProjects()
+            if (projects != null) {
+                for (project in projects) {
+                    sb.append('-')
+                    sb.append(project.name)
+                    val variant = project.currentVariant
+                    if (variant != null) {
+                        sb.append(variant.name)
+                    }
+                }
+            }
+            sb.append(".txt")
+            dumpPerformanceReport(File(sb.toString()))
+        }
+
+        init {
+            enableCollectingPerformanceStatistics()
+            resetAllCounters()
+        }
+    }
+
+    companion object {
+        // Environment variable, system property and internal system property used to tell lint to
+        // override the configuration
+        private const val LINT_OVERRIDE_CONFIGURATION_ENV_VAR = "LINT_OVERRIDE_CONFIGURATION"
+        private const val LINT_CONFIGURATION_OVERRIDE_PROP = "lint.configuration.override"
+
+        /** Whether lint should continue running after a baseline has been created  */
+        fun continueAfterBaseLineCreated(): Boolean {
+            return System.getProperty("lint.baselines.continue") == VALUE_TRUE
+        }
+
+        protected fun getTargetName(baselineVariantName: String): String {
+            if (isGradle) {
+                if (LintBaseline.VARIANT_ALL == baselineVariantName) {
+                    return "lint"
+                } else if (LintBaseline.VARIANT_FATAL == baselineVariantName) {
+                    return "lintVitalRelease"
+                }
+            }
+            return baselineVariantName
+        }
+
+        /** Look up the contents of the given line  */
+        fun getLine(contents: CharSequence, line: Int): String? {
+            val index = getLineOffset(contents, line)
+            return if (index != -1) {
+                getLineOfOffset(contents, index)
+            } else {
+                null
+            }
+        }
+
+        private fun getLineOfOffset(contents: CharSequence, offset: Int): String {
+            var end = CharSequences.indexOf(contents, '\n', offset)
+            if (end == -1) {
+                end = CharSequences.indexOf(contents, '\r', offset)
+            } else if (end > 0 && contents[end - 1] == '\r') {
+                end--
+            }
+            return contents.subSequence(offset, if (end != -1) end else contents.length).toString()
+        }
+
+        /** Look up the contents of the given line  */
+        private fun getLineOffset(contents: CharSequence, line: Int): Int {
+            var index = 0
+            for (i in 0 until line) {
+                index = CharSequences.indexOf(contents, '\n', index)
+                if (index == -1) {
+                    return -1
+                }
+                index++
+            }
+            return index
+        }
+
+        /**
+         * Given a file, it produces a cleaned up path from the file. This will clean up the path such
+         * that `foo/./bar` becomes `foo/bar` and `foo/bar/../baz` becomes `foo/baz`.
+         *
+         * Unlike [java.io.File.getCanonicalPath] however, it will **not** attempt to make
+         * the file canonical, such as expanding symlinks and network mounts.
+         *
+         * @param file the file to compute a clean path for
+         * @return the cleaned up path
+         */
+        @JvmStatic
+        @VisibleForTesting
+        fun getCleanPath(file: File): String {
+            val path = file.path
+            val sb = StringBuilder(path.length)
+            if (path.startsWith(File.separator)) {
+                sb.append(File.separator)
+            }
+            elementLoop@ for (element in Splitter.on(File.separatorChar).omitEmptyStrings().split(
+                path
+            )) {
+                if (element == ".") {
+                    continue
+                } else if (element == "..") {
+                    if (sb.isNotEmpty()) {
+                        for (i in sb.length - 1 downTo 0) {
+                            val c = sb[i]
+                            if (c == File.separatorChar) {
+                                sb.setLength(if (i == 0) 1 else i)
+                                continue@elementLoop
+                            }
+                        }
+                        sb.setLength(0)
+                        continue
+                    }
+                }
+                if (sb.length > 1) {
+                    sb.append(File.separatorChar)
+                } else if (sb.isNotEmpty() && sb[0] != File.separatorChar) {
+                    sb.append(File.separatorChar)
+                }
+                sb.append(element)
+            }
+            if (path.endsWith(File.separator) && sb.isNotEmpty() && sb[sb.length - 1] != File.separatorChar) {
+                sb.append(File.separator)
+            }
+            return sb.toString()
+        }
+
+        private var ourAlreadyWarned: MutableSet<File>? = null
+    }
+}
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCoreApplicationEnvironment.java b/lint/cli/src/main/java/com/android/tools/lint/LintCoreApplicationEnvironment.java
deleted file mode 100644
index 41aef98..0000000
--- a/lint/cli/src/main/java/com/android/tools/lint/LintCoreApplicationEnvironment.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * 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.android.tools.lint;
-
-import com.intellij.codeInsight.ContainerProvider;
-import com.intellij.codeInsight.CustomExceptionHandler;
-import com.intellij.codeInsight.ExternalAnnotationsManager;
-import com.intellij.codeInsight.InferredAnnotationsManager;
-import com.intellij.codeInsight.runner.JavaMainMethodProvider;
-import com.intellij.core.CoreApplicationEnvironment;
-import com.intellij.core.JavaCoreApplicationEnvironment;
-import com.intellij.core.JavaCoreProjectEnvironment;
-import com.intellij.lang.MetaLanguage;
-import com.intellij.lang.java.JavaParserDefinition;
-import com.intellij.mock.MockProject;
-import com.intellij.openapi.Disposable;
-import com.intellij.openapi.application.TransactionGuard;
-import com.intellij.openapi.application.TransactionGuardImpl;
-import com.intellij.openapi.extensions.Extensions;
-import com.intellij.openapi.extensions.ExtensionsArea;
-import com.intellij.openapi.fileTypes.FileTypeExtensionPoint;
-import com.intellij.openapi.fileTypes.PlainTextFileType;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.vfs.impl.ZipHandler;
-import com.intellij.psi.FileContextProvider;
-import com.intellij.psi.JavaModuleSystem;
-import com.intellij.psi.PsiElementFinder;
-import com.intellij.psi.augment.PsiAugmentProvider;
-import com.intellij.psi.augment.TypeAnnotationModifier;
-import com.intellij.psi.compiled.ClassFileDecompilers;
-import com.intellij.psi.impl.JavaClassSupersImpl;
-import com.intellij.psi.impl.PsiTreeChangePreprocessor;
-import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy;
-import com.intellij.psi.meta.MetaDataContributor;
-import com.intellij.psi.stubs.BinaryFileStubBuilders;
-import com.intellij.psi.util.JavaClassSupers;
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
-import org.jetbrains.kotlin.resolve.diagnostics.DiagnosticSuppressor;
-import org.jetbrains.uast.UastContext;
-import org.jetbrains.uast.UastLanguagePlugin;
-import org.jetbrains.uast.evaluation.UEvaluatorExtension;
-import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin;
-
-public class LintCoreApplicationEnvironment extends JavaCoreApplicationEnvironment {
-    private static final Object APPLICATION_LOCK = new Object();
-
-    private static LintCoreApplicationEnvironment ourApplicationEnvironment;
-    private static int ourProjectCount;
-
-    public static LintCoreApplicationEnvironment get() {
-        synchronized (APPLICATION_LOCK) {
-            if (ourApplicationEnvironment != null) {
-                return ourApplicationEnvironment;
-            }
-
-            Disposable parentDisposable = Disposer.newDisposable();
-
-            // TODO: Set to true from unit tests.
-            // We can't do it right now because a lot of UAST code throws exceptions
-            // in that mode.
-            boolean unitTestMode = false;
-
-            ourApplicationEnvironment =
-                    createApplicationEnvironment(parentDisposable, unitTestMode);
-            ourProjectCount = 0;
-            Disposer.register(
-                    parentDisposable,
-                    () -> {
-                        synchronized (APPLICATION_LOCK) {
-                            ourApplicationEnvironment = null;
-                        }
-                    });
-
-            return ourApplicationEnvironment;
-        }
-    }
-
-    public LintCoreApplicationEnvironment(Disposable parentDisposable, boolean unitTestMode) {
-        super(parentDisposable, unitTestMode);
-    }
-
-    private static LintCoreApplicationEnvironment createApplicationEnvironment(
-            Disposable parentDisposable, boolean unitTestMode) {
-        // We don't bundle .dll files in the Gradle plugin for native file system access;
-        // prevent warning logs on Windows when it's not found (see b.android.com/260180)
-        System.setProperty("idea.use.native.fs.for.win", "false");
-
-        Extensions.cleanRootArea(parentDisposable);
-        registerAppExtensionPoints();
-        LintCoreApplicationEnvironment applicationEnvironment =
-                new LintCoreApplicationEnvironment(parentDisposable, unitTestMode);
-
-        registerApplicationServicesForCLI(applicationEnvironment);
-        registerApplicationServices(applicationEnvironment);
-
-        KotlinCoreEnvironment.registerApplicationServices(applicationEnvironment);
-
-        synchronized (APPLICATION_LOCK) {
-            ourProjectCount++;
-        }
-
-        return applicationEnvironment;
-    }
-
-    public static void clearAccessorCache() {
-        synchronized (APPLICATION_LOCK) {
-            ZipHandler.clearFileAccessorCache();
-        }
-    }
-
-    public static void disposeApplicationEnvironment() {
-        synchronized (APPLICATION_LOCK) {
-            LintCoreApplicationEnvironment environment = ourApplicationEnvironment;
-            if (environment == null) {
-                return;
-            }
-            ourApplicationEnvironment = null;
-            Disposer.dispose(environment.getParentDisposable());
-            ZipHandler.clearFileAccessorCache();
-        }
-    }
-
-    private static void registerAppExtensionPoints() {
-        ExtensionsArea rootArea = Extensions.getRootArea();
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, FileContextProvider.EP_NAME, FileContextProvider.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, MetaDataContributor.EP_NAME, MetaDataContributor.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, ContainerProvider.EP_NAME, ContainerProvider.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, ClsCustomNavigationPolicy.EP_NAME, ClsCustomNavigationPolicy.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, MetaLanguage.EP_NAME, MetaLanguage.class);
-
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea,
-                UastLanguagePlugin.Companion.getExtensionPointName(),
-                UastLanguagePlugin.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, CustomExceptionHandler.KEY, CustomExceptionHandler.class); // TODO: Remove
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, JavaModuleSystem.EP_NAME, JavaModuleSystem.class);
-
-        CoreApplicationEnvironment.registerExtensionPoint(
-                rootArea, DiagnosticSuppressor.Companion.getEP_NAME(), DiagnosticSuppressor.class);
-
-        rootArea.getExtensionPoint(UastLanguagePlugin.Companion.getExtensionPointName())
-                .registerExtension(new org.jetbrains.uast.java.JavaUastLanguagePlugin());
-
-        KotlinUastLanguagePlugin plugin = new KotlinUastLanguagePlugin();
-        rootArea.getExtensionPoint(UastLanguagePlugin.Companion.getExtensionPointName())
-                .registerExtension(plugin);
-    }
-
-    private static void registerApplicationServicesForCLI(
-            JavaCoreApplicationEnvironment applicationEnvironment) {
-        // ability to get text from annotations xml files
-        applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml");
-        applicationEnvironment.registerParserDefinition(new JavaParserDefinition());
-    }
-
-    private static void registerApplicationServices(
-            JavaCoreApplicationEnvironment applicationEnvironment) {
-        applicationEnvironment
-                .getApplication()
-                .registerService(JavaClassSupers.class, JavaClassSupersImpl.class);
-        applicationEnvironment
-                .getApplication()
-                .registerService(TransactionGuard.class, TransactionGuardImpl.class);
-    }
-
-    static void registerProjectExtensionPoints(ExtensionsArea area) {
-        CoreApplicationEnvironment.registerExtensionPoint(
-                area, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                area, PsiElementFinder.EP_NAME, PsiElementFinder.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                area,
-                UastLanguagePlugin.Companion.getExtensionPointName(),
-                UastLanguagePlugin.class);
-        CoreApplicationEnvironment.registerExtensionPoint(
-                area,
-                UEvaluatorExtension.Companion.getEXTENSION_POINT_NAME(),
-                UEvaluatorExtension.class);
-    }
-
-    public static void registerProjectServices(JavaCoreProjectEnvironment projectEnvironment) {
-        MockProject project = projectEnvironment.getProject();
-        project.registerService(UastContext.class, new UastContext(project));
-        project.registerService(
-                ExternalAnnotationsManager.class, LintExternalAnnotationsManager.class);
-        project.registerService(
-                InferredAnnotationsManager.class, LintInferredAnnotationsManager.class);
-    }
-}
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCoreProjectEnvironment.java b/lint/cli/src/main/java/com/android/tools/lint/LintCoreProjectEnvironment.java
deleted file mode 100644
index c38630c..0000000
--- a/lint/cli/src/main/java/com/android/tools/lint/LintCoreProjectEnvironment.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * 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.android.tools.lint;
-
-import static com.android.SdkConstants.DOT_SRCJAR;
-
-import com.android.annotations.NonNull;
-import com.google.common.collect.Sets;
-import com.intellij.core.CoreApplicationEnvironment;
-import com.intellij.core.CoreJavaFileManager;
-import com.intellij.core.JavaCoreApplicationEnvironment;
-import com.intellij.core.JavaCoreProjectEnvironment;
-import com.intellij.mock.MockProject;
-import com.intellij.openapi.Disposable;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.extensions.Extensions;
-import com.intellij.openapi.extensions.ExtensionsArea;
-import com.intellij.openapi.vfs.StandardFileSystems;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileSystem;
-import com.intellij.psi.PsiElementFinder;
-import com.intellij.psi.PsiManager;
-import com.intellij.psi.impl.PsiElementFinderImpl;
-import com.intellij.psi.impl.file.impl.JavaFileManager;
-import com.intellij.util.io.URLUtil;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCliJavaFileManagerImpl;
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
-
-public class LintCoreProjectEnvironment extends JavaCoreProjectEnvironment {
-    @NonNull
-    public static LintCoreProjectEnvironment create(
-            @NonNull Disposable parentDisposable,
-            @NonNull JavaCoreApplicationEnvironment applicationEnvironment) {
-        return new LintCoreProjectEnvironment(parentDisposable, applicationEnvironment);
-    }
-
-    @Override
-    protected void preregisterServices() {
-        super.preregisterServices();
-        KotlinCoreEnvironment.registerProjectExtensionPoints(Extensions.getArea(getProject()));
-    }
-
-    @Override
-    protected void registerJavaPsiFacade() {
-        MockProject project = getProject();
-        ExtensionsArea area = Extensions.getArea(project);
-
-        project.registerService(
-                CoreJavaFileManager.class,
-                (CoreJavaFileManager) ServiceManager.getService(project, JavaFileManager.class));
-
-        area.getExtensionPoint(PsiElementFinder.EP_NAME)
-                .registerExtension(
-                        new PsiElementFinderImpl(
-                                project,
-                                ServiceManager.getService(project, JavaFileManager.class)));
-
-        super.registerJavaPsiFacade();
-    }
-
-    public LintCoreProjectEnvironment(
-            Disposable parentDisposable, CoreApplicationEnvironment applicationEnvironment) {
-        super(parentDisposable, applicationEnvironment);
-
-        MockProject project = getProject();
-        ExtensionsArea area = Extensions.getArea(project);
-        LintCoreApplicationEnvironment.registerProjectExtensionPoints(area);
-        LintCoreApplicationEnvironment.registerProjectServices(this);
-    }
-
-    private List<File> myPaths = new ArrayList<>();
-
-    public List<File> getPaths() {
-        return myPaths;
-    }
-
-    public void registerPaths(@NonNull List<File> classpath) {
-        myPaths.addAll(classpath);
-
-        int expectedSize = classpath.size();
-        Set<File> files = Sets.newHashSetWithExpectedSize(expectedSize);
-
-        VirtualFileSystem local = StandardFileSystems.local();
-
-        for (File path : classpath) {
-            if (files.contains(path)) {
-                continue;
-            }
-            files.add(path);
-
-            if (path.exists()) {
-                if (path.isFile()) {
-                    // Make sure these paths are absolute - nested jar file systems
-                    // do not work correctly with relative paths (for example
-                    // JavaPsiFacade.findClass will not find classes in these jar
-                    // file systems.)
-                    if (!path.isAbsolute()) {
-                        path = path.getAbsoluteFile();
-                    }
-                    String pathString = path.getPath();
-                    if (pathString.endsWith(DOT_SRCJAR)) {
-                        // Mount as source files
-                        VirtualFile root =
-                                StandardFileSystems.jar()
-                                        .findFileByPath(pathString + URLUtil.JAR_SEPARATOR);
-                        if (root != null) {
-                            addSourcesToClasspath(root);
-                            continue;
-                        }
-                    }
-                    addJarToClassPath(path);
-                } else if (path.isDirectory()) {
-                    VirtualFile virtualFile = local.findFileByPath(path.getPath());
-                    if (virtualFile != null) {
-                        addSourcesToClasspath(virtualFile);
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    protected JavaFileManager createCoreFileManager() {
-        PsiManager psiManager = PsiManager.getInstance(getProject());
-        return new KotlinCliJavaFileManagerImpl(psiManager);
-    }
-}
diff --git a/lint/cli/src/main/java/com/android/tools/lint/Main.java b/lint/cli/src/main/java/com/android/tools/lint/Main.java
index da1d7a0..f39b74c 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/Main.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/Main.java
@@ -32,6 +32,7 @@
 import com.android.annotations.Nullable;
 import com.android.tools.lint.checks.BuiltinIssueRegistry;
 import com.android.tools.lint.client.api.Configuration;
+import com.android.tools.lint.client.api.DefaultConfiguration;
 import com.android.tools.lint.client.api.IssueRegistry;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.client.api.LintDriver;
@@ -218,6 +219,7 @@
                     @Override
                     public Configuration getConfiguration(
                             @NonNull final Project project, @Nullable LintDriver driver) {
+                        DefaultConfiguration overrideConfiguration = getOverrideConfiguration();
                         if (overrideConfiguration != null) {
                             return overrideConfiguration;
                         }
@@ -327,7 +329,7 @@
                     /** Creates a lint request */
                     @Override
                     @NonNull
-                    protected LintRequest createLintRequest(@NonNull List<File> files) {
+                    protected LintRequest createLintRequest(@NonNull List<? extends File> files) {
                         LintRequest request = super.createLintRequest(files);
                         File descriptor = flags.getProjectDescriptorOverride();
                         if (descriptor != null) {
diff --git a/lint/cli/src/main/java/com/android/tools/lint/UastEnvironment.kt b/lint/cli/src/main/java/com/android/tools/lint/UastEnvironment.kt
new file mode 100644
index 0000000..523cd25
--- /dev/null
+++ b/lint/cli/src/main/java/com/android/tools/lint/UastEnvironment.kt
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.android.tools.lint
+
+import com.android.SdkConstants
+import com.android.tools.lint.client.api.LintClient
+import com.google.common.collect.Sets
+import com.intellij.codeInsight.ContainerProvider
+import com.intellij.codeInsight.CustomExceptionHandler
+import com.intellij.codeInsight.ExternalAnnotationsManager
+import com.intellij.codeInsight.InferredAnnotationsManager
+import com.intellij.codeInsight.runner.JavaMainMethodProvider
+import com.intellij.core.CoreApplicationEnvironment.registerExtensionPoint
+import com.intellij.core.CoreJavaFileManager
+import com.intellij.lang.MetaLanguage
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.components.ServiceManager
+import com.intellij.openapi.extensions.Extensions
+import com.intellij.openapi.extensions.ExtensionsArea
+import com.intellij.openapi.fileTypes.FileTypeExtensionPoint
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.vfs.StandardFileSystems
+import com.intellij.openapi.vfs.impl.ZipHandler
+import com.intellij.psi.FileContextProvider
+import com.intellij.psi.JavaModuleSystem
+import com.intellij.psi.augment.PsiAugmentProvider
+import com.intellij.psi.augment.TypeAnnotationModifier
+import com.intellij.psi.compiled.ClassFileDecompilers
+import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy
+import com.intellij.psi.impl.file.impl.JavaFileManager
+import com.intellij.psi.meta.MetaDataContributor
+import com.intellij.psi.stubs.BinaryFileStubBuilders
+import com.intellij.util.io.URLUtil
+import org.jetbrains.annotations.TestOnly
+import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
+import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
+import org.jetbrains.kotlin.cli.common.toBooleanLenient
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment
+import org.jetbrains.kotlin.config.CommonConfigurationKeys
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.config.JVMConfigurationKeys
+import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
+import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
+import org.jetbrains.kotlin.resolve.diagnostics.DiagnosticSuppressor
+import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
+import org.jetbrains.uast.UastContext
+import org.jetbrains.uast.UastLanguagePlugin
+import org.jetbrains.uast.evaluation.UEvaluatorExtension
+import org.jetbrains.uast.java.JavaUastLanguagePlugin
+import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin
+import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
+import org.jetbrains.uast.kotlin.evaluation.KotlinEvaluatorExtension
+import org.jetbrains.uast.kotlin.internal.CliKotlinUastResolveProviderService
+import org.jetbrains.uast.kotlin.internal.UastAnalysisHandlerExtension
+import java.io.File
+import java.util.ArrayList
+
+/**
+ * Modeled after KotlinCoreEnvironment, but written to reuse as much of it as possible, this
+ * class is responsible for configuring the PSI and UAST environment for command line usage
+ * in lint (and various other command line tools that need access to PSI, such as the
+ * annotation extractions code in the Android Gradle plugin, as well as in Metalava.
+ * <p>
+ * This class tries to delegate as much as possible to KotlinCoreEnvironment for registration
+ * of extension points and services, since those change regularly and KotlinCoreEnvironment
+ * is updated along with the compiler; if lint had inlined its own copy this would easily
+ * get stale and we'd miss some important initialization.
+ */
+class UastEnvironment private constructor(
+    val disposable: Disposable,
+    val projectEnvironment: ProjectEnvironment,
+    initialConfiguration: CompilerConfiguration
+) {
+    class ProjectEnvironment(
+        disposable: Disposable,
+        applicationEnvironment: KotlinCoreApplicationEnvironment
+    ) : KotlinCoreProjectEnvironment(disposable, applicationEnvironment) {
+
+        private var extensionRegistered = false
+
+        override fun preregisterServices() {
+            KotlinCoreEnvironment.registerProjectExtensionPoints(project.extensionArea)
+        }
+
+        fun registerExtensionsFromPlugins(configuration: CompilerConfiguration) {
+            if (!extensionRegistered) {
+                KotlinCoreEnvironment.registerPluginExtensionPoints(project)
+                KotlinCoreEnvironment.registerExtensionsFromPlugins(project, configuration)
+                AnalysisHandlerExtension.registerExtension(project, UastAnalysisHandlerExtension())
+
+                extensionRegistered = true
+            }
+        }
+
+        override fun registerJavaPsiFacade() {
+            with(project) {
+                registerService(
+                    CoreJavaFileManager::class.java,
+                    ServiceManager.getService(
+                        this,
+                        JavaFileManager::class.java
+                    ) as CoreJavaFileManager
+                )
+
+                KotlinCoreEnvironment.registerKotlinLightClassSupport(project)
+            }
+
+            val area = Extensions.getArea(project)
+            registerProjectExtensionPoints(area)
+            registerProjectServices(this)
+
+            super.registerJavaPsiFacade()
+        }
+
+        private fun registerProjectExtensionPoints(area: ExtensionsArea) {
+            registerExtensionPoint(
+                area,
+                UastLanguagePlugin.extensionPointName,
+                UastLanguagePlugin::class.java
+            )
+        }
+
+        private fun registerProjectServices(projectEnvironment: KotlinCoreProjectEnvironment) {
+            val project = projectEnvironment.project
+            with(project) {
+                @Suppress("DEPRECATION") // client code may still look for it
+                registerService(
+                    UastContext::class.java,
+                    UastContext(project)
+                )
+                registerService(
+                    ExternalAnnotationsManager::class.java,
+                    LintExternalAnnotationsManager::class.java
+                )
+                registerService(
+                    InferredAnnotationsManager::class.java,
+                    LintInferredAnnotationsManager::class.java
+                )
+
+                registerService(
+                    KotlinUastResolveProviderService::class.java,
+                    CliKotlinUastResolveProviderService() // or CliKotlinUastResolveProviderService::class.java
+                )
+            }
+        }
+
+        private val myPaths: MutableList<File> = ArrayList()
+        val paths: List<File> get() = myPaths
+
+        fun registerPaths(classpath: List<File>) {
+            myPaths.addAll(classpath)
+            val expectedSize = classpath.size
+            val files: MutableSet<File> = Sets.newHashSetWithExpectedSize(expectedSize)
+            val local = StandardFileSystems.local()
+            for (path in classpath) {
+                @Suppress("NAME_SHADOWING")
+                var path = path
+                if (files.contains(path)) {
+                    continue
+                }
+                files.add(path)
+                if (path.exists()) {
+                    if (path.isFile) {
+                        // Make sure these paths are absolute - nested jar file systems
+                        //
+                        // do not work correctly with relative paths (for example
+                        // JavaPsiFacade.findClass will not find classes in these jar
+                        // file systems.)
+                        if (!path.isAbsolute) {
+                            path = path.absoluteFile
+                        }
+                        val pathString = path.path
+                        if (pathString.endsWith(SdkConstants.DOT_SRCJAR)) {
+                            // Mount as source files
+                            val root = StandardFileSystems.jar()
+                                .findFileByPath(pathString + URLUtil.JAR_SEPARATOR)
+                            if (root != null) {
+                                addSourcesToClasspath(root)
+                                continue
+                            }
+                        }
+                        addJarToClassPath(path)
+                    } else if (path.isDirectory) {
+                        val virtualFile = local.findFileByPath(path.path)
+                        virtualFile?.let { addSourcesToClasspath(it) }
+                    }
+                }
+            }
+        }
+    }
+
+    val configuration: CompilerConfiguration = initialConfiguration.copy()
+
+    init {
+        val project = projectEnvironment.project
+        projectEnvironment.registerExtensionsFromPlugins(configuration)
+        KotlinCoreEnvironment.registerProjectServicesForCLI(projectEnvironment)
+        KotlinCoreEnvironment.registerProjectServices(projectEnvironment.project)
+
+        project.registerService(
+            ModuleVisibilityManager::class.java,
+            CliModuleVisibilityManagerImpl(false)
+        )
+
+        for (extension in CompilerConfigurationExtension.getInstances(project)) {
+            extension.updateConfiguration(configuration)
+        }
+
+        registerAppExtensionPoints()
+        registerAppExtensions(disposable)
+    }
+
+    val project: Project
+        get() = projectEnvironment.project
+
+    companion object {
+        private val DEBUGGING = LintClient.isUnitTest
+        private val APPLICATION_LOCK = Object()
+        private var ourApplicationEnvironment: KotlinCoreApplicationEnvironment? = null
+        private var ourCreator: Throwable? = null
+        private var ourProjectCount = 0
+
+        @JvmStatic
+        fun create(
+            parentDisposable: Disposable
+        ): UastEnvironment {
+            val configuration = CompilerConfiguration()
+            configuration.put(JVMConfigurationKeys.NO_JDK, true)
+            configuration.put(CommonConfigurationKeys.MODULE_NAME, "lint-module")
+            configuration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
+            return createForProduction(parentDisposable, configuration)
+        }
+
+        @JvmStatic
+        fun createForProduction(
+            parentDisposable: Disposable,
+            configuration: CompilerConfiguration
+        ): UastEnvironment {
+            val appEnv =
+                getOrCreateApplicationEnvironmentForProduction(parentDisposable, configuration)
+            val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
+            val environment =
+                UastEnvironment(parentDisposable, projectEnv, configuration)
+
+            synchronized(APPLICATION_LOCK) {
+                ourProjectCount++
+            }
+            return environment
+        }
+
+        @TestOnly
+        @JvmStatic
+        fun createForTests(
+            parentDisposable: Disposable,
+            initialConfiguration: CompilerConfiguration
+        ): UastEnvironment {
+            val configuration = initialConfiguration.copy()
+            // Tests are supposed to create a single project and dispose it right after use
+            val appEnv =
+                KotlinCoreEnvironment.createApplicationEnvironment(
+                    parentDisposable,
+                    configuration,
+                    unitTestMode = true
+                )
+            val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
+            return UastEnvironment(parentDisposable, projectEnv, configuration)
+        }
+
+        val applicationEnvironment: KotlinCoreApplicationEnvironment?
+            get() = ourApplicationEnvironment
+
+        private fun getOrCreateApplicationEnvironmentForProduction(
+            parentDisposable: Disposable,
+            configuration: CompilerConfiguration
+        ): KotlinCoreApplicationEnvironment {
+            // We don't bundle .dll files in the Gradle plugin for native file system access;
+            // prevent warning logs on Windows when it's not found (see b.android.com/260180)
+            System.setProperty("idea.use.native.fs.for.win", "false")
+
+            synchronized(APPLICATION_LOCK) {
+                if (ourApplicationEnvironment == null) {
+                    val disposable = Disposer.newDisposable()
+                    ourApplicationEnvironment = KotlinCoreEnvironment.createApplicationEnvironment(
+                        disposable,
+                        configuration,
+                        unitTestMode = false
+                    )
+                    if (DEBUGGING && ourCreator == null) {
+                        ourCreator = Throwable()
+                    }
+                    ourProjectCount = 0
+                    Disposer.register(disposable, Disposable {
+                        synchronized(APPLICATION_LOCK) {
+                            ourApplicationEnvironment = null
+                            ourCreator = null
+                        }
+                    })
+                }
+
+                // NOTE -- keep-alive may be enabled in Gradle, but in ReflectiveLintRunner
+                // we dispose it anyway!
+                // ----
+                // From KotlinCoreEnvironment:
+                // Disposing of the environment is unsafe in production when parallel
+                // builds are enabled, but turning it off universally
+                // breaks a lot of tests, therefore it is disabled for production and
+                // enabled for tests
+                val keepAlive = System.getProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY)
+                if (keepAlive.toBooleanLenient() != true) {
+                    // JPS may run many instances of the compiler in parallel (there's an option
+                    // for compiling independent modules in parallel in IntelliJ)
+                    // All projects share the same ApplicationEnvironment, and when the
+                    // last project is disposed, the ApplicationEnvironment is disposed
+                    // as well
+                    Disposer.register(parentDisposable, Disposable {
+                        synchronized(APPLICATION_LOCK) {
+                            if (--ourProjectCount <= 0) {
+                                disposeApplicationEnvironment()
+                            }
+                        }
+                    })
+                }
+
+                return ourApplicationEnvironment!!
+            }
+        }
+
+        @JvmStatic
+        fun disposeApplicationEnvironment() {
+            synchronized(APPLICATION_LOCK) {
+                val environment = ourApplicationEnvironment ?: return
+                ourApplicationEnvironment = null
+                ourCreator = null
+                Disposer.dispose(environment.parentDisposable)
+                ZipHandler.clearFileAccessorCache()
+            }
+        }
+
+        @JvmStatic
+        fun ensureDisposed() {
+            if (ourApplicationEnvironment != null) {
+                System.err.println("Lint UAST environment not disposed")
+                val creator = ourCreator
+                disposeApplicationEnvironment()
+                if (creator != null) {
+                    creator.printStackTrace()
+                    error("Application environment not disposed")
+                }
+            }
+        }
+
+        private fun registerAppExtensionPoints() {
+            val rootArea = Extensions.getRootArea()
+            registerExtensionPoint(
+                rootArea,
+                BinaryFileStubBuilders.EP_NAME,
+                FileTypeExtensionPoint::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                FileContextProvider.EP_NAME,
+                FileContextProvider::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                MetaDataContributor.EP_NAME,
+                MetaDataContributor::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                PsiAugmentProvider.EP_NAME,
+                PsiAugmentProvider::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                JavaMainMethodProvider.EP_NAME,
+                JavaMainMethodProvider::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                ContainerProvider.EP_NAME,
+                ContainerProvider::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                ClsCustomNavigationPolicy.EP_NAME,
+                ClsCustomNavigationPolicy::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                ClassFileDecompilers.EP_NAME,
+                ClassFileDecompilers.Decompiler::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                TypeAnnotationModifier.EP_NAME,
+                TypeAnnotationModifier::class.java
+            )
+            registerExtensionPoint(rootArea, MetaLanguage.EP_NAME, MetaLanguage::class.java)
+            registerExtensionPoint(
+                rootArea,
+                UastLanguagePlugin.extensionPointName,
+                UastLanguagePlugin::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                CustomExceptionHandler.KEY,
+                CustomExceptionHandler::class.java
+            )
+            registerExtensionPoint(rootArea, JavaModuleSystem.EP_NAME, JavaModuleSystem::class.java)
+            registerExtensionPoint(
+                rootArea,
+                DiagnosticSuppressor.EP_NAME,
+                DiagnosticSuppressor::class.java
+            )
+            registerExtensionPoint(
+                rootArea,
+                UEvaluatorExtension.EXTENSION_POINT_NAME,
+                UEvaluatorExtension::class.java
+            )
+        }
+
+        private fun registerAppExtensions(disposable: Disposable) {
+            val rootArea = Extensions.getRootArea()
+            with(rootArea) {
+                getExtensionPoint(UastLanguagePlugin.extensionPointName)
+                    .registerExtension(JavaUastLanguagePlugin(), disposable)
+                getExtensionPoint(UEvaluatorExtension.EXTENSION_POINT_NAME)
+                    .registerExtension(KotlinEvaluatorExtension(), disposable)
+                getExtensionPoint(UastLanguagePlugin.extensionPointName)
+                    .registerExtension(KotlinUastLanguagePlugin(), disposable)
+            }
+        }
+    }
+}
diff --git a/lint/cli/src/main/java/com/android/tools/lint/XmlReporter.kt b/lint/cli/src/main/java/com/android/tools/lint/XmlReporter.kt
index 33fd483..b8537a6 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/XmlReporter.kt
+++ b/lint/cli/src/main/java/com/android/tools/lint/XmlReporter.kt
@@ -104,7 +104,7 @@
         writer.write("\n</issues>\n")
         writer.close()
 
-        if (!client.getFlags().isQuiet && (stats.errorCount > 0 || stats.warningCount > 0)) {
+        if (!client.flags.isQuiet && (stats.errorCount > 0 || stats.warningCount > 0)) {
             val url = SdkUtils.fileToUrlString(output.absoluteFile)
             println(String.format("Wrote XML report to %1\$s", url))
         }
diff --git a/lint/libs/lint-api/lint_baseline.xml b/lint/libs/lint-api/lint_baseline.xml
old mode 100755
new mode 100644
index 84775a7..7c7490c
--- a/lint/libs/lint-api/lint_baseline.xml
+++ b/lint/libs/lint-api/lint_baseline.xml
@@ -11,7 +11,7 @@
 
     <issue
         id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
+        message="getUAnnotations used instead of `JavaContext.getAllAnnotations`.">
         <location
             file="libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationLookup.kt"
             line="102"/>
@@ -19,7 +19,7 @@
 
     <issue
         id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
+        message="getUAnnotations used instead of `JavaContext.getAllAnnotations`.">
         <location
             file="libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultJavaEvaluator.kt"
             line="117"/>
@@ -27,7 +27,7 @@
 
     <issue
         id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
+        message="getUAnnotations used instead of `JavaContext.getAllAnnotations`.">
         <location
             file="libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultJavaEvaluator.kt"
             line="150"/>
@@ -35,7 +35,7 @@
 
     <issue
         id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
+        message="getUAnnotations used instead of `JavaContext.getAllAnnotations`.">
         <location
             file="libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaEvaluator.kt"
             line="721"/>
@@ -43,7 +43,7 @@
 
     <issue
         id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
+        message="getUAnnotations used instead of `JavaContext.getAllAnnotations`.">
         <location
             file="libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.kt"
             line="3326"/>
@@ -51,7 +51,7 @@
 
     <issue
         id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
+        message="getUAnnotations used instead of `JavaContext.getAllAnnotations`.">
         <location
             file="libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.kt"
             line="3326"/>
@@ -59,7 +59,7 @@
 
     <issue
         id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
+        message="getUAnnotations used instead of `JavaContext.getAllAnnotations`.">
         <location
             file="libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.kt"
             line="3390"/>
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationHandler.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationHandler.kt
index 89591ca..08bdd01 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationHandler.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationHandler.kt
@@ -617,7 +617,7 @@
         call: UCallExpression,
         containingClass: PsiClass?
     ) {
-        if (method != null && !methodAnnotations.isEmpty()) {
+        if (method != null && methodAnnotations.isNotEmpty()) {
             checkAnnotations(
                 context,
                 call,
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationLookup.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationLookup.kt
index 220c288..75a46e7 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationLookup.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/AnnotationLookup.kt
@@ -17,7 +17,6 @@
 package com.android.tools.lint.client.api
 
 import com.android.tools.lint.detector.api.isKotlin
-import com.intellij.openapi.components.ServiceManager
 import com.intellij.openapi.fileTypes.LanguageFileType
 import com.intellij.psi.PsiAnnotation
 import com.intellij.psi.PsiClass
@@ -27,7 +26,7 @@
 import org.jetbrains.uast.UClass
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.UFile
-import org.jetbrains.uast.UastContext
+import org.jetbrains.uast.UastFacade
 import org.jetbrains.uast.getContainingUFile
 import org.jetbrains.uast.java.JavaUAnnotation
 
@@ -45,9 +44,8 @@
                 // We sometimes get binaries out of Kotlin files after a resolve; find the
                 // original AST nodes
                 val project = resolved.project
-                val uastContext = ServiceManager.getService(project, UastContext::class.java)
                 val cls =
-                    uastContext.convertElement(resolved, null, UClass::class.java) as UClass?
+                    UastFacade.convertElement(resolved, null, UClass::class.java) as UClass?
                 cls?.let {
                     kotlinClass = it
                     resolvedKotlinClassCache[resolved] = it
@@ -75,9 +73,7 @@
                             PsiManager.getInstance(project)
                                 .findFile(resolved.containingFile?.virtualFile!!)
                         if (psiFile != null) {
-                            val uastContext =
-                                ServiceManager.getService(project, UastContext::class.java)
-                            uastContext.convertElementWithParent(
+                            UastFacade.convertElementWithParent(
                                 psiFile,
                                 UFile::class.java
                             ) as? UFile
@@ -99,7 +95,7 @@
         if (kotlinClass != null) {
             val annotationQualifiedName = annotation.qualifiedName
             if (annotationQualifiedName != null) {
-                for (uAnnotation in (kotlinClass as UAnnotated).annotations) {
+                for (uAnnotation in (kotlinClass as UAnnotated).uAnnotations) {
                     if (annotationQualifiedName == uAnnotation.qualifiedName) {
                         return uAnnotation
                     }
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaEvaluator.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaEvaluator.kt
index ba2312e..25dffea 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaEvaluator.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaEvaluator.kt
@@ -768,7 +768,7 @@
      * @return true if the annotation is inherited rather than being declared directly on this owner
      */
     open fun isInherited(annotation: UAnnotation, owner: UAnnotated): Boolean {
-        return owner.annotations.contains(annotation)
+        return owner.uAnnotations.contains(annotation)
     }
 
     abstract fun getPackage(node: PsiElement): PsiPackage?
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.kt
index e873e45..8fbf989 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.kt
@@ -466,7 +466,7 @@
 
         jarFiles.addAll(client.findGlobalRuleJars())
 
-        if (!jarFiles.isEmpty()) {
+        if (jarFiles.isNotEmpty()) {
             val extraRegistries = JarFileIssueRegistry.get(
                 client, jarFiles,
                 currentProject ?: projects.firstOrNull()
@@ -627,8 +627,7 @@
     }
 
     /** Development diagnostics only, run with assertions on  */
-    private // Turn off warnings for the intentional assertion side effect below
-    fun validateScopeList() {
+    private fun validateScopeList() {
         if (assertionsEnabled()) {
             val resourceFileDetectors = scopeDetectors[Scope.RESOURCE_FILE]
             if (resourceFileDetectors != null) {
@@ -1000,7 +999,46 @@
                     }
                 }
             }
+        }
 
+        // Prepare Java/Kotlin compilation. We're not processing these files yet, but
+        // we need to prepare to load various symbol tables such that class lookup
+        // works from resource detectors
+        var uastSourceList: UastSourceList? = null
+        if (scope.contains(Scope.JAVA_FILE) || scope.contains(Scope.ALL_JAVA_FILES)) {
+            val checks = union(
+                scopeDetectors[Scope.JAVA_FILE],
+                scopeDetectors[Scope.ALL_JAVA_FILES]
+            )
+
+            if (checks != null && checks.isNotEmpty()) {
+                val files = project.subset
+                uastSourceList = if (files != null) {
+                    findUastSources(project, main, checks, files)
+                } else {
+                    findUastSources(project, main, checks)
+                }
+                prepareUast(uastSourceList)
+            }
+        }
+
+        // Still need to set up class path when running outside of the IDE
+        // to prepare for class lookup from resource detectors
+        if (uastSourceList == null && !LintClient.isStudio) {
+            val files = project.subset
+            val uastRequest = if (files != null) {
+                findUastSources(project, main, emptyList(), files)
+            } else {
+                findUastSources(project, main, emptyList())
+            }
+            prepareUast(uastRequest)
+        }
+
+        if (isCanceled) {
+            return
+        }
+
+        if (project.isAndroidProject) {
             // Process both Scope.RESOURCE_FILE and Scope.ALL_RESOURCE_FILES detectors together
             // in a single pass through the resource directories.
             if (scope.contains(Scope.ALL_RESOURCE_FILES) ||
@@ -1014,7 +1052,7 @@
                     scopeDetectors[Scope.RESOURCE_FILE],
                     scopeDetectors[Scope.ALL_RESOURCE_FILES]
                 ) ?: emptyList()
-                var haveXmlChecks = !checks.isEmpty()
+                var haveXmlChecks = checks.isNotEmpty()
                 val xmlDetectors: MutableList<XmlScanner>
                 if (haveXmlChecks) {
                     xmlDetectors = ArrayList(checks.size)
@@ -1023,13 +1061,13 @@
                             xmlDetectors.add(detector)
                         }
                     }
-                    haveXmlChecks = !xmlDetectors.isEmpty()
+                    haveXmlChecks = xmlDetectors.isNotEmpty()
                 } else {
                     xmlDetectors = mutableListOf()
                 }
                 if (haveXmlChecks ||
-                    dirChecks != null && !dirChecks.isEmpty() ||
-                    binaryChecks != null && !binaryChecks.isEmpty()
+                    dirChecks != null && dirChecks.isNotEmpty() ||
+                    binaryChecks != null && binaryChecks.isNotEmpty()
                 ) {
                     val files = project.subset
                     if (files != null) {
@@ -1039,7 +1077,7 @@
                         )
                     } else {
                         val resourceFolders = project.resourceFolders
-                        if (!resourceFolders.isEmpty()) {
+                        if (resourceFolders.isNotEmpty()) {
                             for (res in resourceFolders) {
                                 checkResFolder(
                                     project, main, res, xmlDetectors, dirChecks,
@@ -1049,7 +1087,7 @@
                         }
                         if (checkGeneratedSources) {
                             val generatedResourceFolders = project.generatedResourceFolders
-                            if (!generatedResourceFolders.isEmpty()) {
+                            if (generatedResourceFolders.isNotEmpty()) {
                                 for (res in generatedResourceFolders) {
                                     checkResFolder(
                                         project, main, res, xmlDetectors, dirChecks,
@@ -1067,28 +1105,9 @@
             }
         }
 
-        if (scope.contains(Scope.JAVA_FILE) || scope.contains(Scope.ALL_JAVA_FILES)) {
-            val checks = union(
-                scopeDetectors[Scope.JAVA_FILE],
-                scopeDetectors[Scope.ALL_JAVA_FILES]
-            )
-            if (checks != null && !checks.isEmpty()) {
-                val files = project.subset
-                if (files != null) {
-                    checkIndividualJavaFiles(project, main, checks, files)
-                } else {
-                    val sourceFolders = project.javaSourceFolders
-                    val testFolders = if (!ignoreTestSources)
-                        project.testSourceFolders
-                    else
-                        emptyList<File>()
-
-                    val generatedFolders = if (checkGeneratedSources)
-                        project.generatedSourceFolders
-                    else emptyList<File>()
-                    checkJava(project, main, sourceFolders, testFolders, generatedFolders, checks)
-                }
-            }
+        // Java & Kotlin
+        if (uastSourceList != null) {
+            visitUast(project, main, uastSourceList)
         }
 
         if (isCanceled) {
@@ -1349,7 +1368,7 @@
     ) {
         val classFiles = ArrayList<File>(files.size)
         val classFolders = project.javaClassFolders
-        if (!classFolders.isEmpty()) {
+        if (classFolders.isNotEmpty()) {
             for (file in files) {
                 val path = file.path
                 if (file.isFile && path.endsWith(DOT_CLASS)) {
@@ -1362,7 +1381,7 @@
             client, classFiles, classFolders,
             true
         )
-        if (!entries.isEmpty()) {
+        if (entries.isNotEmpty()) {
             entries.sort()
             runClassDetectors(Scope.CLASS_FILE, entries, project, main)
         }
@@ -1384,7 +1403,7 @@
     ) {
         if (this.scope.contains(scope)) {
             val classDetectors = scopeDetectors[scope]
-            if (classDetectors != null && !classDetectors.isEmpty() && !entries.isEmpty()) {
+            if (classDetectors != null && classDetectors.isNotEmpty() && entries.isNotEmpty()) {
                 val visitor = AsmVisitor(client, classDetectors)
 
                 var sourceContents: CharSequence? = null
@@ -1580,22 +1599,27 @@
         return null
     }
 
-    private fun checkJava(
+    private fun findUastSources(
         project: Project,
         main: Project?,
-        sourceFolders: List<File>,
-        testSourceFolders: List<File>,
-        generatedSources: List<File>,
         checks: List<Detector>
-    ) {
-        assert(!checks.isEmpty())
+    ): UastSourceList {
+        val sourceFolders = project.javaSourceFolders
+        val testFolders = if (!ignoreTestSources)
+            project.testSourceFolders
+        else
+            emptyList<File>()
+
+        val generatedFolders = if (checkGeneratedSources)
+            project.generatedSourceFolders
+        else emptyList<File>()
 
         // Gather all Java source files in a single pass; more efficient.
         val sources = ArrayList<File>(100)
         for (folder in sourceFolders) {
             gatherJavaFiles(folder, sources)
         }
-        for (folder in generatedSources) {
+        for (folder in generatedFolders) {
             gatherJavaFiles(folder, sources)
         }
 
@@ -1611,7 +1635,7 @@
             testContexts = emptyList()
         } else {
             sources.clear()
-            for (folder in testSourceFolders) {
+            for (folder in testFolders) {
                 gatherJavaFiles(folder, sources)
             }
             testContexts = ArrayList(sources.size)
@@ -1624,19 +1648,14 @@
             }
         }
 
-        // Visit all contexts
-        if (!contexts.isEmpty() || !testContexts.isEmpty()) {
-            visitJavaFiles(checks, project, main, contexts, testContexts)
-        }
+        return findUastSources(checks, contexts, testContexts)
     }
 
-    private fun visitJavaFiles(
+    private fun findUastSources(
         checks: List<Detector>,
-        project: Project,
-        main: Project?,
         contexts: List<JavaContext>,
         testContexts: List<JavaContext>
-    ) {
+    ): UastSourceList {
         val allContexts: List<JavaContext>
         if (testContexts.isEmpty()) {
             allContexts = contexts
@@ -1646,40 +1665,55 @@
             allContexts.addAll(testContexts)
         }
 
+        val parser = client.getUastParser(currentProject)
+
         // Force all test sources into the normal source check (where all checks apply) ?
-        if (checkTestSources) {
-            visitJavaFiles(checks, project, main, allContexts, allContexts, emptyList())
+        return if (checkTestSources) {
+            UastSourceList(parser, checks, allContexts, allContexts, emptyList())
         } else {
-            visitJavaFiles(checks, project, main, allContexts, contexts, testContexts)
+            UastSourceList(parser, checks, allContexts, contexts, testContexts)
         }
     }
 
-    private fun visitJavaFiles(
-        checks: List<Detector>,
+    private fun prepareUast(
+        sourceList: UastSourceList
+    ) {
+        val parser = sourceList.parser
+        val srcContexts = sourceList.srcContexts
+        val testContexts = sourceList.testContexts
+        val allContexts = sourceList.allContexts
+
+        for (context in allContexts) {
+            context.uastParser = parser
+        }
+        parserErrors = !parser.prepare(srcContexts, testContexts)
+    }
+
+    /** The lists of production and test files for Kotlin and Java to parse and process */
+    private class UastSourceList(
+        val parser: UastParser,
+        val checks: List<Detector>,
+        val allContexts: List<JavaContext>,
+        val srcContexts: List<JavaContext>,
+        val testContexts: List<JavaContext>
+    )
+
+    private fun visitUast(
         project: Project,
         main: Project?,
-        allContexts: List<JavaContext>,
-        srcContexts: List<JavaContext>,
-        testContexts: List<JavaContext>
+        sourceList: UastSourceList
     ) {
-        // Temporary: we still have some builtin checks that aren't migrated to
-        // PSI. Until that's complete, remove them from the list here
-        val uastScanners = ArrayList<Detector>(checks.size)
-        for (detector in checks) {
-            if (detector is SourceCodeScanner) {
-                uastScanners.add(detector)
-            }
-        }
+        val parser = sourceList.parser
+        val uastScanners = sourceList.checks
+        val allContexts = sourceList.allContexts
+        val srcContexts = sourceList.srcContexts
+        val testContexts = sourceList.testContexts
 
-        if (!uastScanners.isEmpty()) {
-            val parser = client.getUastParser(currentProject)
-            for (context in allContexts) {
-                context.uastParser = parser
-            }
+        assert(uastScanners.isNotEmpty())
+
+        if (uastScanners.isNotEmpty()) {
             val uElementVisitor = UElementVisitor(parser, uastScanners)
 
-            parserErrors = !uElementVisitor.prepare(srcContexts, testContexts)
-
             for (context in srcContexts) {
                 fireEvent(EventType.SCANNING_FILE, context)
                 // TODO: Don't hold read lock around the entire process?
@@ -1699,9 +1733,9 @@
             uElementVisitor.visitGroups(projectContext, allContexts)
             uElementVisitor.dispose()
 
-            if (!testContexts.isEmpty()) {
+            if (testContexts.isNotEmpty()) {
                 val testScanners = filterTestScanners(uastScanners)
-                if (!testScanners.isEmpty()) {
+                if (testScanners.isNotEmpty()) {
                     val uTestVisitor = UElementVisitor(parser, testScanners)
 
                     for (context in testContexts) {
@@ -1742,13 +1776,12 @@
         return testScanners
     }
 
-    private fun checkIndividualJavaFiles(
+    private fun findUastSources(
         project: Project,
         main: Project?,
         checks: List<Detector>,
         files: List<File>
-    ) {
-
+    ): UastSourceList {
         val contexts = ArrayList<JavaContext>(files.size)
         val testContexts = ArrayList<JavaContext>(files.size)
         val testFolders = project.testSourceFolders
@@ -1775,15 +1808,11 @@
             }
         }
 
-        if (contexts.isEmpty() && testContexts.isEmpty()) {
-            return
-        }
-
         // We're not sure if these individual files are tests or non-tests; treat them
         // as non-tests now. This gives you warnings if you're editing an individual
         // test file for example.
 
-        visitJavaFiles(checks, project, main, contexts, testContexts)
+        return findUastSources(checks, contexts, testContexts)
     }
 
     private var currentFolderType: ResourceFolderType? = null
@@ -1883,7 +1912,7 @@
 
         // Process the resource folder
 
-        if (dirChecks != null && !dirChecks.isEmpty()) {
+        if (dirChecks != null && dirChecks.isNotEmpty()) {
             val context = ResourceContext(this, project, main, dir, type, "")
             val folderName = dir.name
             fireEvent(EventType.SCANNING_FILE, context)
@@ -3335,7 +3364,7 @@
          */
         @JvmStatic
         fun isSuppressed(issue: Issue, annotated: UAnnotated): Boolean {
-            val annotations = annotated.annotations
+            val annotations = annotated.uAnnotations
             if (annotations.isEmpty()) {
                 return false
             }
@@ -3399,7 +3428,7 @@
             annotated: UAnnotated,
             names: Set<String>
         ): Boolean {
-            val annotations = annotated.annotations
+            val annotations = annotated.uAnnotations
             if (annotations.isEmpty()) {
                 return false
             }
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementHandler.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementHandler.kt
index ee46936..0f91e17 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementHandler.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementHandler.kt
@@ -66,6 +66,7 @@
 import org.jetbrains.uast.UUnaryExpression
 import org.jetbrains.uast.UVariable
 import org.jetbrains.uast.UWhileExpression
+import org.jetbrains.uast.UYieldExpression
 import org.jetbrains.uast.visitor.UastVisitor
 
 /**
@@ -288,6 +289,10 @@
         error(UWhileExpression::class.java)
     }
 
+    open fun visitYieldExpression(node: UYieldExpression) {
+        error(UYieldExpression::class.java)
+    }
+
     companion object {
         val NONE: UElementHandler = object : UElementHandler() {
             override fun error(parameterType: Class<out UElement>) {}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementVisitor.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementVisitor.kt
index 58622c0..e19103e 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementVisitor.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementVisitor.kt
@@ -39,7 +39,6 @@
 import com.intellij.openapi.util.Computable
 import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiClassType
-import com.intellij.psi.PsiLambdaExpression
 import com.intellij.psi.PsiTypeParameter
 import org.jetbrains.uast.UAnnotation
 import org.jetbrains.uast.UArrayAccessExpression
@@ -91,6 +90,7 @@
 import org.jetbrains.uast.UUnaryExpression
 import org.jetbrains.uast.UVariable
 import org.jetbrains.uast.UWhileExpression
+import org.jetbrains.uast.UYieldExpression
 import org.jetbrains.uast.util.isConstructorCall
 import org.jetbrains.uast.util.isMethodCall
 import org.jetbrains.uast.visitor.AbstractUastVisitor
@@ -308,9 +308,6 @@
         }
     }
 
-    fun prepare(contexts: List<JavaContext>, testContexts: List<JavaContext>): Boolean =
-        parser.prepare(contexts, testContexts)
-
     fun visitGroups(
         projectContext: Context,
         allContexts: List<JavaContext>
@@ -419,17 +416,11 @@
         AbstractUastVisitor() {
 
         override fun visitLambdaExpression(node: ULambdaExpression): Boolean {
-            // Have to go to PSI here; not available on ULambdaExpression yet
-            // https://github.com/JetBrains/uast/issues/16
-            //    ULambdaExpression#getFunctionalInterfaceType
-            val psi = node.psi
-            if (psi is PsiLambdaExpression) {
-                val type = psi.functionalInterfaceType
-                if (type is PsiClassType) {
-                    val resolved = type.resolve()
-                    if (resolved != null) {
-                        checkClass(node, null, resolved)
-                    }
+            val type = node.functionalInterfaceType
+            if (type is PsiClassType) {
+                val resolved = type.resolve()
+                if (resolved != null) {
+                    checkClass(node, null, resolved)
                 }
             }
 
@@ -1002,6 +993,16 @@
             }
             return super.visitWhileExpression(node)
         }
+
+        override fun visitYieldExpression(node: UYieldExpression): Boolean {
+            val list = nodePsiTypeDetectors[UYieldExpression::class.java]
+            if (list != null) {
+                for (v in list) {
+                    v.visitor.visitYieldExpression(node)
+                }
+            }
+            return super.visitYieldExpression(node)
+        }
     }
 
     /** Performs common AST searches for method calls and R-type-field references.
@@ -1070,7 +1071,7 @@
 
         private fun visitMethodCallExpression(node: UCallExpression) {
             if (mVisitMethods) {
-                val methodName = node.methodName
+                val methodName = node.methodName ?: node.methodIdentifier?.name
                 if (methodName != null) {
                     val list = methodDetectors[methodName]
                     if (list != null) {
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UastParser.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UastParser.kt
index b53c74f..01eaf81 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UastParser.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UastParser.kt
@@ -25,7 +25,6 @@
 import org.jetbrains.uast.UCallExpression
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.UFile
-import org.jetbrains.uast.UastContext
 import java.io.File
 
 /**
@@ -48,11 +47,6 @@
     abstract val evaluator: JavaEvaluator
 
     /**
-     * Returns a UastContext which can provide UAST representations for source files
-     */
-    abstract val uastContext: UastContext?
-
-    /**
      * Prepare to parse the given contexts. This method will be called before
      * a series of [.parse] calls, which allows some
      * parsers to do up front global computation in case they want to more
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Api.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Api.kt
index 7991a49..2b269b9 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Api.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Api.kt
@@ -25,12 +25,13 @@
  * value, not read the current value from the hosting lint environment
  * when the custom lint checks are loaded into lint.
  */
-const val CURRENT_API = 6
+const val CURRENT_API = 7
 
 /** Describes the given API level */
 fun describeApi(api: Int): String {
     return when (api) {
-        6 -> "3.6+" // 3.6.0-alpha06
+        7 -> "4.0+" // 4.0.0-alpha08
+        6 -> "3.6" // 3.6.0-alpha06
         5 -> "3.5" // 3.5.0-alpha07
         4 -> "3.4" // 3.4.0-alpha03
         3 -> "3.3" // 3.3.0-alpha12
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
index 4704222..73b957c 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
@@ -79,7 +79,7 @@
 import org.jetbrains.uast.USimpleNameReferenceExpression;
 import org.jetbrains.uast.UVariable;
 import org.jetbrains.uast.UastBinaryOperator;
-import org.jetbrains.uast.UastContext;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.UastPrefixOperator;
 import org.jetbrains.uast.UastUtils;
 import org.jetbrains.uast.util.UastExpressionUtils;
@@ -264,8 +264,7 @@
                 }
 
                 PsiVariable variable = (PsiVariable) resolved;
-                UastContext uastContext = UastUtils.getUastContext(node);
-                Object value = UastLintUtils.findLastValue(variable, node, uastContext, this);
+                Object value = UastLintUtils.findLastValue(variable, node, this);
 
                 if (value != null) {
                     if (surroundedByVariableCheck(node, variable)) {
@@ -938,12 +937,11 @@
         public LastAssignmentFinder(
                 @NonNull PsiVariable variable,
                 @NonNull UElement endAt,
-                @NonNull UastContext context,
                 @Nullable ConstantEvaluator constantEvaluator,
                 int variableLevel) {
             mVariable = variable;
             mEndAt = endAt;
-            UExpression initializer = context.getInitializerBody(variable);
+            UExpression initializer = UastFacade.INSTANCE.getInitializerBody(variable);
             mLastAssignment = initializer;
             mConstantEvaluator = constantEvaluator;
             if (initializer != null && constantEvaluator != null) {
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.kt
index 5b2a914..72b9cd2 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.kt
@@ -44,6 +44,7 @@
 import org.jetbrains.uast.UMethod
 import org.jetbrains.uast.USwitchExpression
 import org.jetbrains.uast.UastContext
+import org.jetbrains.uast.getUastContext
 import java.io.File
 
 /**
@@ -487,8 +488,9 @@
         return psi != null && isSuppressedWithComment(psi, issue)
     }
 
+    @Deprecated("Use UastFacade instead", ReplaceWith("org.jetbrains.uast.UastFacade"))
     val uastContext: UastContext
-        get() = uastParser.uastContext!!
+        get() = uastFile?.getUastContext()!!
 
     companion object {
         // TODO: Move to LintUtils etc
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/LintUtils.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/LintUtils.kt
index fb4f78c..82dcb4b 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/LintUtils.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/LintUtils.kt
@@ -122,7 +122,9 @@
 import org.jetbrains.uast.UCallExpression
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.UExpression
+import org.jetbrains.uast.UMethod
 import org.jetbrains.uast.UParenthesizedExpression
+import org.jetbrains.uast.UastFacade
 import org.jetbrains.uast.getContainingFile
 import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
 import org.objectweb.asm.Opcodes
@@ -2108,6 +2110,10 @@
     return null
 }
 
+fun PsiMethod.getUMethod(): UMethod? {
+    return UastFacade.convertElementWithParent(this, UMethod::class.java) as? UMethod
+}
+
 // For compatibility reasons
 @Suppress("unused")
 object LintUtils {
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java
index 53f2db4..e4184a9 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TypeEvaluator.java
@@ -32,8 +32,6 @@
 import org.jetbrains.uast.UMethod;
 import org.jetbrains.uast.UReferenceExpression;
 import org.jetbrains.uast.UVariable;
-import org.jetbrains.uast.UastContext;
-import org.jetbrains.uast.UastUtils;
 import org.jetbrains.uast.util.UastExpressionUtils;
 
 /**
@@ -119,8 +117,7 @@
 
         UElement resolved = node;
         if (resolved instanceof UReferenceExpression) {
-            UastContext uastContext = UastUtils.getUastContext(node);
-            resolved = UastUtils.tryResolveUDeclaration(resolved, uastContext);
+            resolved = UastLintUtils.tryResolveUDeclaration(resolved);
         }
 
         if (resolved instanceof UMethod) {
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/UastLintUtils.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/UastLintUtils.kt
index 67d0b00..9b65fa5 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/UastLintUtils.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/UastLintUtils.kt
@@ -37,17 +37,23 @@
 import org.jetbrains.uast.UPrefixExpression
 import org.jetbrains.uast.UQualifiedReferenceExpression
 import org.jetbrains.uast.UReferenceExpression
+import org.jetbrains.uast.UResolvable
 import org.jetbrains.uast.USimpleNameReferenceExpression
 import org.jetbrains.uast.UUnaryExpression
 import org.jetbrains.uast.UVariable
-import org.jetbrains.uast.UastContext
+import org.jetbrains.uast.UastFacade
 import org.jetbrains.uast.UastPrefixOperator
 import org.jetbrains.uast.getContainingUMethod
 import org.jetbrains.uast.getParentOfType
 import org.jetbrains.uast.getUastContext
+import org.jetbrains.uast.toUElementOfType
 
 class UastLintUtils {
     companion object {
+        @JvmStatic
+        fun UElement.tryResolveUDeclaration(): UDeclaration? {
+            return (this as? UResolvable)?.resolve().toUElementOfType()
+        }
 
         /** Returns the containing file for the given element  */
         @JvmStatic
@@ -158,9 +164,9 @@
             if (!currVariable.hasModifierProperty(PsiModifier.FINAL) && (currVariable is PsiLocalVariable || currVariable is PsiParameter)) {
                 val containingFunction = call.getContainingUMethod()
                 if (containingFunction != null) {
-                    val context = call.getUastContext()
                     val finder = ConstantEvaluator.LastAssignmentFinder(
-                        currVariable, call, context, null, -1)
+                        currVariable, call, null, -1
+                    )
                     containingFunction.accept(finder)
                     lastAssignment = finder.lastAssignment
                 }
@@ -191,7 +197,6 @@
         fun findLastValue(
             variable: PsiVariable,
             call: UElement,
-            context: UastContext,
             evaluator: ConstantEvaluator
         ): Any? {
             var value: Any? = null
@@ -202,13 +207,14 @@
                     val body = containingFunction.uastBody
                     if (body != null) {
                         val finder = ConstantEvaluator.LastAssignmentFinder(
-                            variable, call, context, evaluator, 1)
+                            variable, call, evaluator, 1
+                        )
                         body.accept(finder)
                         value = finder.currentValue
                     }
                 }
             } else {
-                val initializer = context.getInitializerBody(variable)
+                val initializer = UastFacade.getInitializerBody(variable)
                 if (initializer != null) {
                     value = initializer.evaluate()
                 }
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultJavaEvaluator.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultJavaEvaluator.kt
index 49d520b..05823b5 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultJavaEvaluator.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultJavaEvaluator.kt
@@ -119,10 +119,10 @@
             // Therefore, call into UAST to get the full Kotlin annotations, but also
             // merge in external annotations and inherited annotations from the class
             // files, and pick unique.
-            val annotations = owner.annotations
+            val annotations = owner.uAnnotations
             val psiAnnotations = getAllAnnotations(owner.psi, inHierarchy)
 
-            if (!annotations.isEmpty()) {
+            if (annotations.isNotEmpty()) {
                 if (psiAnnotations.isEmpty()) {
                     return annotations
                 }
@@ -152,7 +152,7 @@
             return JavaUAnnotation.wrap(psiAnnotations)
         }
 
-        return owner.annotations
+        return owner.uAnnotations
     }
 
     override fun getAllAnnotations(
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultUastParser.kt b/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultUastParser.kt
index b63748c..98f5652 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultUastParser.kt
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/helpers/DefaultUastParser.kt
@@ -26,7 +26,6 @@
 import com.android.tools.lint.detector.api.Severity
 import com.android.tools.lint.detector.api.UastLintUtils
 import com.intellij.lang.Language
-import com.intellij.openapi.components.ServiceManager
 import com.intellij.openapi.util.TextRange
 import com.intellij.openapi.util.io.FileUtilRt
 import com.intellij.openapi.vfs.StandardFileSystems
@@ -41,29 +40,26 @@
 import org.jetbrains.uast.UCallExpression
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.UFile
-import org.jetbrains.uast.UastContext
+import org.jetbrains.uast.UastFacade
 import org.jetbrains.uast.getContainingUFile
 import org.jetbrains.uast.getIoFile
 import org.jetbrains.uast.psi.UElementWithLocation
 import java.io.File
+import kotlin.math.ceil
+import kotlin.math.log10
+import kotlin.math.pow
 
 open class DefaultUastParser(
     // Fully qualified names here:
     // class traffics in Project from both lint and openapi so be explicit
     project: com.android.tools.lint.detector.api.Project?,
-    p: com.intellij.openapi.project.Project
+    val ideaProject: com.intellij.openapi.project.Project
 ) : UastParser() {
-    private val uContext: UastContext?
     private val javaEvaluator: JavaEvaluator
 
     init {
         @Suppress("LeakingThis")
-        javaEvaluator = createEvaluator(project, p)
-        uContext = if (!p.isDisposed) {
-            ServiceManager.getService(p, UastContext::class.java)
-        } else {
-            null
-        }
+        javaEvaluator = createEvaluator(project, ideaProject)
     }
 
     protected open fun createEvaluator(
@@ -111,9 +107,6 @@
             return context.uastFile
         }
 
-        val uast = uastContext ?: return null
-
-        val ideaProject = uast.project
         if (ideaProject.isDisposed) {
             return null
         }
@@ -141,6 +134,7 @@
                         context.project.getRelativePath(file) +
                         ": Kotlin not configured correctly"
             )
+            return null
         }
 
         if (psiFile is PsiPlainTextFile) { // plain text: file too large to process with PSI
@@ -148,10 +142,7 @@
                 warnedAboutLargeFiles = true
                 val max = FileUtilRt.getUserFileSizeLimit()
                 val size = file.length() / 1024
-                val sizeRoundedUp = Math.pow(
-                    2.0,
-                    Math.ceil(Math.log10(size.toDouble()) / Math.log10(2.0) + 0.2)
-                ).toInt()
+                val sizeRoundedUp = 2.0.pow(ceil(log10(size.toDouble()) / log10(2.0) + 0.2)).toInt()
                 context.report(
                     issue = IssueRegistry.LINT_ERROR,
                     location = Location.create(file),
@@ -164,15 +155,10 @@
             return null
         }
 
-        return uast.convertElementWithParent(psiFile, UFile::class.java) as? UFile ?: return null
+        return UastFacade.convertElementWithParent(psiFile, UFile::class.java) as? UFile ?: return null
     }
 
     /**
-     * Returns a UastContext which can provide UAST representations for source files
-     */
-    override val uastContext = uContext
-
-    /**
      * Returns a [Location] for the given element
      *
      * @param context information about the file being parsed
diff --git a/lint/libs/lint-checks/lint_baseline.xml b/lint/libs/lint-checks/lint_baseline.xml
old mode 100755
new mode 100644
index 1d5683c..fa1aec8
--- a/lint/libs/lint-checks/lint_baseline.xml
+++ b/lint/libs/lint-checks/lint_baseline.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 26.5.0-alpha13">
+<issues format="5" by="lint 4.0.0-dev">
 
     <issue
         id="DefaultLocale"
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java"
-            line="780"/>
+            line="821"/>
     </issue>
 
     <issue
@@ -38,7 +38,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/FirebaseAnalyticsDetector.java"
-            line="333"/>
+            line="337"/>
     </issue>
 
     <issue
@@ -46,7 +46,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/FirebaseAnalyticsDetector.java"
-            line="373"/>
+            line="377"/>
     </issue>
 
     <issue
@@ -78,7 +78,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java"
-            line="982"/>
+            line="1011"/>
     </issue>
 
     <issue
@@ -86,7 +86,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java"
-            line="1057"/>
+            line="1086"/>
     </issue>
 
     <issue
@@ -94,7 +94,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java"
-            line="2201"/>
+            line="2234"/>
     </issue>
 
     <issue
@@ -102,7 +102,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/IconDetector.java"
-            line="2211"/>
+            line="2244"/>
     </issue>
 
     <issue
@@ -126,7 +126,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/NetworkSecurityConfigDetector.java"
-            line="428"/>
+            line="466"/>
     </issue>
 
     <issue
@@ -150,7 +150,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/RtlDetector.java"
-            line="389"/>
+            line="398"/>
     </issue>
 
     <issue
@@ -158,7 +158,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/RtlDetector.java"
-            line="422"/>
+            line="431"/>
     </issue>
 
     <issue
@@ -166,7 +166,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/RtlDetector.java"
-            line="587"/>
+            line="609"/>
     </issue>
 
     <issue
@@ -198,7 +198,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java"
-            line="635"/>
+            line="641"/>
     </issue>
 
     <issue
@@ -206,7 +206,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java"
-            line="799"/>
+            line="805"/>
     </issue>
 
     <issue
@@ -214,7 +214,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java"
-            line="1347"/>
+            line="1353"/>
     </issue>
 
     <issue
@@ -222,7 +222,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java"
-            line="1350"/>
+            line="1356"/>
     </issue>
 
     <issue
@@ -230,7 +230,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java"
-            line="1477"/>
+            line="1483"/>
     </issue>
 
     <issue
@@ -238,7 +238,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java"
-            line="1490"/>
+            line="1496"/>
     </issue>
 
     <issue
@@ -262,7 +262,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/VectorDetector.java"
-            line="284"/>
+            line="285"/>
     </issue>
 
     <issue
@@ -270,7 +270,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/VectorDetector.java"
-            line="309"/>
+            line="310"/>
     </issue>
 
     <issue
@@ -278,7 +278,7 @@
         message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/VectorDetector.java"
-            line="319"/>
+            line="320"/>
     </issue>
 
     <issue
@@ -290,27 +290,11 @@
     </issue>
 
     <issue
-        id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
-        <location
-            file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.kt"
-            line="2477"/>
-    </issue>
-
-    <issue
-        id="ExternalAnnotations"
-        message="getAnnotations used instead of `JavaContext.getAllAnnotations`.">
-        <location
-            file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictToDetector.kt"
-            line="162"/>
-    </issue>
-
-    <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.kt"
-            line="419"/>
+            line="423"/>
     </issue>
 
     <issue
@@ -342,7 +326,7 @@
         message="Do not compare java.io.File with `equals` or `==`: will not work correctly on case insensitive file systems! See `go/files-howto`.">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java"
-            line="1292"/>
+            line="1295"/>
     </issue>
 
     <issue
@@ -350,7 +334,7 @@
         message="Do not compare java.io.File with `equals` or `==`: will not work correctly on case insensitive file systems! See `go/files-howto`.">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java"
-            line="1300"/>
+            line="1303"/>
     </issue>
 
     <issue
@@ -358,7 +342,7 @@
         message="Do not compare java.io.File with `equals` or `==`: will not work correctly on case insensitive file systems! See `go/files-howto`.">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java"
-            line="1311"/>
+            line="1314"/>
     </issue>
 
     <issue
@@ -366,7 +350,7 @@
         message="Do not compare java.io.File with `equals` or `==`: will not work correctly on case insensitive file systems! See `go/files-howto`.">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java"
-            line="1319"/>
+            line="1322"/>
     </issue>
 
     <issue
@@ -374,7 +358,7 @@
         message="Do not compare java.io.File with `equals` or `==`: will not work correctly on case insensitive file systems! See `go/files-howto`.">
         <location
             file="libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java"
-            line="324"/>
+            line="320"/>
     </issue>
 
 </issues>
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
index 07de84c..cb05482 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
@@ -107,6 +107,7 @@
 import org.jetbrains.uast.USwitchClauseExpression;
 import org.jetbrains.uast.USwitchExpression;
 import org.jetbrains.uast.UVariable;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.UastUtils;
 import org.jetbrains.uast.java.JavaUAnnotation;
 import org.jetbrains.uast.java.JavaUTypeCastExpression;
@@ -965,8 +966,8 @@
                             if (!found) {
                                 // Look for local alias
                                 UExpression initializer =
-                                        mContext.getUastContext()
-                                                .getInitializerBody(((PsiField) resolved));
+                                        UastFacade.INSTANCE.getInitializerBody(
+                                                ((PsiField) resolved));
                                 if (initializer instanceof UReferenceExpression) {
                                     resolved = ((UReferenceExpression) initializer).resolve();
                                     if (resolved instanceof PsiField) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.kt
index 12e13f6..772959c 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.kt
@@ -2602,7 +2602,8 @@
                 return -1
             }
 
-            for (annotation in annotated.annotations) {
+            //noinspection AndroidLintExternalAnnotations
+            for (annotation in annotated.uAnnotations) {
                 val fqcn = annotation.qualifiedName
                 if (fqcn != null && isApiLevelAnnotation(fqcn)) {
                     val attributeList = annotation.attributeValues
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetector.java
index b4ae602..e9308ab 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetector.java
@@ -49,8 +49,7 @@
 import org.jetbrains.uast.UMethod;
 import org.jetbrains.uast.UReferenceExpression;
 import org.jetbrains.uast.USuperExpression;
-import org.jetbrains.uast.UastContext;
-import org.jetbrains.uast.UastUtils;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.visitor.AbstractUastVisitor;
 
 /**
@@ -199,7 +198,7 @@
                 context.report(ISSUE, onTouchEvent, context.getNameLocation(onTouchEvent), message);
             } else {
                 // If we override performClick, ensure that it is called inside onTouchEvent.
-                UastContext uastContext = UastUtils.getUastContext(declaration);
+                UastFacade uastContext = UastFacade.INSTANCE;
                 UElement uastMethod = uastContext.convertElement(onTouchEvent, null, UMethod.class);
                 if (uastMethod != null && !performsClick(uastMethod)) {
                     String message =
@@ -216,7 +215,7 @@
         // Ensure that, if performClick is implemented, performClick calls super.performClick.
         if (performClick != null) {
             // If we override performClick, ensure that it is called inside onTouchEvent.
-            UastContext uastContext = UastUtils.getUastContext(declaration);
+            UastFacade uastContext = UastFacade.INSTANCE;
             UElement uastMethod = uastContext.convertElement(performClick, null, UMethod.class);
             if (uastMethod != null && !performsClickCallsSuper(uastMethod)) {
                 String message =
@@ -236,7 +235,7 @@
         PsiMethod[] onTouchMethods = declaration.findMethodsByName(ON_TOUCH, false);
         for (PsiMethod method : onTouchMethods) {
             if (evaluator.parametersMatch(method, CLASS_VIEW, MOTION_EVENT_CLS)) {
-                UastContext uastContext = UastUtils.getUastContext(declaration);
+                UastFacade uastContext = UastFacade.INSTANCE;
                 UElement uastMethod = uastContext.convertElement(method, null, UMethod.class);
                 if (uastMethod != null && !performsClick(uastMethod)) {
                     String message =
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DeprecationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DeprecationDetector.java
index 4301a39..305e05f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DeprecationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DeprecationDetector.java
@@ -88,6 +88,7 @@
                         rootElement,
                         context.getNameLocation(rootElement),
                         "The `android.preference` library is deprecated, it is recommended that you migrate to the AndroidX Preference library instead.");
+                return;
             }
             if (tagName.startsWith("androidx.preference.")) {
                 // Qualified androidx preference tags can skip inheritance checking.
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FirebaseAnalyticsDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FirebaseAnalyticsDetector.java
index 03214f6..3986734 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FirebaseAnalyticsDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FirebaseAnalyticsDetector.java
@@ -27,6 +27,7 @@
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.Lint;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
@@ -219,7 +220,7 @@
                 if (resolvedMethod != null) {
                     UReferenceExpression returnReference =
                             ReturnReferenceExpressionFinder.find(
-                                    mContext.getUastContext().getMethod(resolvedMethod));
+                                    Lint.getUMethod(resolvedMethod));
                     if (returnReference != null) {
                         addParams(find(mContext, returnReference));
                     }
@@ -230,7 +231,7 @@
         }
 
         @Override
-        public boolean visitCallExpression(UCallExpression expression) {
+        public boolean visitCallExpression(@NonNull UCallExpression expression) {
             checkMethodCall(expression);
             return super.visitCallExpression(expression);
         }
@@ -281,7 +282,6 @@
     }
 
     /** Given a method, find the last `return` expression that returns a reference. */
-    @SuppressWarnings("UnsafeReturnStatementVisitor")
     private static class ReturnReferenceExpressionFinder extends AbstractUastVisitor {
 
         private UReferenceExpression mReturnReference = null;
@@ -297,7 +297,10 @@
         }
 
         @Nullable
-        static UReferenceExpression find(UMethod method) {
+        static UReferenceExpression find(@Nullable UMethod method) {
+            if (method == null) {
+                return null;
+            }
             ReturnReferenceExpressionFinder finder = new ReturnReferenceExpressionFinder();
             method.accept(finder);
             return finder.mReturnReference;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java
index 9c432b3..667b29f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaPerformanceDetector.java
@@ -38,6 +38,7 @@
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.Lint;
 import com.android.tools.lint.detector.api.LintFix;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
@@ -229,14 +230,17 @@
                     String argument = node.getValueArguments().get(0).asSourceString();
 
                     String replacedType = typeName.substring(typeName.lastIndexOf('.') + 1);
-                    LintFix fix =
-                            LintFix.create()
-                                    .name("Replace with valueOf()", true)
-                                    .replace()
-                                    .pattern("(new\\s+" + replacedType + ")")
-                                    .with(replacedType + ".valueOf")
-                                    .autoFix()
-                                    .build();
+                    LintFix fix = null;
+                    if (!Lint.isKotlin(node.getSourcePsi())) {
+                        fix =
+                                LintFix.create()
+                                        .name("Replace with valueOf()", true)
+                                        .replace()
+                                        .pattern("(new\\s+" + replacedType + ")")
+                                        .with(replacedType + ".valueOf")
+                                        .autoFix()
+                                        .build();
+                    }
                     mContext.report(
                             USE_VALUE_OF,
                             node,
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.kt
index ea9fc67..63c542c 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/JavaScriptInterfaceDetector.kt
@@ -95,7 +95,7 @@
                 if (modifierList.hasModifierProperty(PsiModifier.PUBLIC)) {
                     return
                 }
-                for (annotation in node.annotations) {
+                for (annotation in node.uAnnotations) {
                     if (annotation.qualifiedName == JAVASCRIPT_INTERFACE_CLS) {
                         context.report(
                             ISSUE, node as UElement, context.getNameLocation(node),
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LeakDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LeakDetector.kt
index a215fba..c4f3ea5 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LeakDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LeakDetector.kt
@@ -50,6 +50,7 @@
 import org.jetbrains.uast.UObjectLiteralExpression
 import org.jetbrains.uast.UQualifiedReferenceExpression
 import org.jetbrains.uast.UResolvable
+import org.jetbrains.uast.UastFacade
 import org.jetbrains.uast.getContainingUClass
 import org.jetbrains.uast.getParentOfType
 import org.jetbrains.uast.toUElement
@@ -233,7 +234,7 @@
             }
 
             for (method in containingClass.constructors) {
-                val methodBody = context.uastContext.getMethodBody(method) ?: continue
+                val methodBody = UastFacade.getMethodBody(method) ?: continue
                 val assignedToAppContext = Ref(false)
 
                 methodBody.accept(
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.kt
index 84aad47..3f3fc1d 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LogDetector.kt
@@ -27,8 +27,10 @@
 import com.android.tools.lint.detector.api.Severity
 import com.android.tools.lint.detector.api.SourceCodeScanner
 import com.android.tools.lint.detector.api.UastLintUtils
+import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiMethod
 import com.intellij.psi.PsiVariable
+import com.intellij.psi.util.PsiTreeUtil
 import org.jetbrains.uast.UBinaryExpression
 import org.jetbrains.uast.UCallExpression
 import org.jetbrains.uast.UClass
@@ -43,9 +45,7 @@
 import org.jetbrains.uast.UQualifiedReferenceExpression
 import org.jetbrains.uast.USimpleNameReferenceExpression
 import org.jetbrains.uast.evaluateString
-import org.jetbrains.uast.getContainingClass
 import org.jetbrains.uast.tryResolveNamed
-import java.util.Arrays
 import java.util.Locale
 
 /**
@@ -115,7 +115,7 @@
     }
 
     override fun getApplicableMethodNames(): List<String>? =
-        Arrays.asList(
+        listOf(
             "d",
             "e",
             "i",
@@ -329,7 +329,7 @@
         val isLoggableLevel = isLoggableArguments[1]
         val resolved = isLoggableLevel.tryResolveNamed() ?: return
         if (resolved is PsiVariable) {
-            val containingClass = resolved.getContainingClass()
+            val containingClass = PsiTreeUtil.getParentOfType(resolved, PsiClass::class.java)
             if (containingClass == null ||
                 "android.util.Log" != containingClass.qualifiedName ||
                 resolved.getName() == null ||
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.kt
index 69c4c7c..d12145c 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.kt
@@ -26,6 +26,7 @@
 import com.android.tools.lint.detector.api.Severity
 import com.android.tools.lint.detector.api.SourceCodeScanner
 import com.android.tools.lint.detector.api.getMethodName
+import com.android.tools.lint.detector.api.getUMethod
 import com.google.common.collect.Lists
 import com.google.common.collect.Maps
 import com.intellij.psi.PsiClass
@@ -80,7 +81,7 @@
         val parameter = parameters[1]
 
         val visitor = ParameterEscapesVisitor(cls, parameter)
-        val method = context.uastContext.getMethod(declaration)
+        val method = declaration.getUMethod() ?: return
         method.accept(visitor)
         if (visitor.variableEscapes()) {
             reportError(context, viewHolder, parameter)
@@ -110,7 +111,7 @@
         declaration: UMethod,
         references: List<UCallExpression>?
     ) {
-        if (references != null && !references.isEmpty()) {
+        if (references != null && references.isNotEmpty()) {
             val targets = Lists.newArrayList<UCallExpression>()
             val sources = Lists.newArrayList<UCallExpression>()
             for (ref in references) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictToDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictToDetector.kt
index 7489b6d..6f4a654 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictToDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RestrictToDetector.kt
@@ -163,7 +163,8 @@
                 UAnnotated::class.java, true
             ) ?: break
 
-            for (annotation in owner.annotations) {
+            //noinspection AndroidLintExternalAnnotations
+            for (annotation in owner.uAnnotations) {
                 val name = annotation.qualifiedName
                 if (RESTRICT_TO_ANNOTATION.isEquals(name)) {
                     val restrictionScope = getRestrictionScope(annotation)
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
index 66d6bca..2aefa30 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
@@ -91,6 +91,7 @@
 import org.jetbrains.uast.UExpression;
 import org.jetbrains.uast.ULiteralExpression;
 import org.jetbrains.uast.UReferenceExpression;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.util.UastExpressionUtils;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -1186,7 +1187,7 @@
                     PsiElement resolved = ((UReferenceExpression) lastArg).resolve();
                     if (resolved instanceof PsiVariable) {
                         UExpression initializer =
-                                context.getUastContext().getInitializerBody((PsiVariable) resolved);
+                                UastFacade.INSTANCE.getInitializerBody((PsiVariable) resolved);
                         if (initializer != null
                                 && (UastExpressionUtils.isNewArray(initializer)
                                         || UastExpressionUtils.isArrayInitializer(initializer))) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ThreadDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ThreadDetector.kt
index 456c2a7..b41185e 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ThreadDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ThreadDetector.kt
@@ -68,6 +68,9 @@
         ANY_THREAD_ANNOTATION.newName()
     )
 
+    override fun isApplicableAnnotationUsage(type: AnnotationUsageType): Boolean =
+        type == METHOD_CALL || type == METHOD_CALL_CLASS || type == METHOD_CALL_PARAMETER
+
     /**
      * Handles a given UAST node relevant to our annotations.
      *
@@ -99,7 +102,6 @@
     ) {
         if (method == null) return
         val usagePsi = usage.sourcePsi ?: return
-
         if (usagePsi.getUserData(CHECKED) == true) return
         usagePsi.putUserData(CHECKED, true)
 
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java
index 779b16e..9d4f880 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TrustAllX509TrustManagerDetector.java
@@ -40,6 +40,7 @@
 import org.jetbrains.uast.UExpression;
 import org.jetbrains.uast.UReturnExpression;
 import org.jetbrains.uast.UastEmptyExpression;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.visitor.AbstractUastVisitor;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.tree.AbstractInsnNode;
@@ -104,7 +105,7 @@
             // reporting an issue if none of these calls are found. ControlFlowGraph
             // may be useful here.
 
-            UExpression body = context.getUastContext().getMethodBody(method);
+            UExpression body = UastFacade.INSTANCE.getMethodBody(method);
 
             ComplexBodyVisitor visitor = new ComplexBodyVisitor();
             if (body != null) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypedefDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypedefDetector.kt
index b9011b6..c60a066 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypedefDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TypedefDetector.kt
@@ -61,6 +61,7 @@
 import org.jetbrains.uast.UReturnExpression
 import org.jetbrains.uast.UVariable
 import org.jetbrains.uast.UastBinaryOperator
+import org.jetbrains.uast.UastFacade
 import org.jetbrains.uast.UastPrefixOperator
 import org.jetbrains.uast.getParentOfType
 import org.jetbrains.uast.java.JavaUAnnotation
@@ -438,7 +439,7 @@
             // Check field initializers provided it's not a class field, in which case
             // we'd be reading out literal values which we don't want to do)
             if (value is PsiField && rangeAnnotation == null) {
-                val initializer = context.uastContext.getInitializerBody(value)
+                val initializer = UastFacade.getInitializerBody(value)
                 if (initializer != null) {
                     checkTypeDefConstant(
                         context, annotation, initializer, errorNode,
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java
index 404b842..cfdc8bb 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnsafeBroadcastReceiverDetector.java
@@ -61,6 +61,7 @@
 import org.jetbrains.uast.UCallExpression;
 import org.jetbrains.uast.UClass;
 import org.jetbrains.uast.USimpleNameReferenceExpression;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.util.UastExpressionUtils;
 import org.jetbrains.uast.visitor.AbstractUastVisitor;
 import org.w3c.dom.Document;
@@ -747,7 +748,7 @@
         // report a finding at all in this case.)
         PsiParameter parameter = method.getParameterList().getParameters()[1];
         OnReceiveVisitor visitor = new OnReceiveVisitor(context.getEvaluator(), parameter);
-        context.getUastContext().getMethodBody(method).accept(visitor);
+        UastFacade.INSTANCE.getMethodBody(method).accept(visitor);
         if (!visitor.getCallsGetAction()) {
             String report;
             if (!visitor.getUsesIntent()) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/VersionChecks.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/VersionChecks.java
index 231b831..2ec6a96 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/VersionChecks.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/VersionChecks.java
@@ -50,7 +50,7 @@
 import org.jetbrains.uast.UThrowExpression;
 import org.jetbrains.uast.UUnaryExpression;
 import org.jetbrains.uast.UastBinaryOperator;
-import org.jetbrains.uast.UastContext;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.UastPrefixOperator;
 import org.jetbrains.uast.UastUtils;
 import org.jetbrains.uast.visitor.AbstractUastVisitor;
@@ -267,8 +267,13 @@
                             evaluator.computeArgumentMapping(call, method);
                     PsiParameter parameter = mapping.get(prev);
                     if (parameter != null) {
-                        UastContext context = UastUtils.getUastContext(element);
-                        UMethod uMethod = context.getMethod(method);
+                        UastFacade uastFacade = UastFacade.INSTANCE;
+                        UMethod uMethod =
+                                (UMethod)
+                                        uastFacade.convertElementWithParent(method, UMethod.class);
+                        if (uMethod == null) {
+                            return false;
+                        }
                         Ref<UCallExpression> match = new Ref<>();
                         String parameterName = parameter.getName();
                         uMethod.accept(
@@ -375,8 +380,8 @@
                 PsiField field = (PsiField) resolved;
                 PsiModifierList modifierList = field.getModifierList();
                 if (modifierList != null && modifierList.hasExplicitModifier(PsiModifier.STATIC)) {
-                    UastContext context = UastUtils.getUastContext(element);
-                    UExpression initializer = context.getInitializerBody(field);
+                    UastFacade facade = UastFacade.INSTANCE;
+                    UExpression initializer = facade.getInitializerBody(field);
                     if (initializer != null) {
                         Boolean ok =
                                 isVersionCheckConditional(
@@ -465,8 +470,8 @@
 
         // Unconditional version utility method? If so just attempt to call it
         if (!method.hasModifierProperty(PsiModifier.ABSTRACT)) {
-            UastContext context = UastUtils.getUastContext(call);
-            UExpression body = context.getMethodBody(method);
+            UastFacade facade = UastFacade.INSTANCE;
+            UExpression body = facade.getMethodBody(method);
             if (body == null) {
                 return null;
             }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongThreadInterproceduralDetector.kt b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongThreadInterproceduralDetector.kt
index 26599d7..cb89ff31 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongThreadInterproceduralDetector.kt
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WrongThreadInterproceduralDetector.kt
@@ -66,7 +66,7 @@
             element.isAnnotatedWith(annotation) ||
                     element.javaPsi.containingClass?.isAnnotatedWith(annotation) ?: false
         }
-        is CallTarget.Lambda -> element.annotations.any { it.qualifiedName == annotation }
+        is CallTarget.Lambda -> element.uAnnotations.any { it.qualifiedName == annotation }
         is CallTarget.DefaultCtor -> element.isAnnotatedWith(annotation)
     }
 
diff --git a/lint/libs/lint-gradle-api/src/main/java/com/android/tools/lint/gradle/api/ReflectiveLintRunner.kt b/lint/libs/lint-gradle-api/src/main/java/com/android/tools/lint/gradle/api/ReflectiveLintRunner.kt
index 4c75c32..5cea29d 100644
--- a/lint/libs/lint-gradle-api/src/main/java/com/android/tools/lint/gradle/api/ReflectiveLintRunner.kt
+++ b/lint/libs/lint-gradle-api/src/main/java/com/android/tools/lint/gradle/api/ReflectiveLintRunner.kt
@@ -97,13 +97,13 @@
             }
 
             // There can be multiple Lint tasks running in parallel, and we would like them
-            // to share the same LintCoreApplicationEnvironment (in order to share caches).
-            // Thus we do not dispose the LintCoreApplicationEnvironment until the entire
+            // to share the same UastEnvironment (in order to share caches).
+            // Thus we do not dispose the UastEnvironment until the entire
             // Gradle invocation finishes.
             if (!buildCompletionListenerRegistered) {
                 buildCompletionListenerRegistered = true
                 gradle.addListener(BuildCompletionListener {
-                    val cls = l.loadClass("com.android.tools.lint.LintCoreApplicationEnvironment")
+                    val cls = l.loadClass("com.android.tools.lint.UastEnvironment")
                     val disposeMethod = cls.getDeclaredMethod("disposeApplicationEnvironment")
                     disposeMethod.invoke(null)
                     buildCompletionListenerRegistered = false
diff --git a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/annotations/Extractor.java b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/annotations/Extractor.java
index 23d4274..9c37f0b 100644
--- a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/annotations/Extractor.java
+++ b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/annotations/Extractor.java
@@ -58,7 +58,6 @@
 import com.google.common.io.Closeables;
 import com.google.common.io.Files;
 import com.google.common.xml.XmlEscapers;
-import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.StandardFileSystems;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -121,8 +120,8 @@
 import org.jetbrains.uast.UNamedExpression;
 import org.jetbrains.uast.UParameter;
 import org.jetbrains.uast.UReferenceExpression;
-import org.jetbrains.uast.UastContext;
 import org.jetbrains.uast.UastEmptyExpression;
+import org.jetbrains.uast.UastFacade;
 import org.jetbrains.uast.UastVisibility;
 import org.jetbrains.uast.java.JavaUAnnotation;
 import org.jetbrains.uast.java.expressions.JavaUAnnotationCallExpression;
@@ -320,12 +319,10 @@
         }
 
         Project project = units.get(0).getProject();
-        UastContext uastContext = ServiceManager.getService(project, UastContext.class);
-
         AnnotationVisitor visitor = new AnnotationVisitor(false, true);
 
         for (PsiFile unit : units) {
-            UElement uFile = uastContext.convertElementWithParent(unit, UFile.class);
+            UElement uFile = UastFacade.INSTANCE.convertElementWithParent(unit, UFile.class);
             if (uFile == null) {
                 System.out.println("Warning: Could not convert " + unit.getName() + " with UAST");
                 continue;
diff --git a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintExtractAnnotations.kt b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintExtractAnnotations.kt
index fa009b9..f5631a9 100644
--- a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintExtractAnnotations.kt
+++ b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintExtractAnnotations.kt
@@ -18,8 +18,7 @@
 
 import com.android.SdkConstants.DOT_KT
 import com.android.tools.lint.KotlinLintAnalyzerFacade
-import com.android.tools.lint.LintCoreApplicationEnvironment
-import com.android.tools.lint.LintCoreProjectEnvironment
+import com.android.tools.lint.UastEnvironment
 import com.android.tools.lint.annotations.Extractor
 import com.android.tools.lint.gradle.api.ExtractAnnotationRequest
 import com.intellij.openapi.util.Disposer
@@ -39,11 +38,11 @@
         val sourceFiles = request.sourceFiles
         val roots = request.roots
 
-        val appEnv = LintCoreApplicationEnvironment.get()
         val parentDisposable = Disposer.newDisposable()
+        val environment = UastEnvironment.create(parentDisposable)
 
         try {
-            val projectEnvironment = LintCoreProjectEnvironment.create(parentDisposable, appEnv)
+            val projectEnvironment = environment.projectEnvironment
             projectEnvironment.registerPaths(roots)
             val parsedUnits = Extractor.createUnitsForFiles(
                 projectEnvironment.project,
@@ -68,7 +67,7 @@
             throw UncheckedIOException(e)
         } finally {
             Disposer.dispose(parentDisposable)
-            LintCoreApplicationEnvironment.clearAccessorCache()
+            UastEnvironment.ensureDisposed()
         }
     }
 }
diff --git a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleClient.java b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleClient.java
index e06d338..a152e47 100644
--- a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleClient.java
+++ b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleClient.java
@@ -16,7 +16,6 @@
 
 package com.android.tools.lint.gradle;
 
-import static com.android.SdkConstants.VALUE_TRUE;
 import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
 import static java.io.File.separator;
 
@@ -91,7 +90,7 @@
         this.sdkHome = sdkHome;
         this.variantInputs = variantInputs;
         this.baselineVariantName = baselineVariantName;
-        this.registry = registry;
+        setRegistry(registry);
         this.buildToolInfoRevision = buildToolInfoRevision;
         this.resolver = resolver;
         this.variant = variant;
@@ -122,6 +121,7 @@
     @NonNull
     @Override
     public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
+        DefaultConfiguration overrideConfiguration = getOverrideConfiguration();
         if (overrideConfiguration != null) {
             return overrideConfiguration;
         }
@@ -139,7 +139,7 @@
             Map<String, Integer> overrides = lintOptions.getSeverityOverrides();
             if (overrides != null && !overrides.isEmpty()) {
                 return new CliConfiguration(
-                        lintXml, getConfiguration(), project, flags.isFatalOnly()) {
+                        lintXml, getConfiguration(), project, getFlags().isFatalOnly()) {
                     @NonNull
                     @Override
                     public Severity getSeverity(@NonNull Issue issue) {
@@ -150,7 +150,7 @@
                         if (optionSeverity != null) {
                             Severity severity = SyncOptions.getSeverity(issue, optionSeverity);
 
-                            if (flags.isFatalOnly() && severity != Severity.FATAL) {
+                            if (getFlags().isFatalOnly() && severity != Severity.FATAL) {
                                 return Severity.IGNORE;
                             }
 
@@ -222,7 +222,7 @@
 
     @Override
     @NonNull
-    protected LintRequest createLintRequest(@NonNull List<File> files) {
+    protected LintRequest createLintRequest(@NonNull List<? extends File> files) {
         LintRequest lintRequest = new LintRequest(this, files);
         LintGradleProject.ProjectSearch search = new LintGradleProject.ProjectSearch();
         Project project =
@@ -246,11 +246,6 @@
         return driver;
     }
 
-    /** Whether lint should continue running after a baseline has been created */
-    public static boolean continueAfterBaseLineCreated() {
-        return VALUE_TRUE.equals(System.getProperty("lint.baselines.continue"));
-    }
-
     /**
      * Run lint with the given registry, optionally fix any warnings found and return the resulting
      * warnings
@@ -261,7 +256,7 @@
         int exitCode = run(registry, Collections.emptyList());
 
         if (exitCode == LintCliFlags.ERRNO_CREATED_BASELINE) {
-            if (continueAfterBaseLineCreated()) {
+            if (LintCliClient.Companion.continueAfterBaseLineCreated()) {
                 return Pair.of(Collections.emptyList(), driver.getBaseline());
             }
             throw new GradleException("Aborting build since new baseline file was created");
@@ -272,7 +267,7 @@
                     "Aborting build since sources were modified to apply quickfixes after compilation");
         }
 
-        return Pair.of(warnings, driver.getBaseline());
+        return Pair.of(getWarnings(), driver.getBaseline());
     }
 
     /**
diff --git a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleExecution.java b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleExecution.java
index c0ea030..b053712 100644
--- a/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleExecution.java
+++ b/lint/libs/lint-gradle/src/main/java/com/android/tools/lint/gradle/LintGradleExecution.java
@@ -30,11 +30,13 @@
 import com.android.ide.common.gradle.model.IdeAndroidProject;
 import com.android.ide.common.gradle.model.IdeAndroidProjectImpl;
 import com.android.ide.common.gradle.model.level2.IdeDependenciesFactory;
+import com.android.tools.lint.LintCliClient;
 import com.android.tools.lint.LintCliFlags;
 import com.android.tools.lint.LintFixPerformer;
 import com.android.tools.lint.LintStats;
 import com.android.tools.lint.Reporter;
 import com.android.tools.lint.TextReporter;
+import com.android.tools.lint.UastEnvironment;
 import com.android.tools.lint.Warning;
 import com.android.tools.lint.XmlReporter;
 import com.android.tools.lint.checks.BuiltinIssueRegistry;
@@ -52,6 +54,7 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
@@ -102,6 +105,8 @@
             // Not applying the Android Gradle plugin
             lintNonAndroid();
         }
+
+        UastEnvironment.ensureDisposed();
     }
 
     @Nullable
@@ -174,9 +179,8 @@
 
         if (warnings != null
                 && client != null
-                &&
                 // See if there's at least one text reporter
-                client.getFlags()
+                && client.getFlags()
                         .getReporters()
                         .stream()
                         .noneMatch(reporter -> reporter instanceof TextReporter)) {
@@ -215,7 +219,8 @@
             @NonNull VariantInputs variantInputs,
             boolean report,
             boolean isAndroid,
-            boolean allowFix) {
+            boolean allowFix,
+            boolean dispose) {
         IssueRegistry registry = createIssueRegistry(isAndroid);
         LintCliFlags flags = new LintCliFlags();
         LintGradleClient client =
@@ -307,6 +312,10 @@
             throw new GradleException("Invalid arguments.", e);
         }
 
+        if (dispose) {
+            client.disposeProjects(Collections.emptyList());
+        }
+
         if (report && client.haveErrors() && flags.isSetExitCode()) {
             abort(client, warnings.getFirst(), isAndroid);
         }
@@ -389,7 +398,7 @@
     public void lintSingleVariant(@NonNull Variant variant) {
         VariantInputs variantInputs = descriptor.getVariantInputs(variant.getName());
         if (variantInputs != null) {
-            runLint(variant, variantInputs, true, true, true);
+            runLint(variant, variantInputs, true, true, true, true);
         }
     }
 
@@ -400,7 +409,7 @@
     public void lintNonAndroid() {
         VariantInputs variantInputs = descriptor.getVariantInputs("");
         if (variantInputs != null) {
-            runLint(null, variantInputs, true, false, true);
+            runLint(null, variantInputs, true, false, true, true);
         }
     }
 
@@ -423,7 +432,7 @@
             final VariantInputs variantInputs = descriptor.getVariantInputs(variant.getName());
             if (variantInputs != null) {
                 Pair<List<Warning>, LintBaseline> pair =
-                        runLint(variant, variantInputs, false, true, first);
+                        runLint(variant, variantInputs, false, true, first, false);
                 first = false;
                 List<Warning> warnings = pair.getFirst();
                 warningMap.put(variant, warnings);
@@ -520,7 +529,7 @@
                             client, flags.isFatalOnly() ? VARIANT_FATAL : VARIANT_ALL);
                     reporter.write(stats, mergedWarnings);
                     System.err.println("Created baseline file " + baselineFile);
-                    if (LintGradleClient.continueAfterBaseLineCreated()) {
+                    if (LintCliClient.Companion.continueAfterBaseLineCreated()) {
                         return;
                     }
                     System.err.println("(Also breaking build in case this was not intentional.)");
diff --git a/lint/libs/lint-gradle/src/test/java/com/android/tools/lint/annotations/ExtractAnnotationsDriver.java b/lint/libs/lint-gradle/src/test/java/com/android/tools/lint/annotations/ExtractAnnotationsDriver.java
index bc00347..3661e63 100644
--- a/lint/libs/lint-gradle/src/test/java/com/android/tools/lint/annotations/ExtractAnnotationsDriver.java
+++ b/lint/libs/lint-gradle/src/test/java/com/android/tools/lint/annotations/ExtractAnnotationsDriver.java
@@ -23,8 +23,7 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.tools.lint.KotlinLintAnalyzerFacade;
-import com.android.tools.lint.LintCoreApplicationEnvironment;
-import com.android.tools.lint.LintCoreProjectEnvironment;
+import com.android.tools.lint.UastEnvironment;
 import com.android.utils.SdkUtils;
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Charsets;
@@ -245,10 +244,9 @@
                 new Extractor(database, rmTypeDefs, verbose, !skipClassRetention, true);
         extractor.setListIgnored(listFiltered);
 
-        LintCoreApplicationEnvironment appEnv = LintCoreApplicationEnvironment.get();
         Disposable parentDisposable = Disposer.newDisposable();
-        LintCoreProjectEnvironment projectEnvironment =
-                LintCoreProjectEnvironment.create(parentDisposable, appEnv);
+        UastEnvironment environment = UastEnvironment.create(parentDisposable);
+        UastEnvironment.ProjectEnvironment projectEnvironment = environment.getProjectEnvironment();
 
         List<File> sourceRoots = findSourceRoots(sources);
         List<File> joined = Lists.newArrayList(sourceRoots);
@@ -257,7 +255,6 @@
         projectEnvironment.registerPaths(joined);
 
         MockProject project = projectEnvironment.getProject();
-        List<File> paths = projectEnvironment.getPaths();
 
         List<File> allSourceFiles = Extractor.gatherSources(sources);
         List<? extends PsiFile> units = Extractor.createUnitsForFiles(project, allSourceFiles);
@@ -269,13 +266,11 @@
             }
         }
 
-        new KotlinLintAnalyzerFacade().analyze(ktFiles, paths, project);
+        new KotlinLintAnalyzerFacade().analyze(ktFiles, joined, project);
         extractor.extractFromProjectSource(units);
 
-        if (mergePaths != null) {
-            for (File jar : mergePaths) {
-                extractor.mergeExisting(jar);
-            }
+        for (File jar : mergePaths) {
+            extractor.mergeExisting(jar);
         }
 
         extractor.export(output, proguard);
@@ -294,8 +289,8 @@
             }
         }
 
-        Disposer.dispose(LintCoreApplicationEnvironment.get().getParentDisposable());
-        LintCoreApplicationEnvironment.clearAccessorCache();
+        Disposer.dispose(parentDisposable);
+        UastEnvironment.ensureDisposed();
     }
 
     private static final String SEP_JAVA_SEP = File.separator + "java" + File.separator;
diff --git a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
index f6def19..f1a3174 100644
--- a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
+++ b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
@@ -40,11 +40,11 @@
 import com.android.testutils.TestUtils;
 import com.android.tools.lint.LintCliClient;
 import com.android.tools.lint.LintCliFlags;
-import com.android.tools.lint.LintCoreApplicationEnvironment;
 import com.android.tools.lint.LintExternalAnnotationsManager;
 import com.android.tools.lint.LintStats;
 import com.android.tools.lint.Reporter;
 import com.android.tools.lint.TextReporter;
+import com.android.tools.lint.UastEnvironment;
 import com.android.tools.lint.Warning;
 import com.android.tools.lint.checks.ApiLookup;
 import com.android.tools.lint.checks.BuiltinIssueRegistry;
@@ -86,6 +86,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.common.io.Files;
+import com.intellij.openapi.util.Disposer;
 import com.intellij.psi.PsiErrorElement;
 import com.intellij.psi.util.PsiTreeUtil;
 import java.awt.image.BufferedImage;
@@ -152,6 +153,9 @@
                 break;
             }
         }
+
+        UastEnvironment.ensureDisposed();
+        Disposer.assertIsEmpty(true);
     }
 
     protected abstract Detector getDetector();
@@ -282,7 +286,7 @@
             deleteFile(f);
         }
 
-        LintCoreApplicationEnvironment.disposeApplicationEnvironment();
+        UastEnvironment.ensureDisposed();
 
         return result;
     }
@@ -705,12 +709,15 @@
     public class TestLintClient extends LintCliClient {
         public TestLintClient() {
             super(new LintCliFlags(), CLIENT_UNIT_TESTS);
+            LintCliFlags flags = getFlags();
             TextReporter reporter = new TextReporter(this, flags, writer, false);
             reporter.setForwardSlashPaths(true); // stable tests
             flags.getReporters().add(reporter);
         }
 
+        @SuppressWarnings("resource")
         protected final StringWriter writer = new StringWriter();
+
         protected File incrementalCheck;
 
         /**
@@ -900,7 +907,7 @@
 
             // Make sure errors are unique!
             Warning prev = null;
-            for (Warning warning : warnings) {
+            for (Warning warning : getWarnings()) {
                 assertNotSame(warning, prev);
                 assert prev == null || !warning.equals(prev) : warning;
                 prev = warning;
@@ -1168,6 +1175,7 @@
 
             // Check compare contract
             Warning prev = null;
+            List<Warning> warnings = getWarnings();
             for (Warning warning : warnings) {
                 if (prev != null) {
                     boolean equals = warning.equals(prev);
@@ -1198,8 +1206,8 @@
                 prev = warning;
             }
 
-            LintStats stats = LintStats.Companion.create(errorCount, warningCount);
-            for (Reporter reporter : flags.getReporters()) {
+            LintStats stats = LintStats.Companion.create(getErrorCount(), getWarningCount());
+            for (Reporter reporter : getFlags().getReporters()) {
                 reporter.write(stats, warnings);
             }
 
diff --git a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintClient.java b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintClient.java
index 75231ac..92b0dda 100644
--- a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintClient.java
+++ b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintClient.java
@@ -169,7 +169,7 @@
 
     protected void setLintTask(@Nullable TestLintTask task) {
         if (task != null && task.optionSetter != null) {
-            task.optionSetter.set(flags);
+            task.optionSetter.set(getFlags());
         }
 
         // Client should not be used outside of the check process
@@ -304,9 +304,10 @@
         // Reset state here in case a client is reused for multiple runs
         output = new StringBuilder();
         writer.getBuffer().setLength(0);
+        List<Warning> warnings = getWarnings();
         warnings.clear();
-        errorCount = 0;
-        warningCount = 0;
+        setErrorCount(0);
+        setWarningCount(0);
 
         String result = analyze(files, issues);
 
@@ -383,6 +384,7 @@
         } catch (IOException ignore) {
             mocker = task.projectMocks.get(dir);
         }
+        LintCliFlags flags = getFlags();
         if (mocker != null && mocker.getProject() != null) {
             mocker.syncFlagsTo(flags);
             flags.setFatalOnly(task.vital);
@@ -435,7 +437,7 @@
         if (!files.isEmpty()) {
             GradleModelMocker mocker = task.projectMocks.get(files.get(0));
             if (mocker != null) {
-                mocker.syncFlagsTo(flags);
+                mocker.syncFlagsTo(getFlags());
             }
         }
 
@@ -483,6 +485,7 @@
 
         // Check compare contract
         Warning prev = null;
+        List<Warning> warnings = getWarnings();
         for (Warning warning : warnings) {
             if (prev != null) {
                 boolean equals = warning.equals(prev);
@@ -513,8 +516,8 @@
             prev = warning;
         }
 
-        LintStats stats = LintStats.Companion.create(errorCount, warningCount);
-        for (Reporter reporter : flags.getReporters()) {
+        LintStats stats = LintStats.Companion.create(getErrorCount(), getWarningCount());
+        for (Reporter reporter : getFlags().getReporters()) {
             reporter.write(stats, warnings);
         }
 
@@ -772,7 +775,7 @@
         // Make sure errors are unique! See documentation for #allowDuplicates.
         if (!task.allowDuplicates) {
             Warning prev = null;
-            for (Warning warning : warnings) {
+            for (Warning warning : getWarnings()) {
                 assertNotSame(warning, prev);
                 assert prev == null || !warning.equals(prev)
                         : "Warning (message, location) reported more than once: " + warning;
@@ -1187,10 +1190,6 @@
         return super.openConnection(url, timeout);
     }
 
-    public void setRegistry(IssueRegistry registry) {
-        this.registry = registry;
-    }
-
     public static class TestProject extends Project {
         @Nullable public final GradleModelMocker mocker;
         private final ProjectDescription projectDescription;
diff --git a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintResult.kt b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintResult.kt
index 691a3cf..aa7add8 100644
--- a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintResult.kt
+++ b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintResult.kt
@@ -17,7 +17,7 @@
 package com.android.tools.lint.checks.infrastructure
 
 import com.android.SdkConstants.DOT_XML
-import com.android.tools.lint.LintCoreApplicationEnvironment
+import com.android.tools.lint.UastEnvironment
 import com.android.tools.lint.LintStats
 import com.android.tools.lint.Reporter
 import com.android.tools.lint.Warning
@@ -642,7 +642,8 @@
     }
 
     private fun cleanup() {
-        LintCoreApplicationEnvironment.disposeApplicationEnvironment()
+        task.client?.disposeProjects(emptyList())
+        UastEnvironment.ensureDisposed()
     }
 
     @Throws(BadLocationException::class)
diff --git a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintTask.java b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintTask.java
index e462ee7..a045903 100644
--- a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintTask.java
+++ b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintTask.java
@@ -1075,6 +1075,6 @@
                 @NonNull Severity severity,
                 @NonNull Location location,
                 @NonNull String message,
-                @NonNull LintFix fixData);
+                @Nullable LintFix fixData);
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliClientTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliClientTest.java
index 3792827..d592bbc 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliClientTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliClientTest.java
@@ -21,7 +21,9 @@
 import com.android.tools.lint.checks.infrastructure.ProjectDescription;
 import com.android.tools.lint.detector.api.Detector;
 import com.intellij.codeInsight.CustomExceptionHandler;
+import com.intellij.openapi.Disposable;
 import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.util.Disposer;
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
@@ -51,9 +53,11 @@
     }
 
     public void testMissingExtensionPoints() {
-        LintCoreApplicationEnvironment.get();
         // Regression test for 37817771
+        Disposable parentDisposable = Disposer.newDisposable();
+        UastEnvironment.create(parentDisposable);
         Extensions.getExtensions(CustomExceptionHandler.KEY);
+        Disposer.dispose(parentDisposable);
     }
 
     /** Test to ensure that LintCliClient throws an exception when it encounters a relative path. */
@@ -87,7 +91,7 @@
                 .run()
                 .expect(
                         "Relative Path found: bin/classes.jar. All paths should be absolute.",
-                        IllegalStateException.class);
+                        IllegalArgumentException.class);
 
         projectDir.delete();
         binFile.delete();
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MemoryLeakTest.kt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MemoryLeakTest.kt
index 84b8313..97d58b9 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MemoryLeakTest.kt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MemoryLeakTest.kt
@@ -87,7 +87,8 @@
                 }
 
                 dependencies {
-                    implementation 'androidx.appcompat:appcompat:1.0.2'
+                    // Higher version to accidentally pick up recent 1.1 version from local cache
+                    implementation 'androidx.appcompat:appcompat:5.0.2'
                 }
             """.trimIndent()),
 
@@ -184,7 +185,7 @@
 
         assertTrue(
             "Detected Lint memory leak; LintCoreProjectEnvironment is reachable",
-            countLiveInstancesOf(LintCoreProjectEnvironment::class.java.name) == 0)
+            countLiveInstancesOf(UastEnvironment.ProjectEnvironment::class.java.name) == 0)
 
         assertTrue(
             "Detected Lint memory leak; PsiWhiteSpaceImpl is reachable",
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
index 95b7651..552386f 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
@@ -44,6 +44,7 @@
                     new LintCliClient(CLIENT_UNIT_TESTS) {
                         @Override
                         public IssueRegistry getRegistry() {
+                            IssueRegistry registry = super.getRegistry();
                             if (registry == null) {
                                 registry =
                                         new IssueRegistry() {
@@ -57,6 +58,7 @@
                                                         ManifestDetector.MOCK_LOCATION);
                                             }
                                         };
+                                super.setRegistry(registry);
                             }
                             return registry;
                         }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java
index f43d98b..ed5a828 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java
@@ -40,10 +40,11 @@
             //noinspection ResultOfMethodCallIgnored
             file.getParentFile().mkdirs();
             FileWriter writer = new FileWriter(file);
-            TextReporter reporter = new TextReporter(client, client.flags, file, writer, true);
+            LintCliFlags flags = client.getFlags();
+            TextReporter reporter = new TextReporter(client, flags, file, writer, true);
             Project project =
                     Project.create(client, new File("/foo/bar/Foo"), new File("/foo/bar/Foo"));
-            client.flags.setShowEverything(true);
+            flags.setShowEverything(true);
 
             Warning warning1 =
                     new Warning(
@@ -133,11 +134,12 @@
             //noinspection ResultOfMethodCallIgnored
             file.getParentFile().mkdirs();
             FileWriter writer = new FileWriter(file);
-            TextReporter reporter = new TextReporter(client, client.flags, file, writer, true);
-            client.flags.setExplainIssues(true);
+            LintCliFlags flags = client.getFlags();
+            TextReporter reporter = new TextReporter(client, flags, file, writer, true);
+            flags.setExplainIssues(true);
             Project project =
                     Project.create(client, new File("/foo/bar/Foo"), new File("/foo/bar/Foo"));
-            client.flags.setShowEverything(true);
+            flags.setShowEverything(true);
 
             Warning warning1 =
                     new Warning(
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java
index cea2b71..3d67d5c 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java
@@ -110,7 +110,7 @@
                                         lintRequest);
                         configureDriver(driver);
                         driver.analyze();
-                        warningsHolder.set(warnings);
+                        warningsHolder.set(getWarnings());
                         return null;
                     }
                 };
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
index b9e28e0..661e94b 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
@@ -2925,6 +2925,90 @@
                 .expectClean();
     }
 
+    public void testSuspendFunctions() {
+        // Regression test for b/141622949 along with some additional scenarios from
+        // failing metalava tests
+        lint().files(
+                        manifest().minSdk(19),
+                        kotlin(
+                                ""
+                                        + "@file:Suppress(\"NOTHING_TO_INLINE\", \"RedundantVisibilityModifier\", \"unused\")\n"
+                                        + "package test.pkg\n"
+                                        + "import android.content.Context\n"
+                                        + "import android.net.ConnectivityManager\n"
+                                        + "class MainActivity : android.app.Activity() {\n"
+                                        + "    val cm = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager\n"
+                                        + "    fun works1() = cm.activeNetwork\n"
+                                        + "    private fun works2() = cm.activeNetwork\n"
+                                        + "    suspend fun works3() = cm.activeNetwork\n"
+                                        + "    private suspend fun fails() = cm.activeNetwork\n"
+                                        + "\n"
+                                        + "    inline fun <T> a(t: T) = cm.activeNetwork\n"
+                                        + "    inline fun <reified T> b(t: T) = cm.activeNetwork\n"
+                                        + "    private inline fun <reified T> c(t: T) = cm.activeNetwork\n"
+                                        + "    internal inline fun <reified T> d(t: T) = cm.activeNetwork\n"
+                                        + "    public inline fun <reified T> e(t: T) = cm.activeNetwork\n"
+                                        + "    inline fun <reified T> T.f(t: T) = cm.activeNetwork\n"
+                                        + "}"))
+                .run()
+                .expect(
+                        ""
+                                + "src/test/pkg/MainActivity.kt:7: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    fun works1() = cm.activeNetwork\n"
+                                + "                      ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:8: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    private fun works2() = cm.activeNetwork\n"
+                                + "                              ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:9: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    suspend fun works3() = cm.activeNetwork\n"
+                                + "                              ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:10: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    private suspend fun fails() = cm.activeNetwork\n"
+                                + "                                     ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:12: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    inline fun <T> a(t: T) = cm.activeNetwork\n"
+                                + "                                ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:13: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    inline fun <reified T> b(t: T) = cm.activeNetwork\n"
+                                + "                                        ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:14: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    private inline fun <reified T> c(t: T) = cm.activeNetwork\n"
+                                + "                                                ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:15: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    internal inline fun <reified T> d(t: T) = cm.activeNetwork\n"
+                                + "                                                 ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:16: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    public inline fun <reified T> e(t: T) = cm.activeNetwork\n"
+                                + "                                               ~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/MainActivity.kt:17: Error: Call requires API level 23 (current min is 19): android.net.ConnectivityManager#getActiveNetwork [NewApi]\n"
+                                + "    inline fun <reified T> T.f(t: T) = cm.activeNetwork\n"
+                                + "                                          ~~~~~~~~~~~~~\n"
+                                + "10 errors, 0 warnings");
+    }
+
+    public void testReifiedFunctions() {
+        // Regression test for
+        // https://youtrack.jetbrains.com/issue/KT-34316
+        lint().files(
+                        manifest().minSdk(19),
+                        kotlin(
+                                ""
+                                        + "package test.pkg\n"
+                                        + "import android.content.Context\n"
+                                        + "inline fun <reified T> Context.systemService1() = getSystemService(T::class.java)\n"
+                                        + "inline fun Context.systemService2() = getSystemService(String::class.java)"))
+                .run()
+                .expect(
+                        ""
+                                + "src/test/pkg/.java).kt:3: Error: Call requires API level 23 (current min is 19): android.content.Context#getSystemService [NewApi]\n"
+                                + "inline fun <reified T> Context.systemService1() = getSystemService(T::class.java)\n"
+                                + "                                                  ~~~~~~~~~~~~~~~~\n"
+                                + "src/test/pkg/.java).kt:4: Error: Call requires API level 23 (current min is 19): android.content.Context#getSystemService [NewApi]\n"
+                                + "inline fun Context.systemService2() = getSystemService(String::class.java)\n"
+                                + "                                      ~~~~~~~~~~~~~~~~\n"
+                                + "2 errors, 0 warnings");
+    }
+
     public void testThisCall() {
         // Regression test for https://code.google.com/p/android/issues/detail?id=93158
         // Make sure we properly resolve super classes in Class.this.call()
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.kt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.kt
index d6e6fef..fd85064 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.kt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.kt
@@ -22,6 +22,7 @@
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Scope
 import junit.framework.TestCase
+import junit.framework.TestCase.assertTrue
 import java.util.EnumSet
 import java.util.HashSet
 
@@ -30,7 +31,7 @@
         val registry = TestIssueRegistry()
         val issues = registry.issues
         val issueCount = issues.size
-        TestCase.assertTrue(
+        assertTrue(
             Integer.toString(issueCount), BuiltinIssueRegistry.INITIAL_CAPACITY >= issueCount
         )
     }
@@ -58,7 +59,7 @@
         val categories = HashSet<Category>()
         for (issue in TestIssueRegistry().issues) {
             val id = issue.id
-            TestCase.assertTrue("Duplicate id $id", !ids.contains(id))
+            assertTrue("Duplicate id $id", !ids.contains(id))
             ids.add(id)
             categories.add(issue.category)
         }
@@ -68,7 +69,7 @@
         // with category names too
         for ((_, id) in categories) {
             if (ids.contains(id)) {
-                TestCase.assertTrue("Category id clashes with issue id $id", !ids.contains(id))
+                assertTrue("Category id clashes with issue id $id", !ids.contains(id))
             }
         }
 
@@ -76,7 +77,7 @@
         ids.clear()
         for ((_, id) in categories) {
             if (ids.contains(id)) {
-                TestCase.assertTrue("Duplicate category name $id", !ids.contains(id))
+                assertTrue("Duplicate category name $id", !ids.contains(id))
             }
         }
     }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
index b1992f5..f67c0c1 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.tools.lint.checks;
 
+import static com.android.tools.lint.checks.JavaPerformanceDetector.USE_VALUE_OF;
+
 import com.android.tools.lint.detector.api.Detector;
 
 @SuppressWarnings("javadoc")
@@ -612,6 +614,31 @@
                 .expectInlinedMessages(true);
     }
 
+    public void testKotlin2() {
+        lint().files(
+                        kotlin(
+                                        "package test.pkg\n"
+                                                + "\n"
+                                                + "fun test(libs: List<String>) {\n"
+                                                + "    @Suppress(\"PLATFORM_CLASS_MAPPED_TO_KOTLIN\") \n"
+                                                + "    val libraryToIndexMap = HashMap<String, Integer>()\n"
+                                                + "\n"
+                                                + "    libs.asSequence().forEachIndexed { index,lib ->\n"
+                                                + "        libraryToIndexMap.put(lib, Integer(index))\n"
+                                                + "        \n"
+                                                + "    }\n"
+                                                + "}")
+                                .indented())
+                .issues(USE_VALUE_OF)
+                .run()
+                .expect(
+                        ""
+                                + "src/test/pkg/test.kt:8: Warning: Use Integer.valueOf(index) instead [UseValueOf]\n"
+                                + "        libraryToIndexMap.put(lib, Integer(index))\n"
+                                + "                                   ~~~~~~~~~~~~~~\n"
+                                + "0 errors, 1 warnings");
+    }
+
     public void testIsInitialized() {
         // Regression test for
         // 130892328: False positive for DrawAllocation with Kotlin nullable.isInitialized
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
index 60bfc3e..96090e2 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
@@ -16,7 +16,7 @@
 
 package com.android.tools.lint.detector.api;
 
-import com.android.tools.lint.LintCoreApplicationEnvironment;
+import com.android.tools.lint.UastEnvironment;
 import com.android.utils.Pair;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.util.Disposer;
@@ -175,7 +175,7 @@
             Object expected, @Language("JAVA") String source, final String targetVariable) {
         checkJavaUast(expected, source, targetVariable);
         checkPsi(expected, source, targetVariable);
-        LintCoreApplicationEnvironment.disposeApplicationEnvironment();
+        UastEnvironment.ensureDisposed();
     }
 
     private static void checkStatements(
@@ -234,7 +234,7 @@
                         + "}\n";
 
         checkKotlinUast(expected, source, "expression");
-        LintCoreApplicationEnvironment.disposeApplicationEnvironment();
+        UastEnvironment.ensureDisposed();
     }
 
     public void testStrings() {
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.kt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.kt
index 705dc95..eefa5af 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.kt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.kt
@@ -28,6 +28,7 @@
 import com.android.sdklib.IAndroidTarget
 import com.android.testutils.TestUtils
 import com.android.tools.lint.LintCliClient
+import com.android.tools.lint.UastEnvironment
 import com.android.tools.lint.checks.infrastructure.ClassName
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
 import com.android.tools.lint.checks.infrastructure.TestFile
@@ -66,7 +67,6 @@
 import com.google.common.io.Files
 import com.google.common.truth.Truth.assertThat
 import com.intellij.openapi.Disposable
-import com.intellij.psi.PsiFile
 import junit.framework.TestCase
 import org.intellij.lang.annotations.Language
 import org.mockito.Mockito.`when`
@@ -384,10 +384,12 @@
         xml = TestFiles.xml("res/values/strings.xml", "<resources>\n</resources>\n")
         context = createXmlContext(xml.getContents(), File(xml.targetPath))
         assertNull(getLocale(context))
+        dispose(context)
 
         xml = TestFiles.xml("res/values-no/strings.xml", "<resources>\n</resources>\n")
         context = createXmlContext(xml.getContents(), File(xml.targetPath))
         assertEquals("no", getLocale(context)!!.language)
+        dispose(context)
 
         xml = TestFiles.xml(
             "res/values/strings.xml",
@@ -397,6 +399,7 @@
         )
         context = createXmlContext(xml.getContents(), File(xml.targetPath))
         assertEquals("nb", getLocale(context)!!.language)
+        dispose(context)
 
         // tools:locale wins over folder location
         xml = TestFiles.xml(
@@ -407,6 +410,13 @@
         )
         context = createXmlContext(xml.getContents(), File(xml.targetPath))
         assertEquals("nb", getLocale(context)!!.language)
+        dispose(context)
+
+        UastEnvironment.ensureDisposed()
+    }
+
+    private fun dispose(context: XmlContext) {
+        (context.project.client as? LintCliClient)?.disposeProjects(listOf(context.project))
     }
 
     fun testGetLocaleAndRegion() {
@@ -1016,7 +1026,7 @@
                 val uFile = uastParser.parse(context)
                 context.uastFile = uFile
                 assert(uFile != null)
-                context.setJavaFile(uFile!!.sourcePsi as PsiFile)
+                context.setJavaFile(uFile!!.sourcePsi)
             }
 
             val disposable = Disposable {
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
index 8248996..76d25f6 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
@@ -23,6 +23,7 @@
 import com.android.tools.lint.detector.api.Location.SearchHints;
 import com.android.utils.Pair;
 import com.intellij.openapi.Disposable;
+import com.intellij.openapi.util.Disposer;
 import java.io.File;
 import junit.framework.TestCase;
 
@@ -189,5 +190,6 @@
                 new Location.DefaultLocationHandle(pair.getFirst(), 0, 10);
         Location location = handle.resolve();
         assertEquals(10, location.getEnd().getOffset());
+        Disposer.dispose(pair.getSecond());
     }
 }
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ResolveTest.kt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ResolveTest.kt
index 073904f..a9d8aac 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ResolveTest.kt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ResolveTest.kt
@@ -24,7 +24,7 @@
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.UFile
 import org.jetbrains.uast.UResolvable
-import org.jetbrains.uast.visitor.UastVisitor
+import org.jetbrains.uast.visitor.AbstractUastVisitor
 import org.junit.Rule
 import org.junit.rules.TemporaryFolder
 
@@ -66,7 +66,7 @@
             """
             UFile (package = pkg) [package pkg...]
                 UClass (name = TestKt) [public final class TestKt {...}]
-                    UAnnotationMethod (name = test) [public static final fun test() : void {...}]
+                    UMethod (name = test) [public static final fun test() : void {...}]
                         UBlockExpression [{...}]
                             UQualifiedReferenceExpression [Foo.test()] => PsiMethod:test
                                 USimpleNameReferenceExpression (identifier = Foo) [Foo] => PsiClass:Foo
@@ -102,7 +102,7 @@
             UFile (package = ) [import kotlin.reflect.full.declaredMemberFunctions...]
                 UImportStatement (isOnDemand = false) [import kotlin.reflect.full.declaredMemberFunctions] => <FAILED>
                 UClass (name = KotlinTest) [public final class KotlinTest {...}]
-                    UAnnotationMethod (name = test) [public final fun test() : void {...}]
+                    UMethod (name = test) [public final fun test() : void {...}]
                         UBlockExpression [{...}]
                             UQualifiedReferenceExpression [KotlinTest.members] => <FAILED>
                                 UClassLiteralExpression [KotlinTest]
@@ -110,7 +110,7 @@
                             UQualifiedReferenceExpression [KotlinTest.declaredMemberFunctions] => <FAILED>
                                 UClassLiteralExpression [KotlinTest]
                                 USimpleNameReferenceExpression (identifier = declaredMemberFunctions) [declaredMemberFunctions] => PsiMethod:getDeclaredMemberFunctions
-                    UAnnotationMethod (name = KotlinTest) [public fun KotlinTest() = UastEmptyExpression]
+                    UMethod (name = KotlinTest) [public fun KotlinTest() = UastEmptyExpression]
             """.trimIndent().trim(),
             file?.asResolveString()?.trim()
         )
@@ -143,7 +143,7 @@
                 UImportStatement (isOnDemand = false) [import lib.Bar] => <FAILED>
                 UImportStatement (isOnDemand = false) [import lib.Bar2] => PsiClass:Bar2
                 UClass (name = TestKt) [public final class TestKt {...}]
-                    UAnnotationMethod (name = test) [public static final fun test() : void {...}]
+                    UMethod (name = test) [public static final fun test() : void {...}]
                         UBlockExpression [{...}]
                             UDeclarationsExpression [var bar: lib.Bar = <init>("hello1")]
                                 ULocalVariable (name = bar) [var bar: lib.Bar = <init>("hello1")]
@@ -266,7 +266,7 @@
     this@asResolveString.accept(this)
 }.toString()
 
-class ResolveLogger : UastVisitor {
+class ResolveLogger : AbstractUastVisitor() {
 
     val builder = StringBuilder()
 
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypesTest.kt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypesTest.kt
index 052a628..d65a7d6 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypesTest.kt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TypesTest.kt
@@ -22,7 +22,7 @@
 import org.jetbrains.uast.UExpression
 import org.jetbrains.uast.UFile
 import org.jetbrains.uast.asRecursiveLogString
-import org.jetbrains.uast.visitor.UastVisitor
+import org.jetbrains.uast.visitor.AbstractUastVisitor
 import java.io.File
 
 // Misc tests to verify type handling in the Kotlin UAST initialization.
@@ -67,23 +67,22 @@
                     "        UField (name = property1) [@org.jetbrains.annotations.NotNull private final var property1: java.lang.String = \"Default Value\"]\n" +
                     "            UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
                     "            ULiteralExpression (value = \"Default Value\") [\"Default Value\"] : PsiType:String\n" +
-                    "        UAnnotationMethod (name = method) [public fun method() : java.lang.String {...}]\n" +
+                    "        UMethod (name = method) [public fun method() : java.lang.String {...}]\n" +
                     "            UBlockExpression [{...}]\n" +
                     "                UReturnExpression [return \"Hello World\"]\n" +
                     "                    ULiteralExpression (value = \"Hello World\") [\"Hello World\"] : PsiType:String\n" +
-                    "        UAnnotationMethod (name = otherMethod) [public final fun otherMethod(@org.jetbrains.annotations.NotNull ok: boolean, @org.jetbrains.annotations.NotNull times: int) : void {...}]\n" +
+                    "        UMethod (name = otherMethod) [public final fun otherMethod(@org.jetbrains.annotations.NotNull ok: boolean, @org.jetbrains.annotations.NotNull times: int) : void {...}]\n" +
                     "            UParameter (name = ok) [@org.jetbrains.annotations.NotNull var ok: boolean]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
                     "            UParameter (name = times) [@org.jetbrains.annotations.NotNull var times: int]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
                     "            UBlockExpression [{...}] : PsiType:void\n" +
-                    "        UAnnotationMethod (name = getProperty2) [public final fun getProperty2() : java.lang.String = UastEmptyExpression]\n" +
-                    "        UAnnotationMethod (name = setProperty2) [public final fun setProperty2(@org.jetbrains.annotations.Nullable p: java.lang.String) : void = UastEmptyExpression]\n" +
+                    "        UMethod (name = getProperty2) [public final fun getProperty2() : java.lang.String = UastEmptyExpression]\n" +
+                    "        UMethod (name = setProperty2) [public final fun setProperty2(@org.jetbrains.annotations.Nullable p: java.lang.String) : void = UastEmptyExpression]\n" +
                     "            UParameter (name = p) [@org.jetbrains.annotations.Nullable var p: java.lang.String]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.Nullable) [@org.jetbrains.annotations.Nullable]\n" +
-                    "        UAnnotationMethod (name = getProperty1) [public final fun getProperty1() : java.lang.String = UastEmptyExpression]\n" +
-                    "            ULiteralExpression (value = \"Default Value\") [\"Default Value\"] : PsiType:String\n" +
-                    "        UAnnotationMethod (name = Kotlin) [public fun Kotlin(@org.jetbrains.annotations.NotNull property1: java.lang.String, @org.jetbrains.annotations.NotNull arg2: int) {...}]\n" +
+                    "        UMethod (name = getProperty1) [public final fun getProperty1() : java.lang.String = UastEmptyExpression]\n" +
+                    "        UMethod (name = Kotlin) [public fun Kotlin(@org.jetbrains.annotations.NotNull property1: java.lang.String, @org.jetbrains.annotations.NotNull arg2: int) {...}]\n" +
                     "            UParameter (name = property1) [@org.jetbrains.annotations.NotNull var property1: java.lang.String = \"Default Value\"]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
                     "                ULiteralExpression (value = \"Default Value\") [\"Default Value\"] : PsiType:String\n" +
@@ -94,11 +93,11 @@
                     "                    UIdentifier (Identifier (Parent)) [UIdentifier (Identifier (Parent))]\n" +
                     "                    USimpleNameReferenceExpression (identifier = <init>, resolvesTo = Parent) [<init>]\n" +
                     "    UClass (name = Parent) [public class Parent {...}]\n" +
-                    "        UAnnotationMethod (name = method) [public fun method() : java.lang.String {...}]\n" +
+                    "        UMethod (name = method) [public fun method() : java.lang.String {...}]\n" +
                     "            UBlockExpression [{...}]\n" +
                     "                UReturnExpression [return null]\n" +
                     "                    ULiteralExpression (value = null) [null] : PsiType:Void\n" +
-                    "        UAnnotationMethod (name = method2) [public fun method2(@org.jetbrains.annotations.NotNull value: boolean, @org.jetbrains.annotations.Nullable value: java.lang.Boolean) : java.lang.String {...}]\n" +
+                    "        UMethod (name = method2) [public fun method2(@org.jetbrains.annotations.NotNull value: boolean, @org.jetbrains.annotations.Nullable value: java.lang.Boolean) : java.lang.String {...}]\n" +
                     "            UParameter (name = value) [@org.jetbrains.annotations.NotNull var value: boolean]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
                     "            UParameter (name = value) [@org.jetbrains.annotations.Nullable var value: java.lang.Boolean]\n" +
@@ -106,7 +105,7 @@
                     "            UBlockExpression [{...}]\n" +
                     "                UReturnExpression [return null]\n" +
                     "                    ULiteralExpression (value = null) [null] : PsiType:Void\n" +
-                    "        UAnnotationMethod (name = method3) [public fun method3(@org.jetbrains.annotations.Nullable value: java.lang.Integer, @org.jetbrains.annotations.NotNull value2: int) : int {...}]\n" +
+                    "        UMethod (name = method3) [public fun method3(@org.jetbrains.annotations.Nullable value: java.lang.Integer, @org.jetbrains.annotations.NotNull value2: int) : int {...}]\n" +
                     "            UParameter (name = value) [@org.jetbrains.annotations.Nullable var value: java.lang.Integer]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.Nullable) [@org.jetbrains.annotations.Nullable]\n" +
                     "            UParameter (name = value2) [@org.jetbrains.annotations.NotNull var value2: int]\n" +
@@ -114,7 +113,7 @@
                     "            UBlockExpression [{...}]\n" +
                     "                UReturnExpression [return null]\n" +
                     "                    ULiteralExpression (value = null) [null] : PsiType:Void\n" +
-                    "        UAnnotationMethod (name = Parent) [public fun Parent() = UastEmptyExpression]\n",
+                    "        UMethod (name = Parent) [public fun Parent() = UastEmptyExpression]\n",
             file?.asLogTypes()
         )
 
@@ -135,23 +134,22 @@
                     UField (name = property1)
                         UAnnotation (fqName = org.jetbrains.annotations.NotNull)
                         ULiteralExpression (value = "Default Value")
-                    UAnnotationMethod (name = method)
+                    UMethod (name = method)
                         UBlockExpression
                             UReturnExpression
                                 ULiteralExpression (value = "Hello World")
-                    UAnnotationMethod (name = otherMethod)
+                    UMethod (name = otherMethod)
                         UParameter (name = ok)
                             UAnnotation (fqName = org.jetbrains.annotations.NotNull)
                         UParameter (name = times)
                             UAnnotation (fqName = org.jetbrains.annotations.NotNull)
                         UBlockExpression
-                    UAnnotationMethod (name = getProperty2)
-                    UAnnotationMethod (name = setProperty2)
+                    UMethod (name = getProperty2)
+                    UMethod (name = setProperty2)
                         UParameter (name = p)
                             UAnnotation (fqName = org.jetbrains.annotations.Nullable)
-                    UAnnotationMethod (name = getProperty1)
-                        ULiteralExpression (value = "Default Value")
-                    UAnnotationMethod (name = Kotlin)
+                    UMethod (name = getProperty1)
+                    UMethod (name = Kotlin)
                         UParameter (name = property1)
                             UAnnotation (fqName = org.jetbrains.annotations.NotNull)
                             ULiteralExpression (value = "Default Value")
@@ -162,11 +160,11 @@
                                 UIdentifier (Identifier (Parent))
                                 USimpleNameReferenceExpression (identifier = <init>, resolvesTo = Parent)
                 UClass (name = Parent)
-                    UAnnotationMethod (name = method)
+                    UMethod (name = method)
                         UBlockExpression
                             UReturnExpression
                                 ULiteralExpression (value = null)
-                    UAnnotationMethod (name = method2)
+                    UMethod (name = method2)
                         UParameter (name = value)
                             UAnnotation (fqName = org.jetbrains.annotations.NotNull)
                         UParameter (name = value)
@@ -174,7 +172,7 @@
                         UBlockExpression
                             UReturnExpression
                                 ULiteralExpression (value = null)
-                    UAnnotationMethod (name = method3)
+                    UMethod (name = method3)
                         UParameter (name = value)
                             UAnnotation (fqName = org.jetbrains.annotations.Nullable)
                         UParameter (name = value2)
@@ -182,7 +180,7 @@
                         UBlockExpression
                             UReturnExpression
                                 ULiteralExpression (value = null)
-                    UAnnotationMethod (name = Parent)
+                    UMethod (name = Parent)
 
             """.trimIndent(),
             file?.asRecursiveLogString()?.replace("\r", "")
@@ -204,7 +202,7 @@
             "" +
                     "UFile (package = test.pkg) [package test.pkg...]\n" +
                     "    UClass (name = TestKt) [public final class TestKt {...}]\n" +
-                    "        UAnnotationMethod (name = calc) [public static final fun calc(@org.jetbrains.annotations.NotNull @java.lang.Override x: int, @org.jetbrains.annotations.Nullable y: java.lang.Integer, @org.jetbrains.annotations.Nullable z: java.lang.String) : int {...}]\n" +
+                    "        UMethod (name = calc) [public static final fun calc(@org.jetbrains.annotations.NotNull @java.lang.Override x: int, @org.jetbrains.annotations.Nullable y: java.lang.Integer, @org.jetbrains.annotations.Nullable z: java.lang.String) : int {...}]\n" +
                     "            UParameter (name = x) [@org.jetbrains.annotations.NotNull @java.lang.Override var x: int]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
                     "                UAnnotation (fqName = java.lang.Override) [@java.lang.Override]\n" +
@@ -286,16 +284,16 @@
                     "    UClass (name = Parent) [public class Parent {...}]\n" +
                     "        UField (name = number) [@org.jetbrains.annotations.NotNull private final var number: int]\n" +
                     "            UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
-                    "        UAnnotationMethod (name = test) [public final fun test() : int {...}]\n" +
+                    "        UMethod (name = test) [public final fun test() : int {...}]\n" +
                     "            UBlockExpression [{...}]\n" +
                     "                UReturnExpression [return 6]\n" +
                     "                    ULiteralExpression (value = 6) [6] : PsiType:int\n" +
-                    "        UAnnotationMethod (name = getNumber) [public final fun getNumber() : int = UastEmptyExpression]\n" +
-                    "        UAnnotationMethod (name = Parent) [public fun Parent(@org.jetbrains.annotations.NotNull number: int) = UastEmptyExpression]\n" +
+                    "        UMethod (name = getNumber) [public final fun getNumber() : int = UastEmptyExpression]\n" +
+                    "        UMethod (name = Parent) [public fun Parent(@org.jetbrains.annotations.NotNull number: int) = UastEmptyExpression]\n" +
                     "            UParameter (name = number) [@org.jetbrains.annotations.NotNull var number: int]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
                     "    UClass (name = Five) [public final class Five : Parent {...}]\n" +
-                    "        UAnnotationMethod (name = Five) [public fun Five() {...}]\n" +
+                    "        UMethod (name = Five) [public fun Five() {...}]\n" +
                     "            UBlockExpression [{...}]\n" +
                     "                UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1)) [<init>(5)]\n" +
                     "                    UIdentifier (Identifier (Parent)) [UIdentifier (Identifier (Parent))]\n" +
@@ -372,8 +370,8 @@
                     "            ULiteralExpression (value = 3) [3] : PsiType:int\n" +
                     "        UField (name = resId) [@org.jetbrains.annotations.NotNull private final var resId: int]\n" +
                     "            UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n" +
-                    "        UAnnotationMethod (name = getResId) [public final fun getResId() : int = UastEmptyExpression]\n" +
-                    "        UAnnotationMethod (name = KotlinEnum) [private fun KotlinEnum(@org.jetbrains.annotations.NotNull resId: int) = UastEmptyExpression]\n" +
+                    "        UMethod (name = getResId) [public final fun getResId() : int = UastEmptyExpression]\n" +
+                    "        UMethod (name = KotlinEnum) [private fun KotlinEnum(@org.jetbrains.annotations.NotNull resId: int) = UastEmptyExpression]\n" +
                     "            UParameter (name = resId) [@org.jetbrains.annotations.NotNull var resId: int]\n" +
                     "                UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]\n",
             file?.asLogTypes()
@@ -387,7 +385,7 @@
     this@asLogTypes.accept(this)
 }.toString()
 
-class TypesLogger : UastVisitor {
+class TypesLogger : AbstractUastVisitor() {
 
     val builder = StringBuilder()
 
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/UastTest.kt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/UastTest.kt
index ee6784f..b56b54b 100644
--- a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/UastTest.kt
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/UastTest.kt
@@ -153,7 +153,7 @@
                 """
                 UFile (package = ) [public final class BaseKt {...]
                     UClass (name = BaseKt) [public final class BaseKt {...}]
-                        UAnnotationMethod (name = createBase) [public static final fun createBase(@org.jetbrains.annotations.NotNull i: int) : Base {...}]
+                        UMethod (name = createBase) [public static final fun createBase(@org.jetbrains.annotations.NotNull i: int) : Base {...}]
                             UParameter (name = i) [@org.jetbrains.annotations.NotNull var i: int]
                                 UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
                             UBlockExpression [{...}] : PsiType:Void
@@ -163,18 +163,18 @@
                                         USimpleNameReferenceExpression (identifier = <init>, resolvesTo = BaseImpl) [<init>] : PsiType:BaseImpl
                                         USimpleNameReferenceExpression (identifier = i) [i] : PsiType:int
                     UClass (name = Base) [public abstract interface Base {...}]
-                        UAnnotationMethod (name = print) [public abstract fun print() : void = UastEmptyExpression]
+                        UMethod (name = print) [public abstract fun print() : void = UastEmptyExpression]
                     UClass (name = BaseImpl) [public final class BaseImpl : Base {...}]
                         UField (name = x) [@org.jetbrains.annotations.NotNull private final var x: int]
                             UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
-                        UAnnotationMethod (name = print) [public fun print() : void {...}]
+                        UMethod (name = print) [public fun print() : void {...}]
                             UBlockExpression [{...}] : PsiType:void
                                 UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) [println(x)] : PsiType:void
                                     UIdentifier (Identifier (println)) [UIdentifier (Identifier (println))]
                                     USimpleNameReferenceExpression (identifier = println, resolvesTo = null) [println] : PsiType:void
                                     USimpleNameReferenceExpression (identifier = x) [x] : PsiType:int
-                        UAnnotationMethod (name = getX) [public final fun getX() : int = UastEmptyExpression]
-                        UAnnotationMethod (name = BaseImpl) [public fun BaseImpl(@org.jetbrains.annotations.NotNull x: int) = UastEmptyExpression]
+                        UMethod (name = getX) [public final fun getX() : int = UastEmptyExpression]
+                        UMethod (name = BaseImpl) [public fun BaseImpl(@org.jetbrains.annotations.NotNull x: int) = UastEmptyExpression]
                             UParameter (name = x) [@org.jetbrains.annotations.NotNull var x: int]
                                 UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
                     UClass (name = Derived) [public final class Derived : Base {...}]
@@ -184,7 +184,7 @@
                                 UIdentifier (Identifier (createBase)) [UIdentifier (Identifier (createBase))]
                                 USimpleNameReferenceExpression (identifier = createBase, resolvesTo = null) [createBase] : PsiType:Base
                                 ULiteralExpression (value = 10) [10] : PsiType:int
-                        UAnnotationMethod (name = Derived) [public fun Derived(@org.jetbrains.annotations.NotNull b: Base) = UastEmptyExpression]
+                        UMethod (name = Derived) [public fun Derived(@org.jetbrains.annotations.NotNull b: Base) = UastEmptyExpression]
                             UParameter (name = b) [@org.jetbrains.annotations.NotNull var b: Base]
                                 UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
 
@@ -263,7 +263,7 @@
                             UField (name = ubyte) [@org.jetbrains.annotations.NotNull private static final var ubyte: byte = -1]
                                 UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
                                 ULiteralExpression (value = -1) [-1] : PsiType:byte
-                            UAnnotationMethod (name = test) [public static final fun test(@org.jetbrains.annotations.NotNull s: java.lang.String) : java.lang.Object {...}]
+                            UMethod (name = test) [public static final fun test(@org.jetbrains.annotations.NotNull s: java.lang.String) : java.lang.Object {...}]
                                 UParameter (name = s) [@org.jetbrains.annotations.NotNull var s: java.lang.String]
                                     UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
                                 UBlockExpression [{...}]
@@ -291,9 +291,9 @@
                                                     UExpressionList (when_entry) [{...]
                                                         ULiteralExpression (value = "") [""] : PsiType:String
                                                         UBreakExpression (label = null) [break]
-                            UAnnotationMethod (name = getUint) [public static final fun getUint() : int = UastEmptyExpression]
-                            UAnnotationMethod (name = getUlong) [public static final fun getUlong() : long = UastEmptyExpression]
-                            UAnnotationMethod (name = getUbyte) [public static final fun getUbyte() : byte = UastEmptyExpression]
+                            UMethod (name = getUint) [public static final fun getUint() : int = UastEmptyExpression]
+                            UMethod (name = getUlong) [public static final fun getUlong() : long = UastEmptyExpression]
+                            UMethod (name = getUbyte) [public static final fun getUbyte() : byte = UastEmptyExpression]
                         UClass (name = FooInterface) [public abstract interface FooInterface {...}]
                             UField (name = answer) [@org.jetbrains.annotations.NotNull @kotlin.jvm.JvmField public static final var answer: int = 42]
                                 UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
@@ -301,7 +301,7 @@
                                 ULiteralExpression (value = 42) [42] : PsiType:int
                             UField (name = Companion) [@null public static final var Companion: test.pkg.FooInterface.Companion]
                                 UAnnotation (fqName = null) [@null]
-                            UAnnotationMethod (name = sayHello) [@kotlin.jvm.JvmStatic...}]
+                            UMethod (name = sayHello) [@kotlin.jvm.JvmStatic...}]
                                 UAnnotation (fqName = kotlin.jvm.JvmStatic) [@kotlin.jvm.JvmStatic]
                                 UBlockExpression [{...}] : PsiType:void
                                     UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) [println("Hello, world!")] : PsiType:void
@@ -309,14 +309,14 @@
                                         USimpleNameReferenceExpression (identifier = println, resolvesTo = null) [println] : PsiType:void
                                         ULiteralExpression (value = "Hello, world!") ["Hello, world!"] : PsiType:String
                             UClass (name = Companion) [public static final class Companion {...}]
-                                UAnnotationMethod (name = sayHello) [@kotlin.jvm.JvmStatic...}]
+                                UMethod (name = sayHello) [@kotlin.jvm.JvmStatic...}]
                                     UAnnotation (fqName = kotlin.jvm.JvmStatic) [@kotlin.jvm.JvmStatic]
                                     UBlockExpression [{...}] : PsiType:void
                                         UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) [println("Hello, world!")] : PsiType:void
                                             UIdentifier (Identifier (println)) [UIdentifier (Identifier (println))]
                                             USimpleNameReferenceExpression (identifier = println, resolvesTo = null) [println] : PsiType:void
                                             ULiteralExpression (value = "Hello, world!") ["Hello, world!"] : PsiType:String
-                                UAnnotationMethod (name = Companion) [private fun Companion() = UastEmptyExpression]
+                                UMethod (name = Companion) [private fun Companion() = UastEmptyExpression]
                         UClass (name = FooAnnotation) [public abstract annotation FooAnnotation {...}]
                             UField (name = Companion) [@null public static final var Companion: test.pkg.FooAnnotation.Companion]
                                 UAnnotation (fqName = null) [@null]
@@ -333,48 +333,48 @@
                                 UEnumConstant (name = RIGHT) [@null RIGHT]
                                     UAnnotation (fqName = null) [@null]
                                     USimpleNameReferenceExpression (identifier = Direction) [Direction]
-                                UAnnotationMethod (name = Direction) [private fun Direction() = UastEmptyExpression]
+                                UMethod (name = Direction) [private fun Direction() = UastEmptyExpression]
                             UClass (name = Bar) [public static abstract annotation Bar {...}]
                             UClass (name = Companion) [public static final class Companion {...}]
                                 UField (name = bar) [@org.jetbrains.annotations.NotNull private static final var bar: int = 42]
                                     UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
                                     ULiteralExpression (value = 42) [42] : PsiType:int
-                                UAnnotationMethod (name = foo) [public final fun foo() : int {...}]
+                                UMethod (name = foo) [public final fun foo() : int {...}]
                                     UBlockExpression [{...}]
                                         UReturnExpression [return 42]
                                             ULiteralExpression (value = 42) [42] : PsiType:int
-                                UAnnotationMethod (name = getBar) [public final fun getBar() : int = UastEmptyExpression]
-                                UAnnotationMethod (name = Companion) [private fun Companion() = UastEmptyExpression]
+                                UMethod (name = getBar) [public final fun getBar() : int = UastEmptyExpression]
+                                UMethod (name = Companion) [private fun Companion() = UastEmptyExpression]
                         UClass (name = Name) [public final class Name {...}]
                             UField (name = s) [@org.jetbrains.annotations.NotNull private final var s: java.lang.String]
                                 UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
-                            UAnnotationMethod (name = getS) [public final fun getS() : java.lang.String = UastEmptyExpression]
-                            UAnnotationMethod (name = constructor-impl) [public static fun constructor-impl(@org.jetbrains.annotations.NotNull s: java.lang.String) : java.lang.String = UastEmptyExpression]
+                            UMethod (name = getS) [public final fun getS() : java.lang.String = UastEmptyExpression]
+                            UMethod (name = constructor-impl) [public static fun constructor-impl(@org.jetbrains.annotations.NotNull s: java.lang.String) : java.lang.String = UastEmptyExpression]
                                 UParameter (name = s) [@org.jetbrains.annotations.NotNull var s: java.lang.String]
                                     UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
-                            UAnnotationMethod (name = toString-impl) [public static fun toString-impl(@null p: java.lang.String) : java.lang.String = UastEmptyExpression]
+                            UMethod (name = toString-impl) [public static fun toString-impl(@null p: java.lang.String) : java.lang.String = UastEmptyExpression]
                                 UParameter (name = p) [@null var p: java.lang.String]
                                     UAnnotation (fqName = null) [@null]
-                            UAnnotationMethod (name = hashCode-impl) [public static fun hashCode-impl(@null p: java.lang.String) : int = UastEmptyExpression]
+                            UMethod (name = hashCode-impl) [public static fun hashCode-impl(@null p: java.lang.String) : int = UastEmptyExpression]
                                 UParameter (name = p) [@null var p: java.lang.String]
                                     UAnnotation (fqName = null) [@null]
-                            UAnnotationMethod (name = equals-impl) [public static fun equals-impl(@null p: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Object) : boolean = UastEmptyExpression]
+                            UMethod (name = equals-impl) [public static fun equals-impl(@null p: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Object) : boolean = UastEmptyExpression]
                                 UParameter (name = p) [@null var p: java.lang.String]
                                     UAnnotation (fqName = null) [@null]
                                 UParameter (name = p1) [@org.jetbrains.annotations.Nullable var p1: java.lang.Object]
                                     UAnnotation (fqName = org.jetbrains.annotations.Nullable) [@org.jetbrains.annotations.Nullable]
-                            UAnnotationMethod (name = equals-impl0) [public static final fun equals-impl0(@org.jetbrains.annotations.NotNull p1: java.lang.String, @org.jetbrains.annotations.NotNull p2: java.lang.String) : boolean = UastEmptyExpression]
+                            UMethod (name = equals-impl0) [public static final fun equals-impl0(@org.jetbrains.annotations.NotNull p1: java.lang.String, @org.jetbrains.annotations.NotNull p2: java.lang.String) : boolean = UastEmptyExpression]
                                 UParameter (name = p1) [@org.jetbrains.annotations.NotNull var p1: java.lang.String]
                                     UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
                                 UParameter (name = p2) [@org.jetbrains.annotations.NotNull var p2: java.lang.String]
                                     UAnnotation (fqName = org.jetbrains.annotations.NotNull) [@org.jetbrains.annotations.NotNull]
-                            UAnnotationMethod (name = toString) [public fun toString() : java.lang.String = UastEmptyExpression]
-                            UAnnotationMethod (name = hashCode) [public fun hashCode() : int = UastEmptyExpression]
-                            UAnnotationMethod (name = equals) [public fun equals(@null p: java.lang.Object) : boolean = UastEmptyExpression]
+                            UMethod (name = toString) [public fun toString() : java.lang.String = UastEmptyExpression]
+                            UMethod (name = hashCode) [public fun hashCode() : int = UastEmptyExpression]
+                            UMethod (name = equals) [public fun equals(@null p: java.lang.Object) : boolean = UastEmptyExpression]
                                 UParameter (name = p) [@null var p: java.lang.Object]
                                     UAnnotation (fqName = null) [@null]
                         UClass (name = FooInterface2) [public abstract interface FooInterface2 {...}]
-                            UAnnotationMethod (name = foo) [@kotlin.jvm.JvmDefault...}]
+                            UMethod (name = foo) [@kotlin.jvm.JvmDefault...}]
                                 UAnnotation (fqName = kotlin.jvm.JvmDefault) [@kotlin.jvm.JvmDefault]
                                 UBlockExpression [{...}]
                                     UReturnExpression [return 42]
@@ -385,4 +385,56 @@
                 )
             })
     }
+
+    fun testSuspend() {
+        // Regression test for
+        // https://youtrack.jetbrains.com/issue/KT-32031:
+        // UAST: Method body missing for suspend functions
+        val source = kotlin(
+            """
+            package test.pkg
+            import android.widget.TextView
+            class Test : android.app.Activity {
+                private suspend fun setUi(x: Int, y: Int) {
+                    val z = x + y
+                }
+            }
+            """
+        ).indented()
+
+        check(source, { file ->
+            assertEquals(
+                """
+                package test.pkg
+
+                import android.widget.TextView
+
+                public final class Test : android.app.Activity {
+                    public fun Test() = UastEmptyExpression
+                    fun setUi() {
+                        var z: int = x + y
+                    }
+                }
+
+                """.trimIndent(), file.asSourceString())
+
+            assertEquals(
+                """
+                UFile (package = test.pkg) [package test.pkg...]
+                    UImportStatement (isOnDemand = false) [import android.widget.TextView]
+                    UClass (name = Test) [public final class Test : android.app.Activity {...}]
+                        UMethod (name = Test) [public fun Test() = UastEmptyExpression]
+                        UMethod (name = setUi) [fun setUi() {...}]
+                            UBlockExpression [{...}] : PsiType:void
+                                UDeclarationsExpression [var z: int = x + y]
+                                    ULocalVariable (name = z) [var z: int = x + y]
+                                        UBinaryExpression (operator = +) [x + y] : PsiType:int
+                                            USimpleNameReferenceExpression (identifier = x) [x] : PsiType:int
+                                            USimpleNameReferenceExpression (identifier = y) [y] : PsiType:int
+
+                """.trimIndent(),
+                file.asLogTypes()
+            )
+        })
+    }
 }
diff --git a/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/ExternalAnnotationsDetector.kt b/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/ExternalAnnotationsDetector.kt
index ee52ea7..9cb719c 100644
--- a/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/ExternalAnnotationsDetector.kt
+++ b/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/ExternalAnnotationsDetector.kt
@@ -46,7 +46,7 @@
             explanation = """
                 Lint supports XML files with "external annotations", which means any detectors that
                 recognize certain annotations should get them from `JavaEvaluator.getAllAnnotations`
-                and not by calling `annotations` directly on UAST or PSI elements.
+                and not by calling `uAnnotations` directly on UAST or PSI elements.
             """.trimIndent(),
             severity = Severity.ERROR,
             implementation = Implementation(
@@ -63,8 +63,8 @@
         )
     }
 
-    override fun getApplicableMethodNames() = listOf("getAnnotations")
-    override fun getApplicableReferenceNames() = listOf("annotations")
+    override fun getApplicableMethodNames() = listOf("getAnnotations", "getUAnnotations")
+    override fun getApplicableReferenceNames() = listOf("annotations", "uAnnotations")
 
     override fun visitMethodCall(
         context: JavaContext,
diff --git a/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/GradleApiUsageDetector.kt b/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/GradleApiUsageDetector.kt
index 388134b..f0ca368 100644
--- a/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/GradleApiUsageDetector.kt
+++ b/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/GradleApiUsageDetector.kt
@@ -92,7 +92,6 @@
                 ISSUE, node, context.getNameLocation(node),
                 "Avoid using org.gradle.api.Project.exec as it is incompatible with Gradle instant execution."
             )
-
         }
     }
 }
diff --git a/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/IntellijThreadDetector.kt b/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/IntellijThreadDetector.kt
index 935fa51..5c814be 100644
--- a/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/IntellijThreadDetector.kt
+++ b/lint/studio-checks/src/main/java/com/android/tools/lint/checks/studio/IntellijThreadDetector.kt
@@ -69,6 +69,9 @@
 class IntellijThreadDetector : Detector(), SourceCodeScanner {
     override fun applicableAnnotations(): List<String> = THREADING_ANNOTATIONS.toList()
 
+    override fun isApplicableAnnotationUsage(type: AnnotationUsageType): Boolean =
+        type == METHOD_CALL || type == METHOD_CALL_CLASS || type == METHOD_CALL_PARAMETER
+
     /**
      * Handles a given UAST node relevant to our annotations.
      *