Full Code of slackhq/slack-lints for AI

main f58cfb5fc403 cached
160 files
940.0 KB
215.0k tokens
11 symbols
1 requests
Download .txt
Showing preview only (1,002K chars total). Download the full file or copy to clipboard to get everything.
Repository: slackhq/slack-lints
Branch: main
Commit: f58cfb5fc403
Files: 160
Total size: 940.0 KB

Directory structure:
gitextract_dsk5zpit/

├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── maintainers_guide.md
│   └── workflows/
│       ├── ci.yml
│       ├── increment_version.sh
│       ├── renovate.yml
│       └── scriptUtil.sh
├── .gitignore
├── CHANGELOG.md
├── CODEOWNERS
├── LICENSE.txt
├── README.md
├── RELEASING.md
├── build.gradle.kts
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── release.sh
├── renovate.json
├── settings.gradle.kts
├── slack-lint-annotations/
│   ├── build.gradle.kts
│   ├── gradle.properties
│   └── src/
│       └── main/
│           └── kotlin/
│               └── slack/
│                   └── lint/
│                       └── annotations/
│                           ├── AllowUnitResult.kt
│                           ├── DoNotMock.kt
│                           ├── JavaOnly.kt
│                           ├── KotlinOnly.kt
│                           ├── MustUseNamedParams.kt
│                           └── RestrictCallsTo.kt
├── slack-lint-checks/
│   ├── build.gradle.kts
│   ├── gradle.properties
│   ├── lint-baseline.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── slack/
│       │           └── lint/
│       │               ├── AlwaysNullReadOnlyVariableDetector.kt
│       │               ├── AnnotatedClassOrMethodUsageDetector.kt
│       │               ├── ArgInFormattedQuantityStringResDetector.kt
│       │               ├── CircuitScreenDataClassDetector.kt
│       │               ├── DaggerIssuesDetector.kt
│       │               ├── DeprecatedAnnotationDetector.kt
│       │               ├── DeprecatedSqlUsageDetector.kt
│       │               ├── DoNotCallProvidersDetector.kt
│       │               ├── ExceptionMessageDetector.kt
│       │               ├── FragmentDaggerFieldInjectionDetector.kt
│       │               ├── GuavaPreconditionsDetector.kt
│       │               ├── InjectInJavaDetector.kt
│       │               ├── JavaOnlyDetector.kt
│       │               ├── JsonInflaterMoshiCompatibilityDetector.kt
│       │               ├── MainScopeUsageDetector.kt
│       │               ├── MoshiUsageDetector.kt
│       │               ├── MustUseNamedParamsDetector.kt
│       │               ├── NonKotlinPairDetector.kt
│       │               ├── NotNullOperatorDetector.kt
│       │               ├── NullableConcurrentHashMapDetector.kt
│       │               ├── RawDispatchersUsageDetector.kt
│       │               ├── RedactedUsageDetector.kt
│       │               ├── RestrictCallsToDetector.kt
│       │               ├── SerializableDetector.kt
│       │               ├── SlackIssueRegistry.kt
│       │               ├── TestParameterSiteTargetDetector.kt
│       │               ├── ViewContextDetector.kt
│       │               ├── denylistedapis/
│       │               │   └── DenyListedApiDetector.kt
│       │               ├── eithernet/
│       │               │   └── DoNotExposeEitherNetInRepositoriesDetector.kt
│       │               ├── inclusive/
│       │               │   ├── InclusiveNamingChecker.kt
│       │               │   ├── InclusiveNamingResourceScanner.kt
│       │               │   └── InclusiveNamingSourceCodeScanner.kt
│       │               ├── mocking/
│       │               │   ├── AnyMockDetector.kt
│       │               │   ├── AutoValueMockDetector.kt
│       │               │   ├── DataClassMockDetector.kt
│       │               │   ├── DoNotMockMockDetector.kt
│       │               │   ├── ErrorProneDoNotMockDetector.kt
│       │               │   ├── MockDetector.kt
│       │               │   ├── ObjectClassMockDetector.kt
│       │               │   ├── PlatformTypeMockDetector.kt
│       │               │   ├── RecordClassMockDetector.kt
│       │               │   ├── SealedClassMockDetector.kt
│       │               │   └── ValueClassMockDetector.kt
│       │               ├── moshi/
│       │               │   └── MoshiLintUtil.kt
│       │               ├── parcel/
│       │               │   └── ParcelizeFunctionPropertyDetector.kt
│       │               ├── resources/
│       │               │   ├── FullyQualifiedResourceDetector.kt
│       │               │   ├── ImportAliasesLoader.kt
│       │               │   ├── MissingResourceImportAliasDetector.kt
│       │               │   ├── WrongResourceImportAliasDetector.kt
│       │               │   └── model/
│       │               │       └── RootIssueData.kt
│       │               ├── retrofit/
│       │               │   └── RetrofitUsageDetector.kt
│       │               ├── rx/
│       │               │   ├── RxObservableEmitDetector.kt
│       │               │   └── RxSubscribeOnMainDetector.kt
│       │               ├── text/
│       │               │   └── SpanMarkPointMissingMaskDetector.kt
│       │               ├── ui/
│       │               │   ├── DoNotCallViewToString.kt
│       │               │   └── ItemDecorationViewBindingDetector.kt
│       │               └── util/
│       │                   ├── BooleanLintOption.kt
│       │                   ├── LintOption.kt
│       │                   ├── LintUtils.kt
│       │                   ├── MetadataJavaEvaluator.kt
│       │                   ├── Names.kt
│       │                   ├── OptionLoadingDetector.kt
│       │                   ├── Priorities.kt
│       │                   └── StringSetLintOption.kt
│       └── test/
│           ├── java/
│           │   └── slack/
│           │       └── lint/
│           │           ├── AlwaysNullReadOnlyVariableDetectorTest.kt
│           │           ├── ArgInFormattedQuantityStringResDetectorTest.kt
│           │           ├── BaseSlackLintTest.kt
│           │           ├── CircuitScreenDataClassDetectorTest.kt
│           │           ├── DaggerIssuesDetectorTest.kt
│           │           ├── DeprecatedAnnotationDetectorTest.kt
│           │           ├── DeprecatedSqlUsageDetectorTest.kt
│           │           ├── DoNotCallProvidersDetectorTest.kt
│           │           ├── ExceptionMessageDetectorTest.kt
│           │           ├── FragmentDaggerFieldInjectionDetectorTest.kt
│           │           ├── GuavaPreconditionsDetectorTest.kt
│           │           ├── InjectInJavaDetectorTest.kt
│           │           ├── JavaOnlyDetectorTest.kt
│           │           ├── JsonInflaterMoshiCompatibilityDetectorTest.kt
│           │           ├── LintKotlinVersionCheckTest.kt
│           │           ├── MainScopeUsageDetectorTest.kt
│           │           ├── MoshiEnumUsageDetectorTest.kt
│           │           ├── MoshiUsageDetectorTest.kt
│           │           ├── MustUseNamedParamsDetectorTest.kt
│           │           ├── NonKotlinPairDetectorTest.kt
│           │           ├── NotNullOperatorDetectorTest.kt
│           │           ├── NullableConcurrentHashMapDetectorTest.kt
│           │           ├── RawDispatchersUsageDetectorTest.kt
│           │           ├── RedactedUsageDetectorTest.kt
│           │           ├── RestrictCallsToDetectorTest.kt
│           │           ├── SerializableDetectorTest.kt
│           │           ├── TestParameterSiteTargetDetectorTest.kt
│           │           ├── ViewContextDetectorTest.kt
│           │           ├── denylistedapis/
│           │           │   └── DenyListedApiDetectorTest.kt
│           │           ├── eithernet/
│           │           │   └── DoNotExposeEitherNetInRepositoriesDetectorTest.kt
│           │           ├── inclusive/
│           │           │   └── InclusiveNamingDetectorTest.kt
│           │           ├── mocking/
│           │           │   ├── AutoValueMockDetectorTest.kt
│           │           │   ├── DataClassMockDetectorTest.kt
│           │           │   ├── DoNotMockMockDetectorTest.kt
│           │           │   ├── DoNotMockUsageDetectorTest.kt
│           │           │   ├── MockDetectorOptionsTest.kt
│           │           │   ├── MockFileStubs.kt
│           │           │   ├── MockReportTest.kt
│           │           │   ├── ObjectClassMockDetectorTest.kt
│           │           │   ├── PlatformTypeMockDetectorTest.kt
│           │           │   ├── RecordClassMockDetectorTest.kt
│           │           │   ├── SealedClassMockDetectorTest.kt
│           │           │   └── ValueClassMockDetectorTest.kt
│           │           ├── parcel/
│           │           │   └── ParcelizeFunctionPropertyDetectorTest.kt
│           │           ├── resources/
│           │           │   ├── FullyQualifiedResourceDetectorTest.kt
│           │           │   ├── MissingResourceImportAliasDetectorTest.kt
│           │           │   └── WrongResourceImportAliasDetectorTest.kt
│           │           ├── retrofit/
│           │           │   ├── RetrofitJarLoader.kt
│           │           │   └── RetrofitUsageDetectorTest.kt
│           │           ├── rx/
│           │           │   ├── RxJavaJarLoader.kt
│           │           │   ├── RxObservableEmitDetectorTest.kt
│           │           │   └── RxSubscribeOnMainDetectorTest.kt
│           │           ├── text/
│           │           │   └── SpanMarkPointMissingMaskDetectorTest.kt
│           │           └── util/
│           │               └── LintUtilsTest.kt
│           └── resources/
│               ├── com/
│               │   └── slack/
│               │       └── lint/
│               │           └── data/
│               │               └── testStubs/
│               │                   ├── ViewContextDetectorTestContentProvider.java
│               │                   ├── ViewContextDetectorTestCustomViewInternalCaller.java
│               │                   ├── ViewContextDetectorTestExternalCallerOnCustomView.java
│               │                   └── ViewContextDetectorTestExternalCallerOnView.java
│               ├── retrofit-3.0.0.jar
│               └── rxjava-3.1.0.jar
└── spotless/
    └── spotless.kt

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Code of Conduct

## Introduction

Diversity and inclusion make our community strong. We encourage participation from the most varied and diverse backgrounds possible and want to be very clear about where we stand.

Our goal is to maintain a safe, helpful and friendly community for everyone, regardless of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other defining characteristic.

This code and related procedures also apply to unacceptable behavior occurring outside the scope of community activities, in all community venues (online and in-person) as well as in all one-on-one communications, and anywhere such behavior has the potential to adversely affect the safety and well-being of community members.

