blob: 649eca3fa41eefb9dad0448eb7a31cf4a2c90bbc [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
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
12### Create a module
13
14If this is the first Lint rule for a library, you will need to create a module
15by doing the following:
16
17Add a new `ignore` rule to the `PublishDocsRules.kt` file to prevent the module
18from showing up in published docs:
19
20```
21ignore(LibraryGroups.MyLibrary.group, "mylibrary-lint")
22```
23
24Include the project in the top-level `settings.gradle` file so that it shows up
25in Android Studio's list of modules:
26
27```
28includeProject(":mylibrary:mylibrary-lint", "mylibrary/mylibrary-lint")
29```
30
31Manually create a new module in `frameworks/support` (preferably in the
32directory you are making lint rules for). In the new module, add a `src` folder
33and a `build.gradle` file containing the needed dependencies.
34
35build.gradle
36
37```
38import static androidx.build.dependencies.DependenciesKt.*
39import androidx.build.AndroidXExtension
40import androidx.build.CompilationTarget
41import androidx.build.LibraryGroups
42import androidx.build.LibraryVersions
43import androidx.build.SdkHelperKt
44import androidx.build.Publish
45
46plugins {
47 id("AndroidXPlugin")
48 id("kotlin")
49}
50
51dependencies {
AndroidX Core Team2e416b22020-12-03 22:58:07 +000052 // compileOnly because lint runtime is provided when checks are run
53 // Use latest lint for running from IDE to make sure checks always run
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -070054 if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
55 compileOnly LINT_API_LATEST
56 } else {
57 compileOnly LINT_API_MIN
58 }
59 compileOnly KOTLIN_STDLIB
60
61 testImplementation KOTLIN_STDLIB
62 testImplementation LINT_CORE
63 testImplementation LINT_TESTS
64}
65
66androidx {
67 name = "Android MyLibrary Lint Checks"
68 toolingProject = true
69 publish = Publish.NONE
70 mavenVersion = LibraryVersions.MYLIBRARY
71 mavenGroup = LibraryGroups.MYLIBRARY
72 inceptionYear = "2019"
73 description = "Android MyLibrary Lint Checks"
74 url = AndroidXExtension.ARCHITECTURE_URL
75 compilationTarget = CompilationTarget.HOST
76}
77```
78
79Build the project and a `mylibrary-lint.iml` file should be created
80automatically in the module directory.
81
82### Issue registry
83
84Your new module will need to have a registry that contains a list of all of the
85checks to be performed on the library. There is an
AndroidX Core Team2e416b22020-12-03 22:58:07 +000086[`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 -070087class provided by the tools team. Extend this class into your own
88`IssueRegistry` class, and provide it with the issues in the module.
89
90MyLibraryIssueRegistry.kt
91
92```kotlin
93class MyLibraryIssueRegistry : IssueRegistry() {
94 override val api = 6
95 override val minApi = CURRENT_API
96 override val issues get() = listOf(MyLibraryDetector.ISSUE)
97}
98```
99
100The maximum version this Lint check will will work with is defined by `api = 6`,
101where versions 0-6 correspond to Lint/Studio versions 3.0-3.6.
102
103`minApi = CURRENT_API` sets the lowest version of Lint that this will work with.
104
105`CURRENT_API` is defined by the Lint API version against which your project is
106compiled, as defined in the module's `build.gradle` file. Jetpack Lint modules
107should compile using Lint API version 3.3 defined in
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000108[Dependencies.kt](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt;l=176).
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700109
110We guarantee that our Lint checks work with versions 3.3-3.6 by running our
111tests with both versions 3.3 and 3.6. For newer versions of Android Studio (and
112consequently, Lint) the API variable will need to be updated.
113
114The `IssueRegistry` requires a list of all of the issues to check. You must
115override the `IssueRegistry.getIssues()` method. Here, we override that method
116with a Kotlin `get()` property delegate:
117
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000118[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 -0700119
120There are 4 primary types of Lint checks:
121
1221. Code - Applied to source code, ex. `.java` and `.kt` files
1231. XML - Applied to XML resource files
1241. Android Manifest - Applied to `AndroidManifest.xml`
1251. Gradle - Applied to Gradle configuration files, ex. `build.gradle`
126
127It is also possible to apply Lint checks to compiled bytecode (`.class` files)
128or binary resource files like images, but these are less common.
129
130## PSI & UAST mapping
131
132To view the PSI structure of any file in Android Studio, use the
133[PSI Viewer](https://www.jetbrains.com/help/idea/psi-viewer.html) located in
134`Tools > View PSI Structure`. The PSI Viewer should be enabled by default on the
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000135Android Studio configuration loaded by `studiow` in `androidx-main`. If it is
136not available under `Tools`, you must enable it by adding the line
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700137`idea.is.internal=true` to `idea.properties.`
138
139<table>
140 <tr>
141 <td><strong>PSI</strong>
142 </td>
143 <td><strong>UAST</strong>
144 </td>
145 </tr>
146 <tr>
147 <td>PsiAnnotation
148 </td>
149 <td>UAnnotation
150 </td>
151 </tr>
152 <tr>
153 <td>PsiAnonymousClass
154 </td>
155 <td>UAnonymousClass
156 </td>
157 </tr>
158 <tr>
159 <td>PsiArrayAccessExpression
160 </td>
161 <td>UArrayAccessExpression
162 </td>
163 </tr>
164 <tr>
165 <td>PsiBinaryExpression
166 </td>
167 <td>UArrayAccesExpression
168 </td>
169 </tr>
170 <tr>
171 <td>PsiCallExpression
172 </td>
173 <td>UCallExpression
174 </td>
175 </tr>
176 <tr>
177 <td>PsiCatchSection
178 </td>
179 <td>UCatchClause
180 </td>
181 </tr>
182 <tr>
183 <td>PsiClass
184 </td>
185 <td>UClass
186 </td>
187 </tr>
188 <tr>
189 <td>PsiClassObjectAccessExpression
190 </td>
191 <td>UClassLiteralExpression
192 </td>
193 </tr>
194 <tr>
195 <td>PsiConditionalExpression
196 </td>
197 <td>UIfExpression
198 </td>
199 </tr>
200 <tr>
201 <td>PsiDeclarationStatement
202 </td>
203 <td>UDeclarationExpression
204 </td>
205 </tr>
206 <tr>
207 <td>PsiDoWhileStatement
208 </td>
209 <td>UDoWhileExpression
210 </td>
211 </tr>
212 <tr>
213 <td>PsiElement
214 </td>
215 <td>UElement
216 </td>
217 </tr>
218 <tr>
219 <td>PsiExpression
220 </td>
221 <td>UExpression
222 </td>
223 </tr>
224 <tr>
225 <td>PsiForeachStatement
226 </td>
227 <td>UForEachExpression
228 </td>
229 </tr>
230 <tr>
231 <td>PsiIdentifier
232 </td>
233 <td>USimpleNameReferenceExpression
234 </td>
235 </tr>
236 <tr>
237 <td>PsiLiteral
238 </td>
239 <td>ULiteralExpression
240 </td>
241 </tr>
242 <tr>
243 <td>PsiLocalVariable
244 </td>
245 <td>ULocalVariable
246 </td>
247 </tr>
248 <tr>
249 <td>PsiMethod
250 </td>
251 <td>UMethod
252 </td>
253 </tr>
254 <tr>
255 <td>PsiMethodCallExpression
256 </td>
257 <td>UCallExpression
258 </td>
259 </tr>
260 <tr>
261 <td>PsiParameter
262 </td>
263 <td>UParameter
264 </td>
265 </tr>
266</table>
267
268## Code detector
269
270These are Lint checks that will apply to source code files -- primarily Java and
271Kotlin, but can also be used for other similar file types. All code detectors
272that analyze Java or Kotlin files should implement the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000273[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 -0700274
275### API surface
276
277#### Calls to specific methods
278
279##### getApplicableMethodNames
280
281This defines the list of methods where lint will call the visitMethodCall
282callback.
283
284```kotlin
285override fun getApplicableMethodNames(): List<String>? = listOf(METHOD_NAMES)
286```
287
288##### visitMethodCall
289
290This defines the callback that Lint will call when it encounters a call to an
291applicable method.
292
293```kotlin
294override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {}
295```
296
297#### Calls to specific class instantiations
298
299##### getApplicableConstructorTypes
300
301```kotlin
302override fun getApplicableConstructorTypes(): List<String>? = listOf(CLASS_NAMES)
303```
304
305##### visitConstructor
306
307```kotlin
308override fun visitConstructor(context: JavaContext, node: UCallExpression, method: PsiMethod) {}
309```
310
311#### Classes that extend given superclasses
312
313##### getApplicableSuperClasses
314
315```kotlin
316override fun applicableSuperClasses(): List<String>? = listOf(CLASS_NAMES)
317```
318
319##### visitClass
320
321```kotlin
322override fun visitClass(context: JavaContext, declaration: UClass) {}
323```
324
325#### Call graph support
326
327It is possible to perform analysis on the call graph of a project. However, this
328is highly resource intensive since it generates a single call graph of the
329entire project and should only be used for whole project analysis. To perform
330this analysis you must enable call graph support by overriding the
331`isCallGraphRequired` method and access the call graph with the
332`analyzeCallGraph(context: Context, callGraph: CallGraphResult)` callback
333method.
334
335For performing less resource intensive, on-the-fly analysis it is best to
336recursively analyze method bodies. However, when doing this there should be a
337depth limit on the exploration. If possible, lint should also not explore within
338files that are currently not open in studio.
339
340### Method call analysis
341
342#### resolve()
343
344Resolves into a `UCallExpression` or `UMethod` to perform analysis requiring the
345method body or containing class.
346
347#### ReceiverType
348
349Each `UCallExpression` has a `receiverType` corresponding to the `PsiType` of
350the receiver of the method call.
351
352```kotlin
353public abstract class LiveData<T> {
354 public void observe() {}
355}
356
357public abstract class MutableLiveData<T> extends LiveData<T> {}
358
359MutableLiveData<String> liveData = new MutableLiveData<>();
360liveData.observe() // receiverType = PsiType<MutableLiveData>
361```
362
363#### Kotlin named parameter mapping
364
365`JavaEvaluator`contains a helper method `computeArgumentMapping(call:
366UCallExpression, method: PsiMethod)` that creates a mapping between method call
367parameters and the corresponding resolved method arguments, accounting for
368Kotlin named parameters.
369
370```kotlin
371override fun visitMethodCall(context: JavaContext, node: UCallExpression,
372 method: PsiMethod) {
373 val argMap: Map<UExpression, PsiParameter> = context.evaluator.computArgumentMapping(node, psiMethod)
374}
375```
376
377### Testing
378
379Because the `LintDetectorTest` API does not have access to library classes and
380methods, you must implement stubs for any necessary classes and include these as
381additional files in your test cases. For example, if a lint check involves
382Fragment's `getViewLifecycleOwner` and `onViewCreated` methods, then we must
383create a stub for this:
384
385```
386java("""
387 package androidx.fragment.app;
388
389 import androidx.lifecycle.LifecycleOwner;
390
391 public class Fragment {
392 public LifecycleOwner getViewLifecycleOwner() {}
393 public void onViewCreated() {}
394 }
395""")
396```
397
398Since this class also depends on the `LifecycleOwner` class it is necessary to
399create another stub for this.
400
401## XML resource detector
402
403These are Lint rules that will apply to resource files including `anim`,
404`layout`, `values`, etc. Lint rules being applied to resource files should
405extend
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000406[`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 -0700407The `Detector` must define the issue it is going to detect, most commonly as a
408static variable of the class.
409
410```kotlin
411companion object {
412 val ISSUE = Issue.create(
413 id = "TitleOfMyIssue",
414 briefDescription = "Short description of issue. This will be what the studio inspection menu shows",
415 explanation = """Here is where you define the reason that this lint rule exists in detail.""",
416 category = Category.CORRECTNESS,
417 severity = Severity.LEVEL,
418 implementation = Implementation(
419 MyIssueDetector::class.java, Scope.RESOURCE_FILE_SCOPE
420 ),
421 androidSpecific = true
422 ).addMoreInfo(
423 "https://linkToMoreInfo.com"
424 )
425}
426```
427
428### API surface
429
430The following methods can be overridden:
431
432```kotlin
433appliesTo(folderType: ResourceFolderType)
434getApplicableElements()
435visitElement(context: XmlContext, element: Element)
436```
437
438#### appliesTo
439
440This determines the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000441[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 -0700442that the check will run against.
443
444```kotlin
445override fun appliesTo(folderType: ResourceFolderType): Boolean {
446 return folderType == ResourceFolderType.TYPE
447}
448```
449
450#### getApplicableElements
451
452This defines the list of elements where Lint will call your visitElement
453callback method when encountered.
454
455```kotlin
456override fun getApplicableElements(): Collection<String>? = Collections.singleton(ELEMENT)
457```
458
459#### visitElement
460
461This defines the behavior when an applicable element is found. Here you normally
462place the actions you want to take if a violation of the Lint check is found.
463
464```kotlin
465override fun visitElement(context: XmlContext, element: Element) {
466 context.report(
467 ISSUE,
468 context.getNameLocation(element),
469 "My issue message",
470 fix().replace()
471 .text(ELEMENT)
472 .with(REPLACEMENT TEXT)
473 .build()
474 )
475}
476```
477
478In this instance, the call to `report()` takes the definition of the issue, the
479location of the element that has the issue, the message to display on the
480element, as well as a quick fix. In this case we replace our element text with
481some other text.
482
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000483[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 -0700484
485### Testing
486
487You need tests for two things. First, you must test that the API Lint version is
488properly set. That is done with a simple `ApiLintVersionTest` class. It asserts
489the api version code set earlier in the `IssueRegistry()` class. This test
490intentionally fails in the IDE because different Lint API versions are used in
491the studio and command line.
492
493Example `ApiLintVersionTest`:
494
495```kotlin
496class ApiLintVersionsTest {
497
498 @Test
499 fun versionsCheck() {
500 val registry = MyLibraryIssueRegistry()
501 assertThat(registry.api).isEqualTo(CURRENT_API)
502 assertThat(registry.minApi).isEqualTo(3)
503 }
504}
505```
506
507Next, you must test the `Detector` class. The Tools team provides a
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000508[`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 -0700509class that should be extended. Override `getDetector()` to return an instance of
510the `Detector` class:
511
512```kotlin
513override fun getDetector(): Detector = MyLibraryDetector()
514```
515
516Override `getIssues()` to return the list of Detector Issues:
517
518```kotlin
519getIssues(): MutableList<Issue> = mutableListOf(MyLibraryDetector.ISSUE)
520```
521
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000522[`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 -0700523provides a `lint()` method that returns a
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000524[`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 -0700525`TestLintTask` is a builder class for setting up lint tests. Call the `files()`
526method and provide an `.xml` test file, along with a file stub. After completing
527the set up, call `run()` which returns a
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000528[`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 -0700529`TestLintResult` provides methods for checking the outcome of the provided
530`TestLintTask`. `ExpectClean()` means the output is expected to be clean because
531the lint rule was followed. `Expect()` takes a string literal of the expected
532output of the `TestLintTask` and compares the actual result to the input string.
533If a quick fix was implemented, you can check that the fix is correct by calling
534`checkFix()` and providing the expected output file stub.
535
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000536[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 -0700537
538## Android manifest detector
539
540Lint checks targeting `AndroidManifest.xml` files should implement the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000541[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 -0700542and define target scope in issues as `Scope.MANIFEST`
543
544## Gradle detector
545
546Lint checks targeting Gradle configuration files should implement the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000547[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 -0700548and define target scope in issues as `Scope.GRADLE_SCOPE`
549
550### API surface
551
552#### checkDslPropertyAssignment
553
554Analyzes each DSL property assignment, providing the property and value strings.
555
556```kotlin
557fun checkDslPropertyAssignment(
558 context: GradleContext,
559 property: String,
560 value: String,
561 parent: String,
562 parentParent: String?,
563 propertyCookie: Any,
564 valueCookie: Any,
565 statementCookie: Any
566) {}
567```
568
569The property, value, and parent string parameters provided by this callback are
570the literal values in the gradle file. Any string values in the Gradle file will
571be quote enclosed in the value parameter. Any constant values cannot be resolved
572to their values.
573
574The cookie parameters should be used for reporting Lint errors. To report an
575issue on the value, use `context.getLocation(statementCookie)`.
576
577## Enabling Lint for a library
578
579Once the Lint module is implemented we need to enable it for the desired
580library. This can be done by adding a `lintPublish` rule to the `build.gradle`
581of the library the Lint check should apply to.
582
583```
584lintPublish(project(':mylibrary:mylibrary-lint'))
585```
586
587This adds a `lint.jar` file into the `.aar` bundle of the desired library.
588
589Then we should add a `com.android.tools.lint.client.api.IssueRegistry` file in
590`main > resources > META-INF > services`. The file should contain a single line
591that has the `IssueRegistry` class name with the full path. This class can
592contain more than one line if the module contains multiple registries.
593
594```
595androidx.mylibrary.lint.MyLibraryIssueRegistry
596```
597
598## Advanced topics:
599
600### Analyzing multiple different file types
601
602Sometimes it is necessary to implement multiple different scanners in a Lint
603detector. For example, the
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000604[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)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700605Lint check implements an XML and SourceCode Scanner in order to determine if
606resources defined in XML files are ever references in the Java/Kotlin source
607code.
608
609#### File type iteration order
610
611The Lint system processes files in a predefined order:
612
6131. Manifests
6141. Android XML Resources (alphabetical by folder type)
6151. Java & Kotlin
6161. Bytecode
6171. Gradle
618
619### Multi-pass analysis
620
621It is often necessary to process the sources more than once. This can be done by
622using `context.driver.requestRepeat(detector, scope)`.
623
624## Useful classes/packages
625
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000626### [`SdkConstants`](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-master-dev:common/src/main/java/com/android/SdkConstants.java)
Jeremy Woodsfeffecaf2020-10-15 12:08:38 -0700627
628Contains most of the canonical names for android core library classes, as well
629as XML tag names.
630
631## Helpful links
632
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000633[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 -0700634
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000635[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 -0700636
637[Creating Custom Link Checks (external)](https://twitter.com/alexjlockwood/status/1176675045281693696)
638
639[Android Custom Lint Rules by Tor](https://github.com/googlesamples/android-custom-lint-rules)
640
641[Public lint-dev Google Group](https://groups.google.com/forum/#!forum/lint-dev)
642
643[In-depth Lint Video Presentation by Tor](https://www.youtube.com/watch?v=p8yX5-lPS6o)
644(partially out-dated)
645([Slides](https://resources.jetbrains.com/storage/products/kotlinconf2017/slides/KotlinConf+Lint+Slides.pdf))
646
647[ADS 19 Presentation by Alan & Rahul](https://www.youtube.com/watch?v=jCmJWOkjbM0)
648
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000649[META-INF vs Manifest](https://groups.google.com/forum/#!msg/lint-dev/z3NYazgEIFQ/hbXDMYp5AwAJ)