1# Adding custom lint checks 2 3[TOC] 4 5## Getting started 6 7Lint is a static analysis tool that checks Android project source files. Lint 8checks come with Android Studio by default, but custom lint checks can be added 9to specific library modules to help avoid potential bugs and encourage best code 10practices. 11 12This guide is targeted to developers who would like to quickly get started with 13adding lint checks in the AndroidX development workflow. For a complete guide to 14writing and running lint checks, see the official 15[Android lint documentation](https://googlesamples.github.io/android-custom-lint-rules/). 16 17### Create a module 18 19If this is the first lint rule for a library, you will need to create a module 20by doing the following: 21 22Include the project in the top-level `settings.gradle` file so that it shows up 23in Android Studio's list of modules: 24 25``` 26includeProject(":mylibrary:mylibrary-lint", "mylibrary/mylibrary-lint") 27``` 28 29Manually create a new module in `frameworks/support` (preferably in the 30directory you are making lint rules for). In the new module, add a `src` folder 31and a `build.gradle` file containing the needed dependencies. 32 33`mylibrary/mylibrary-lint/build.gradle`: 34 35``` 36import androidx.build.LibraryType 37 38plugins { 39 id("AndroidXPlugin") 40 id("kotlin") 41} 42 43dependencies { 44 compileOnly(libs.androidLintMinApi) 45 compileOnly(libs.kotlinStdlib) 46 47 testImplementation(libs.kotlinStdlib) 48 testImplementation(libs.androidLint) 49 testImplementation(libs.androidLintTests) 50 testImplementation(libs.junit) 51 testImplementation(libs.truth) 52} 53 54androidx { 55 name = "MyLibrary lint checks" 56 type = LibraryType.LINT 57 mavenGroup = LibraryGroups.MYLIBRARY 58 inceptionYear = "2022" 59 description = "Lint checks for MyLibrary" 60} 61``` 62 63### Issue registry 64 65Your new module will need to have a registry that contains a list of all of the 66checks to be performed on the library. There is an 67[`IssueRegistry`](https://cs.android.com/android/platform/superproject/+/master:tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/IssueRegistry.java;l=47) 68class provided by the tools team. Extend this class into your own 69`IssueRegistry` class, and provide it with the issues in the module. 70 71`MyLibraryIssueRegistry.kt` 72 73```kotlin 74class MyLibraryIssueRegistry : IssueRegistry() { 75 override val minApi = CURRENT_API 76 override val api = 13 77 override val issues get() = listOf(MyLibraryDetector.ISSUE) 78 override val vendor = Vendor( 79 feedbackUrl = "https://issuetracker.google.com/issues/new?component=######", 80 identifier = "androidx.mylibrary", 81 vendorName = "Android Open Source Project", 82 ) 83} 84``` 85 86The maximum version this lint check will will work with is defined by `api = 8713`. Typically, this should track `CURRENT_API`. 88 89`minApi = CURRENT_API` sets the lowest version of the Lint tool that this will 90work with. 91 92`CURRENT_API` is defined by the Lint tool API version against which your project 93is compiled, as defined in the module's `build.gradle` file. Jetpack lint check 94modules should compile using the Lint tool API version referenced in 95[libs.versions.toml](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:gradle/libs.versions.toml;l=8). 96 97We guarantee that our lint checks work with the versions referenced by `minApi` 98and `api` by running our tests with both versions. For newer versions of Android 99Studio (and consequently, the Lint tool) the API variable will need to be 100updated. 101 102The `IssueRegistry` requires a list of all of the issues to check. You must 103override the `IssueRegistry.getIssues()` method. Here, we override that method 104with a Kotlin `get()` property delegate: 105 106[Example `IssueRegistry` Implementation](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:fragment/fragment-lint/src/main/java/androidx/fragment/lint/FragmentIssueRegistry.kt) 107 108There are 4 primary types of lint checks: 109 1101. Code - Applied to source code, ex. `.java` and `.kt` files 1111. XML - Applied to XML resource files 1121. Android Manifest - Applied to `AndroidManifest.xml` 1131. Gradle - Applied to Gradle configuration files, ex. `build.gradle` 114 115It is also possible to apply lint checks to compiled bytecode (`.class` files) 116or binary resource files like images, but these are less common. 117 118## PSI & UAST mapping 119 120To view the PSI structure of any file in Android Studio, use the 121[PSI Viewer](https://www.jetbrains.com/help/idea/psi-viewer.html) located in 122`Tools > View PSI Structure`. 123 124Note: The PSI Viewer requires enabling internal mode. Follow the directions 125[here](https://plugins.jetbrains.com/docs/intellij/enabling-internal.html) to 126add `idea.is.internal=true` to `idea.properties.` 127 128<table> 129 <tr> 130 <td><strong>PSI</strong> 131 </td> 132 <td><strong>UAST</strong> 133 </td> 134 </tr> 135 <tr> 136 <td>PsiAnnotation 137 </td> 138 <td>UAnnotation 139 </td> 140 </tr> 141 <tr> 142 <td>PsiAnonymousClass 143 </td> 144 <td>UAnonymousClass 145 </td> 146 </tr> 147 <tr> 148 <td>PsiArrayAccessExpression 149 </td> 150 <td>UArrayAccessExpression 151 </td> 152 </tr> 153 <tr> 154 <td>PsiBinaryExpression 155 </td> 156 <td>UArrayAccesExpression 157 </td> 158 </tr> 159 <tr> 160 <td>PsiCallExpression 161 </td> 162 <td>UCallExpression 163 </td> 164 </tr> 165 <tr> 166 <td>PsiCatchSection 167 </td> 168 <td>UCatchClause 169 </td> 170 </tr> 171 <tr> 172 <td>PsiClass 173 </td> 174 <td>UClass 175 </td> 176 </tr> 177 <tr> 178 <td>PsiClassObjectAccessExpression 179 </td> 180 <td>UClassLiteralExpression 181 </td> 182 </tr> 183 <tr> 184 <td>PsiConditionalExpression 185 </td> 186 <td>UIfExpression 187 </td> 188 </tr> 189 <tr> 190 <td>PsiDeclarationStatement 191 </td> 192 <td>UDeclarationExpression 193 </td> 194 </tr> 195 <tr> 196 <td>PsiDoWhileStatement 197 </td> 198 <td>UDoWhileExpression 199 </td> 200 </tr> 201 <tr> 202 <td>PsiElement 203 </td> 204 <td>UElement 205 </td> 206 </tr> 207 <tr> 208 <td>PsiExpression 209 </td> 210 <td>UExpression 211 </td> 212 </tr> 213 <tr> 214 <td>PsiForeachStatement 215 </td> 216 <td>UForEachExpression 217 </td> 218 </tr> 219 <tr> 220 <td>PsiIdentifier 221 </td> 222 <td>USimpleNameReferenceExpression 223 </td> 224 </tr> 225 <tr> 226 <td>PsiLiteral 227 </td> 228 <td>ULiteralExpression 229 </td> 230 </tr> 231 <tr> 232 <td>PsiLocalVariable 233 </td> 234 <td>ULocalVariable 235 </td> 236 </tr> 237 <tr> 238 <td>PsiMethod 239 </td> 240 <td>UMethod 241 </td> 242 </tr> 243 <tr> 244 <td>PsiMethodCallExpression 245 </td> 246 <td>UCallExpression 247 </td> 248 </tr> 249 <tr> 250 <td>PsiParameter 251 </td> 252 <td>UParameter 253 </td> 254 </tr> 255</table> 256 257## Code detector 258 259These are lint checks that will apply to source code files -- primarily Java and 260Kotlin, but can also be used for other similar file types. All code detectors 261that analyze Java or Kotlin files should implement the 262[SourceCodeScanner](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/SourceCodeScanner.kt). 263 264### API surface 265 266#### Calls to specific methods 267 268##### `getApplicableMethodNames` 269 270This defines the list of methods where lint will call the visitMethodCall 271callback. 272 273```kotlin 274override fun getApplicableMethodNames(): List<String>? = listOf(METHOD_NAMES) 275``` 276 277##### `visitMethodCall` 278 279This defines the callback that the Lint tool will call when it encounters a call 280to an applicable method. 281 282```kotlin 283override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {} 284``` 285 286#### Calls to specific class instantiations 287 288##### `getApplicableConstructorTypes` 289 290```kotlin 291override fun getApplicableConstructorTypes(): List<String>? = listOf(CLASS_NAMES) 292``` 293 294##### visitConstructor 295 296```kotlin 297override fun visitConstructor(context: JavaContext, node: UCallExpression, method: PsiMethod) {} 298``` 299 300#### Classes that extend given superclasses 301 302##### `getApplicableSuperClasses` 303 304```kotlin 305override fun applicableSuperClasses(): List<String>? = listOf(CLASS_NAMES) 306``` 307 308##### `visitClass` 309 310```kotlin 311override fun visitClass(context: JavaContext, declaration: UClass) {} 312``` 313 314#### Call graph support 315 316It is possible to perform analysis on the call graph of a project. However, this 317is highly resource intensive since it generates a single call graph of the 318entire project and should only be used for whole project analysis. To perform 319this analysis you must enable call graph support by overriding the 320`isCallGraphRequired` method and access the call graph with the 321`analyzeCallGraph(context: Context, callGraph: CallGraphResult)` callback 322method. 323 324For performing less resource intensive, on-the-fly analysis it is best to 325recursively analyze method bodies. However, when doing this there should be a 326depth limit on the exploration. If possible, lint should also not explore within 327files that are currently not open in studio. 328 329### Method call analysis 330 331#### `resolve()` 332 333Resolves into a `UCallExpression` or `UMethod` to perform analysis requiring the 334method body or containing class. 335 336#### `receiverType` 337 338Each `UCallExpression` has a `receiverType` corresponding to the `PsiType` of 339the receiver of the method call. 340 341```kotlin 342public abstract class LiveData<T> { 343 public void observe() {} 344} 345 346public abstract class MutableLiveData<T> extends LiveData<T> {} 347 348MutableLiveData<String> liveData = new MutableLiveData<>(); 349liveData.observe() // receiverType = PsiType<MutableLiveData> 350``` 351 352#### Kotlin named parameter mapping 353 354`JavaEvaluator`contains a helper method `computeArgumentMapping(call: 355UCallExpression, method: PsiMethod)` that creates a mapping between method call 356parameters and the corresponding resolved method arguments, accounting for 357Kotlin named parameters. 358 359```kotlin 360override fun visitMethodCall(context: JavaContext, node: UCallExpression, 361 method: PsiMethod) { 362 val argMap: Map<UExpression, PsiParameter> = context.evaluator.computArgumentMapping(node, psiMethod) 363} 364``` 365 366### Testing 367 368Because the `LintDetectorTest` API does not have access to library classes and 369methods, you must implement stubs for any necessary classes and include these as 370additional files in your test cases. For example, if a lint check involves 371Fragment's `getViewLifecycleOwner` and `onViewCreated` methods, then we must 372create a stub for this: 373 374``` 375java(""" 376 package androidx.fragment.app; 377 378 import androidx.lifecycle.LifecycleOwner; 379 380 public class Fragment { 381 public LifecycleOwner getViewLifecycleOwner() {} 382 public void onViewCreated() {} 383 } 384""") 385``` 386 387Since this class also depends on the `LifecycleOwner` class it is necessary to 388create another stub for this. 389 390NOTE `package-info.java` cannot be represented by a source stub and must be 391provided as bytecode. See [Updating bytecode](#tips-bytecode) for tips on using 392bytecode in lint tests. 393 394## XML resource detector 395 396These are lint checks that will apply to resource files including `anim`, 397`layout`, `values`, etc. lint checks being applied to resource files should 398extend 399[`ResourceXmlDetector`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ResourceXmlDetector.java). 400The `Detector` must define the issue it is going to detect, most commonly as a 401static variable of the class. 402 403```kotlin 404companion object { 405 val ISSUE = Issue.create( 406 id = "TitleOfMyIssue", 407 briefDescription = "Short description of issue. This will be what the studio inspection menu shows", 408 explanation = """Here is where you define the reason that this lint rule exists in detail.""", 409 category = Category.CORRECTNESS, 410 severity = Severity.LEVEL, 411 implementation = Implementation( 412 MyIssueDetector::class.java, Scope.RESOURCE_FILE_SCOPE 413 ), 414 androidSpecific = true 415 ).addMoreInfo( 416 "https://linkToMoreInfo.com" 417 ) 418} 419``` 420 421### API surface 422 423The following methods can be overridden: 424 425```kotlin 426appliesTo(folderType: ResourceFolderType) 427getApplicableElements() 428visitElement(context: XmlContext, element: Element) 429``` 430 431#### `appliesTo` 432 433This determines the 434[ResourceFolderType](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:layoutlib-api/src/main/java/com/android/resources/ResourceFolderType.java) 435that the check will run against. 436 437```kotlin 438override fun appliesTo(folderType: ResourceFolderType): Boolean { 439 return folderType == ResourceFolderType.TYPE 440} 441``` 442 443#### `getApplicableElements` 444 445This defines the list of elements where the Lint tool will call your 446`visitElement` callback method when encountered. 447 448```kotlin 449override fun getApplicableElements(): Collection<String>? = Collections.singleton(ELEMENT) 450``` 451 452#### `visitElement` 453 454This defines the behavior when an applicable element is found. Here you normally 455place the actions you want to take if a violation of the lint check is found. 456 457```kotlin 458override fun visitElement(context: XmlContext, element: Element) { 459 val fix = LintFix.create() 460 .replace() 461 .text(ELEMENT) 462 .with(REPLACEMENT_TEXT) 463 .build() 464 465 context.report( 466 issue = ISSUE, 467 location = context.getNameLocation(element), 468 message = "My issue message", 469 quickFixData = fix 470 ) 471} 472``` 473 474In this instance, the call to `report()` takes the definition of the issue, the 475location of the element that has the issue, the message to display on the 476element, as well as a quick fix. In this case we replace our element text with 477some other text. 478 479[Example Detector Implementation](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:fragment/fragment-lint/src/main/java/androidx/fragment/lint/FragmentTagDetector.kt) 480 481### Testing 482 483You need tests for two things. First, you must test that the Lint tool API 484version is properly set. That is done with a simple `ApiLintVersionTest` class. 485It asserts the API version code set earlier in the `IssueRegistry()` class. This 486test intentionally fails in the IDE because different Lint tool API versions are 487used in Studio and the command line. 488 489Example `ApiLintVersionTest`: 490 491```kotlin 492class ApiLintVersionsTest { 493 494 @Test 495 fun versionsCheck() { 496 LintClient.clientName = LintClient.CLIENT_UNIT_TESTS 497 val registry = MyLibraryIssueRegistry() 498 assertThat(registry.api).isEqualTo(CURRENT_API) 499 assertThat(registry.minApi).isEqualTo(10) 500 } 501} 502``` 503 504Next, you must test the `Detector` class. The Tools team provides a 505[`LintDetectorTest`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java) 506class that should be extended. Override `getDetector()` to return an instance of 507the `Detector` class: 508 509```kotlin 510override fun getDetector(): Detector = MyLibraryDetector() 511``` 512 513Override `getIssues()` to return the list of Detector Issues: 514 515```kotlin 516getIssues(): MutableList<Issue> = mutableListOf(MyLibraryDetector.ISSUE) 517``` 518 519[`LintDetectorTest`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java) 520provides a `lint()` method that returns a 521[`TestLintTask`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintTask.java). 522`TestLintTask` is a builder class for setting up lint tests. Call the `files()` 523method and provide an `.xml` test file, along with a file stub. After completing 524the set up, call `run()` which returns a 525[`TestLintResult`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/TestLintResult.kt). 526`TestLintResult` provides methods for checking the outcome of the provided 527`TestLintTask`. `ExpectClean()` means the output is expected to be clean because 528the lint check was followed. `Expect()` takes a string literal of the expected 529output of the `TestLintTask` and compares the actual result to the input string. 530If a quick fix was implemented, you can check that the fix is correct by calling 531`checkFix()` and providing the expected output file stub. 532 533[TestExample](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:fragment/fragment-lint/src/test/java/androidx/fragment/lint/FragmentTagDetectorTest.kt) 534 535## Android manifest detector 536 537Lint checks targeting `AndroidManifest.xml` files should implement the 538[XmlScanner](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/XmlScanner.kt) 539and define target scope in issues as `Scope.MANIFEST` 540 541## Gradle detector 542 543Lint checks targeting Gradle configuration files should implement the 544[GradleScanner](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/GradleScanner.kt) 545and define target scope in issues as `Scope.GRADLE_SCOPE` 546 547### API surface 548 549#### `checkDslPropertyAssignment` 550 551Analyzes each DSL property assignment, providing the property and value strings. 552 553```kotlin 554fun checkDslPropertyAssignment( 555 context: GradleContext, 556 property: String, 557 value: String, 558 parent: String, 559 parentParent: String?, 560 propertyCookie: Any, 561 valueCookie: Any, 562 statementCookie: Any 563) {} 564``` 565 566The property, value, and parent string parameters provided by this callback are 567the literal values in the gradle file. Any string values in the Gradle file will 568be quote enclosed in the value parameter. Any constant values cannot be resolved 569to their values. 570 571The cookie parameters should be used for reporting lint check errors. To report 572an issue on the value, use `context.getLocation(statementCookie)`. 573 574## Enabling lint checks for a library 575 576Once the lint module is implemented we need to enable it for the desired 577library. This can be done by adding a `lintPublish` rule to the `build.gradle` 578of the library the lint check should apply to. 579 580``` 581lintPublish(project(':mylibrary:mylibrary-lint')) 582``` 583 584This adds a `lint.jar` file into the `.aar` bundle of the desired library. 585 586Then we should add a `com.android.tools.lint.client.api.IssueRegistry` file in 587`mylibrary > mylibrary-lint > main > resources > META-INF > services`. The file 588should contain a single line that has the `IssueRegistry` class name with the 589full path. This class can contain more than one line if the module contains 590multiple registries. 591 592``` 593androidx.mylibrary.lint.MyLibraryIssueRegistry 594``` 595 596Note that `lintPublish` only publishes the lint module, it doesn't include it 597when running lint on the module that `lintPublish` is attached to. In order to 598also run these lint checks as part of the module that is publishing them, you 599can add `lintChecks` in the same way. 600 601``` 602lintChecks(project(':mylibrary:mylibrary-lint')) 603``` 604 605## Advanced topics 606 607### Analyzing multiple different file types 608 609Sometimes it is necessary to implement multiple different scanners in a lint 610detector. For example, the 611[Unused Resource](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UnusedResourceDetector.java) 612lint check implements an XML and SourceCodeScanner in order to determine if 613resources defined in XML files are ever references in the Java/Kotlin source 614code. 615 616#### File type iteration order 617 618The Lint tool processes files in a predefined order: 619 6201. Manifests 6211. Android XML Resources (alphabetical by folder type) 6221. Java & Kotlin 6231. Bytecode 6241. Gradle 625 626### Multi-pass analysis 627 628It is often necessary to process the sources more than once. This can be done by 629using `context.driver.requestRepeat(detector, scope)`. 630 631### Debugging custom lint checks 632 633Using Android Studio, there are a few ways to debug custom lint checks: 634 635#### Debug against lint running from the command line 636 6371. Set breakpoint(s) in the desired lint detector sources 6381. Click the `Gradle` icon on the right menu bar 6391. Run the `lintDebug` Gradle task and then hit the `Stop` icon in the top menu 640 bar. This creates a Run configuration. 6411. Click the `Debug` icon in the top menu bar for the newly-selected Run 642 configuration 6431. Breakpoint will get hit 644 645#### Debug against a single lint check test 646 6471. Set breakpoint(s) in the desired lint detector sources 6481. Open a lint check test, such as 649 [`AnnotationRetentionDetectorTest`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:annotation/annotation-experimental-lint/src/test/kotlin/androidx/annotation/experimental/lint/AnnotationRetentionDetectorTest.kt) 6501. Right-click on a test method and select `Debug` 6511. Breakpoint will get hit 652 653#### Debug against lint running inside Android Studio 654 655The UAST environment can be different when a lint check is running on the fly 656inside Android Studio, instead of the command line (for example b/191508358). To 657debug issues with a lint check that only occur inside the IDE, you can debug the 658lint check when it runs inside Studio. 659 6601. Set breakpoint(s) in the desired lint detector sources (make sure that the 661 sources you have match the sources being used in the library version you 662 want to test against) 6632. Download a separate Studio instance (such as latest canary), and open it. 6643. With the new Studio instance, go to Help -> Edit Custom VM Options - this 665 will open up studio.vmoptions. Add the following lines: 666 667``` 668-Xdebug 669-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=y 670``` 671 6721. Then close Android Studio. This will allow attaching a debugger next time 673 this Studio instance is opened. 6742. In the original (AndroidX) Studio instance, create a 675 [remote JVM debug configuration](https://www.jetbrains.com/help/idea/tutorial-remote-debug.html#create-run-configurations). 676 The default configuration should be correct, double check that the port 677 specified is the same as the address you specified in studio.vmoptions in 678 the previous step. 6793. Launch the separate Studio instance: it should wait for a debugger to be 680 attached 6814. In the AndroidX Studio instance, press debug with the remote configuration - 682 this should attach the debugger, and the separate Studio instance should 683 continue to open normally 6845. In the separate Studio instance, open / create a sample project that uses 685 the library with the lint check you want to debug, and add some code that 686 should trigger / not trigger an error accordingly. 6876. Breakpoint will get hit 688 689## Helpful tips {#tips} 690 691### Useful classes/packages 692 693[`SdkConstants`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:common/src/main/java/com/android/SdkConstants.java) - 694contains most of the canonical names for Android core library classes, as well 695as XML tag names. 696 697### Updating bytecode and checksum in tests {#tips-bytecode} 698 699When updating a file that is used in a lint test, the following error may appear 700when running tests: 701 702``` 703The checksum does not match for java/androidx/sample/deprecated/DeprecatedKotlinClass.kt; 704expected 0x1af1856 but was 0x6692f601. 705Has the source file been changed without updating the binaries? 706Don't just update the checksum -- delete the binary file arguments and re-run the test first! 707java.lang.AssertionError: The checksum does not match for java/androidx/sample/deprecated/DeprecatedKotlinClass.kt; 708expected 0x1af1856 but was 0x6692f601. 709Has the source file been changed without updating the binaries? 710Don't just update the checksum -- delete the binary file arguments and re-run the test first! 711 at org.junit.Assert.fail(Assert.java:89) 712 ... 713``` 714 715To fix this, you will need access to `kotlinc` (Kotlin Compiler) to generate a 716bytecode replacement. While Studio does include `kotlinc`, it is not available 717to the terminal and the permissions can not be changed to allow you to use it. 718That means you need your own `kotlinc` available in the terminal. 719 720For instructions on how install the compiler, please read 721[this Kotlin page](https://kotlinlang.org/docs/command-line.html#install-the-compiler). 722 723Important Note: It's best practice to match the compiler version you install to 724the kotlin version in your project to avoid issues. 725 726Otherwise, below are the steps to generate the bytecode: 727 7281. Remove the arguments in `compiled()`: 729 730 ``` 731 // Before 732 compiled( 733 "libs/ktlib.jar", 734 ktSample("androidx.sample.deprecated.DeprecatedKotlinClass"), 735 0x6692f601, 736 """ 737 META-INF/main.kotlin_module: 738 H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM2gxKDFAABNj30wGAAAAA== 739 """, 740 """ 741 androidx/sample/deprecated/DeprecatedKotlinClass.class: 742 H4sIAAAAAAAAAJVSy27TQBQ9YydxcQNNH5SUZyivlkWSpuxAiFIEighBCiit 743 // rest of bytecode 744 """ 745 ) 746 747 // After 748 compiled( 749 "libs/ktlib.jar", 750 ktSample("androidx.sample.deprecated.DeprecatedKotlinClass"), 751 ) 752 ``` 753 7542. Set `$LINT_TEST_KOTLINC` to the location of `kotlinc` if you haven't 755 already, and add it to the test run configuration's environment variables. 756 757 Note: The location of `kotlinc` can vary; use your system's file finder to 758 determine the exact location. For gLinux, search under 759 `~/.local/share/JetBrains`. For Mac, search under `<your androidx checkout 760 root>/frameworks/support/studio` 761 762 If it's not set (or set incorrectly), this error message appears when 763 running tests: 764 765 ``` 766 Couldn't find kotlinc to update test file java/androidx/sample/deprecated/DeprecatedKotlinClass.kt with. 767 Point to it with $LINT_TEST_KOTLINC 768 ``` 769 7703. Run the test, which will output the new bytecode and checksum: 771 772 ``` 773 Update the test source declaration for java/androidx/sample/deprecated/DeprecatedKotlinClass.kt with this list of encodings: 774 775 Kotlin: 776 compiled( 777 "libs/ktlib.jar", 778 kotlin( 779 """ 780 package java.androidx.sample.deprecated 781 782 @Deprecated( 783 // (rest of inlined sample file) 784 """ 785 ).indented(), 786 0x5ba03e2d, 787 """ 788 META-INF/main.kotlin_module: 789 H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM2gxKDFAABNj30wGAAAAA== 790 // rest of bytecode 791 """, 792 """ 793 java/androidx/sample/deprecated/DeprecatedKotlinClass.class: 794 """ 795 ) 796 ``` 797 798Note: the generated replacement code will inline the specified sample file (in 799our case, `ktSample("androidx.sample.deprecated.DeprecatedKotlinClass")`). 800Replace the inlined code with the sample declaration. 801 802### Lint checks with WARNING severity (my lint check won't run!) {#tips-warnings} 803 804In AndroidX lint checks with a severity of `WARNING` are ignored by default to 805prevent noise from bundled lint checks. If your lint check has this severity, 806and you want it to run inside AndroidX, you'll need to override the severity: in 807Compose for example this happens in 808[AndroidXComposeLintIssues](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeLintIssues.kt). 809 810## Helpful links 811 812[Writing Custom Lint Rules](https://googlesamples.github.io/android-custom-lint-rules/) 813 814[Studio Lint Rules](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/) 815 816[Lint Detectors and Scanners Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/) 817 818[Creating Custom Link Checks (external)](https://twitter.com/alexjlockwood/status/1176675045281693696) 819 820[Android Custom Lint Rules by Tor](https://github.com/googlesamples/android-custom-lint-rules) 821 822[Public lint-dev Google Group](https://groups.google.com/forum/#!forum/lint-dev) 823 824[In-depth Lint Video Presentation by Tor](https://www.youtube.com/watch?v=p8yX5-lPS6o) 825(partially out-dated) 826([Slides](https://resources.jetbrains.com/storage/products/kotlinconf2017/slides/KotlinConf+Lint+Slides.pdf)) 827 828[ADS 19 Presentation by Alan & Rahul](https://www.youtube.com/watch?v=jCmJWOkjbM0) 829 830[META-INF vs Manifest](https://groups.google.com/forum/#!msg/lint-dev/z3NYazgEIFQ/hbXDMYp5AwAJ) 831