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