blob: 9a0fdb748149cbf16c89fe90d3a44ccc02747eed [file] [log] [blame] [view]
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -07001# Adding custom Lint checks
2
AndroidX Core Team2e416b22020-12-03 22:58:07 +00003[TOC]
4
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -07005## Getting started
6
7Lint is a static analysis tool that checks Android project source files. Lint
AndroidX Core Teamee9c1aa2021-04-06 17:29:05 +00008checks come with Android Studio by default, but custom lint checks can be added
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -07009to specific library modules to help avoid potential bugs and encourage best code
10practices.
11
AndroidX Core Teamee9c1aa2021-04-06 17:29:05 +000012This 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://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:lint/docs/README.md.html).
16
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070017### 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
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070022Include 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
AndroidX Core Teame80aab72021-09-29 08:44:33 -070033`mylibrary/mylibrary-lint/build.gradle`:
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070034
35```
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070036import androidx.build.LibraryGroups
AndroidX Core Teame80aab72021-09-29 08:44:33 -070037import androidx.build.LibraryType
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070038import androidx.build.LibraryVersions
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070039
40plugins {
41 id("AndroidXPlugin")
42 id("kotlin")
43}
44
45dependencies {
AndroidX Core Teame80aab72021-09-29 08:44:33 -070046 compileOnly(libs.androidLintMinApi)
47 compileOnly(libs.kotlinStdlib)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070048
AndroidX Core Teame80aab72021-09-29 08:44:33 -070049 testImplementation(libs.kotlinStdlib)
50 testImplementation(libs.androidLint)
51 testImplementation(libs.androidLintTests)
52 testImplementation(libs.junit)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070053}
54
55androidx {
AndroidX Core Teame80aab72021-09-29 08:44:33 -070056 name = "MyLibrary lint checks"
57 type = LibraryType.LINT
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070058 mavenVersion = LibraryVersions.MYLIBRARY
59 mavenGroup = LibraryGroups.MYLIBRARY
60 inceptionYear = "2019"
AndroidX Core Teame80aab72021-09-29 08:44:33 -070061 description = "Lint checks for MyLibrary"
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070062}
63```
64
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070065### Issue registry
66
67Your new module will need to have a registry that contains a list of all of the
68checks to be performed on the library. There is an
AndroidX Core Team2e416b22020-12-03 22:58:07 +000069[`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)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070070class provided by the tools team. Extend this class into your own
71`IssueRegistry` class, and provide it with the issues in the module.
72
AndroidX Core Teame80aab72021-09-29 08:44:33 -070073`MyLibraryIssueRegistry.kt`
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070074
75```kotlin
76class MyLibraryIssueRegistry : IssueRegistry() {
AndroidX Core Teame80aab72021-09-29 08:44:33 -070077 override val api = 11
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070078 override val minApi = CURRENT_API
79 override val issues get() = listOf(MyLibraryDetector.ISSUE)
80}
81```
82
AndroidX Core Teame80aab72021-09-29 08:44:33 -070083The maximum version this Lint check will will work with is defined by `api =
8411`, where versions `0`-`11` correspond to Lint/Studio versions `3.0`-`3.11`.
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070085
86`minApi = CURRENT_API` sets the lowest version of Lint that this will work with.
87
88`CURRENT_API` is defined by the Lint API version against which your project is
89compiled, as defined in the module's `build.gradle` file. Jetpack Lint modules
AndroidX Core Teame80aab72021-09-29 08:44:33 -070090should compile using the Lint API version referenced in
AndroidX Core Team4cc85fa2021-11-23 15:58:34 +000091[Dependencies.kt](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/public/src/main/kotlin/androidx/build/dependencies/Dependencies.kt;l=176).
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070092
AndroidX Core Teame80aab72021-09-29 08:44:33 -070093We guarantee that our Lint checks work with the versions referenced by `minApi`
94and `api` by running our tests with both versions. For newer versions of Android
95Studio (and consequently, Lint) the API variable will need to be updated.
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070096
97The `IssueRegistry` requires a list of all of the issues to check. You must
98override the `IssueRegistry.getIssues()` method. Here, we override that method
99with a Kotlin `get()` property delegate:
100
AndroidX Core Teame80aab72021-09-29 08:44:33 -0700101[Example `IssueRegistry` Implementation](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:fragment/fragment-lint/src/main/java/androidx/fragment/lint/FragmentIssueRegistry.kt)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700102
103There are 4 primary types of Lint checks:
104
1051. Code - Applied to source code, ex. `.java` and `.kt` files
1061. XML - Applied to XML resource files
1071. Android Manifest - Applied to `AndroidManifest.xml`
1081. Gradle - Applied to Gradle configuration files, ex. `build.gradle`
109
110It is also possible to apply Lint checks to compiled bytecode (`.class` files)
111or binary resource files like images, but these are less common.
112
113## PSI & UAST mapping
114
115To view the PSI structure of any file in Android Studio, use the
116[PSI Viewer](https://www.jetbrains.com/help/idea/psi-viewer.html) located in
117`Tools > View PSI Structure`. The PSI Viewer should be enabled by default on the
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000118Android Studio configuration loaded by `studiow` in `androidx-main`. If it is
119not available under `Tools`, you must enable it by adding the line
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700120`idea.is.internal=true` to `idea.properties.`
121
122<table>
123 <tr>
124 <td><strong>PSI</strong>
125 </td>
126 <td><strong>UAST</strong>
127 </td>
128 </tr>
129 <tr>
130 <td>PsiAnnotation
131 </td>
132 <td>UAnnotation
133 </td>
134 </tr>
135 <tr>
136 <td>PsiAnonymousClass
137 </td>
138 <td>UAnonymousClass
139 </td>
140 </tr>
141 <tr>
142 <td>PsiArrayAccessExpression
143 </td>
144 <td>UArrayAccessExpression
145 </td>
146 </tr>
147 <tr>
148 <td>PsiBinaryExpression
149 </td>
150 <td>UArrayAccesExpression
151 </td>
152 </tr>
153 <tr>
154 <td>PsiCallExpression
155 </td>
156 <td>UCallExpression
157 </td>
158 </tr>
159 <tr>
160 <td>PsiCatchSection
161 </td>
162 <td>UCatchClause
163 </td>
164 </tr>
165 <tr>
166 <td>PsiClass
167 </td>
168 <td>UClass
169 </td>
170 </tr>
171 <tr>
172 <td>PsiClassObjectAccessExpression
173 </td>
174 <td>UClassLiteralExpression
175 </td>
176 </tr>
177 <tr>
178 <td>PsiConditionalExpression
179 </td>
180 <td>UIfExpression
181 </td>
182 </tr>
183 <tr>
184 <td>PsiDeclarationStatement
185 </td>
186 <td>UDeclarationExpression
187 </td>
188 </tr>
189 <tr>
190 <td>PsiDoWhileStatement
191 </td>
192 <td>UDoWhileExpression
193 </td>
194 </tr>
195 <tr>
196 <td>PsiElement
197 </td>
198 <td>UElement
199 </td>
200 </tr>
201 <tr>
202 <td>PsiExpression
203 </td>
204 <td>UExpression
205 </td>
206 </tr>
207 <tr>
208 <td>PsiForeachStatement
209 </td>
210 <td>UForEachExpression
211 </td>
212 </tr>
213 <tr>
214 <td>PsiIdentifier
215 </td>
216 <td>USimpleNameReferenceExpression
217 </td>
218 </tr>
219 <tr>
220 <td>PsiLiteral
221 </td>
222 <td>ULiteralExpression
223 </td>
224 </tr>
225 <tr>
226 <td>PsiLocalVariable
227 </td>
228 <td>ULocalVariable
229 </td>
230 </tr>
231 <tr>
232 <td>PsiMethod
233 </td>
234 <td>UMethod
235 </td>
236 </tr>
237 <tr>
238 <td>PsiMethodCallExpression
239 </td>
240 <td>UCallExpression
241 </td>
242 </tr>
243 <tr>
244 <td>PsiParameter
245 </td>
246 <td>UParameter
247 </td>
248 </tr>
249</table>
250
251## Code detector
252
253These are Lint checks that will apply to source code files -- primarily Java and
254Kotlin, but can also be used for other similar file types. All code detectors
255that analyze Java or Kotlin files should implement the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000256[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).
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700257
258### API surface
259
260#### Calls to specific methods
261
262##### getApplicableMethodNames
263
264This defines the list of methods where lint will call the visitMethodCall
265callback.
266
267```kotlin
268override fun getApplicableMethodNames(): List<String>? = listOf(METHOD_NAMES)
269```
270
271##### visitMethodCall
272
273This defines the callback that Lint will call when it encounters a call to an
274applicable method.
275
276```kotlin
277override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {}
278```
279
280#### Calls to specific class instantiations
281
282##### getApplicableConstructorTypes
283
284```kotlin
285override fun getApplicableConstructorTypes(): List<String>? = listOf(CLASS_NAMES)
286```
287
288##### visitConstructor
289
290```kotlin
291override fun visitConstructor(context: JavaContext, node: UCallExpression, method: PsiMethod) {}
292```
293
294#### Classes that extend given superclasses
295
296##### getApplicableSuperClasses
297
298```kotlin
299override fun applicableSuperClasses(): List<String>? = listOf(CLASS_NAMES)
300```
301
302##### visitClass
303
304```kotlin
305override fun visitClass(context: JavaContext, declaration: UClass) {}
306```
307
308#### Call graph support
309
310It is possible to perform analysis on the call graph of a project. However, this
311is highly resource intensive since it generates a single call graph of the
312entire project and should only be used for whole project analysis. To perform
313this analysis you must enable call graph support by overriding the
314`isCallGraphRequired` method and access the call graph with the
315`analyzeCallGraph(context: Context, callGraph: CallGraphResult)` callback
316method.
317
318For performing less resource intensive, on-the-fly analysis it is best to
319recursively analyze method bodies. However, when doing this there should be a
320depth limit on the exploration. If possible, lint should also not explore within
321files that are currently not open in studio.
322
323### Method call analysis
324
325#### resolve()
326
327Resolves into a `UCallExpression` or `UMethod` to perform analysis requiring the
328method body or containing class.
329
330#### ReceiverType
331
332Each `UCallExpression` has a `receiverType` corresponding to the `PsiType` of
333the receiver of the method call.
334
335```kotlin
336public abstract class LiveData<T> {
337 public void observe() {}
338}
339
340public abstract class MutableLiveData<T> extends LiveData<T> {}
341
342MutableLiveData<String> liveData = new MutableLiveData<>();
343liveData.observe() // receiverType = PsiType<MutableLiveData>
344```
345
346#### Kotlin named parameter mapping
347
348`JavaEvaluator`contains a helper method `computeArgumentMapping(call:
349UCallExpression, method: PsiMethod)` that creates a mapping between method call
350parameters and the corresponding resolved method arguments, accounting for
351Kotlin named parameters.
352
353```kotlin
354override fun visitMethodCall(context: JavaContext, node: UCallExpression,
355 method: PsiMethod) {
356 val argMap: Map<UExpression, PsiParameter> = context.evaluator.computArgumentMapping(node, psiMethod)
357}
358```
359
360### Testing
361
362Because the `LintDetectorTest` API does not have access to library classes and
363methods, you must implement stubs for any necessary classes and include these as
364additional files in your test cases. For example, if a lint check involves
365Fragment's `getViewLifecycleOwner` and `onViewCreated` methods, then we must
366create a stub for this:
367
368```
369java("""
370 package androidx.fragment.app;
371
372 import androidx.lifecycle.LifecycleOwner;
373
374 public class Fragment {
375 public LifecycleOwner getViewLifecycleOwner() {}
376 public void onViewCreated() {}
377 }
378""")
379```
380
381Since this class also depends on the `LifecycleOwner` class it is necessary to
382create another stub for this.
383
384## XML resource detector
385
386These are Lint rules that will apply to resource files including `anim`,
387`layout`, `values`, etc. Lint rules being applied to resource files should
388extend
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000389[`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).
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700390The `Detector` must define the issue it is going to detect, most commonly as a
391static variable of the class.
392
393```kotlin
394companion object {
395 val ISSUE = Issue.create(
396 id = "TitleOfMyIssue",
397 briefDescription = "Short description of issue. This will be what the studio inspection menu shows",
398 explanation = """Here is where you define the reason that this lint rule exists in detail.""",
399 category = Category.CORRECTNESS,
400 severity = Severity.LEVEL,
401 implementation = Implementation(
402 MyIssueDetector::class.java, Scope.RESOURCE_FILE_SCOPE
403 ),
404 androidSpecific = true
405 ).addMoreInfo(
406 "https://linkToMoreInfo.com"
407 )
408}
409```
410
411### API surface
412
413The following methods can be overridden:
414
415```kotlin
416appliesTo(folderType: ResourceFolderType)
417getApplicableElements()
418visitElement(context: XmlContext, element: Element)
419```
420
421#### appliesTo
422
423This determines the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000424[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)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700425that the check will run against.
426
427```kotlin
428override fun appliesTo(folderType: ResourceFolderType): Boolean {
429 return folderType == ResourceFolderType.TYPE
430}
431```
432
433#### getApplicableElements
434
435This defines the list of elements where Lint will call your visitElement
436callback method when encountered.
437
438```kotlin
439override fun getApplicableElements(): Collection<String>? = Collections.singleton(ELEMENT)
440```
441
442#### visitElement
443
444This defines the behavior when an applicable element is found. Here you normally
445place the actions you want to take if a violation of the Lint check is found.
446
447```kotlin
448override fun visitElement(context: XmlContext, element: Element) {
AndroidX Core Teamcf946032022-02-11 15:52:08 -0800449 val lintFix = fix().replace()
450 .text(ELEMENT)
451 .with(REPLACEMENT TEXT)
452 .build()
453
454 val incident = Incident(context)
455 .fix(lintFix)
456 .issue(ISSUE)
457 .location(context.getLocation(node))
458 .message("My issue message")
459 .scope(context.getNameLocation(element))
460
461 context.report(incident)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700462}
463```
464
465In this instance, the call to `report()` takes the definition of the issue, the
466location of the element that has the issue, the message to display on the
467element, as well as a quick fix. In this case we replace our element text with
468some other text.
469
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000470[Example Detector Implementation](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:fragment/fragment-lint/src/main/java/androidx/fragment/lint/FragmentTagDetector.kt)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700471
472### Testing
473
474You need tests for two things. First, you must test that the API Lint version is
475properly set. That is done with a simple `ApiLintVersionTest` class. It asserts
476the api version code set earlier in the `IssueRegistry()` class. This test
477intentionally fails in the IDE because different Lint API versions are used in
478the studio and command line.
479
480Example `ApiLintVersionTest`:
481
482```kotlin
483class ApiLintVersionsTest {
484
485 @Test
486 fun versionsCheck() {
487 val registry = MyLibraryIssueRegistry()
488 assertThat(registry.api).isEqualTo(CURRENT_API)
489 assertThat(registry.minApi).isEqualTo(3)
490 }
491}
492```
493
494Next, you must test the `Detector` class. The Tools team provides a
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000495[`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)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700496class that should be extended. Override `getDetector()` to return an instance of
497the `Detector` class:
498
499```kotlin
500override fun getDetector(): Detector = MyLibraryDetector()
501```
502
503Override `getIssues()` to return the list of Detector Issues:
504
505```kotlin
506getIssues(): MutableList<Issue> = mutableListOf(MyLibraryDetector.ISSUE)
507```
508
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000509[`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)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700510provides a `lint()` method that returns a
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000511[`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).
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700512`TestLintTask` is a builder class for setting up lint tests. Call the `files()`
513method and provide an `.xml` test file, along with a file stub. After completing
514the set up, call `run()` which returns a
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000515[`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).
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700516`TestLintResult` provides methods for checking the outcome of the provided
517`TestLintTask`. `ExpectClean()` means the output is expected to be clean because
518the lint rule was followed. `Expect()` takes a string literal of the expected
519output of the `TestLintTask` and compares the actual result to the input string.
520If a quick fix was implemented, you can check that the fix is correct by calling
521`checkFix()` and providing the expected output file stub.
522
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000523[TestExample](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:fragment/fragment-lint/src/test/java/androidx/fragment/lint/FragmentTagDetectorTest.kt)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700524
525## Android manifest detector
526
527Lint checks targeting `AndroidManifest.xml` files should implement the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000528[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)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700529and define target scope in issues as `Scope.MANIFEST`
530
531## Gradle detector
532
533Lint checks targeting Gradle configuration files should implement the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000534[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)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700535and define target scope in issues as `Scope.GRADLE_SCOPE`
536
537### API surface
538
539#### checkDslPropertyAssignment
540
541Analyzes each DSL property assignment, providing the property and value strings.
542
543```kotlin
544fun checkDslPropertyAssignment(
545 context: GradleContext,
546 property: String,
547 value: String,
548 parent: String,
549 parentParent: String?,
550 propertyCookie: Any,
551 valueCookie: Any,
552 statementCookie: Any
553) {}
554```
555
556The property, value, and parent string parameters provided by this callback are
557the literal values in the gradle file. Any string values in the Gradle file will
558be quote enclosed in the value parameter. Any constant values cannot be resolved
559to their values.
560
561The cookie parameters should be used for reporting Lint errors. To report an
562issue on the value, use `context.getLocation(statementCookie)`.
563
564## Enabling Lint for a library
565
566Once the Lint module is implemented we need to enable it for the desired
567library. This can be done by adding a `lintPublish` rule to the `build.gradle`
568of the library the Lint check should apply to.
569
570```
571lintPublish(project(':mylibrary:mylibrary-lint'))
572```
573
574This adds a `lint.jar` file into the `.aar` bundle of the desired library.
575
576Then we should add a `com.android.tools.lint.client.api.IssueRegistry` file in
577`main > resources > META-INF > services`. The file should contain a single line
578that has the `IssueRegistry` class name with the full path. This class can
579contain more than one line if the module contains multiple registries.
580
581```
582androidx.mylibrary.lint.MyLibraryIssueRegistry
583```
584
585## Advanced topics:
586
587### Analyzing multiple different file types
588
589Sometimes it is necessary to implement multiple different scanners in a Lint
590detector. For example, the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000591[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)
AndroidX Core Teame80aab72021-09-29 08:44:33 -0700592Lint check implements an XML and SourceCodeScanner in order to determine if
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700593resources defined in XML files are ever references in the Java/Kotlin source
594code.
595
596#### File type iteration order
597
598The Lint system processes files in a predefined order:
599
6001. Manifests
6011. Android XML Resources (alphabetical by folder type)
6021. Java & Kotlin
6031. Bytecode
6041. Gradle
605
606### Multi-pass analysis
607
608It is often necessary to process the sources more than once. This can be done by
609using `context.driver.requestRepeat(detector, scope)`.
610
AndroidX Core Teame31e9592021-12-09 11:27:33 -0800611## Helpful tips {#tips}
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700612
AndroidX Core Teame31e9592021-12-09 11:27:33 -0800613### Useful classes/packages
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700614
AndroidX Core Teame31e9592021-12-09 11:27:33 -0800615[`SdkConstants`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:common/src/main/java/com/android/SdkConstants.java) -
616contains most of the canonical names for Android core library classes, as well
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700617as XML tag names.
618
AndroidX Core Teame31e9592021-12-09 11:27:33 -0800619### Updating bytecode and checksum in tests
620
621When updating a file that is used in a lint test, the following error may appear
622when running tests:
623
624```
625The checksum does not match for java/androidx/sample/deprecated/DeprecatedKotlinClass.kt;
626expected 0x1af1856 but was 0x6692f601.
627Has the source file been changed without updating the binaries?
628Don't just update the checksum -- delete the binary file arguments and re-run the test first!
629java.lang.AssertionError: The checksum does not match for java/androidx/sample/deprecated/DeprecatedKotlinClass.kt;
630expected 0x1af1856 but was 0x6692f601.
631Has the source file been changed without updating the binaries?
632Don't just update the checksum -- delete the binary file arguments and re-run the test first!
633 at org.junit.Assert.fail(Assert.java:89)
634 ...
635```
636
637Here are the steps to fix this:
638
6391. Remove the arguments in `compiled()`:
640
641 ```
642 // Before
643 compiled(
644 "libs/ktlib.jar",
645 ktSample("androidx.sample.deprecated.DeprecatedKotlinClass"),
646 0x6692f601,
647 """
648 META-INF/main.kotlin_module:
649 H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM2gxKDFAABNj30wGAAAAA==
650 """,
651 """
652 androidx/sample/deprecated/DeprecatedKotlinClass.class:
653 H4sIAAAAAAAAAJVSy27TQBQ9YydxcQNNH5SUZyivlkWSpuxAiFIEighBCiit
654 // rest of bytecode
655 """
656 )
657
658 // After
659 compiled(
660 "libs/ktlib.jar",
661 ktSample("androidx.sample.deprecated.DeprecatedKotlinClass"),
662 )
663 ```
664
6652. Set `$LINT_TEST_KOTLINC` to the location of `kotlinc` if you haven't
666 already, and add it to the test run configuration's environment variables.
667
668 Note: The location of `kotlinc` can vary; use your system's file finder to
669 determine the exact location. For gLinux, search under
670 `~/.local/share/JetBrains`. For Mac, search under `<your androidx checkout
671 root>/frameworks/support/studio`
672
673 If it's not set (or set incorrectly), this error message appears when
674 running tests:
675
676 ```
677 Couldn't find kotlinc to update test file java/androidx/sample/deprecated/DeprecatedKotlinClass.kt with.
678 Point to it with $LINT_TEST_KOTLINC
679 ```
680
6813. Run the test, which will output the new bytecode and checksum:
682
683 ```
684 Update the test source declaration for java/androidx/sample/deprecated/DeprecatedKotlinClass.kt with this list of encodings:
685
686 Kotlin:
687 compiled(
688 "libs/ktlib.jar",
689 kotlin(
690 """
691 package java.androidx.sample.deprecated
692
693 @Deprecated(
694 // (rest of inlined sample file)
695 """
696 ).indented(),
697 0x5ba03e2d,
698 """
699 META-INF/main.kotlin_module:
700 H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM2gxKDFAABNj30wGAAAAA==
701 // rest of bytecode
702 """,
703 """
704 java/androidx/sample/deprecated/DeprecatedKotlinClass.class:
705 """
706 )
707 ```
708
709Note: the generated replacement code will inline the specified sample file (in
710our case, `ktSample("androidx.sample.deprecated.DeprecatedKotlinClass")`).
711Replace the inlined code with the sample declaration.
712
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700713## Helpful links
714
AndroidX Core Team8a082f92021-07-01 11:46:10 -0700715[Writing Custom Lint Rules](https://googlesamples.github.io/android-custom-lint-rules/)
716
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000717[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/)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700718
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000719[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/)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700720
721[Creating Custom Link Checks (external)](https://twitter.com/alexjlockwood/status/1176675045281693696)
722
723[Android Custom Lint Rules by Tor](https://github.com/googlesamples/android-custom-lint-rules)
724
725[Public lint-dev Google Group](https://groups.google.com/forum/#!forum/lint-dev)
726
727[In-depth Lint Video Presentation by Tor](https://www.youtube.com/watch?v=p8yX5-lPS6o)
728(partially out-dated)
729([Slides](https://resources.jetbrains.com/storage/products/kotlinconf2017/slides/KotlinConf+Lint+Slides.pdf))
730
731[ADS 19 Presentation by Alan & Rahul](https://www.youtube.com/watch?v=jCmJWOkjbM0)
732
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000733[META-INF vs Manifest](https://groups.google.com/forum/#!msg/lint-dev/z3NYazgEIFQ/hbXDMYp5AwAJ)