For more information on our code of conduct, please visit [https://slackhq.github.io/code-of-conduct](https://slackhq.github.io/code-of-conduct)


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributors Guide

Note that this project is considered READ-ONLY. You are welcome to discuss or ask questions in the
discussions section of the repo, but we do not normally accept external contributions without prior
discussion.

## Development

Check out this repo with Android Studio or IntelliJ. It's a standard gradle project and
conventional to check out.

The primary project is `slack-lint`.

Kotlin should be used for more idiomatic use with lint APIs.

## Setup

Be sure your devel environment has `ANDROID_HOME` defined or you'll have trouble running tests
that require the Android SDK. If you've added it and still seeing the error about not having it
defined while running tests, try closing and re-opening Android Studio.

## Lint Documentation

[The Android Lint API Guide](https://googlesamples.github.io/android-custom-lint-rules/book.html) provides an excellent overview of lint's purpose, how it works, and how to author custom checks.

## Lint Guidelines
- Limited scopes. Remember this will run in a slow build step or during the IDE, performance matters!
    - If your check only matters for java or kotlin, only run on appropriate files
    - Use the smallest necessary scope. Avoid tree walking through the AST if it can be avoided, there
      are usually more appropriate hooks.
- Use `UElementHandler` (via overriding `createUastHandler()`) rather than overriding `Detector`
  callback methods. `Detector` callback methods tend only to be useful for tricky scenarios, like
  annotated elements. For basic `UElement` types it's best to just use `UElementHandler` as it affords
  a standard API and is easy to conditionally avoid nested parsing.
- For testing, prefer writing source stubs directly in the test rather than extract individual files
  in `resources` for stubs. Stubs in resources add friction for source glancing and tedious to
  maintain, and should only be used for extremely complex source files.
- Use our `implementation<*Detector>()` helper functions for wiring your `Issue` information. This
  is important because it will help ensure your check works in both command line and in the IDE.

## Maintainers

There are more details about processes and workflow in the [Maintainer's Guide](./maintainers_guide.md).


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
###  Summary

Describe the goal of this PR. Mention any related Issue numbers. Please note that we do not normally
accept new externally-contributed PRs to this repo unless it's something Slack-relevant and came 
up in prior discussion

### Requirements (place an `x` in each `[ ]`)

* [ ] I've read and understood the [Contributing Guidelines](https://github.com/{project_slug}/blob/master/.github/contributing.md) and have done my best effort to follow them.
* [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).

> The following point can be removed after setting up CI (such as Travis) with coverage reports (such as Codecov)

* [ ] I've written tests to cover the new code and functionality included in this PR.

> The following point can be removed after setting up a CLA reporting tool such as cla-assistant.io

* [ ] I've read, agree to, and signed the [Contributor License Agreement (CLA)](https://cla-assistant.io/{project_slug}).


================================================
FILE: .github/maintainers_guide.md
================================================
# Maintainers Guide

This document describes tools, tasks and workflow that one needs to be familiar with in order to effectively maintain
this project. If you use this package within your own software as is but don't plan on modifying it, this guide is
**not** for you.

## Tools (optional)

> Are there any build tools, dependencies, or other programs someone maintaining this project
> needs to be familiar with?

## Tasks

### Testing

> How do you run the tests?

### Generating Documentation (optional)

> If the documentation is generated from source, how does someone run the generation?
> Are the docs published on a website (GitHub Pages)?

### Releasing

> A description of the process to make a release for this project. Do not share any secrets here.

## Workflow

### Versioning and Tags

> Does this project use semver? What does the numbering system look like? Are releases tagged in git?

### Branches

> Describe any specific branching workflow. For example:
> `master` is where active development occurs.
> Long running branches named feature branches are occasionally created for collaboration on a feature that has a large scope (because everyone cannot push commits to another person's open Pull Request)
> At some point in the future after a major version increment, there may be maintenance branches
> for older major versions.

### Issue Management

Labels are used to run issues through an organized workflow. Here are the basic definitions:

*  `bug`: A confirmed bug report. A bug is considered confirmed when reproduction steps have been
   documented and the issue has been reproduced.
*  `enhancement`: A feature request for something this package might not already do.
*  `docs`: An issue that is purely about documentation work.
*  `tests`: An issue that is purely about testing work.
*  `needs feedback`: An issue that may have claimed to be a bug but was not reproducible, or was otherwise missing some information.
*  `discussion`: An issue that is purely meant to hold a discussion. Typically the maintainers are looking for feedback in this issues.
*  `question`: An issue that is like a support request because the user's usage was not correct.
*  `semver:major|minor|patch`: Metadata about how resolving this issue would affect the version number.
*  `security`: An issue that has special consideration for security reasons.
*  `good first contribution`: An issue that has a well-defined relatively-small scope, with clear expectations. It helps when the testing approach is also known.
*  `duplicate`: An issue that is functionally the same as another issue. Apply this only if you've linked the other issue by number.

> You may want to add more labels for subsystems of your project, depending on how complex it is.

**Triage** is the process of taking new issues that aren't yet "seen" and marking them with a basic
level of information with labels. An issue should have **one** of the following labels applied:
`bug`, `enhancement`, `question`, `needs feedback`, `docs`, `tests`, or `discussion`.

Issues are closed when a resolution has been reached. If for any reason a closed issue seems
relevant once again, reopening is great and better than creating a duplicate issue.

## Everything else

When in doubt, find the other maintainers and ask.


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  # Only run push on main
  push:
    branches:
      - main
    paths-ignore:
      - '**/*.md'
  # Always run on PRs
  pull_request:
    branches: [ main ]
  merge_group:

concurrency:
  group: 'ci-${{ github.event.merge_group.head_ref || github.head_ref }}-${{ github.workflow }}'
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Install JDK
        uses: actions/setup-java@v5
        with:
          distribution: 'zulu'
          java-version: '23'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v6

      - name: Build and run tests
        id: gradle
        run: ./gradlew check --quiet

      - uses: mikepenz/action-junit-report@v6
        if: success() || failure()
        with:
          report_paths: '**/build/test-results/test/TEST-*.xml'

      - name: (Fail-only) Upload build reports
        if: failure()
        uses: actions/upload-artifact@v7
        with:
          name: reports
          path: |
            **/build/reports/**

      - name: Publish snapshot (main branch only)
        if: github.repository == 'slackhq/slack-lint-checks' && github.ref == 'refs/heads/main'
        run: ./gradlew publish -PmavenCentralUsername=${{ secrets.SONATYPEUSERNAME }} -PmavenCentralPassword=${{ secrets.SONATYPEPASSWORD }}


================================================
FILE: .github/workflows/increment_version.sh
================================================
#!/usr/bin/env bash

# Target project to update, required
TARGET=$1
# Version to increment to, optional. If blank or not specified, will auto-increment.
REQUESTED_VERSION=$2
source .github/workflows/scriptUtil.sh
# Parse the current version, strip leading zeros, increment
CURRENT_VERSION=$(getProperty 'VERSION_NAME' "$TARGET"/gradle.properties)

# Export the coordinates while we're at it for later use in publish.yml
# Group is always in our root dir
GROUP=$(getProperty 'GROUP' gradle.properties)
ARTIFACT=$(getProperty 'POM_ARTIFACT_ID' "$TARGET"/gradle.properties)

STRIPPED_CURRENT=$(echo "$CURRENT_VERSION" | sed 's/^0*//')
if [[ "$REQUESTED_VERSION" != "" ]]; then
  NEW_VERSION=$REQUESTED_VERSION
else
  ((STRIPPED_CURRENT++))
  NEW_VERSION=$(printf "%05d\n" $STRIPPED_CURRENT)
fi
sed -i -e "s/${CURRENT_VERSION}/${NEW_VERSION}/g" "$TARGET"/gradle.properties
echo "current: $CURRENT_VERSION"
echo "CURRENT_VERSION=$CURRENT_VERSION" >> $GITHUB_ENV
echo "new: $NEW_VERSION"
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
# We just use the artifact ID in the android repo for the coordinate
echo "COORDINATES=$ARTIFACT" >> $GITHUB_ENV


================================================
FILE: .github/workflows/renovate.yml
================================================
name: Renovate

on:
  schedule:
    - cron: "0 8 * * *" # 8am daily
  workflow_dispatch:

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Self-hosted Renovate
        uses: renovatebot/github-action@v32.190.6
        with:
          configurationFile: renovate.json
          token: ${{ secrets.SLACKHQ_MBR_GITHUB_TOKEN }}

================================================
FILE: .github/workflows/scriptUtil.sh
================================================
# Source this file to use its functions

# Gets a property out of a .properties file
# usage: getProperty $key $filename
function getProperty() {
    grep "${1}" "$2" | cut -d'=' -f2
}


================================================
FILE: .gitignore
================================================
.gradle
.kotlin/
local.properties
.idea
!/.idea/codeStyles
!/.idea/inspectionProfiles
!/.idea/icon.png
*.iml
*.so
.DS_Store
build
captures
version.properties
env
node_modules
reports
.cxx

# IDE-generated dir
out/

# We have one gitignore but project generation often generates extra ones. Ignore those
*/**/.gitignore

================================================
FILE: CHANGELOG.md
================================================
Changelog
=========

**Unreleased**
--------------

0.11.1
------

_2025-10-10_

- **Fix**: Allow sealed classes to be Moshi-compatible.
- **Fix**: Allow primitives in collections to be Moshi-compatible.
- **Fix**: Allow sealed interfaces to be Moshi-compatible.
- **Fix**: Allow enums to be Moshi-compatible.
- Update Kotlin to `2.2.10`.
- Update plugin lint to `8.13.0`.
- Update dependency gradle to `9.1.0`.
- Update various build plugins and GitHub Actions.

0.11.0
------

_2025-07-17_

- **New**: Add `CircuitScreenDataClassDetector` check to ensure that [Circuit](https://github.com/slackhq/circuit) `Screen` classes are data classes or data objects.
- **Fix**: Enable the `JsonInflaterMoshiCompatibilityDetector` in slack-lint's lint registry.
- **Fix**: _Actually_ fix "You must override visitCallExpression (and don't call super.visitCallExpression!)" error.
- **Change**: Disable `AlwaysNullReadOnlyVariableDetector` for now as this was causing the issue above it seems and we haven't had a change to investigate further.
- Build against lint `31.12.0-alpha09`.

0.10.1
------

_2025-07-14_

- Fix "You must override visitCallExpression (and don't call super.visitCallExpression!)" error.

0.10.0
------

_2025-06-30_

- **New:** Add `JsonInflaterMoshiCompatibilityDetector` check. This lint is only usable to slack's internal repo.
- **New:** Add `RxObservableEmitDetector` to ensure that `rxObservable`/`rxFlowable` lambda expressions call `send`/`trySend`.
- **New:** Add `AlwaysNullReadOnlyVariableDetector` to lint against read-only variables always initialized to null.
- Allow `suspend` Retrofit functions to return `Unit` if annotated with `@AllowUnitResult`.
- Update lint to `31.12.0-alpha07`.
- Only test against K2 UAST now.
- Test against Retrofit `3.0.0`.

Special thanks to [@henni99](https://github.com/henni99) for contributing to this release!

0.9.0
-----

_2025-03-27_

- **New**: Add `TestParameterSiteTarget` check to protect against https://github.com/google/TestParameterInjector/issues/49.
- **New**: Add `NullableConcurrentHashMap` check to protect against putting null keys or values into `ConcurrentHashMap`s.
- Add mockito-kotlin mock/spy functions to default `DoNotMock` checks.
- Add `java.util.Calendar` to `DenyListedApiDetector`.
- Don't require `ExceptionMessage` lint in tests.
- Update EitherNet checks to EitherNet 2.0.
- Raise lint registry API version to `16` (`8.7.0-alpha04`).
- Build against Kotlin `2.1.20`.
- Build against lint `31.10.0-alpha03`.

Special thanks to [@mformetal](https://github.com/mformetal) and [@jbduncan](https://github.com/jbduncan) for contributing to this release!

0.8.2
-----

_2024-10-14_

- **Enhancement**: Handle `@Multipart` and `@Part` annotations in Retrofit lints.

0.8.1
-----

_2024-10-03_

- Open-source `AvoidUsingNotNullOperator`, `InflationInItemDecoration`, and `DoNotCallViewToString` checks.

0.8.0
-----

_2024-10-02_

- **Enhancement**: Tweak explanation for default dispatcher use in rx<->flow interop.
- **Enhancement**: Switch to stable kotlin-metadata artifact
- **Fix**: Allow Dagger providers to be called from test sources.
- Build against lint `8.8.0-alpha04`.
- Update `api`/`minApi` to `16` (Lint 8.7.0+).
- Build against Kotlin `2.0.20`.
- Target Kotlin language version `1.9` in lint-checks (imposed by lint), `2.0` in lint-annotations.

0.7.3
-----

_2024-05-03_

- Fix `DoNotMockAnything` to use `setEnabledByDefault(false)`.

0.7.2
-----

_2024-05-02_

- Add new `DoNotMockAnything` check. This is disabled by default. This marks _any_ mock as a lint error. This is useful for enforcing a no-mocks policy in your codebase.
- Update lint to `31.5.0-alpha07`.
- Update to kotlin `1.9.23`.
- [docs] Expand Mock option explanation for use with multiple issues.

Special thanks to [@utwyko](https://github.com/utwyko) for contributing to this release!

0.7.1
-----

_2024-03-27_

- Add `MustUseNamedParamsDetector` to lint registry.
- Update lint to `31.5.0-alpha02`.
- Target Kotlin API/language version `1.9`.

0.7.0
-----

_2023-10-27_

- Lower lint API back to `31.3.0-alpha05` as newer versions targeted kotlin 1.9.20 betas without us realizing it.
- Improve explanation for sealed class mock detector to mention that Mockito can't mock them at all in Java 17+.
- Promote `PlatformTypeMockDetector` to error severity.
- Make `DenyListedApi` entries more configurable. Initial change is that blocking APIs are now reported with the ID `DenyListedBlockingApi`.
- Support multiple mock report modes for the `mock-report` option. Modes are `NONE`, `ERRORS`, and `ALL`. Default is `NONE`. Now the report file is `build/reports/mockdetector/mock-report.csv` and the second column is the severity. This allows reporting all mocks for extra analysis.

0.6.1
-----

_2023-10-09_

- **Enhancement**: Add `mock-report` option to `MockDetector`s to generate a report of all mocked types in a project.
- Update to lint `31.3.0-alpha07`.

0.6.0
-----

_2023-09-28_

- **New**: Add `ExceptionMessage` check that ensures that calls to `check`, `checkNotNull`, `require`, and `requireNotNull` functions always include a message.
- **Enhancement**: Add support for custom mock factories and mock annotations to `MockDetector`.
  - `mock-annotations` is a comma-separated list of mock annotations' fully-qualified names. Default is `org.mockito.Mock,org.mockito.Spy`.
  - `mock-factories` is a comma-separated list of mock factories (i.e. `org.mockito.Mockito#methodName`). Default is `org.mockito.Mockito#mock,org.mockito.Mockito#spy,slack.test.mockito.MockitoHelpers#mock,slack.test.mockito.MockitoHelpersKt#mock`.
- Update lint to `31.3.0-alpha05`.

Special thanks to [@SimonMarquis](https://github.com/SimonMarquis) for contributing to this release!

0.5.1
-----

_2023-09-09_

- **Fix**: Allow `@Provides` in companion objects of `@Module` classes.

0.5.0
-----

_2023-09-08_

- **New**: Add a bunch more checks around correct usage of Dagger `@Binds` and `@Provides` methods.
- **Fix**: Remove `BindsCanBeExtensionFunction` lint as this is prohibited now in Dagger 2.48+.
- Update to lint `31.3.0-alpha03`.
- Update to Kotlin `1.9.10`.

0.4.0
-----

_2023-07-20_

- **New**: Denylist blocking RxJava 3 operators and coroutines' `runBlocking` in favor of `TestObserver` and `runTest`/Turbine.
- **New**: Denylist coroutines' `runCatching`.
- **New**: Denylist `java.util.Date`, `java.text.DateFormat`, and `java.text.SimpleDateFormat` in favor of `java.time.*`/`kotlin.time.*`/etc APIs.
- **Enhancement**: Specifically report denylisted function name only in lint report, not the whole call expression.
- **Enhancement**: Update kotlinx-metadata to `0.7.0` to better support Kotlin 2.0.
- Update to lint `31.2.0-alpha13` (lint API `14`).

0.3.0
-----

_2023-05-31_

- **New**: Use kotlinx-metadata to parse `Metadata` annotations of `PsiCompiledElement` types to better handle Kotlin language features. Currently used in mock checks and Moshi checks. Please star this issue: https://issuetracker.google.com/issues/283654244.
- **New**: Add DoNotMock check for `object` types.
- **New**: Add DoNotMock check for `sealed` types. Subtypes should be used instead.
- **New**: Add DoNotMock check for `record` types. Same motivation as data classes.
- **New**: Add DoNotMock check for platform types (e.g. `java.*`, `kotlin.*`, `android.*`, their `*x.*` variants). Prefer real implementations or fakes instead.
  - This is a big change so this one is just a warning for now.
- **Enhancement**: `MockDetector` revamp. All mock checks now run within the same detector to better utilize metadata catching.
- **Enhancement**: Improve mock check location reporting.
- **Enhancement**: Improve mock check messages to specify the erroring type.
- **Enhancement**: Add `reason` properties to `@KotlinOnly`/`@JavaOnly` annotations.
- **Enhancement**: Add more information to the `Vendor` details.
- Raise min lint API to `14`.
- Update kotlin to `1.8.21`. Updated language version to this too to match lint.
- Update lint to `31.2.0-alpha06`.

0.2.3
-----

_2023-02-22_

- **New**: `ParcelizeFunctionProperty` check that errors when a `@Parcelize` class has a function property.

0.2.2
-----

_2023-02-09_

- **Removed**: Compose lints have been removed and published in a separate project: https://github.com/slackhq/compose-lints

0.2.1
-----

_2023-01-26_

- **Fix**: Improve and fix a number of explanation string formatting in the new compose lints.

0.2.0
-----

_2023-01-25_

- **New**: Ported most of the Twitter [compose-rules](https://github.com/twitter/compose-rules) checks to lint. We're packaging them in this project right now, but will likely publish them from a separate repo in the future.
- Target lint-api `31.1.0-alpha01`.
- Update to Kotlin API version `1.7`. Lint `8.1.0-alpha01` or later is now required.
- Modernize various build infra (Kotlin `1.8.0`, JDK 19, Gradle 7.6).

0.1.1
-----

_2022-11-30_

- **Fix**: Fallback to file package name in `MissingResourceImportAliasDetector` if project package name is null.

0.1.0
-----

_2022-11-17_

* Initial release on maven central.


================================================
FILE: CODEOWNERS
================================================
# Comment line immediately above ownership line is reserved for related other information. Please be careful while editing.
#ECCN:Open Source
#GUSINFO:Open Source,Open Source Workflow


================================================
FILE: LICENSE.txt
================================================
                                 Apache License
                           Version 2.0, January 2004
                        https://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   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

       https://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.

================================================
FILE: README.md
================================================
slack-lints
===========

This repository contains a collection of custom Android/Kotlin lint checks we use in our Android and Kotlin code bases at Slack.

While we do publish artifacts to Maven Central, some of the lints checks may only really be relevant to Slack's codebase. We [develop
these in the open](https://slack.engineering/developing-in-the-open/) to knowledge-share with the community.

## Installation

Add the dependency to the `lintChecks` configuration. Note for non-android projects, you must apply the `com.android.lint` Gradle plugin to use this.

[![Maven Central](https://img.shields.io/maven-central/v/com.slack.lint/slack-lint-checks.svg)](https://mvnrepository.com/artifact/com.slack.lint/slack-lint-checks)

```kotlin
dependencies {
  lintChecks("com.slack.lint:slack-lint-checks:<version>")
}
```

## Overview

### Do Not Mock

The `slack.lint.mocking` package contains several detectors and utilities for detecting mocking
of types that should not be mocked. This is similar to ErrorProne's `DoNotMockChecker` and acts as
an enforcement layer to APIs and classes annotated with `@DoNotMock`. This also detects common types that should never be mocked, such as Kotlin `data` classes or AutoValue classes.

### Inclusivity

In order to write more inclusive code, we have an `InclusiveNamingChecker` tool to check for a
configurable list of non-inclusive names.

### Moshi

`MoshiUsageDetector` contains a wealth of checks for common programmer errors when writing classes
for use with [Moshi](https://github.com/square/moshi) and [MoshiX](https://github.com/ZacSweers/MoshiX).

### Misc

* `JavaOnlyDetector` - detects use of Java-only APIs from Kotlin. Based on the original unreleased implementation in [uber/lint-checks](https://github.com/uber/lint-checks).
* `DaggerKotlinIssuesDetector` - detects some known issues when using [Dagger](https://github.com/google/dagger) in Kotlin code.
* `RetrofitUsageDetector` - detects some common issues when using [Retrofit](https://github.com/square/retrofit).
* `DenyListedApi` – detects use of APIs that just shouldn't be used.
* `MustUseNamedParams` – can be used on functions that should _always_ use named parameters. Useful for APIs that have a lot of parameters and/or may change their order and you want to keep changes source-compatible.
* ...and a plethora of others!

License
--------

    Copyright 2021 Slack Technologies, LLC

    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.


================================================
FILE: RELEASING.md
================================================
Releasing
=========

1. Update the `CHANGELOG.md` for the impending release.
2. Run `./release.sh <version>`.
3. Publish the release on the repo's releases tab.


================================================
FILE: build.gradle.kts
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
import com.diffplug.gradle.spotless.KotlinExtension
import com.diffplug.gradle.spotless.SpotlessExtension
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import io.gitlab.arturbosch.detekt.Detekt
import org.jetbrains.dokka.gradle.DokkaTaskPartial
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
  alias(libs.plugins.kotlin.jvm) apply false
  alias(libs.plugins.spotless) apply false
  alias(libs.plugins.mavenPublish) apply false
  alias(libs.plugins.dokka)
  alias(libs.plugins.detekt)
  alias(libs.plugins.lint) apply false
  alias(libs.plugins.ksp) apply false
}

dokka {
  dokkaPublications.html {
    outputDirectory.set(rootDir.resolve("docs/api/0.x"))
    includes.from(project.layout.projectDirectory.file("README.md"))
  }
}

val ktfmtVersion = libs.versions.ktfmt.get()

allprojects {
  apply(plugin = "com.diffplug.spotless")
  configure<SpotlessExtension> {
    format("misc") {
      target("*.md", ".gitignore")
      trimTrailingWhitespace()
      endWithNewline()
    }

    val externalSourceGlobs =
      arrayOf("**/denylistedapis/*.kt", "**/ExceptionMessageDetector*.kt", "**/util/Names.kt")

    kotlin {
      target("**/*.kt")
      ktfmt(ktfmtVersion).googleStyle()
      trimTrailingWhitespace()
      endWithNewline()
      licenseHeaderFile(rootProject.file("spotless/spotless.kt"))
      targetExclude("**/spotless.kt", *externalSourceGlobs)
    }
    // Externally adapted sources that should preserve their license header
    format("kotlinExternal", KotlinExtension::class.java) {
      target(*externalSourceGlobs)
      ktfmt(ktfmtVersion).googleStyle()
      trimTrailingWhitespace()
      endWithNewline()
    }
    kotlinGradle {
      ktfmt(ktfmtVersion).googleStyle()
      trimTrailingWhitespace()
      endWithNewline()
      licenseHeaderFile(
        rootProject.file("spotless/spotless.kt"),
        "(import|plugins|buildscript|dependencies|pluginManagement)",
      )
    }
  }
}

val jdk = libs.versions.jdk.get().toInt()
val lintJvmTargetString: String = libs.versions.lintJvmTarget.get()
val runtimeJvmTargetString: String = libs.versions.runtimeJvmTarget.get()

subprojects {
  val isChecksProject = path == ":slack-lint-checks"
  val jvmTargetString =
    if (isChecksProject) {
      lintJvmTargetString
    } else {
      runtimeJvmTargetString
    }
  val jvmTargetInt = jvmTargetString.toInt()
  pluginManager.withPlugin("java") {
    configure<JavaPluginExtension> {
      toolchain { languageVersion.set(JavaLanguageVersion.of(jdk)) }
    }

    tasks.withType<JavaCompile>().configureEach { options.release.set(jvmTargetInt) }
  }

  pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
    tasks.withType<KotlinCompile>().configureEach {
      compilerOptions {
        jvmTarget.set(JvmTarget.fromTarget(jvmTargetString))
        // TODO re-enable on checks if lint ever targets latest kotlin versions
        if (isChecksProject) {
          // Lint forces Kotlin 1.9 still
          languageVersion.set(KotlinVersion.KOTLIN_1_9)
        } else {
          allWarningsAsErrors.set(true)
        }
      }
    }
  }

  tasks.withType<Detekt>().configureEach { jvmTarget = jvmTargetString }

  pluginManager.withPlugin("com.vanniktech.maven.publish") {
    apply(plugin = "org.jetbrains.dokka")

    tasks.withType<DokkaTaskPartial>().configureEach {
      outputDirectory.set(layout.buildDirectory.dir("docs/partial"))
      dokkaSourceSets.configureEach { skipDeprecated.set(true) }
    }

    configure<MavenPublishBaseExtension> {
      publishToMavenCentral(automaticRelease = true)
      signAllPublications()
    }
  }
}


================================================
FILE: gradle/libs.versions.toml
================================================
[versions]
kotlin = "2.3.21"
ktfmt = "0.58"
jdk = "23"
# lint checks must target JDK 17, but the runtime should remain 11
lintJvmTarget = "17"
runtimeJvmTarget = "11"
lint = "31.12.0-alpha09"

[plugins]
buildConfig = { id = "com.github.gmazzo.buildconfig", version = "6.0.9" }
detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.8" }
dokka = { id = "org.jetbrains.dokka", version = "2.2.0" }
lint = { id = "com.android.lint", version = "9.2.0" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version = "2.3.7" }
mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.36.0" }
shadow = { id = "com.gradleup.shadow", version = "9.4.1" }
spotless = { id = "com.diffplug.spotless", version = "8.4.0" }

[libraries]
autoService-annotations = "com.google.auto.service:auto-service-annotations:1.1.1"
autoService-ksp = "dev.zacsweers.autoservice:auto-service-ksp:1.2.0"
junit = "junit:junit:4.13.2"
kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" }
ktfmt = { module = "com.facebook:ktfmt", version.ref = "ktfmt" }
eithernet = "com.slack.eithernet:eithernet:2.0.0"
retrofit = "com.squareup.retrofit2:retrofit:3.0.0"
lint-api = { module = "com.android.tools.lint:lint-api", version.ref = "lint" }
lint-checks = { module = "com.android.tools.lint:lint-checks", version.ref = "lint" }
lint = { module = "com.android.tools.lint:lint", version.ref = "lint" }
lint-tests = { module = "com.android.tools.lint:lint-tests", version.ref = "lint" }
lint-testUtils = { module = "com.android.tools:testutils", version.ref = "lint" }

[bundles]
lintApi = ["lint-api", "lint-checks"]
lintTest = ["lint", "lint-tests", "lint-testUtils"]


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists


================================================
FILE: gradle.properties
================================================
org.gradle.jvmargs=-Xms1g -Xmx4g -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1g

org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.configuration-cache=true

android.suppressUnsupportedOptionWarnings=android.suppressUnsupportedOptionWarnings,\
  android.experimental.lint.missingBaselineIsEmptyBaseline,\
  android.lint.useK2Uast

android.experimental.lint.missingBaselineIsEmptyBaseline=true
android.lint.useK2Uast=true
# Always use/test K2 now
systemProp.lint.use.fir.uast=true

ksp.useKSP2=true

org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true

# Versioning bits
GROUP=com.slack.lint
POM_URL=https://github.com/slackhq/slack-lints/
POM_SCM_URL=https://github.com/slackhq/slack-lints/
POM_SCM_CONNECTION=scm:git:git://github.com/slackhq/slack-lints.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/slackhq/slack-lints.git
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=slackhq
POM_DEVELOPER_NAME=Slack Technologies, LLC.
POM_DEVELOPER_URL=https://github.com/slackhq
POM_INCEPTION_YEAR=2021
VERSION_NAME=1.0.0-SNAPSHOT


================================================
FILE: gradlew
================================================
#!/bin/sh

#
# Copyright © 2015-2021 the original authors.
#
# 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
#
#      https://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.
#
# SPDX-License-Identifier: Apache-2.0
#

##############################################################################
#
#   Gradle start up script for POSIX generated by Gradle.
#
#   Important for running:
#
#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
#       noncompliant, but you have some other compliant shell such as ksh or
#       bash, then to run this script, type that shell name before the whole
#       command line, like:
#
#           ksh Gradle
#
#       Busybox and similar reduced shells will NOT work, because this script
#       requires all of these POSIX shell features:
#         * functions;
#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
#         * compound commands having a testable exit status, especially «case»;
#         * various built-in commands including «command», «set», and «ulimit».
#
#   Important for patching:
#
#   (2) This script targets any POSIX shell, so it avoids extensions provided
#       by Bash, Ksh, etc; in particular arrays are avoided.
#
#       The "traditional" practice of packing multiple parameters into a
#       space-separated string is a well documented source of bugs and security
#       problems, so this is (mostly) avoided, by progressively accumulating
#       options in "$@", and eventually passing that to Java.
#
#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
#       see the in-line comments for details.
#
#       There are tweaks for specific operating systems such as AIX, CygWin,
#       Darwin, MinGW, and NonStop.
#
#   (3) This script is generated from the Groovy template
#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
#       within the Gradle project.
#
#       You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################

# Attempt to set APP_HOME

# Resolve links: $0 may be a link
app_path=$0

# Need this for daisy-chained symlinks.
while
    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
    [ -h "$app_path" ]
do
    ls=$( ls -ld "$app_path" )
    link=${ls#*' -> '}
    case $link in             #(
      /*)   app_path=$link ;; #(
      *)    app_path=$APP_HOME$link ;;
    esac
done

# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

warn () {
    echo "$*"
} >&2

die () {
    echo
    echo "$*"
    echo
    exit 1
} >&2

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in                #(
  CYGWIN* )         cygwin=true  ;; #(
  Darwin* )         darwin=true  ;; #(
  MSYS* | MINGW* )  msys=true    ;; #(
  NONSTOP* )        nonstop=true ;;
esac

CLASSPATH="\\\"\\\""


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD=$JAVA_HOME/jre/sh/java
    else
        JAVACMD=$JAVA_HOME/bin/java
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD=java
    if ! command -v java >/dev/null 2>&1
    then
        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
fi

# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
    case $MAX_FD in #(
      max*)
        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC2039,SC3045
        MAX_FD=$( ulimit -H -n ) ||
            warn "Could not query maximum file descriptor limit"
    esac
    case $MAX_FD in  #(
      '' | soft) :;; #(
      *)
        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC2039,SC3045
        ulimit -n "$MAX_FD" ||
            warn "Could not set maximum file descriptor limit to $MAX_FD"
    esac
fi

# Collect all arguments for the java command, stacking in reverse order:
#   * args from the command line
#   * the main class name
#   * -classpath
#   * -D...appname settings
#   * --module-path (only if needed)
#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.

# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )

    JAVACMD=$( cygpath --unix "$JAVACMD" )

    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    for arg do
        if
            case $arg in                                #(
              -*)   false ;;                            # don't mess with options #(
              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
                    [ -e "$t" ] ;;                      #(
              *)    false ;;
            esac
        then
            arg=$( cygpath --path --ignore --mixed "$arg" )
        fi
        # Roll the args list around exactly as many times as the number of
        # args, so each arg winds up back in the position where it started, but
        # possibly modified.
        #
        # NB: a `for` loop captures its iteration list before it begins, so
        # changing the positional parameters here affects neither the number of
        # iterations, nor the values presented in `arg`.
        shift                   # remove old arg
        set -- "$@" "$arg"      # push replacement arg
    done
fi


# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command:
#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
#     and any embedded shellness will be escaped.
#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
#     treated as '${Hostname}' itself on the command line.

set -- \
        "-Dorg.gradle.appname=$APP_BASE_NAME" \
        -classpath "$CLASSPATH" \
        -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
        "$@"

# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
    die "xargs is not available"
fi

# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
#   set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#

eval "set -- $(
        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
        xargs -n1 |
        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
        tr '\n' ' '
    )" '"$@"'

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem      https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

:execute
@rem Setup the command line

set CLASSPATH=


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*

:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: release.sh
================================================
#!/usr/bin/env bash

set -exo pipefail

# Gets a property out of a .properties file
# usage: getProperty $key $filename
function getProperty() {
    grep "${1}" "$2" | cut -d'=' -f2
}

NEW_VERSION=$1
SNAPSHOT_VERSION=$(getProperty 'VERSION_NAME' gradle.properties)

echo "Publishing $NEW_VERSION"

# Prepare release
sed -i '' "s/${SNAPSHOT_VERSION}/${NEW_VERSION}/g" gradle.properties
git commit -am "Prepare for release $NEW_VERSION."
git tag -a "$NEW_VERSION" -m "Version $NEW_VERSION"

# Publish
./gradlew publish

# Prepare next snapshot
echo "Restoring snapshot version $SNAPSHOT_VERSION"
sed -i '' "s/${NEW_VERSION}/${SNAPSHOT_VERSION}/g" gradle.properties
git commit -am "Prepare next development version."

# Push it all up
git push && git push --tags

================================================
FILE: renovate.json
================================================
{
  "extends": [
    "config:base"
  ],
  "branchPrefix": "renovate/",
  "gitAuthor": "OSS-Bot <svc-oss-bot@slack-corp.com>",
  "prHourlyLimit": 10,
  "repositories": [
    "slackhq/slack-lints"
  ],
  "packageRules": [
    {
      "matchPackageNames": [
        "renovatebot/github-action"
      ],
      "extends": [
        "schedule:monthly"
      ]
    }
  ]
}

================================================
FILE: settings.gradle.kts
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
import java.util.Locale

pluginManagement {
  repositories {
    mavenCentral()
    google()
    // Last because this proxies jcenter!
    gradlePluginPortal()
  }
}

dependencyResolutionManagement {
  versionCatalogs {
    if (System.getenv("DEP_OVERRIDES") == "true") {
      val overrides = System.getenv().filterKeys { it.startsWith("DEP_OVERRIDE_") }
      maybeCreate("libs").apply {
        for ((key, value) in overrides) {
          val catalogKey = key.removePrefix("DEP_OVERRIDE_").lowercase(Locale.getDefault())
          println("Overriding $catalogKey with $value")
          version(catalogKey, value)
        }
      }
    }
  }
  repositories {
    google()
    mavenCentral()
  }
}

rootProject.name = "slack-lints"

include(":slack-lint-checks", ":slack-lint-annotations")


================================================
FILE: slack-lint-annotations/build.gradle.kts
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
plugins {
  alias(libs.plugins.kotlin.jvm)
  alias(libs.plugins.mavenPublish)
}


================================================
FILE: slack-lint-annotations/gradle.properties
================================================
POM_ARTIFACT_ID=slack-lint-annotations
POM_NAME=Slack Lint Annotations
POM_DESCRIPTION=Slack lint annotations.

================================================
FILE: slack-lint-annotations/src/main/kotlin/slack/lint/annotations/AllowUnitResult.kt
================================================
// Copyright (C) 2025 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint.annotations

/**
 * When applied to a suspend Retrofit function, this annotation permits the function to have a
 * return type of [Unit], which otherwise will be flagged as an issue.
 */
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class AllowUnitResult


================================================
FILE: slack-lint-annotations/src/main/kotlin/slack/lint/annotations/DoNotMock.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint.annotations

import java.lang.annotation.Inherited
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.CLASS

/**
 * Annotation representing a type that should not be mocked.
 *
 * When marking a type `@DoNotMock`, you should always point to alternative testing solutions such
 * as standard fakes or other testing utilities.
 *
 * Mockito tests can enforce this annotation by using a custom MockMaker which intercepts creation
 * of mocks.
 */
@Inherited
@Target(CLASS)
@Retention(RUNTIME)
annotation class DoNotMock(
  /**
   * The reason why the annotated type should not be mocked.
   *
   * This should suggest alternative APIs to use for testing objects of this type.
   */
  val value: String = "Create a real instance instead"
)


================================================
FILE: slack-lint-annotations/src/main/kotlin/slack/lint/annotations/JavaOnly.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint.annotations

/**
 * An annotation that is used to denote methods that should not be called from any context other
 * than Java code. This is important for cases in APIs that support both Kotlin and Java in
 * different ways.
 *
 * @property reason An optional reason why this API is intended for Java only.
 */
annotation class JavaOnly(val reason: String = "")


================================================
FILE: slack-lint-annotations/src/main/kotlin/slack/lint/annotations/KotlinOnly.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint.annotations

/**
 * An annotation that is used to denote methods that should not be called from any context other
 * than Kotlin code. This is important for cases in APIs that support both Kotlin and Java in
 * different ways.
 *
 * @property reason An optional reason why this API is intended for Java only.
 */
annotation class KotlinOnly(val reason: String = "")


================================================
FILE: slack-lint-annotations/src/main/kotlin/slack/lint/annotations/MustUseNamedParams.kt
================================================
// Copyright (C) 2022 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint.annotations

/**
 * Callers to this function must named all parameters. This is useful in cases where arguments may
 * change in order and you want to avoid source-breaking changes.
 */
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.RUNTIME)
annotation class MustUseNamedParams


================================================
FILE: slack-lint-annotations/src/main/kotlin/slack/lint/annotations/RestrictCallsTo.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint.annotations

import java.lang.annotation.Inherited
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.FUNCTION

/**
 * Annotation representing a function or property that should not be called outside of a given
 * [scope]. Similar to androidx's `RestrictTo` annotation but just for calls.
 */
@Inherited
@Target(FUNCTION)
@Retention(RUNTIME)
annotation class RestrictCallsTo(
  /** The target scope. Only file is supported for now, toe-hold left for possible future scopes. */
  val scope: Int = FILE
) {
  companion object {
    const val FILE = 0
  }
}


================================================
FILE: slack-lint-checks/build.gradle.kts
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
  alias(libs.plugins.kotlin.jvm)
  // Run lint on the lints! https://groups.google.com/g/lint-dev/c/q_TVEe85dgc
  alias(libs.plugins.lint)
  alias(libs.plugins.ksp)
  alias(libs.plugins.mavenPublish)
  alias(libs.plugins.shadow)
  alias(libs.plugins.buildConfig)
}

val lintKotlinVersion = KotlinVersion(2, 1, 21)

buildConfig {
  packageName("slack.lint")
  useKotlinOutput { internalVisibility = true }
  sourceSets.getByName("test") {
    buildConfigField(
      "kotlin.KotlinVersion",
      "LINT_KOTLIN_VERSION",
      "KotlinVersion(${lintKotlinVersion.major}, ${lintKotlinVersion.minor}, ${lintKotlinVersion.patch})",
    )
  }
}

lint {
  htmlReport = true
  xmlReport = true
  textReport = true
  absolutePaths = false
  checkTestSources = true
  baseline = file("lint-baseline.xml")
  disable += setOf("GradleDependency")
  fatal += setOf("LintDocExample", "LintImplPsiEquals", "UastImplementation")
}

val shade: Configuration = configurations.maybeCreate("compileShaded")

configurations.getByName("compileOnly").extendsFrom(shade)

tasks.test {
  // Disable noisy java applications launching during tests
  jvmArgs("-Djava.awt.headless=true")
  maxParallelForks = Runtime.getRuntime().availableProcessors() * 2
}

dependencies {
  compileOnly(libs.bundles.lintApi)
  ksp(libs.autoService.ksp)
  implementation(libs.autoService.annotations)
  shade(libs.kotlin.metadata) { exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") }

  // Dupe the dep because the shaded version is compileOnly in the eyes of the gradle configurations
  testImplementation(libs.kotlin.metadata) {
    exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
  }
  testImplementation(libs.bundles.lintTest)
  testImplementation(libs.junit)

  // For IDE linking of APIs
  testImplementation(libs.retrofit)
  testImplementation(libs.eithernet) { exclude(group = "org.jetbrains.kotlin") }
}

val kgpKotlinVersion =
  KotlinVersion.fromVersion(lintKotlinVersion.toString().substringBeforeLast('.'))

tasks.withType<KotlinCompile>().configureEach {
  compilerOptions {
    // Lint forces Kotlin (regardless of what version the project uses), so this
    // forces a matching language level for now. Similar to `targetCompatibility` for Java.
    // This should match the value in LintKotlinVersionCheckTest.kt
    apiVersion.set(kgpKotlinVersion)
    languageVersion.set(kgpKotlinVersion)
  }
}

val shadowJar =
  tasks.shadowJar.apply {
    configure {
      archiveClassifier.set("")
      configurations = listOf(shade)
      relocate("kotlinx.metadata", "slack.lint.shaded.kotlinx.metadata")
      transformers.add(ServiceFileTransformer())
    }
  }

artifacts {
  runtimeOnly(shadowJar)
  archives(shadowJar)
}


================================================
FILE: slack-lint-checks/gradle.properties
================================================
POM_ARTIFACT_ID=slack-lint-checks
POM_NAME=Slack Lint Checks
POM_DESCRIPTION=Slack lint checks.

# Opt-out flag for bundling Kotlin standard library because Lint forces its own version
# See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
kotlin.stdlib.default.dependency=false


================================================
FILE: slack-lint-checks/lint-baseline.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.11.0" type="baseline" client="gradle" dependencies="false" name="AGP (8.11.0)" variant="all" version="8.11.0">

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  @Test"
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/DeprecatedSqlUsageDetectorTest.kt"
            line="16"
            column="3"/>
    </issue>

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  @Test"
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/resources/FullyQualifiedResourceDetectorTest.kt"
            line="25"
            column="3"/>
    </issue>

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  @Test"
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/resources/MissingResourceImportAliasDetectorTest.kt"
            line="24"
            column="3"/>
    </issue>

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  @Test"
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/mocking/MockDetectorOptionsTest.kt"
            line="27"
            column="3"/>
    </issue>

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  private fun testFiles() ="
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/MoshiUsageDetectorTest.kt"
            line="2077"
            column="3"/>
    </issue>

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  private fun testViolatingExpressionLeft(markPoint: String) {"
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/text/SpanMarkPointMissingMaskDetectorTest.kt"
            line="58"
            column="3"/>
    </issue>

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  @Test"
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/ViewContextDetectorTest.kt"
            line="16"
            column="3"/>
    </issue>

    <issue
        id="LintDocExample"
        message="Expected to also find a documentation example test (`testDocumentationExample`) which shows a simple, typical scenario which triggers the test, and which will be extracted into lint&apos;s per-issue documentation pages"
        errorLine1="  @Test"
        errorLine2="  ^">
        <location
            file="src/test/java/slack/lint/resources/WrongResourceImportAliasDetectorTest.kt"
            line="24"
            column="3"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        &quot;Count value in formatted string resource.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="31"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        BRIEF_DESCRIPTION_PREFIX_DEFAULT + BRIEF_DESCRIPTION_SUFFIX,"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/DeprecatedAnnotationDetector.kt"
            line="64"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Dagger provider methods should not be called directly by user code.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/DoNotCallProvidersDetector.kt"
            line="27"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;EitherNet&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`EitherNet\`"
        errorLine1="            message = &quot;Repository APIs should not expose EitherNet types directly.&quot;,"
        errorLine2="                                                         ~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/eithernet/DoNotExposeEitherNetInRepositoriesDetector.kt"
            line="86"
            column="58"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="Single sentence error messages should not end with a period"
        errorLine1="            message = &quot;Repository APIs should not expose EitherNet types directly.&quot;,"
        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/eithernet/DoNotExposeEitherNetInRepositoriesDetector.kt"
            line="86"
            column="24"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        briefDescription = &quot;Repository APIs should not expose EitherNet types directly.&quot;,"
        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/eithernet/DoNotExposeEitherNetInRepositoriesDetector.kt"
            line="108"
            column="29"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;EitherNet&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`EitherNet\`"
        errorLine1="          &quot;EitherNet (and networking in general) should be an implementation detail of the repository layer.&quot;,"
        errorLine2="           ~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/eithernet/DoNotExposeEitherNetInRepositoriesDetector.kt"
            line="110"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        &quot;Use Slack&apos;s internal `@DoNotMock` annotation.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/mocking/ErrorProneDoNotMockDetector.kt"
            line="25"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="          &quot;Fragment dependencies should be injected using constructor injections only.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/FragmentDaggerFieldInjectionDetector.kt"
            line="68"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="          &quot;Fragment dependencies should be injected using constructor injections only.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/FragmentDaggerFieldInjectionDetector.kt"
            line="68"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="          &quot;Fragment dependencies should be injected using the Fragment&apos;s constructor.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/FragmentDaggerFieldInjectionDetector.kt"
            line="85"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="          &quot;Fragment dependencies should be injected using the Fragment&apos;s constructor.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/FragmentDaggerFieldInjectionDetector.kt"
            line="85"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="          &quot;Resources should use an import alias instead of being fully qualified.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/resources/FullyQualifiedResourceDetector.kt"
            line="152"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="Multi-line issue explanation strings will interpret line separators as hard breaks, and this looks like a continuation of the same paragraph. Consider using \ at the end of the previous line to indicate that the lines should be joined, or add a blank line between unrelated sentences, or suppress this issue type here."
        errorLine1="            &quot;import slack.l10n.R as L10nR\n&quot; +"
        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/resources/FullyQualifiedResourceDetector.kt"
            line="154"
            column="14"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;...getString(L10nR.string.app_name)&quot; looks like a call; surround with backtics in string to display as symbol, e.g. \`...getString(L10nR.string.app_name)\`"
        errorLine1="            &quot;...getString(L10nR.string.app_name)&quot;,"
        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/resources/FullyQualifiedResourceDetector.kt"
            line="156"
            column="14"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Use Slack&apos;s JavaPreconditions instead of Guava&apos;s Preconditions checks&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/GuavaPreconditionsDetector.kt"
            line="127"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Use Slack&apos;s JavaPreconditions instead of Guava&apos;s Preconditions checks&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/GuavaPreconditionsDetector.kt"
            line="127"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Kotlin precondition checks should use the Kotlin standard library checks&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/GuavaPreconditionsDetector.kt"
            line="140"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Kotlin precondition checks should use the Kotlin standard library checks&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/GuavaPreconditionsDetector.kt"
            line="140"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Only Kotlin classes should be injected in order for Anvil to work.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/InjectInJavaDetector.kt"
            line="65"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        MESSAGE_LINT_ERROR_TITLE,"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/JavaOnlyDetector.kt"
            line="53"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Using JsonInflater.inflate/deflate with a Moshi-incompatible type.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/JsonInflaterMoshiCompatibilityDetector.kt"
            line="173"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;JsonInflater&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`JsonInflater\`"
        errorLine1="          Classes used with JsonInflater.inflate/deflate must be annotated with @JsonClass or @AdaptedBy to make it \"
        errorLine2="                            ~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/JsonInflaterMoshiCompatibilityDetector.kt"
            line="175"
            column="29"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        &quot;Use slack.foundation.coroutines.android.MainScope.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MainScopeUsageDetector.kt"
            line="38"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="          &quot;Missing import alias for R class.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/resources/MissingResourceImportAliasDetector.kt"
            line="145"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="Single sentence error messages should not end with a period"
        errorLine1="        &quot;Name &apos;$jsonName&apos; is duplicated by member &apos;${existingMember.name}&apos;.&quot;,"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="855"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="Single sentence error messages should not end with a period"
        errorLine1="        &quot;Name &apos;$jsonName&apos; is duplicated by member &apos;${member.name}&apos;.&quot;,"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="860"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Calls to @MustUseNamedParams-annotated methods must name all parameters.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MustUseNamedParamsDetector.kt"
            line="58"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;MustUseNamedParams&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`MustUseNamedParams\`"
        errorLine1="        &quot;Calls to @MustUseNamedParams-annotated methods must name all parameters.&quot;,"
        errorLine2="                   ~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MustUseNamedParamsDetector.kt"
            line="59"
            column="20"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Use Kotlin&apos;s $FQN_KOTLIN_PAIR instead of other Pair types from other libraries like AndroidX and Slack commons&quot;,"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/NonKotlinPairDetector.kt"
            line="95"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Use Kotlin&apos;s $FQN_KOTLIN_PAIR instead of other Pair types from other libraries like AndroidX and Slack commons&quot;,"
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/NonKotlinPairDetector.kt"
            line="95"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;ConcurrentHashMap&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`ConcurrentHashMap\`"
        errorLine1="          context.report(ISSUE, location, &quot;ConcurrentHashMap should not use nullable $name types&quot;)"
        errorLine2="                                           ~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/NullableConcurrentHashMapDetector.kt"
            line="65"
            column="44"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;ConcurrentHashMap&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`ConcurrentHashMap\`"
        errorLine1="          context.report(ISSUE, location, &quot;ConcurrentHashMap should not use nullable $name types&quot;)"
        errorLine2="                                           ~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/NullableConcurrentHashMapDetector.kt"
            line="111"
            column="44"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;ConcurrentHashMap&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`ConcurrentHashMap\`"
        errorLine1="        ConcurrentHashMap does not support null keys or values. \"
        errorLine2="        ~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/NullableConcurrentHashMapDetector.kt"
            line="137"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        &quot;Use SlackDispatchers.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/RawDispatchersUsageDetector.kt"
            line="40"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="        &quot;Methods annotated with @RestrictedCallsTo should only be called from the specified scope.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/RestrictCallsToDetector.kt"
            line="27"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;ok&quot; is usually capitalized as &quot;OK&quot;"
        errorLine1="          ok."
        errorLine2="          ~~">
        <location
            file="src/main/java/slack/lint/RestrictCallsToDetector.kt"
            line="31"
            column="11"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        &quot;This is replaced by the caller.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/retrofit/RetrofitUsageDetector.kt"
            line="176"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        briefDescription = &quot;subscribeOn called with the main thread scheduler.&quot;,"
        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="43"
            column="29"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be capitalized"
        errorLine1="        briefDescription = &quot;subscribeOn called with the main thread scheduler.&quot;,"
        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="43"
            column="29"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="Multi-line issue explanation strings will interpret line separators as hard breaks, and this looks like a continuation of the same paragraph. Consider using \ at the end of the previous line to indicate that the lines should be joined, or add a blank line between unrelated sentences, or suppress this issue type here."
        errorLine1="        on the main thread - that is, code above this line."
        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="47"
            column="9"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="        &quot;Don&apos;t use Serializable.&quot;,"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/SerializableDetector.kt"
            line="47"
            column="10"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should be shorter; typically just a 3-6 words; it&apos;s used as a topic header in HTML reports and in the IDE inspections window"
        errorLine1="          &quot;Check that Span flags use the bitwise mask SPAN_POINT_MARK_MASK when being compared to.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/text/SpanMarkPointMissingMaskDetector.kt"
            line="31"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="The issue summary should *not* end with a period (think of it as a headline)"
        errorLine1="          &quot;Wrong import alias for this R class.&quot;,"
        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/resources/WrongResourceImportAliasDetector.kt"
            line="127"
            column="12"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="Multi-line issue explanation strings will interpret line separators as hard breaks, and this looks like a continuation of the same paragraph. Consider using \ at the end of the previous line to indicate that the lines should be joined, or add a blank line between unrelated sentences, or suppress this issue type here."
        errorLine1="            &quot;import slack.l10n.R as L10nR\n&quot; +"
        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/resources/WrongResourceImportAliasDetector.kt"
            line="129"
            column="14"/>
    </issue>

    <issue
        id="LintImplTextFormat"
        message="&quot;UiKit&quot; looks like a code reference; surround with backtics in string to display as symbol, e.g. \`UiKit\`"
        errorLine1="            &quot;import slack.uikit.R as UiKitR&quot;,"
        errorLine2="                                     ~~~~~">
        <location
            file="src/main/java/slack/lint/resources/WrongResourceImportAliasDetector.kt"
            line="130"
            column="38"/>
    </issue>

    <issue
        id="LintImplUseUast"
        message="Don&apos;t call PsiField#getInitializer(); you must use UAST instead. If you don&apos;t have a UField call UastFacade.getInitializerBody(field)"
        errorLine1="      val assignment = variable?.initializer as? PsiMethodCallExpression"
        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="90"
            column="24"/>
    </issue>

    <issue
        id="LintImplUseUast"
        message="Don&apos;t call PsiField#getInitializer(); you must use UAST instead. If you don&apos;t have a UField call UastFacade.getInitializerBody(field)"
        errorLine1="              val initializer = reference.initializer"
        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="134"
            column="33"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="              containingClass.classKind == JvmClassKind.CLASS -> {"
        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/DaggerIssuesDetector.kt"
            line="176"
            column="15"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="              containingClass.classKind == JvmClassKind.INTERFACE &amp;&amp; isProvides -> {"
        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/DaggerIssuesDetector.kt"
            line="195"
            column="15"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="                  node.parameterList"
        errorLine2="                  ~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/DaggerIssuesDetector.kt"
            line="212"
            column="19"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="            val nodeLocation = node.returnTypeElement ?: node"
        errorLine2="                               ~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/DaggerIssuesDetector.kt"
            line="231"
            column="32"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="            if (firstParam.isReceiver()) {"
        errorLine2="                ~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/DaggerIssuesDetector.kt"
            line="242"
            column="17"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          return containingClass.name?.endsWith(&quot;Repository&quot;) == true"
        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/eithernet/DoNotExposeEitherNetInRepositoriesDetector.kt"
            line="94"
            column="18"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="    constructors.any {"
        errorLine2="    ~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/FragmentDaggerFieldInjectionDetector.kt"
            line="56"
            column="5"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        checker.check(node, node.name, &quot;class&quot;)"
        errorLine2="                            ~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/inclusive/InclusiveNamingSourceCodeScanner.kt"
            line="52"
            column="29"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        val type = if (isKotlin(node.language)) &quot;function&quot; else &quot;method&quot;"
        errorLine2="                                ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/inclusive/InclusiveNamingSourceCodeScanner.kt"
            line="57"
            column="33"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="              if (isKotlin(node.language)) {"
        errorLine2="                           ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/inclusive/InclusiveNamingSourceCodeScanner.kt"
            line="66"
            column="28"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        if (!containingClass.implements(ITEM_DECORATION_CLASS_NAME)) return"
        errorLine2="             ~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ui/ItemDecorationViewBindingDetector.kt"
            line="32"
            column="14"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          node.baseClassType.resolve()?.let { psiClass ->"
        errorLine2="          ~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/JavaOnlyDetector.kt"
            line="91"
            column="11"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        return listOfNotNull(node.javaPsi.superClass, *node.interfaces)"
        errorLine2="                                                       ~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/JavaOnlyDetector.kt"
            line="116"
            column="56"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="              val source = node.text"
        errorLine2="                           ~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/JavaOnlyDetector.kt"
            line="121"
            column="28"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        return context.evaluator.getSuperMethod(node)?.let { method ->"
        errorLine2="                                                ~~~~">
        <location
            file="src/main/java/slack/lint/JavaOnlyDetector.kt"
            line="184"
            column="49"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="            val modifier = node.modifierList.children.joinToString(separator = &quot; &quot;) { it.text }"
        errorLine2="                           ~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/JavaOnlyDetector.kt"
            line="192"
            column="28"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="  containingClass ?: return false"
        errorLine2="  ~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/util/LintUtils.kt"
            line="81"
            column="3"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="  if (isKotlin(language) &amp;&amp; evaluator.hasModifier(this, KtTokens.INNER_KEYWORD)) return true"
        errorLine2="               ~~~~~~~~">
        <location
            file="src/main/java/slack/lint/util/LintUtils.kt"
            line="87"
            column="16"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="  if (language == KotlinLanguage.INSTANCE &amp;&amp; context.evaluator.isSuspend(this)) {"
        errorLine2="      ~~~~~~~~">
        <location
            file="src/main/java/slack/lint/util/LintUtils.kt"
            line="293"
            column="7"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="    val classReference = parameterList.parameters.lastOrNull()?.type as? PsiClassType ?: return null"
        errorLine2="                         ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/util/LintUtils.kt"
            line="294"
            column="26"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        if (uClass.classKind in applicableClassKinds) {"
        errorLine2="            ~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/util/MetadataJavaEvaluator.kt"
            line="165"
            column="13"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        if (cls.classKind in applicableClassKinds) {"
        errorLine2="            ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/util/MetadataJavaEvaluator.kt"
            line="192"
            column="13"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        if (isKotlin(node.language)) {"
        errorLine2="                     ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/mocking/MockDetector.kt"
            line="182"
            column="22"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        } else if (isJava(node.language) &amp;&amp; isMockAnnotated(node)) {"
        errorLine2="                          ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/mocking/MockDetector.kt"
            line="189"
            column="27"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        if (node.isEnum) {"
        errorLine2="            ~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="76"
            column="13"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          slackEvaluator.isObject(node) ||"
        errorLine2="                                  ~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="199"
            column="35"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          if (slackEvaluator.isObject(node)) {"
        errorLine2="                                      ~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="206"
            column="39"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          node.constructors"
        errorLine2="          ~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="234"
            column="11"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="                    .name(&quot;Make ${parameter.name} &apos;val&apos;&quot;)"
        errorLine2="                                  ~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="303"
            column="35"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="                    .name(&quot;Make ${parameter.name} &apos;internal&apos;&quot;)"
        errorLine2="                                  ~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="323"
            column="35"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        val typeLabelAnnotation = node.getAnnotation(FQCN_TYPE_LABEL)"
        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="446"
            column="35"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        val defaultObjectAnnotation = node.getAnnotation(FQCN_DEFAULT_OBJECT)"
        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="447"
            column="39"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        if (isTypeLabeled &amp;&amp; node.hasTypeParameters()) {"
        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="467"
            column="30"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          node.superTypes"
        errorLine2="          ~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="483"
            column="11"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          &quot;Could not load class for ${psiType.className} on ${parameter.getUastParentOfType&lt;UClass>()!!.name}.${parameter.name}&quot;"
        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="616"
            column="63"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          &quot;Could not load class for ${psiType.className} on ${parameter.getUastParentOfType&lt;UClass>()!!.name}.${parameter.name}&quot;"
        errorLine2="                                                                                                                ~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="616"
            column="113"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        jsonName == member.name -> {"
        errorLine2="                    ~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="833"
            column="21"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        context.getNameLocation(member as PsiElement),"
        errorLine2="                                ~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="854"
            column="33"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        &quot;Name &apos;$jsonName&apos; is duplicated by member &apos;${member.name}&apos;.&quot;,"
        errorLine2="                                                     ~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="860"
            column="54"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="    val isKotlin = isKotlin(node.language)"
        errorLine2="                            ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="912"
            column="29"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="    val unknownIndex = constants.indexOfFirst { it.name == &quot;UNKNOWN&quot; }"
        errorLine2="                                                ~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="1069"
            column="49"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        val jsonAnnotation = constant.getAnnotation(FQCN_JSON)"
        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="1087"
            column="30"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="      val name = constant.name"
        errorLine2="                 ~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="1115"
            column="18"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          node.constructors"
        errorLine2="          ~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/parcel/ParcelizeFunctionPropertyDetector.kt"
            line="40"
            column="11"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="            it.findSuperMethods()[0].toUElementOfType()"
        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/RestrictCallsToDetector.kt"
            line="76"
            column="13"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="              val currentText = node.text"
        errorLine2="                                ~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/retrofit/RetrofitUsageDetector.kt"
            line="93"
            column="33"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="        if (node.isEnum) return"
        errorLine2="            ~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/SerializableDetector.kt"
            line="23"
            column="13"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          node.implements(&quot;java.io.Serializable&quot;) { fqcn ->"
        errorLine2="          ~~~~">
        <location
            file="src/main/java/slack/lint/SerializableDetector.kt"
            line="25"
            column="11"/>
    </issue>

    <issue
        id="UElementAsPsi"
        message="Do not use `UElement` as `PsiElement`"
        errorLine1="          if (node.implements(&quot;android.os.Parcelable&quot;)) return"
        errorLine2="              ~~~~">
        <location
            file="src/main/java/slack/lint/SerializableDetector.kt"
            line="31"
            column="15"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`, `UElementWithLocation`"
        errorLine1="import org.jetbrains.uast.java.JavaUCallExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="19"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCompositeQualifiedExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="import org.jetbrains.uast.java.JavaUCompositeQualifiedExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="20"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`"
        errorLine1="import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="21"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="import org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="22"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCompositeQualifiedExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="            is JavaUCompositeQualifiedExpression ->"
        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="53"
            column="16"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`, `UElementWithLocation`"
        errorLine1="              checkCall { JavaUCallExpression::class.safeCast(arg.selector) }"
        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="54"
            column="27"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`, `UElementWithLocation`"
        errorLine1="            is JavaUCallExpression -> checkCall { arg }"
        errorLine2="               ~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="55"
            column="16"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="            is KotlinUQualifiedReferenceExpression ->"
        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="56"
            column="16"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`"
        errorLine1="              checkCall { KotlinUFunctionCallExpression::class.safeCast(arg.selector) }"
        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="57"
            column="27"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`"
        errorLine1="            is KotlinUFunctionCallExpression -> checkCall { arg }"
        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt"
            line="58"
            column="16"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUAnnotation is UAST implementation. Consider using one of its corresponding UAST interfaces: `UAnnotationEx`, `UAnnotation`, `UResolvable`, `UAnchorOwner`"
        errorLine1="import org.jetbrains.uast.kotlin.KotlinUAnnotation"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="48"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUClassLiteralExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UClassLiteralExpression`"
        errorLine1="import org.jetbrains.uast.kotlin.KotlinUClassLiteralExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="49"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUAnnotation is UAST implementation. Consider using one of its corresponding UAST interfaces: `UAnnotationEx`, `UAnnotation`, `UResolvable`, `UAnchorOwner`"
        errorLine1="                (annotationEntry.toUElement() as KotlinUAnnotation).takeIf {"
        errorLine2="                                                 ~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="363"
            column="50"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUClassLiteralExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UClassLiteralExpression`"
        errorLine1="      (adaptedByAnnotation.findAttributeValue(&quot;adapter&quot;) as? KotlinUClassLiteralExpression)"
        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/MoshiUsageDetector.kt"
            line="545"
            column="62"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`, `UElementWithLocation`"
        errorLine1="import org.jetbrains.uast.java.JavaUCallExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="24"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCompositeQualifiedExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="import org.jetbrains.uast.java.JavaUCompositeQualifiedExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="25"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`"
        errorLine1="import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="26"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="import org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="27"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUSimpleReferenceExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `USimpleNameReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="import org.jetbrains.uast.kotlin.KotlinUSimpleReferenceExpression"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="28"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable is UAST implementation. Consider using one of its corresponding UAST interfaces."
        errorLine1="import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable"
        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="29"
            column="1"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCompositeQualifiedExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="      is JavaUCompositeQualifiedExpression ->"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="75"
            column="10"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`, `UElementWithLocation`"
        errorLine1="        checkCall { JavaUCallExpression::class.safeCast(arg.selector) }"
        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="76"
            column="21"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.java.JavaUCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`, `UElementWithLocation`"
        errorLine1="      is JavaUCallExpression -> checkCall { arg }"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="77"
            column="10"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UQualifiedReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="      is KotlinUQualifiedReferenceExpression ->"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="78"
            column="10"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`"
        errorLine1="        checkCall { KotlinUFunctionCallExpression::class.safeCast(arg.selector) }"
        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="79"
            column="21"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `UCallExpression`, `UResolvable`"
        errorLine1="      is KotlinUFunctionCallExpression -> checkCall { arg }"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="80"
            column="10"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.KotlinUSimpleReferenceExpression is UAST implementation. Consider using one of its corresponding UAST interfaces: `UExpression`, `UAnnotated`, `USimpleNameReferenceExpression`, `UReferenceExpression`, `UResolvable`"
        errorLine1="      is KotlinUSimpleReferenceExpression -> {"
        errorLine2="         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="126"
            column="10"/>
    </issue>

    <issue
        id="UastImplementation"
        message="org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable is UAST implementation. Consider using one of its corresponding UAST interfaces."
        errorLine1="            is UastKotlinPsiVariable -> { // The variable reference is local"
        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~">
        <location
            file="src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt"
            line="133"
            column="16"/>
    </issue>

</issues>


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/AlwaysNullReadOnlyVariableDetector.kt
================================================
// Copyright (C) 2025 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.isNull
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.ULiteralExpression
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UVariable
import org.jetbrains.uast.kotlin.isKotlin
import slack.lint.util.sourceImplementation

class AlwaysNullReadOnlyVariableDetector : Detector(), SourceCodeScanner {
  override fun getApplicableUastTypes() =
    listOf(UVariable::class.java, UCallExpression::class.java, UMethod::class.java)

  override fun createUastHandler(context: JavaContext): UElementHandler? {
    if (!isKotlin(context.uastFile?.lang)) return null

    return object : UElementHandler() {

      private fun isNullInitializedForReadOnlyVariable(node: UVariable): Boolean {
        val uastInitializer = node.uastInitializer ?: return false
        val isNullInitialized = uastInitializer is ULiteralExpression && uastInitializer.isNull
        if (!isNullInitialized) return false
        val sourcePsi = node.sourcePsi
        val isReadOnlyVariable =
          sourcePsi is KtProperty &&
            !sourcePsi.isVar &&
            !sourcePsi.hasModifier(KtTokens.OPEN_KEYWORD)
        return isReadOnlyVariable
      }

      override fun visitVariable(node: UVariable) {
        if (isNullInitializedForReadOnlyVariable(node)) {
          context.report(
            ISSUE_ALWAYS_INITIALIZE_NULL,
            context.getLocation(node.uastInitializer),
            ISSUE_ALWAYS_INITIALIZE_NULL.getBriefDescription(TextFormat.TEXT),
          )
        }
      }

      override fun visitMethod(node: UMethod) {
        val sourcePsi = node.sourcePsi?.parent as? KtProperty ?: return
        val getter = sourcePsi.getter ?: return

        val isReadOnlyVariable = !sourcePsi.isVar
        if (isReadOnlyVariable) {

          // get() = null
          val bodyExpression = getter.bodyExpression ?: return

          if (bodyExpression.isNull()) {
            context.report(
              ISSUE_ALWAYS_RETURN_NULL_IN_GETTER,
              context.getLocation(bodyExpression),
              ISSUE_ALWAYS_RETURN_NULL_IN_GETTER.getBriefDescription(TextFormat.TEXT),
            )
          }

          // get() { return null }
          val returnExpression = bodyExpression.collectDescendantsOfType<KtReturnExpression>()
          returnExpression.forEach { expression ->
            val returnedExpression = expression.returnedExpression ?: return@forEach
            if (returnedExpression.isNull()) {
              context.report(
                ISSUE_ALWAYS_RETURN_NULL_IN_GETTER,
                context.getLocation(returnedExpression),
                ISSUE_ALWAYS_RETURN_NULL_IN_GETTER.getBriefDescription(TextFormat.TEXT),
              )
            }
          }
        }
      }
    }
  }

  companion object {
    val ISSUE_ALWAYS_INITIALIZE_NULL: Issue =
      Issue.create(
        "AvoidNullInitForReadOnlyVariables",
        "Avoid initializing read-only variable with null in Kotlin",
        """
          Avoid unnecessary `null` initialization for read-only variables, as they can never be reassigned. \
          Assigning null explicitly does not provide any real benefit and may mislead readers into thinking the value could change later. \
          If the variable needs to be modified later, it's better to use `var` instead of `val`, or consider using `lateinit var` if it is guaranteed to be initialized before use.
        """,
        Category.CORRECTNESS,
        6,
        Severity.WARNING,
        sourceImplementation<AlwaysNullReadOnlyVariableDetector>(),
      )

    val ISSUE_ALWAYS_RETURN_NULL_IN_GETTER: Issue =
      Issue.create(
        "AvoidReturningNullInGetter",
        "Avoid returning null in getter for read-only properties in Kotlin",
        """
          Avoid defining a getter that always returns `null` for a read-only (`val`) property. \
        Since `val` properties cannot be reassigned, having a getter that consistently returns `null` serves no real purpose \
        and may cause confusion.

        If the value needs to be dynamically computed, ensure the getter returns a meaningful result. \
        Otherwise, consider using a function (`fun`) instead of a property.
        """,
        Category.CORRECTNESS,
        6,
        Severity.WARNING,
        sourceImplementation<AlwaysNullReadOnlyVariableDetector>(),
      )

    val ISSUES: List<Issue> =
      listOf(ISSUE_ALWAYS_INITIALIZE_NULL, ISSUE_ALWAYS_RETURN_NULL_IN_GETTER)
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/AnnotatedClassOrMethodUsageDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.checks.AbstractAnnotationDetector
import com.android.tools.lint.detector.api.AnnotationInfo
import com.android.tools.lint.detector.api.AnnotationUsageInfo
import com.android.tools.lint.detector.api.AnnotationUsageType
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import org.jetbrains.uast.UElement

/**
 * Raises a warning whenever we use deprecated methods or classes. Generally used for keeping track
 * of health score.
 */
abstract class AnnotatedClassOrMethodUsageDetector :
  AbstractAnnotationDetector(), SourceCodeScanner {

  abstract val annotationNames: List<String>
  abstract val issue: Issue
  open val isEnabled: Boolean = true

  override fun applicableAnnotations(): List<String> =
    if (isEnabled) annotationNames else emptyList()

  override fun isApplicableAnnotationUsage(type: AnnotationUsageType): Boolean {
    // If it's not enabled, no need to scan further
    if (!isEnabled) return false
    @Suppress("DEPRECATION") // METHOD_CALL_CLASS doesn't have a replacement
    return type == AnnotationUsageType.METHOD_CALL ||
      type == AnnotationUsageType.METHOD_CALL_CLASS ||
      type == AnnotationUsageType.METHOD_CALL_PARAMETER
  }

  override fun visitAnnotationUsage(
    context: JavaContext,
    element: UElement,
    annotationInfo: AnnotationInfo,
    usageInfo: AnnotationUsageInfo,
  ) {
    if (isEnabled && applicableAnnotations().contains(annotationInfo.qualifiedName)) {
      val issueToReport = issue
      val location = context.getLocation(element)
      report(
        context,
        issueToReport,
        element,
        location,
        issueToReport.getBriefDescription(TextFormat.TEXT),
        null,
      )
    }
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
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.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiLocalVariable
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiMethodCallExpression
import kotlin.reflect.full.safeCast
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.java.JavaUCallExpression
import org.jetbrains.uast.java.JavaUCompositeQualifiedExpression
import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
import org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression
import org.jetbrains.uast.sourcePsiElement

class ArgInFormattedQuantityStringResDetector : Detector(), SourceCodeScanner {

  companion object {
    val ISSUE_ARG_IN_QUANTITY_STRING_FORMAT: Issue =
      Issue.create(
        "ArgInFormattedQuantityStringRes",
        "Count value in formatted string resource.",
        "Some languages require modifiers to counted values in written text. Consider consulting #plz-localization " +
          "if you are unsure if this formatted string requires a special modifier. If one is required, consider using " +
          "`LocalizationUtils.getFormattedCount()`. If not, suppress this warning.",
        Category.I18N,
        6,
        Severity.WARNING,
        Implementation(ArgInFormattedQuantityStringResDetector::class.java, Scope.JAVA_FILE_SCOPE),
      )

    val issues: List<Issue> = listOf(ISSUE_ARG_IN_QUANTITY_STRING_FORMAT)
  }

  override fun getApplicableMethodNames(): List<String> = listOf("getQuantityString")

  override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
    // Ignore methods that aren't in a subclass of Android "Resources"
    if (context.evaluator.isMemberInSubClassOf(method, "android.content.res.Resources", false)) {
      node.valueArguments
        .drop(2) // Ignore the first 2 arguments passed to "getQuantityString"
        .forEach { arg ->
          when (arg) {
            is JavaUCompositeQualifiedExpression ->
              checkCall { JavaUCallExpression::class.safeCast(arg.selector) }
            is JavaUCallExpression -> checkCall { arg }
            is KotlinUQualifiedReferenceExpression ->
              checkCall { KotlinUFunctionCallExpression::class.safeCast(arg.selector) }
            is KotlinUFunctionCallExpression -> checkCall { arg }
            else -> checkVariable { arg }
          }.let { countFormatFound ->
            if (!countFormatFound) {
              context.report(
                ISSUE_ARG_IN_QUANTITY_STRING_FORMAT,
                context.getLocation(arg),
                "This may require a localized count modifier. If so, use `LocalizationUtils.getFormattedCount()`. Consult #plz-localization if you are unsure.",
              )
            }
          }
        }
    }

    super.visitMethodCall(context, node, method)
  }

  /**
   * return true if the resolved [UCallExpression] has method name "getFormattedCount", false
   * otherwise
   */
  private fun checkCall(fn: () -> UCallExpression?): Boolean {
    return fn()?.let { call -> "getFormattedCount" == call.methodName } ?: false
  }

  /**
   * return true if the resolved [UExpression] was created from the "getFormattedCount" method,
   * false otherwise
   */
  private fun checkVariable(fn: () -> UExpression?): Boolean {
    return fn()?.let { exp ->
      val variable = exp.sourcePsiElement?.reference?.resolve() as? PsiLocalVariable
      val assignment = variable?.initializer as? PsiMethodCallExpression
      return assignment?.resolveMethod()?.name == "getFormattedCount"
    } ?: false
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/CircuitScreenDataClassDetector.kt
================================================
// Copyright (C) 2025 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.getUMethod
import org.jetbrains.kotlin.analysis.utils.isLocalClass
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UElement
import slack.lint.util.implements
import slack.lint.util.sourceImplementation

class CircuitScreenDataClassDetector : Detector(), SourceCodeScanner {
  override fun getApplicableUastTypes(): List<Class<out UElement>> = listOf(UClass::class.java)

  override fun createUastHandler(context: JavaContext): UElementHandler {
    return object : UElementHandler() {

      override fun visitClass(node: UClass) {
        val sourceNode = node.sourcePsi as? KtClassOrObject

        if (
          sourceNode != null &&
            !sourceNode.isData() &&
            !node.isInterface &&
            !sourceNode.hasModifier(
              KtTokens.OPEN_KEYWORD
            ) && // Open classes cannot be "data" classes
            !sourceNode.hasModifier(
              KtTokens.INNER_KEYWORD
            ) && // Screens must be parcelable and inner classes cannot be parcelable
            !sourceNode.hasModifier(
              KtTokens.ABSTRACT_KEYWORD
            ) && // Cannot have abstract data class / object
            !sourceNode.hasModifier(
              KtTokens.SEALED_KEYWORD
            ) && // Cannot have sealed data class / object
            !sourceNode.hasModifier(
              KtTokens.COMPANION_KEYWORD
            ) && // Cannot have companion data object
            !node.isLocalClass() &&
            node.implements(QUALIFIED_CIRCUIT_SCREEN)
        ) {
          val hasProperties =
            !node.constructors
              .asSequence()
              .mapNotNull { it.getUMethod() }
              .firstOrNull { it.sourcePsi is KtPrimaryConstructor }
              ?.uastParameters
              .isNullOrEmpty()
          val classKeyword =
            when (sourceNode) {
              is KtClass -> sourceNode.getClassKeyword()
              is KtObjectDeclaration -> sourceNode.getObjectKeyword() ?: return
              else -> return
            }
          val isObject = classKeyword?.node?.elementType == KtTokens.OBJECT_KEYWORD
          val originalKeyword = if (isObject) KtTokens.OBJECT_KEYWORD else KtTokens.CLASS_KEYWORD
          val replacement =
            if (hasProperties) "${KtTokens.DATA_KEYWORD} ${KtTokens.CLASS_KEYWORD}"
            else "${KtTokens.DATA_KEYWORD} ${KtTokens.OBJECT_KEYWORD}"
          val keywordLocation = context.getLocation(classKeyword)
          val quickFix =
            fix()
              .replace()
              .name("Replace with $replacement")
              .range(keywordLocation)
              .text(originalKeyword.value)
              .with(replacement)
              .reformat(true)
              .build()
          context.report(ISSUE, keywordLocation, MESSAGE, quickFix)
        }
      }
    }
  }

  companion object {
    const val QUALIFIED_CIRCUIT_SCREEN = "com.slack.circuit.runtime.screen.Screen"
    const val MESSAGE =
      "Circuit Screen implementations should be data classes or data objects, not regular classes."
    const val ISSUE_ID = "CircuitScreenShouldBeDataClass"
    const val BRIEF_DESCRIPTION = "Circuit Screen should be a data class or data object"
    const val EXPLANATION =
      """Circuit Screen implementations should be data classes or data objects to ensure proper
equality, hashCode, and toString implementations. Regular classes can cause issues with
screen comparison and navigation."""

    val ISSUE: Issue =
      Issue.create(
        ISSUE_ID,
        BRIEF_DESCRIPTION,
        EXPLANATION,
        Category.CORRECTNESS,
        8,
        Severity.ERROR,
        sourceImplementation<CircuitScreenDataClassDetector>(),
      )
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/DaggerIssuesDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import com.android.tools.lint.detector.api.isDuplicatedOverload
import com.android.tools.lint.detector.api.isReceiver
import com.intellij.lang.jvm.JvmClassKind
import com.intellij.psi.PsiTypes
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.uast.UAnnotated
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.getContainingUClass
import slack.lint.util.sourceImplementation

/** This is a simple lint check to catch common Dagger+Kotlin usage issues. */
class DaggerIssuesDetector : Detector(), SourceCodeScanner {

  companion object {
    private val ISSUE_BINDS_MUST_BE_IN_MODULE: Issue =
      Issue.create(
        "MustBeInModule",
        "@Binds/@Provides functions must be in modules",
        "@Binds/@Provides functions must be in `@Module`-annotated classes.",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private val ISSUE_BINDS_TYPE_MISMATCH: Issue =
      Issue.create(
        "BindsTypeMismatch",
        "@Binds parameter/return must be type-assignable",
        "@Binds function parameters must be type-assignable to their return types.",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private val ISSUE_RETURN_TYPE: Issue =
      Issue.create(
        "BindingReturnType",
        "@Binds/@Provides must have a return type",
        "@Binds/@Provides functions must have a return type. Cannot be void or Unit.",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private val ISSUE_RECEIVER_PARAMETER: Issue =
      Issue.create(
        "BindingReceiverParameter",
        "@Binds/@Provides functions cannot be extensions",
        "@Binds/@Provides functions cannot be extension functions. Move the receiver type to a parameter via IDE inspection (option+enter and convert to parameter).",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private val ISSUE_BINDS_WRONG_PARAMETER_COUNT: Issue =
      Issue.create(
        "BindsWrongParameterCount",
        "@Binds must have one parameter",
        "@Binds functions require a single parameter as an input to bind.",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private val ISSUE_BINDS_MUST_BE_ABSTRACT: Issue =
      Issue.create(
        "BindsMustBeAbstract",
        "@Binds functions must be abstract",
        "@Binds functions must be abstract and cannot have function bodies.",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private val ISSUE_PROVIDES_CANNOT_BE_ABSTRACT: Issue =
      Issue.create(
        "ProvidesMustNotBeAbstract",
        "@Provides functions cannot be abstract",
        "@Provides functions cannot be abstract.",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private val ISSUE_BINDS_REDUNDANT: Issue =
      Issue.create(
        "RedundantBinds",
        "@Binds functions should return a different type",
        "@Binds functions should return a different type (including annotations) than the input type.",
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        sourceImplementation<DaggerIssuesDetector>(),
      )

    private const val BINDS_ANNOTATION = "dagger.Binds"
    private const val PROVIDES_ANNOTATION = "dagger.Provides"

    val ISSUES: List<Issue> =
      listOf(
        ISSUE_BINDS_TYPE_MISMATCH,
        ISSUE_RETURN_TYPE,
        ISSUE_RECEIVER_PARAMETER,
        ISSUE_BINDS_WRONG_PARAMETER_COUNT,
        ISSUE_BINDS_MUST_BE_ABSTRACT,
        ISSUE_BINDS_REDUNDANT,
        ISSUE_BINDS_MUST_BE_IN_MODULE,
        ISSUE_PROVIDES_CANNOT_BE_ABSTRACT,
      )
  }

  override fun getApplicableUastTypes() = listOf(UMethod::class.java)

  override fun createUastHandler(context: JavaContext): UElementHandler {
    return object : UElementHandler() {
      override fun visitMethod(node: UMethod) {
        if (node.isDuplicatedOverload()) {
          return
        }
        if (!node.isConstructor) {
          val isBinds = node.hasAnnotation(BINDS_ANNOTATION)
          val isProvides = node.hasAnnotation(PROVIDES_ANNOTATION)

          if (!isBinds && !isProvides) return

          val containingClass = node.getContainingUClass()
          if (containingClass != null) {
            // Fine to not use MetadataJavaEvaluator since we only care about current module
            val moduleClass =
              if (context.evaluator.hasModifier(containingClass, KtTokens.COMPANION_KEYWORD)) {
                checkNotNull(containingClass.getContainingUClass()) {
                  "Companion object must be nested in a class"
                }
              } else {
                containingClass
              }
            when {
              !moduleClass.hasAnnotation("dagger.Module") -> {
                context.report(
                  ISSUE_BINDS_MUST_BE_IN_MODULE,
                  context.getLocation(node),
                  ISSUE_BINDS_MUST_BE_IN_MODULE.getBriefDescription(TextFormat.TEXT),
                )
                return
              }
              isBinds && containingClass.isInterface -> {
                // Cannot have a default impl in interfaces
                if (node.uastBody != null) {
                  context.report(
                    ISSUE_BINDS_MUST_BE_ABSTRACT,
                    context.getLocation(node.uastBody),
                    ISSUE_BINDS_MUST_BE_ABSTRACT.getBriefDescription(TextFormat.TEXT),
                  )
                  return
                }
              }
              containingClass.classKind == JvmClassKind.CLASS -> {
                val isAbstract = context.evaluator.isAbstract(node)
                // Binds must be abstract
                if (isBinds && !isAbstract) {
                  context.report(
                    ISSUE_BINDS_MUST_BE_ABSTRACT,
                    context.getLocation(node),
                    ISSUE_BINDS_MUST_BE_ABSTRACT.getBriefDescription(TextFormat.TEXT),
                  )
                  return
                } else if (isProvides && isAbstract) {
                  context.report(
                    ISSUE_PROVIDES_CANNOT_BE_ABSTRACT,
                    context.getLocation(node),
                    ISSUE_PROVIDES_CANNOT_BE_ABSTRACT.getBriefDescription(TextFormat.TEXT),
                  )
                  return
                }
              }
              containingClass.classKind == JvmClassKind.INTERFACE && isProvides -> {
                context.report(
                  ISSUE_PROVIDES_CANNOT_BE_ABSTRACT,
                  context.getLocation(node),
                  ISSUE_PROVIDES_CANNOT_BE_ABSTRACT.getBriefDescription(TextFormat.TEXT),
                )
                return
              }
            }
          }

          if (isBinds) {
            if (node.uastParameters.size != 1) {
              val locationToHighlight =
                if (node.uastParameters.isEmpty()) {
                  node
                } else {
                  node.parameterList
                }
              context.report(
                ISSUE_BINDS_WRONG_PARAMETER_COUNT,
                context.getLocation(locationToHighlight),
                ISSUE_BINDS_WRONG_PARAMETER_COUNT.getBriefDescription(TextFormat.TEXT),
              )
              return
            }
          }

          val returnType =
            node.returnType?.takeUnless {
              it == PsiTypes.voidType() ||
                context.evaluator.getTypeClass(it)?.qualifiedName == "kotlin.Unit"
            }

          if (returnType == null) {
            // Report missing return type
            val nodeLocation = node.returnTypeElement ?: node
            context.report(
              ISSUE_RETURN_TYPE,
              context.getLocation(nodeLocation),
              ISSUE_RETURN_TYPE.getBriefDescription(TextFormat.TEXT),
            )
            return
          }

          if (node.uastParameters.isNotEmpty()) {
            val firstParam = node.uastParameters[0]
            if (firstParam.isReceiver()) {
              context.report(
                ISSUE_RECEIVER_PARAMETER,
                context.getNameLocation(firstParam),
                ISSUE_RECEIVER_PARAMETER.getBriefDescription(TextFormat.TEXT),
              )
              return
            }

            if (isBinds) {
              val instanceType = firstParam.type
              if (instanceType == returnType) {
                // Check that they have different annotations, otherwise it's redundant
                if (firstParam.qualifiers() == node.qualifiers()) {
                  context.report(
                    ISSUE_BINDS_REDUNDANT,
                    context.getLocation(node),
                    ISSUE_BINDS_REDUNDANT.getBriefDescription(TextFormat.TEXT),
                  )
                  return
                }
              }

              if (!returnType.isAssignableFrom(instanceType)) {
                context.report(
                  ISSUE_BINDS_TYPE_MISMATCH,
                  context.getLocation(node),
                  ISSUE_BINDS_TYPE_MISMATCH.getBriefDescription(TextFormat.TEXT),
                )
              }
            }
          }
        }
      }
    }
  }

  private fun UAnnotated.qualifiers() =
    uAnnotations
      .asSequence()
      .filter { it.resolve()?.hasAnnotation("javax.inject.Qualifier") == true }
      .toSet()
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/DeprecatedAnnotationDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.LintClient
import com.android.tools.lint.detector.api.AnnotationInfo
import com.android.tools.lint.detector.api.AnnotationUsageInfo
import com.android.tools.lint.detector.api.Category
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.Severity
import com.android.tools.lint.detector.api.UastLintUtils
import org.jetbrains.uast.UElement
import slack.lint.util.Priorities
import slack.lint.util.sourceImplementation

private const val DEPRECATED_ANNOTATION_NAME_JAVA = "java.lang.Deprecated"
private const val DEPRECATED_ANNOTATION_NAME_KOTLIN = "kotlin.Deprecated"
private const val BRIEF_DESCRIPTION_PREFIX_DEFAULT = "This class or method"
private const val BRIEF_DESCRIPTION_SUFFIX = " is deprecated; consider using an alternative."

/**
 * Raises a warning whenever we use deprecated methods or classes. Generally used for keeping track
 * of health score.
 */
class DeprecatedAnnotationDetector : AnnotatedClassOrMethodUsageDetector() {

  override val annotationNames =
    listOf(DEPRECATED_ANNOTATION_NAME_KOTLIN, DEPRECATED_ANNOTATION_NAME_JAVA)
  override val issue = ISSUE_DEPRECATED_CALL

  // Only enable on CLI
  override val isEnabled: Boolean
    get() = !LintClient.isStudio

  override fun visitAnnotationUsage(
    context: JavaContext,
    element: UElement,
    annotationInfo: AnnotationInfo,
    usageInfo: AnnotationUsageInfo,
  ) {
    if (isEnabled && applicableAnnotations().contains(annotationInfo.qualifiedName)) {
      val issueToReport = issue
      val location = context.getLocation(element)
      val messagePrefix =
        usageInfo.referenced?.let(UastLintUtils.Companion::getQualifiedName)
          ?: BRIEF_DESCRIPTION_PREFIX_DEFAULT
      report(
        context,
        issueToReport,
        element,
        location,
        messagePrefix + BRIEF_DESCRIPTION_SUFFIX,
        null,
      )
    }
  }

  companion object {
    private fun Implementation.toIssue(): Issue {
      return Issue.create(
        "DeprecatedCall",
        BRIEF_DESCRIPTION_PREFIX_DEFAULT + BRIEF_DESCRIPTION_SUFFIX,
        "Using deprecated classes is not advised; please consider using an alternative.",
        Category.CORRECTNESS,
        Priorities.NORMAL,
        Severity.WARNING,
        this,
      )
    }

    val ISSUE_DEPRECATED_CALL = sourceImplementation<DeprecatedAnnotationDetector>().toIssue()
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/DeprecatedSqlUsageDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
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.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import org.jetbrains.uast.UCallExpression
import slack.lint.util.sourceImplementation

/**
 * Raises a warning on direct sqlite database usage and encourages
 * [SqlDelight](https://cashapp.github.io/sqldelight/) usage.
 */
@Suppress("UnstableApiUsage")
class DeprecatedSqlUsageDetector : Detector(), SourceCodeScanner {

  override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)

  override fun createUastHandler(context: JavaContext) =
    object : UElementHandler() {
      override fun visitCallExpression(node: UCallExpression) {
        if (
          APPLICABLE_RECEIVER_TYPES.contains(node.receiverType?.canonicalText) &&
            APPLICABLE_CALL_NAMES.contains(node.methodIdentifier?.name)
        ) {
          context.report(
            issue = ISSUE,
            location = context.getLocation(node),
            message = "All SQL querying should be performed using `SqlDelight`",
          )
        }
      }
    }

  companion object {
    private fun Implementation.toIssue(): Issue {
      return Issue.create(
        id = "DeprecatedSqlUsage",
        briefDescription = "Use SqlDelight!",
        explanation = "Safer, faster, etc",
        category = Category.CORRECTNESS,
        priority = 0,
        severity = Severity.WARNING,
        implementation = this,
      )
    }

    val ISSUE: Issue = sourceImplementation<DeprecatedSqlUsageDetector>().toIssue()

    private val APPLICABLE_CALL_NAMES = listOf("query", "insert", "update", "delete", "execSQL")
    private val APPLICABLE_RECEIVER_TYPES =
      listOf("android.database.sqlite.SQLiteDatabase", "androidx.sqlite.db.SupportSQLiteDatabase")
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/DoNotCallProvidersDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UClass
import org.jetbrains.uast.getParentOfType
import slack.lint.util.sourceImplementation

class DoNotCallProvidersDetector : Detector(), SourceCodeScanner {

  companion object {
    private val SCOPES =
      sourceImplementation<DoNotCallProvidersDetector>(shouldRunOnTestSources = false)

    val ISSUE: Issue =
      Issue.create(
        "DoNotCallProviders",
        "Dagger provider methods should not be called directly by user code.",
        """
          Dagger provider methods should not be called directly by user code. These are intended solely for use \
          by Dagger-generated code and it is programmer error to call them from user code.
      """,
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        SCOPES,
      )

    private val PROVIDER_ANNOTATIONS =
      setOf("dagger.Binds", "dagger.Provides", "dagger.producers.Produces")
    private val GENERATED_ANNOTATIONS =
      setOf("javax.annotation.Generated", "javax.annotation.processing.Generated")
  }

  override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)

  override fun createUastHandler(context: JavaContext) =
    object : UElementHandler() {
      override fun visitCallExpression(node: UCallExpression) {
        val enclosingClass = node.getParentOfType<UClass>() ?: return
        if (GENERATED_ANNOTATIONS.any(enclosingClass::hasAnnotation)) return
        val method = node.resolve() ?: return
        if (PROVIDER_ANNOTATIONS.any(method::hasAnnotation)) {
          context.report(
            ISSUE,
            context.getLocation(node),
            ISSUE.getBriefDescription(TextFormat.TEXT),
          )
        }
      }
    }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/ExceptionMessageDetector.kt
================================================
/*
 * Copyright 2023 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 slack.lint

import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.getParameterForArgument
import slack.lint.util.Name
import slack.lint.util.Package
import slack.lint.util.isInPackageName
import slack.lint.util.sourceImplementation

class ExceptionMessageDetector : Detector(), SourceCodeScanner {

  override fun getApplicableMethodNames(): List<String> =
    listOf(Check, CheckNotNull, Require, RequireNotNull).map { it.shortName }

  override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {

    // We ignore other functions with the same name.
    if (!method.isInPackageName(KotlinPackage)) return

    val lazyMessage =
      node.valueArguments.find { node.getParameterForArgument(it)?.name == "lazyMessage" }
    if (lazyMessage == null) {
      context.report(
        ISSUE,
        node,
        context.getNameLocation(node),
        "Please specify a `lazyMessage` param for ${node.methodName}",
      )
    }
  }

  internal companion object {
    val KotlinPackage = Package("kotlin")
    val Check = Name(KotlinPackage, "check")
    val CheckNotNull = Name(KotlinPackage, "checkNotNull")
    val Require = Name(KotlinPackage, "require")
    val RequireNotNull = Name(KotlinPackage, "requireNotNull")
    val ISSUE =
      Issue.create(
        id = "ExceptionMessage",
        briefDescription = "Please provide a string for the `lazyMessage` parameter",
        explanation =
          """
                Calls to `check()`, `checkNotNull()`, `require()` and `requireNotNull()` \
                should include a message string that can be used to debug issues \
                experienced by users.

                When we read user-supplied logs, the line numbers are sometimes not\
                sufficient to determine the cause of a bug. Inline functions can\
                sometimes make it hard to determine which file threw an exception.\
                Consider supplying a `lazyMessage` parameter to identify the `check()`\
                or `require()` call.
            """,
        category = Category.CORRECTNESS,
        priority = 3,
        severity = Severity.ERROR,
        implementation =
          sourceImplementation<ExceptionMessageDetector>(shouldRunOnTestSources = false),
      )
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/FragmentDaggerFieldInjectionDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UField
import slack.lint.util.implements
import slack.lint.util.sourceImplementation

/**
 * Detects that Fragments should use constructor injection in order to obtain references to its
 * dependencies.
 */
class FragmentDaggerFieldInjectionDetector : Detector(), SourceCodeScanner {

  override fun getApplicableUastTypes() = listOf(UField::class.java)

  override fun createUastHandler(context: JavaContext): UElementHandler {
    return object : UElementHandler() {

      override fun visitField(node: UField) {
        if (node.isStatic || node.findAnnotation(FQN_JAVAX_INJECT) == null) return

        val nodeParent = node.uastParent
        if (nodeParent !is UClass || nodeParent.isInterface) return

        if (!nodeParent.isFragment()) return

        val issueToReport =
          if (nodeParent.hasConstructorInjection()) {
            ISSUE_FRAGMENT_CONSTRUCTOR_INJECTION_AVAILABLE
          } else {
            ISSUE_FRAGMENT_FIELD_INJECTION_USED
          }

        context.report(
          issueToReport,
          context.getLocation(node),
          issueToReport.getBriefDescription(TextFormat.TEXT),
        )
      }
    }
  }

  private fun UClass.isFragment() = implements("androidx.fragment.app.Fragment")

  private fun UClass.hasConstructorInjection() =
    constructors.any {
      it.hasAnnotation(FQN_JAVAX_INJECT) || it.hasAnnotation(FQN_DAGGER_ASSISTED_INJECT)
    }

  companion object {
    private const val FQN_JAVAX_INJECT = "javax.inject.Inject"
    private const val FQN_DAGGER_ASSISTED_INJECT = "dagger.assisted.AssistedInject"

    private val ISSUE_FRAGMENT_CONSTRUCTOR_INJECTION_AVAILABLE: Issue =
      Issue.create(
        id = "FragmentConstructorInjection",
        briefDescription =
          "Fragment dependencies should be injected using constructor injections only.",
        explanation =
          """
        This Fragment has been set up to inject its dependencies through the constructor. \
        This dependency should be declared in the constructor where dagger will handle the \
        injection at runtime.
      """,
        category = Category.CORRECTNESS,
        priority = 6,
        severity = Severity.ERROR,
        implementation = sourceImplementation<FragmentDaggerFieldInjectionDetector>(),
      )

    private val ISSUE_FRAGMENT_FIELD_INJECTION_USED: Issue =
      Issue.create(
        id = "FragmentFieldInjection",
        briefDescription =
          "Fragment dependencies should be injected using the Fragment's constructor.",
        explanation =
          """
        This dependency should be injected by dagger via the constructor. Add this field's type \
        into the parameter list of the Fragment's constructor. This constructor should be annotated \
        with either `@AssistedInject` or `@Inject`. Annotate with `@AssistedInject` if this \
        Fragment requires runtime arguments via a `Bundle`. Annotate with `@Inject` if this \
        Fragment does not require any runtime arguments. If this is an abstract class, the \
        constructor does not need to be annotated with `@Inject` or `@AssistedInject`.
      """,
        category = Category.CORRECTNESS,
        priority = 6,
        severity = Severity.ERROR,
        implementation = sourceImplementation<FragmentDaggerFieldInjectionDetector>(),
      )

    val issues =
      listOf(ISSUE_FRAGMENT_CONSTRUCTOR_INJECTION_AVAILABLE, ISSUE_FRAGMENT_FIELD_INJECTION_USED)
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/GuavaPreconditionsDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
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.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import com.android.tools.lint.detector.api.isKotlin
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UQualifiedReferenceExpression
import slack.lint.util.sourceImplementation

/**
 * Detect usages of Guava's Preconditions and recommend to use the JavaPreconditions that uses
 * Kotlin stdlib alternatives.
 */
class GuavaPreconditionsDetector : Detector(), SourceCodeScanner {

  override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)

  override fun createUastHandler(context: JavaContext): UElementHandler {
    return object : UElementHandler() {
      override fun visitCallExpression(node: UCallExpression) {
        if (isUsingGuavaPreconditions(node)) {
          if (isKotlin(node.lang)) {
            reportKotlin(context, node)
          } else {
            reportJavaGuavaUsage(context, node)
          }
        }
      }
    }
  }

  private fun reportJavaGuavaUsage(context: JavaContext, node: UCallExpression) {
    val issueToReport = ISSUE_GUAVA_CHECKS_USED

    val lintFix =
      fix()
        .name("Use Slack's JavaPreconditions checks")
        .replace()
        .shortenNames()
        .range(context.getLocation(node))
        .text(createLintFixTextReplaceString(node))
        .with("$FQN_SLACK_JAVA_PRECONDITIONS.${node.methodName}")
        .autoFix()
        .build()
    reportIssue(context, node, issueToReport, lintFix)
  }

  private fun reportKotlin(context: JavaContext, node: UCallExpression) {
    val issueToReport = ISSUE_GUAVA_PRECONDITIONS_USED_IN_KOTLIN

    val updatedKotlinCheckMethod =
      when (node.methodName) {
        METHOD_GUAVA_CHECK_STATE -> METHOD_KOTLIN_CHECK_STATE
        METHOD_GUAVA_CHECK_ARGUMENT -> METHOD_KOTLIN_CHECK_ARGUMENT
        METHOD_GUAVA_CHECK_NOT_NULL -> METHOD_KOTLIN_CHECK_NOT_NULL
        else -> null
      }

    val lintFix =
      updatedKotlinCheckMethod?.let { updatedCheckMethod ->
        fix()
          .name("Use Kotlin's standard library checks")
          .replace()
          .shortenNames()
          .range(context.getLocation(node))
          .text(createLintFixTextReplaceString(node))
          .with(updatedCheckMethod)
          .autoFix()
          .build()
      }
    reportIssue(context, node, issueToReport, lintFix)
  }

  private fun reportIssue(
    context: JavaContext,
    node: UCallExpression,
    issue: Issue,
    quickFix: LintFix? = null,
  ) {
    context.report(
      issue,
      context.getNameLocation(node),
      issue.getBriefDescription(TextFormat.TEXT),
      quickFix,
    )

    check(true)
  }

  private fun createLintFixTextReplaceString(node: UCallExpression): String {
    val nodeParent = node.uastParent
    return if (nodeParent is UQualifiedReferenceExpression) {
      "${nodeParent.receiver.sourcePsi?.text}.${node.methodName}"
    } else {
      "${node.methodName}"
    }
  }

  private fun isUsingGuavaPreconditions(node: UCallExpression): Boolean {
    return node.resolve()?.containingClass?.qualifiedName == FQN_GUAVA_PRECONDITIONS
  }

  companion object {
    private const val FQN_GUAVA_PRECONDITIONS = "com.google.common.base.Preconditions"
    private const val FQN_SLACK_JAVA_PRECONDITIONS = "slack.commons.JavaPreconditions"

    private const val METHOD_GUAVA_CHECK_STATE = "checkState"
    private const val METHOD_GUAVA_CHECK_ARGUMENT = "checkArgument"
    private const val METHOD_GUAVA_CHECK_NOT_NULL = "checkNotNull"

    private const val METHOD_KOTLIN_CHECK_STATE = "check"
    private const val METHOD_KOTLIN_CHECK_ARGUMENT = "require"
    private const val METHOD_KOTLIN_CHECK_NOT_NULL = "checkNotNull"

    private val ISSUE_GUAVA_CHECKS_USED: Issue =
      Issue.create(
        "GuavaChecksUsed",
        "Use Slack's JavaPreconditions instead of Guava's Preconditions checks",
        """Precondition checks in Java should use Slack's internal `JavaPreconditions.kt` \
        instead of Guava's Preconditions.
      """,
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        implementation = sourceImplementation<GuavaPreconditionsDetector>(true),
      )

    private val ISSUE_GUAVA_PRECONDITIONS_USED_IN_KOTLIN: Issue =
      Issue.create(
        "GuavaPreconditionsUsedInKotlin",
        "Kotlin precondition checks should use the Kotlin standard library checks",
        """All Kotlin classes that require precondition checks should use the \
        preconditions checks that are available in the Kotlin standard library in Preconditions.kt.
        """,
        Category.CORRECTNESS,
        6,
        Severity.ERROR,
        implementation = sourceImplementation<GuavaPreconditionsDetector>(true),
      )

    val issues: List<Issue> =
      listOf(ISSUE_GUAVA_CHECKS_USED, ISSUE_GUAVA_PRECONDITIONS_USED_IN_KOTLIN)
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/InjectInJavaDetector.kt
================================================
// Copyright (C) 2021 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import org.jetbrains.uast.UAnnotated
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UField
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.java.isJava
import slack.lint.util.sourceImplementation

/**
 * A simple detector that ensures that `@Inject`, `@Module`, and `@AssistedInject` are not used in
 * Java files in order to properly support Anvil factory generation.
 */
class InjectInJavaDetector : Detector(), SourceCodeScanner {

  override fun getApplicableUastTypes(): List<Class<out UElement>> {
    return listOf(UClass::class.java, UField::class.java, UMethod::class.java)
  }

  override fun createUastHandler(context: JavaContext): UElementHandler? {
    // Only applicable to Java files
    if (!isJava(context.uastFile?.lang)) return null

    return object : UElementHandler() {
      private fun checkNode(node: UAnnotated) {
        node.findInjectionAnnotation()?.let { annotation ->
          context.report(
            ISSUE,
            context.getNameLocation(annotation),
            ISSUE.getBriefDescription(TextFormat.TEXT),
            quickfixData = null,
          )
        }
      }

      override fun visitClass(node: UClass) = checkNode(node)

      override fun visitMethod(node: UMethod) = checkNode(node)

      override fun visitField(node: UField) = checkNode(node)
    }
  }

  companion object {
    private const val FQCN_INJECT = "javax.inject.Inject"
    private const val FQCN_MODULE = "dagger.Module"
    private const val FQCN_ASSISTED_INJECT = "dagger.assisted.AssistedInject"
    private const val FQCN_ASSISTED_FACTORY = "dagger.assisted.AssistedFactory"

    val ISSUE: Issue =
      Issue.create(
        "InjectInJava",
        "Only Kotlin classes should be injected in order for Anvil to work.",
        """
        Only Kotlin classes should be injected in order for Anvil to work. If you \
        cannot easily convert this to Kotlin, consider manually providing it via a Kotlin \
        `@Module`-annotated object.
      """,
        Category.CORRECTNESS,
        9,
        Severity.ERROR,
        sourceImplementation<InjectInJavaDetector>(),
      )

    private val ANNOTATIONS =
      setOf(FQCN_INJECT, FQCN_ASSISTED_INJECT, FQCN_MODULE, FQCN_ASSISTED_FACTORY)

    private fun UAnnotated.findInjectionAnnotation(): UAnnotation? {
      for (annotation in ANNOTATIONS) {
        return findAnnotation(annotation) ?: continue
      }
      return null
    }
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/JavaOnlyDetector.kt
================================================
// Copyright (C) 2020 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
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.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.PsiClassType
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UAnonymousClass
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UCallableReferenceExpression
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.ULambdaExpression
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UReturnExpression
import org.jetbrains.uast.getContainingUClass
import org.jetbrains.uast.getContainingUMethod
import org.jetbrains.uast.kotlin.isKotlin
import org.jetbrains.uast.toUElementOfType
import slack.lint.util.sourceImplementation

/**
 * Logic adapted from the analogous KotlinOnlyChecker in Error-Prone.
 *
 * Consuming repos should create and use `@KotlinOnly` and `@JavaOnly` annotations from the
 * `slack-lint-annotations` artifact. We would normally like to consume these via properties
 * defining them, but lint APIs only allow reading APIs from project-local gradle.properties and not
 * root properties files.
 *
 * Copied recipe from https://github.com/uber/lint-checks
 */
class JavaOnlyDetector : Detector(), SourceCodeScanner {
  companion object {
    private const val KOTLIN_ONLY = "slack.lint.annotations.KotlinOnly"
    private const val JAVA_ONLY = "slack.lint.annotations.JavaOnly"
    private const val ISSUE_ID = "JavaOnlyDetector"
    private const val MESSAGE_LINT_ERROR_TITLE = "Using @JavaOnly elements in Kotlin code."
    private const val MESSAGE_LINT_ERROR_EXPLANATION = "This should not be called from Kotlin code"
    @JvmField
    val ISSUE =
      Issue.create(
        ISSUE_ID,
        MESSAGE_LINT_ERROR_TITLE,
        MESSAGE_LINT_ERROR_EXPLANATION,
        Category.INTEROPERABILITY_KOTLIN,
        6,
        Severity.ERROR,
        sourceImplementation<JavaOnlyDetector>(),
      )

    private fun anonymousTypeString(psiClass: PsiClass, type: String): String {
      return "Cannot create $type instances of @JavaOnly-annotated type ${UastLintUtils.getClassName(psiClass)} (in ${psiClass.containingFile.name}) " +
        "in Kotlin. Make a concrete class instead."
    }
  }

  override fun createUastHandler(context: JavaContext): UElementHandler? {
    // We only run this on Kotlin files, the ErrorProne analogue handles Java files. Can revisit
    // if we get lint in the IDE or otherwise unify
    if (!isKotlin(context.uastFile?.lang)) return null

    return object : UElementHandler() {
      override fun visitClass(node: UClass) {
        val hasJavaOnly = context.evaluator.getAnnotation(node, JAVA_ONLY) != null
        val hasKotlinOnly = context.evaluator.getAnnotation(node, KOTLIN_ONLY) != null
        if (hasJavaOnly && hasKotlinOnly) {
          context.report(
            ISSUE,
            context.getLocation(node.sourcePsi!!),
            "Cannot annotate types with both `@KotlinOnly` and `@JavaOnly`",
          )
          return
        }
        if (hasJavaOnly || hasKotlinOnly) {
          return
        }
        if (node is UAnonymousClass) {
          if (node.uastParent.isReturnExpression() && node.isEnclosedInJavaOnlyMethod()) {
            return
          }
          node.baseClassType.resolve()?.let { psiClass ->
            context.evaluator.getAnnotation(psiClass, JAVA_ONLY)?.run {
              val message = anonymousTypeString(psiClass, "anonymous")
              context.report(ISSUE, context.getLocation(node.sourcePsi!!), message)
            }
          }
          return
        }
        val reportData =
          checkMissingSubclass(node, KOTLIN_ONLY, "KotlinOnly")
            ?: checkMissingSubclass(node, JAVA_ONLY, "JavaOnly")
            ?: return
        context.report(
          ISSUE,
          context.getLocation(node.sourcePsi!!),
          reportData.first,
          reportData.second,
        )
      }

      private fun checkMissingSubclass(
        node: UClass,
        targetAnnotation: String,
        targetAnnotationSimpleName: String,
      ): Pair<String, LintFix>? {
        return listOfNotNull(node.javaPsi.superClass, *node.interfaces)
          .mapNotNull { psiClass ->
            context.evaluator.getAnnotation(psiClass, targetAnnotation)?.run {
              val message =
                "Type subclasses/implements ${UastLintUtils.getClassName(psiClass)} in ${psiClass.containingFile.name} which is annotated @$targetAnnotationSimpleName, it should also be annotated."
              val source = node.text
              return@mapNotNull message to
                fix()
                  .replace()
                  .name("Add @$targetAnnotationSimpleName")
                  .range(context.getLocation(node.sourcePsi!!))
                  .shortenNames()
                  .text(source)
                  .with("@$targetAnnotation $source")
                  .autoFix()
                  .build()
            }
          }
          .firstOrNull()
      }

      override fun visitLambdaExpression(node: ULambdaExpression) {
        if (node.isReturnExpression() && node.isEnclosedInJavaOnlyMethod()) {
          return
        }
        node.functionalInterfaceType?.let { type ->
          if (type is PsiClassType) {
            type.resolve()?.let { psiClass ->
              context.evaluator.getAnnotation(psiClass, JAVA_ONLY)?.let {
                val message = anonymousTypeString(psiClass, "lambda")
                context.report(ISSUE, context.getLocation(node.sourcePsi!!), message)
                return
              }
              val functionalMethod = psiClass.methods.firstOrNull() ?: return
              functionalMethod.toUElementOfType<UMethod>()?.isAnnotationPresent()?.let {
                node.report(it, "expressed as a lambda in Kotlin")
              }
            }
          }
        }
      }

      override fun visitMethod(node: UMethod) {
        val hasJavaOnly = context.evaluator.getAnnotation(node, JAVA_ONLY) != null
        val hasKotlinOnly = context.evaluator.getAnnotation(node, KOTLIN_ONLY) != null
        if (hasJavaOnly && hasKotlinOnly) {
          context.report(
            ISSUE,
            context.getLocation(node.sourcePsi!!),
            "Cannot annotate functions with both `@KotlinOnly` and `@JavaOnly`",
          )
          return
        }
        if (hasJavaOnly || hasKotlinOnly) {
          return
        }
        val reportData =
          checkMissingOverride(node, KOTLIN_ONLY, "KotlinOnly")
            ?: checkMissingOverride(node, JAVA_ONLY, "JavaOnly")
            ?: return
        context.report(ISSUE, context.getLocation(node), reportData.first, reportData.second)
      }

      private fun checkMissingOverride(
        node: UMethod,
        targetAnnotation: String,
        targetAnnotationSimpleName: String,
      ): Pair<String, LintFix>? {
        return context.evaluator.getSuperMethod(node)?.let { method ->
          context.evaluator.getAnnotation(method, targetAnnotation)?.run {
            val message =
              "Function overrides ${method.name} in ${
                UastLintUtils.getClassName(
                  method.containingClass!!
                )
              } which is annotated @$targetAnnotationSimpleName, it should also be annotated."
            val modifier = node.modifierList.children.joinToString(separator = " ") { it.text }
            return@let message to
              fix()
                .replace()
                .name("Add @$targetAnnotationSimpleName")
                .range(context.getLocation(node))
                .shortenNames()
                .text(modifier)
                .with("@$targetAnnotation $modifier")
                .autoFix()
                .build()
          }
        }
      }

      override fun visitCallExpression(node: UCallExpression) {
        node.resolve().toUElementOfType<UMethod>()?.isAnnotationPresent()?.let { node.report(it) }
      }

      override fun visitCallableReferenceExpression(node: UCallableReferenceExpression) {
        node.resolve().toUElementOfType<UMethod>()?.isAnnotationPresent()?.let { node.report(it) }
      }

      private fun UExpression.report(
        javaOnlyMessage: String?,
        callString: String = "called from Kotlin",
      ) {
        val message = StringBuilder("This method should not be $callString")
        if (javaOnlyMessage.isNullOrBlank()) {
          message.append(", see its documentation for details.")
        } else {
          message.append(": $javaOnlyMessage")
        }
        context.report(ISSUE, context.getLocation(this), message.toString())
      }

      private fun UElement?.isReturnExpression(): Boolean =
        this != null && uastParent is UReturnExpression

      private fun UElement.isEnclosedInJavaOnlyMethod(): Boolean {
        return getContainingUMethod()?.isAnnotationPresent() != null
      }

      private fun UMethod.isAnnotationPresent(): String? {
        findAnnotation(JAVA_ONLY)?.let {
          return it.extractValue()
        }
        getContainingUClass()?.findAnnotation(JAVA_ONLY)?.let {
          return it.extractValue()
        }
        context.evaluator.getPackage(this)?.let { pkg ->
          context.evaluator.getAnnotation(pkg, KOTLIN_ONLY)?.let {
            return it.extractValue()
          }
        }
        return null
      }

      private fun UAnnotation.extractValue(): String {
        return UastLintUtils.getAnnotationStringValue(this, "reason").orEmpty()
      }
    }
  }

  override fun getApplicableUastTypes(): List<Class<out UElement>> {
    return listOf(
      UMethod::class.java,
      UCallExpression::class.java,
      UCallableReferenceExpression::class.java,
      ULambdaExpression::class.java,
      UClass::class.java,
    )
  }
}


================================================
FILE: slack-lint-checks/src/main/java/slack/lint/JsonInflaterMoshiCompatibilityDetector.kt
================================================
// Copyright (C) 2025 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
package slack.lint

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import com.intellij.psi.PsiCapturedWildcardType
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiPrimitiveType
import com.intellij.psi.PsiType
import com.intellij.psi.PsiWildcardType
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.uast.UCallExpression
import slack.lint.moshi.MoshiLintUtil.hasMoshiAnnotation
import slack.lint.util.sourceImplementation

/**
 * A detector that checks if a type passed into the JsonInflater.inflate/deflate methods follows
 * Moshi's requirements for serialization/deserialization.
 *
 * `JsonInflater` is a JSON serialization indirection we have internally at Slack.
 */
class JsonInflaterMoshiCompatibilityDetector : Detector(), SourceCodeScanner {

  override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)

  override fun createUastHandler(context: JavaContext): UElementHandler {
    return object : UElementHandler() {
      override fun visitCallExpression(node: UCallExpression) {
        val method = node.resolve() ?: return

        if (isJsonInflaterInflateOrDeflate(node)) {
          when (method.name) {
            "inflate" -> validateInflateReturnType(context, node)
            "deflate" -> validateDeflateArguments(context, node)
          }
        }
      }
    }
  }

  private fun isJsonInflaterInflateOrDeflate(node: UCallExpression): Boolean {
    // Get the method being called
    val method = node.resolve() ?: return false

    // Check if it's from JsonInflater class
    val containingClass = method.containingClass?.qualifiedName ?: return false
    if (containingClass != FQCN_JSON_INFLATER) return false

    // Check if it's an inflate or deflate method
    return method.name == "inflate" || method.name == "deflate"
  }

  private fun validateInflateReturnType(context: JavaContext, node: UCallExpression) {
    // Get the return type of the inflate call
    val returnType = node.getExpressionType() ?: return

    // Skip checking primitive types and String
    if (isJsonPrimitive(returnType)) return

    // Validate if the class is Moshi-compatible
    validateClassForMoshiCompatibility(context, node, returnType)
  }

  private fun isJsonPrimitive(type: PsiType): Boolean {
    val isString =
      if (type is PsiClassType) {
        type.resolve()?.qualifiedName == FQCN_JAVA_STRING
      } else {
        false
      }
    return type is PsiPrimitiveType || isString
  }

  private fun validateDeflateArguments(context: JavaContext, node: UCallExpression) {
    val method = node.resolve() ?: return

    val parameters = method.parameterList.parameters
    val valueParamIndex = parameters.indexOfFirst { it.name == "value" }
    if (valueParamIndex == -1) return

    val valueArg = node.getArgumentForParameter(valueParamIndex) ?: return
    val valueArgType = valueArg.getExpressionType() ?: return

    validateClassForMoshiCompatibility(context, node, valueArgType)
  }

  private fun validateClassForMoshiCompatibility(
    context: JavaContext,
    node: UCallExpression,
    typeToValidate: PsiType,
  ) {
    val modelClasses = extractModelClasses(typeToValidate)

    if (modelClasses.any { !isMoshiCompatible(it) }) {
      context.report(
        issue = ISSUE,
        location = context.getLocation(node),
        message = ISSUE.getBriefDescription(TextFormat.TEXT),
      )
    }
  }

  private fun extractModelClasses(type: PsiType): List<PsiClass> {
    val result = mutableListOf<PsiClass>()

    fun processType(t: PsiType) {
      val unwrapped =
        when (t) {
          is PsiWildcardType -> t.bound ?: return
          is PsiCapturedWildcardType -> t.wildcard.bound ?: return
          else -> t
        }

      val psiClass = PsiTypesUtil.getPsiClass(unwrapped)
      if (psiClass != null) {
        result.add(psiClass)
      }

      if (unwrapped is PsiClassType) {
        for (param in unwrapped.parameters) {
          processType(param)
        }
      }
    }

    processType(type)
    return result
  }

  private fun isMoshiCompatible(psiClass: PsiClass): Boolean {
    if (isPrimitiveType(psiClass)) return true

    if (isCollectionType(psiClass)) return true

    if (isAbstractOrNonPublicClass(psiClass)) return false

    if (psiClass.isInterface && !isSealedInterface(psiClass)) return false

    return psiClass.hasMoshiAnnotation()
  }

  private f
Download .txt
gitextract_dsk5zpit/

├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── maintainers_guide.md
│   └── workflows/
│       ├── ci.yml
│       ├── increment_version.sh
│       ├── renovate.yml
│       └── scriptUtil.sh
├── .gitignore
├── CHANGELOG.md
├── CODEOWNERS
├── LICENSE.txt
├── README.md
├── RELEASING.md
├── build.gradle.kts
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── release.sh
├── renovate.json
├── settings.gradle.kts
├── slack-lint-annotations/
│   ├── build.gradle.kts
│   ├── gradle.properties
│   └── src/
│       └── main/
│           └── kotlin/
│               └── slack/
│                   └── lint/
│                       └── annotations/
│                           ├── AllowUnitResult.kt
│                           ├── DoNotMock.kt
│                           ├── JavaOnly.kt
│                           ├── KotlinOnly.kt
│                           ├── MustUseNamedParams.kt
│                           └── RestrictCallsTo.kt
├── slack-lint-checks/
│   ├── build.gradle.kts
│   ├── gradle.properties
│   ├── lint-baseline.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── slack/
│       │           └── lint/
│       │               ├── AlwaysNullReadOnlyVariableDetector.kt
│       │               ├── AnnotatedClassOrMethodUsageDetector.kt
│       │               ├── ArgInFormattedQuantityStringResDetector.kt
│       │               ├── CircuitScreenDataClassDetector.kt
│       │               ├── DaggerIssuesDetector.kt
│       │               ├── DeprecatedAnnotationDetector.kt
│       │               ├── DeprecatedSqlUsageDetector.kt
│       │               ├── DoNotCallProvidersDetector.kt
│       │               ├── ExceptionMessageDetector.kt
│       │               ├── FragmentDaggerFieldInjectionDetector.kt
│       │               ├── GuavaPreconditionsDetector.kt
│       │               ├── InjectInJavaDetector.kt
│       │               ├── JavaOnlyDetector.kt
│       │               ├── JsonInflaterMoshiCompatibilityDetector.kt
│       │               ├── MainScopeUsageDetector.kt
│       │               ├── MoshiUsageDetector.kt
│       │               ├── MustUseNamedParamsDetector.kt
│       │               ├── NonKotlinPairDetector.kt
│       │               ├── NotNullOperatorDetector.kt
│       │               ├── NullableConcurrentHashMapDetector.kt
│       │               ├── RawDispatchersUsageDetector.kt
│       │               ├── RedactedUsageDetector.kt
│       │               ├── RestrictCallsToDetector.kt
│       │               ├── SerializableDetector.kt
│       │               ├── SlackIssueRegistry.kt
│       │               ├── TestParameterSiteTargetDetector.kt
│       │               ├── ViewContextDetector.kt
│       │               ├── denylistedapis/
│       │               │   └── DenyListedApiDetector.kt
│       │               ├── eithernet/
│       │               │   └── DoNotExposeEitherNetInRepositoriesDetector.kt
│       │               ├── inclusive/
│       │               │   ├── InclusiveNamingChecker.kt
│       │               │   ├── InclusiveNamingResourceScanner.kt
│       │               │   └── InclusiveNamingSourceCodeScanner.kt
│       │               ├── mocking/
│       │               │   ├── AnyMockDetector.kt
│       │               │   ├── AutoValueMockDetector.kt
│       │               │   ├── DataClassMockDetector.kt
│       │               │   ├── DoNotMockMockDetector.kt
│       │               │   ├── ErrorProneDoNotMockDetector.kt
│       │               │   ├── MockDetector.kt
│       │               │   ├── ObjectClassMockDetector.kt
│       │               │   ├── PlatformTypeMockDetector.kt
│       │               │   ├── RecordClassMockDetector.kt
│       │               │   ├── SealedClassMockDetector.kt
│       │               │   └── ValueClassMockDetector.kt
│       │               ├── moshi/
│       │               │   └── MoshiLintUtil.kt
│       │               ├── parcel/
│       │               │   └── ParcelizeFunctionPropertyDetector.kt
│       │               ├── resources/
│       │               │   ├── FullyQualifiedResourceDetector.kt
│       │               │   ├── ImportAliasesLoader.kt
│       │               │   ├── MissingResourceImportAliasDetector.kt
│       │               │   ├── WrongResourceImportAliasDetector.kt
│       │               │   └── model/
│       │               │       └── RootIssueData.kt
│       │               ├── retrofit/
│       │               │   └── RetrofitUsageDetector.kt
│       │               ├── rx/
│       │               │   ├── RxObservableEmitDetector.kt
│       │               │   └── RxSubscribeOnMainDetector.kt
│       │               ├── text/
│       │               │   └── SpanMarkPointMissingMaskDetector.kt
│       │               ├── ui/
│       │               │   ├── DoNotCallViewToString.kt
│       │               │   └── ItemDecorationViewBindingDetector.kt
│       │               └── util/
│       │                   ├── BooleanLintOption.kt
│       │                   ├── LintOption.kt
│       │                   ├── LintUtils.kt
│       │                   ├── MetadataJavaEvaluator.kt
│       │                   ├── Names.kt
│       │                   ├── OptionLoadingDetector.kt
│       │                   ├── Priorities.kt
│       │                   └── StringSetLintOption.kt
│       └── test/
│           ├── java/
│           │   └── slack/
│           │       └── lint/
│           │           ├── AlwaysNullReadOnlyVariableDetectorTest.kt
│           │           ├── ArgInFormattedQuantityStringResDetectorTest.kt
│           │           ├── BaseSlackLintTest.kt
│           │           ├── CircuitScreenDataClassDetectorTest.kt
│           │           ├── DaggerIssuesDetectorTest.kt
│           │           ├── DeprecatedAnnotationDetectorTest.kt
│           │           ├── DeprecatedSqlUsageDetectorTest.kt
│           │           ├── DoNotCallProvidersDetectorTest.kt
│           │           ├── ExceptionMessageDetectorTest.kt
│           │           ├── FragmentDaggerFieldInjectionDetectorTest.kt
│           │           ├── GuavaPreconditionsDetectorTest.kt
│           │           ├── InjectInJavaDetectorTest.kt
│           │           ├── JavaOnlyDetectorTest.kt
│           │           ├── JsonInflaterMoshiCompatibilityDetectorTest.kt
│           │           ├── LintKotlinVersionCheckTest.kt
│           │           ├── MainScopeUsageDetectorTest.kt
│           │           ├── MoshiEnumUsageDetectorTest.kt
│           │           ├── MoshiUsageDetectorTest.kt
│           │           ├── MustUseNamedParamsDetectorTest.kt
│           │           ├── NonKotlinPairDetectorTest.kt
│           │           ├── NotNullOperatorDetectorTest.kt
│           │           ├── NullableConcurrentHashMapDetectorTest.kt
│           │           ├── RawDispatchersUsageDetectorTest.kt
│           │           ├── RedactedUsageDetectorTest.kt
│           │           ├── RestrictCallsToDetectorTest.kt
│           │           ├── SerializableDetectorTest.kt
│           │           ├── TestParameterSiteTargetDetectorTest.kt
│           │           ├── ViewContextDetectorTest.kt
│           │           ├── denylistedapis/
│           │           │   └── DenyListedApiDetectorTest.kt
│           │           ├── eithernet/
│           │           │   └── DoNotExposeEitherNetInRepositoriesDetectorTest.kt
│           │           ├── inclusive/
│           │           │   └── InclusiveNamingDetectorTest.kt
│           │           ├── mocking/
│           │           │   ├── AutoValueMockDetectorTest.kt
│           │           │   ├── DataClassMockDetectorTest.kt
│           │           │   ├── DoNotMockMockDetectorTest.kt
│           │           │   ├── DoNotMockUsageDetectorTest.kt
│           │           │   ├── MockDetectorOptionsTest.kt
│           │           │   ├── MockFileStubs.kt
│           │           │   ├── MockReportTest.kt
│           │           │   ├── ObjectClassMockDetectorTest.kt
│           │           │   ├── PlatformTypeMockDetectorTest.kt
│           │           │   ├── RecordClassMockDetectorTest.kt
│           │           │   ├── SealedClassMockDetectorTest.kt
│           │           │   └── ValueClassMockDetectorTest.kt
│           │           ├── parcel/
│           │           │   └── ParcelizeFunctionPropertyDetectorTest.kt
│           │           ├── resources/
│           │           │   ├── FullyQualifiedResourceDetectorTest.kt
│           │           │   ├── MissingResourceImportAliasDetectorTest.kt
│           │           │   └── WrongResourceImportAliasDetectorTest.kt
│           │           ├── retrofit/
│           │           │   ├── RetrofitJarLoader.kt
│           │           │   └── RetrofitUsageDetectorTest.kt
│           │           ├── rx/
│           │           │   ├── RxJavaJarLoader.kt
│           │           │   ├── RxObservableEmitDetectorTest.kt
│           │           │   └── RxSubscribeOnMainDetectorTest.kt
│           │           ├── text/
│           │           │   └── SpanMarkPointMissingMaskDetectorTest.kt
│           │           └── util/
│           │               └── LintUtilsTest.kt
│           └── resources/
│               ├── com/
│               │   └── slack/
│               │       └── lint/
│               │           └── data/
│               │               └── testStubs/
│               │                   ├── ViewContextDetectorTestContentProvider.java
│               │                   ├── ViewContextDetectorTestCustomViewInternalCaller.java
│               │                   ├── ViewContextDetectorTestExternalCallerOnCustomView.java
│               │                   └── ViewContextDetectorTestExternalCallerOnView.java
│               ├── retrofit-3.0.0.jar
│               └── rxjava-3.1.0.jar
└── spotless/
    └── spotless.kt
Download .txt
SYMBOL INDEX (11 symbols across 4 files)

FILE: slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestContentProvider.java
  class DummyProvider (line 4) | public abstract class DummyProvider extends ContentProvider {
    method bar (line 5) | public void bar() {

FILE: slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestCustomViewInternalCaller.java
  class Example (line 6) | public class Example extends TextView {
    method Example (line 7) | public Example(Context context, AttributeSet attrs) {
    method bar (line 10) | public void bar() {

FILE: slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestExternalCallerOnCustomView.java
  class Example (line 6) | public class Example {
    method Example (line 8) | public Example(TextView v) {
    method bar (line 11) | public void bar() {

FILE: slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestExternalCallerOnView.java
  class Example (line 6) | public class Example {
    method Example (line 8) | public Example(View v) {
    method bar (line 11) | public void bar() {
Condensed preview — 160 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,018K chars).
[
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 969,
    "preview": "# Code of Conduct\n\n## Introduction\n\nDiversity and inclusion make our community strong. We encourage participation from t"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 2260,
    "preview": "# Contributors Guide\n\nNote that this project is considered READ-ONLY. You are welcome to discuss or ask questions in the"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 982,
    "preview": "###  Summary\n\nDescribe the goal of this PR. Mention any related Issue numbers. Please note that we do not normally\naccep"
  },
  {
    "path": ".github/maintainers_guide.md",
    "chars": 3292,
    "preview": "# Maintainers Guide\n\nThis document describes tools, tasks and workflow that one needs to be familiar with in order to ef"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1418,
    "preview": "name: CI\n\non:\n  # Only run push on main\n  push:\n    branches:\n      - main\n    paths-ignore:\n      - '**/*.md'\n  # Alway"
  },
  {
    "path": ".github/workflows/increment_version.sh",
    "chars": 1142,
    "preview": "#!/usr/bin/env bash\n\n# Target project to update, required\nTARGET=$1\n# Version to increment to, optional. If blank or not"
  },
  {
    "path": ".github/workflows/renovate.yml",
    "chars": 402,
    "preview": "name: Renovate\n\non:\n  schedule:\n    - cron: \"0 8 * * *\" # 8am daily\n  workflow_dispatch:\n\njobs:\n  renovate:\n    runs-on:"
  },
  {
    "path": ".github/workflows/scriptUtil.sh",
    "chars": 185,
    "preview": "# Source this file to use its functions\n\n# Gets a property out of a .properties file\n# usage: getProperty $key $filename"
  },
  {
    "path": ".gitignore",
    "chars": 318,
    "preview": ".gradle\n.kotlin/\nlocal.properties\n.idea\n!/.idea/codeStyles\n!/.idea/inspectionProfiles\n!/.idea/icon.png\n*.iml\n*.so\n.DS_St"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 9092,
    "preview": "Changelog\n=========\n\n**Unreleased**\n--------------\n\n0.11.1\n------\n\n_2025-10-10_\n\n- **Fix**: Allow sealed classes to be M"
  },
  {
    "path": "CODEOWNERS",
    "chars": 184,
    "preview": "# Comment line immediately above ownership line is reserved for related other information. Please be careful while editi"
  },
  {
    "path": "LICENSE.txt",
    "chars": 11358,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 2969,
    "preview": "slack-lints\n===========\n\nThis repository contains a collection of custom Android/Kotlin lint checks we use in our Androi"
  },
  {
    "path": "RELEASING.md",
    "chars": 161,
    "preview": "Releasing\n=========\n\n1. Update the `CHANGELOG.md` for the impending release.\n2. Run `./release.sh <version>`.\n3. Publish"
  },
  {
    "path": "build.gradle.kts",
    "chars": 3807,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\nimport com.diffplug.gradle.spotless"
  },
  {
    "path": "gradle/libs.versions.toml",
    "chars": 1750,
    "preview": "[versions]\nkotlin = \"2.3.21\"\nktfmt = \"0.58\"\njdk = \"23\"\n# lint checks must target JDK 17, but the runtime should remain 1"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 252,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradle.properties",
    "chars": 1137,
    "preview": "org.gradle.jvmargs=-Xms1g -Xmx4g -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1g\n\norg.gradle.parallel=true\norg.gradle.conf"
  },
  {
    "path": "gradlew",
    "chars": 8710,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "gradlew.bat",
    "chars": 2937,
    "preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
  },
  {
    "path": "release.sh",
    "chars": 759,
    "preview": "#!/usr/bin/env bash\n\nset -exo pipefail\n\n# Gets a property out of a .properties file\n# usage: getProperty $key $filename\n"
  },
  {
    "path": "renovate.json",
    "chars": 365,
    "preview": "{\n  \"extends\": [\n    \"config:base\"\n  ],\n  \"branchPrefix\": \"renovate/\",\n  \"gitAuthor\": \"OSS-Bot <svc-oss-bot@slack-corp.c"
  },
  {
    "path": "settings.gradle.kts",
    "chars": 877,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\nimport java.util.Locale\n\npluginMana"
  },
  {
    "path": "slack-lint-annotations/build.gradle.kts",
    "chars": 165,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\nplugins {\n  alias(libs.plugins.kotl"
  },
  {
    "path": "slack-lint-annotations/gradle.properties",
    "chars": 110,
    "preview": "POM_ARTIFACT_ID=slack-lint-annotations\nPOM_NAME=Slack Lint Annotations\nPOM_DESCRIPTION=Slack lint annotations."
  },
  {
    "path": "slack-lint-annotations/src/main/kotlin/slack/lint/annotations/AllowUnitResult.kt",
    "chars": 398,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.annotations\n\n/**"
  },
  {
    "path": "slack-lint-annotations/src/main/kotlin/slack/lint/annotations/DoNotMock.kt",
    "chars": 884,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.annotations\n\nimp"
  },
  {
    "path": "slack-lint-annotations/src/main/kotlin/slack/lint/annotations/JavaOnly.kt",
    "chars": 466,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.annotations\n\n/**"
  },
  {
    "path": "slack-lint-annotations/src/main/kotlin/slack/lint/annotations/KotlinOnly.kt",
    "chars": 470,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.annotations\n\n/**"
  },
  {
    "path": "slack-lint-annotations/src/main/kotlin/slack/lint/annotations/MustUseNamedParams.kt",
    "chars": 431,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.annotations\n\n/**"
  },
  {
    "path": "slack-lint-annotations/src/main/kotlin/slack/lint/annotations/RestrictCallsTo.kt",
    "chars": 703,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.annotations\n\nimp"
  },
  {
    "path": "slack-lint-checks/build.gradle.kts",
    "chars": 3015,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\nimport com.github.jengelman.gradle."
  },
  {
    "path": "slack-lint-checks/gradle.properties",
    "chars": 316,
    "preview": "POM_ARTIFACT_ID=slack-lint-checks\nPOM_NAME=Slack Lint Checks\nPOM_DESCRIPTION=Slack lint checks.\n\n# Opt-out flag for bund"
  },
  {
    "path": "slack-lint-checks/lint-baseline.xml",
    "chars": 66132,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<issues format=\"6\" by=\"lint 8.11.0\" type=\"baseline\" client=\"gradle\" dependencies="
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/AlwaysNullReadOnlyVariableDetector.kt",
    "chars": 5229,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/AnnotatedClassOrMethodUsageDetector.kt",
    "chars": 1990,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/ArgInFormattedQuantityStringResDetector.kt",
    "chars": 4067,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/CircuitScreenDataClassDetector.kt",
    "chars": 4472,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/DaggerIssuesDetector.kt",
    "chars": 10309,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/DeprecatedAnnotationDetector.kt",
    "chars": 2641,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/DeprecatedSqlUsageDetector.kt",
    "chars": 2196,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/DoNotCallProvidersDetector.kt",
    "chars": 2281,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/ExceptionMessageDetector.kt",
    "chars": 3272,
    "preview": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\""
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/FragmentDaggerFieldInjectionDetector.kt",
    "chars": 4043,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/GuavaPreconditionsDetector.kt",
    "chars": 5353,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/InjectInJavaDetector.kt",
    "chars": 3063,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/JavaOnlyDetector.kt",
    "chars": 10357,
    "preview": "// Copyright (C) 2020 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/JsonInflaterMoshiCompatibilityDetector.kt",
    "chars": 8509,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/MainScopeUsageDetector.kt",
    "chars": 3584,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/MoshiUsageDetector.kt",
    "chars": 67392,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/MustUseNamedParamsDetector.kt",
    "chars": 2340,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/NonKotlinPairDetector.kt",
    "chars": 4182,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/NotNullOperatorDetector.kt",
    "chars": 1872,
    "preview": "// Copyright (C) 2024 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/NullableConcurrentHashMapDetector.kt",
    "chars": 5270,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/RawDispatchersUsageDetector.kt",
    "chars": 4111,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/RedactedUsageDetector.kt",
    "chars": 2723,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/RestrictCallsToDetector.kt",
    "chars": 3265,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/SerializableDetector.kt",
    "chars": 2061,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/SlackIssueRegistry.kt",
    "chars": 3328,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/TestParameterSiteTargetDetector.kt",
    "chars": 4297,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/ViewContextDetector.kt",
    "chars": 2868,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/denylistedapis/DenyListedApiDetector.kt",
    "chars": 26052,
    "preview": "/*\nCopyright 2022 Square, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/eithernet/DoNotExposeEitherNetInRepositoriesDetector.kt",
    "chars": 4442,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.eithernet\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/inclusive/InclusiveNamingChecker.kt",
    "chars": 5136,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\n@file:Suppress(\"UnstableApiUsage\")\n"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/inclusive/InclusiveNamingResourceScanner.kt",
    "chars": 1220,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.inclusive\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/inclusive/InclusiveNamingSourceCodeScanner.kt",
    "chars": 2933,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.inclusive\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/AnyMockDetector.kt",
    "chars": 1367,
    "preview": "// Copyright (C) 2024 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/AutoValueMockDetector.kt",
    "chars": 1040,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/DataClassMockDetector.kt",
    "chars": 1581,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/DoNotMockMockDetector.kt",
    "chars": 2023,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/ErrorProneDoNotMockDetector.kt",
    "chars": 2512,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/MockDetector.kt",
    "chars": 11182,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/ObjectClassMockDetector.kt",
    "chars": 1431,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/PlatformTypeMockDetector.kt",
    "chars": 1818,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/RecordClassMockDetector.kt",
    "chars": 1793,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/SealedClassMockDetector.kt",
    "chars": 1898,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/mocking/ValueClassMockDetector.kt",
    "chars": 1607,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/moshi/MoshiLintUtil.kt",
    "chars": 500,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.moshi\n\nimport co"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/parcel/ParcelizeFunctionPropertyDetector.kt",
    "chars": 3201,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.parcel\n\nimport c"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/resources/FullyQualifiedResourceDetector.kt",
    "chars": 5661,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/resources/ImportAliasesLoader.kt",
    "chars": 1009,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/resources/MissingResourceImportAliasDetector.kt",
    "chars": 5677,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/resources/WrongResourceImportAliasDetector.kt",
    "chars": 4921,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/resources/model/RootIssueData.kt",
    "chars": 246,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources.model\n"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/retrofit/RetrofitUsageDetector.kt",
    "chars": 7272,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.retrofit\n\nimport"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/rx/RxObservableEmitDetector.kt",
    "chars": 4049,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.rx\n\nimport com.a"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/rx/RxSubscribeOnMainDetector.kt",
    "chars": 6229,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.rx\n\nimport com.a"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/text/SpanMarkPointMissingMaskDetector.kt",
    "chars": 4248,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.text\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/ui/DoNotCallViewToString.kt",
    "chars": 1861,
    "preview": "// Copyright (C) 2024 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.ui\n\nimport com.a"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/ui/ItemDecorationViewBindingDetector.kt",
    "chars": 3121,
    "preview": "// Copyright (C) 2024 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.ui\n\nimport com.a"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/BooleanLintOption.kt",
    "chars": 463,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/LintOption.kt",
    "chars": 394,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/LintUtils.kt",
    "chars": 11950,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/MetadataJavaEvaluator.kt",
    "chars": 11579,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/Names.kt",
    "chars": 2616,
    "preview": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\""
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/OptionLoadingDetector.kt",
    "chars": 585,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/Priorities.kt",
    "chars": 344,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\n/**\n * Pri"
  },
  {
    "path": "slack-lint-checks/src/main/java/slack/lint/util/StringSetLintOption.kt",
    "chars": 459,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/AlwaysNullReadOnlyVariableDetectorTest.kt",
    "chars": 4736,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/ArgInFormattedQuantityStringResDetectorTest.kt",
    "chars": 10403,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/BaseSlackLintTest.kt",
    "chars": 3257,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/CircuitScreenDataClassDetectorTest.kt",
    "chars": 10535,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/DaggerIssuesDetectorTest.kt",
    "chars": 19918,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/DeprecatedAnnotationDetectorTest.kt",
    "chars": 7450,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/DeprecatedSqlUsageDetectorTest.kt",
    "chars": 3060,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/DoNotCallProvidersDetectorTest.kt",
    "chars": 6158,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\n@file:Suppress(\"UnstableApiUsage\")\n"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/ExceptionMessageDetectorTest.kt",
    "chars": 11842,
    "preview": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\""
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/FragmentDaggerFieldInjectionDetectorTest.kt",
    "chars": 14087,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\n@file:Suppress(\"UnstableApiUsage\")\n"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/GuavaPreconditionsDetectorTest.kt",
    "chars": 21406,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/InjectInJavaDetectorTest.kt",
    "chars": 4415,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/JavaOnlyDetectorTest.kt",
    "chars": 23359,
    "preview": "// Copyright (C) 2020 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/JsonInflaterMoshiCompatibilityDetectorTest.kt",
    "chars": 22483,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/LintKotlinVersionCheckTest.kt",
    "chars": 2647,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/MainScopeUsageDetectorTest.kt",
    "chars": 2294,
    "preview": "// Copyright (C) 2020 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/MoshiEnumUsageDetectorTest.kt",
    "chars": 23970,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/MoshiUsageDetectorTest.kt",
    "chars": 65599,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/MustUseNamedParamsDetectorTest.kt",
    "chars": 1967,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/NonKotlinPairDetectorTest.kt",
    "chars": 9115,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/NotNullOperatorDetectorTest.kt",
    "chars": 1608,
    "preview": "// Copyright (C) 2024 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport kotlin.t"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/NullableConcurrentHashMapDetectorTest.kt",
    "chars": 6245,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/RawDispatchersUsageDetectorTest.kt",
    "chars": 3835,
    "preview": "// Copyright (C) 2020 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/RedactedUsageDetectorTest.kt",
    "chars": 3507,
    "preview": "// Copyright (C) 2020 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/RestrictCallsToDetectorTest.kt",
    "chars": 3294,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/SerializableDetectorTest.kt",
    "chars": 4651,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/TestParameterSiteTargetDetectorTest.kt",
    "chars": 3294,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport org.juni"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/ViewContextDetectorTest.kt",
    "chars": 3305,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint\n\nimport com.andr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/denylistedapis/DenyListedApiDetectorTest.kt",
    "chars": 32997,
    "preview": "// Copyright Square, Inc.\n// Apache-2.0\npackage slack.lint.denylistedapis\n\nimport com.android.tools.lint.checks.infrastr"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/eithernet/DoNotExposeEitherNetInRepositoriesDetectorTest.kt",
    "chars": 7363,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.eithernet\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/inclusive/InclusiveNamingDetectorTest.kt",
    "chars": 6672,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.inclusive\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/AutoValueMockDetectorTest.kt",
    "chars": 10442,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/DataClassMockDetectorTest.kt",
    "chars": 7137,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/DoNotMockMockDetectorTest.kt",
    "chars": 4360,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/DoNotMockUsageDetectorTest.kt",
    "chars": 4448,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/MockDetectorOptionsTest.kt",
    "chars": 12811,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/MockFileStubs.kt",
    "chars": 1306,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/MockReportTest.kt",
    "chars": 7968,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/ObjectClassMockDetectorTest.kt",
    "chars": 5985,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/PlatformTypeMockDetectorTest.kt",
    "chars": 5083,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/RecordClassMockDetectorTest.kt",
    "chars": 8116,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/SealedClassMockDetectorTest.kt",
    "chars": 11904,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/mocking/ValueClassMockDetectorTest.kt",
    "chars": 6191,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.mocking\n\nimport "
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/parcel/ParcelizeFunctionPropertyDetectorTest.kt",
    "chars": 4114,
    "preview": "// Copyright (C) 2023 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.parcel\n\nimport o"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/resources/FullyQualifiedResourceDetectorTest.kt",
    "chars": 7872,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/resources/MissingResourceImportAliasDetectorTest.kt",
    "chars": 7015,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/resources/WrongResourceImportAliasDetectorTest.kt",
    "chars": 6581,
    "preview": "// Copyright (C) 2022 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.resources\n\nimpor"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/retrofit/RetrofitJarLoader.kt",
    "chars": 450,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.retrofit\n\nimport"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/retrofit/RetrofitUsageDetectorTest.kt",
    "chars": 6598,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.retrofit\n\nimport"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/rx/RxJavaJarLoader.kt",
    "chars": 438,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.rx\n\nimport com.a"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/rx/RxObservableEmitDetectorTest.kt",
    "chars": 11957,
    "preview": "// Copyright (C) 2025 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.rx\n\nimport com.a"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/rx/RxSubscribeOnMainDetectorTest.kt",
    "chars": 23855,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.rx\n\nimport com.a"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/text/SpanMarkPointMissingMaskDetectorTest.kt",
    "chars": 11791,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.text\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/test/java/slack/lint/util/LintUtilsTest.kt",
    "chars": 1489,
    "preview": "// Copyright (C) 2021 Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0\npackage slack.lint.util\n\nimport com"
  },
  {
    "path": "slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestContentProvider.java",
    "chars": 205,
    "preview": "package foo;\nimport android.content.ContentProvider;\nimport android.content.Context;\npublic abstract class DummyProvider"
  },
  {
    "path": "slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestCustomViewInternalCaller.java",
    "chars": 337,
    "preview": "package foo;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport andro"
  },
  {
    "path": "slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestExternalCallerOnCustomView.java",
    "chars": 311,
    "preview": "package foo;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport andro"
  },
  {
    "path": "slack-lint-checks/src/test/resources/com/slack/lint/data/testStubs/ViewContextDetectorTestExternalCallerOnView.java",
    "chars": 297,
    "preview": "package foo;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport andro"
  },
  {
    "path": "spotless/spotless.kt",
    "chars": 85,
    "preview": "// Copyright (C) $YEAR Slack Technologies, LLC\n// SPDX-License-Identifier: Apache-2.0"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the slackhq/slack-lints GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 160 files (940.0 KB), approximately 215.0k tokens, and a symbol index with 11 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!