(#) Unexpected URL Domain !!! ERROR: Unexpected URL Domain This is an error. Id : `LintImplUnexpectedDomain` Summary : Unexpected URL Domain Note : **This issue is disabled by default**; use `--enable LintImplUnexpectedDomain` Severity : Error Category : Lint Implementation Issues Platform : JDK Vendor : Android Open Source Project Feedback : https://issuetracker.google.com/issues/new?component=192708 Since : 4.1.0 (October 2020) Affects : Kotlin and Java files Editing : This check runs on the fly in the IDE editor Implementation : [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LintDetectorDetector.kt) Tests : [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LintDetectorDetectorTest.kt) This checks flags URLs to domains that have not been explicitly allowed for use as a documentation source. (##) Example Here is an example of lint warnings produced by this check: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~text src/test/pkg/MyJavaLintDetector.java:36:Error: Unexpected URL host my.personal.blogger.com; for the builtin Android Lint checks make sure to use an authoritative link (http://my.personal.blogger.com/aboutme.htm) [LintImplUnexpectedDomain] .addMoreInfo("http://my.personal.blogger.com/aboutme.htm") ------------------------------------------ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here are the relevant source files: `src/test/pkg/MyJavaLintDetector.java`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~java linenumbers /* Copyright (C) 2020 The Android Open Source Project */ package test.pkg; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiCallExpression; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiField; import com.intellij.psi.PsiMethod; import com.intellij.psi.util.PsiTreeUtil; import com.android.tools.lint.detector.api.Detector; import org.jetbrains.uast.UFile; import org.jetbrains.uast.UMethod; import org.jetbrains.uast.UField; import com.android.tools.lint.detector.api.Category; import com.android.tools.lint.detector.api.Detector; import com.android.tools.lint.detector.api.Implementation; import com.android.tools.lint.detector.api.Issue; import com.android.tools.lint.detector.api.JavaContext; import com.android.tools.lint.detector.api.Scope; import com.android.tools.lint.detector.api.Severity; import org.jetbrains.uast.UCallExpression; import java.util.EnumSet; @SuppressWarnings({"MethodMayBeStatic", "ClassNameDiffersFromFileName", "StatementWithEmptyBody", "deprecation"}) public class MyJavaLintDetector extends Detector { public static final Issue ISSUE = Issue.create( "com.android.namespaced.lint.check.FooDetector", "Wrong use of ", "As described in " + "https://code.google.com/p/android/issues/detail?id=65351 blah blah blah.", Category.A11Y, 3, Severity.WARNING, new Implementation(MyJavaLintDetector.class, EnumSet.of(Scope.RESOURCE_FILE, Scope.JAVA_FILE))) .addMoreInfo("file://explanation.doc") .addMoreInfo("http://my.personal.blogger.com/aboutme.htm") .addMoreInfo("mailto:lint@example.com"); public void testGetBody(PsiMethod method) { method.getBody(); // ERROR - must use UAST } public void testGetBody(UMethod method) { method.getBody(); // ERROR - must use UAST } public void testGetContainingClass(UMethod method, UField field) { method.getContainingClass(); // ERROR - must use UAST field.getContainingClass(); // ERROR - must use UAST } public void testGetContainingClass(PsiMethod method, PsiField field) { method.getContainingClass(); // OK - legitimate uses after resolve field.getContainingClass(); // OK - legitimate uses after resolve } public void testEquals(PsiCallExpression element1, PsiExpression element2) { if (element1.equals(element2)) { } if (element2.equals(element1)) { } if (element1 == element2) { } if (element1 != element2) { } } public void testGetInitializer(PsiField field) { field.getInitializer(); // ERROR - must use UAST } public void testParents(PsiField field, UMethod method) { PsiElement parent = field.getParent(); // OK PsiElement parent = method.getParent(); // ERROR PsiTreeUtil.getParentOfType(field, PsiClass.class); // OK PsiTreeUtil.getParentOfType(method, PsiClass.class); // ERROR } public void testReport(JavaContext context, UCallExpression node) { context.report(ISSUE, node, context.getLocation(node), "Wrong use of LinearLayout."); context.report(ISSUE, node, context.getLocation(node), "First problem. Second problem."); context.report(ISSUE, node, context.getLocation(node), "This is teh typo"); String message = "Welcome to Andriod"; context.report(ISSUE, node, context.getLocation(node), message); context.report(ISSUE, node, context.getLocation(node), "Should you use `x ?: y` instead of ```foo ? 1 : 0``` ?"); } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `src/test/pkg/MyKotlinLintDetector.kt`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers /* Copyright (C) 2020 The Android Open Source Project */ package test.pkg import com.intellij.psi.PsiCallExpression import com.intellij.psi.PsiExpression import com.intellij.psi.PsiField import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Issue import com.android.tools.lint.detector.api.JavaContext import com.android.tools.lint.detector.api.Scope import com.android.tools.lint.detector.api.Severity import org.jetbrains.uast.UCallExpression class MyKotlinLintDetector : Detector() { fun testGetBody(method: PsiMethod) { val body = method.body // ERROR - must use UAST } @Suppress("ReplaceCallWithBinaryOperator","ControlFlowWithEmptyBody") fun testEquals(element1: PsiCallExpression, element2: PsiExpression) { if (element1.equals(element2)) { } if (element2.equals(element1)) { } if (element1 == element2) { } if (element1 === element2) { } if (element1 != element2) { } if (element1 !== element2) { } if (element1 == null) { } // OK if (element1 === null) { } // OK if (element1 != null) { } // OK if (element1 !== null) { } // OK } @Suppress("UsePropertyAccessSyntax") fun testGetInitializer(field: PsiField) { field.getInitializer() // ERROR - must use UAST field.initializer // ERROR - must use UAST } fun testParents(field: PsiField) { val parent = field.parent val method = PsiTreeUtil.getParentOfType(field, PsiMethod::class.java) } fun testReport(context: JavaContext, node: UCallExpression) { context.report(ISSUE, node, context.getLocation(node), """ |Instead you should call foo().bar().baz() here. |""".trimIndent()) } companion object { private val IMPLEMENTATION = Implementation( MyKotlinLintDetector::class.java, Scope.JAVA_FILE_SCOPE ) val ISSUE = Issue.create( id = "badlyCapitalized id", briefDescription = "checks MyLintDetector.", explanation = """ Some description here. Here's a call: foo.bar.baz(args). This line continuation is okay. \ But this one is missing a space.\ Okay? """.trimIndent(), category = Category.INTEROPERABILITY_KOTLIN, moreInfo = "https://code.google.com/p/android/issues/detail?id=65351", // OBSOLETE priority = 4, severity = Severity.WARNING, implementation = IMPLEMENTATION ) .addMoreInfo("https://issuetracker.google.com/issues/3733548") // ERROR - missing digit .addMoreInfo("https://issuetracker.google.com/issues/373354878") // OK - including digit .addMoreInfo("http://issuetracker.google.com/issues/37335487") // ERROR - http instead of https .addMoreInfo("https://b.corp.google.com/issues/139153781") // ERROR - don't point to buganizer with internal link .addMoreInfo("https://goo.gle/policy-storage-help") // OK - regression test for goo.gle } override fun visitAnnotationUsage( context: JavaContext, element: org.jetbrains.uast.UElement.UElement, annotationInfo: com.android.tools.lint.detector.api.AnnotationInfo, usageInfo: com.android.tools.lint.detector.api.AnnotationUsageInfo ) { // Invalid recursion! super.visitAnnotationUsage(context, element, annotationInfo, usageInfo) } fun misc() { System.out.print("Debugging") println("Debugging code") } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `src/test/pkg/MyIssueRegistry.kt`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers package test.pkg import com.android.tools.lint.client.api.IssueRegistry class MyIssueRegistry : IssueRegistry() { override val issues = listOf( MyJavaLintDetector.ISSUE, MyKotlinLintDetector.Companion.ISSUE ) } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `src/test/pkg/MyVendorIssueRegistry.kt`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers package test.pkg import com.android.tools.lint.client.api.IssueRegistry import com.android.tools.lint.client.api.Vendor class MyVendorIssueRegistry : IssueRegistry() { // Supplies a vendor: no warning override var vendor: Vendor? = Vendor("Unit test") override val issues = listOf( MyJavaLintDetector.ISSUE, MyKotlinLintDetector.Companion.ISSUE ) } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `src/test/pkg/InheritingRegistry.kt`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers package test.pkg import com.android.tools.lint.client.api.IssueRegistry import com.android.tools.lint.client.api.Vendor class InheritingRegistry : MyVendorIssueRegistry() { // NO WARNING // This registry doesn't supply a vendor but inherits one; no warning override val issues = listOf( MyJavaLintDetector.ISSUE, MyKotlinLintDetector.Companion.ISSUE ) } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `src/test/pkg/MyKotlinLintDetectorTest.kt`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers // Copyright (C) 2021 The Android Open Source Project package test.pkg import com.android.tools.lint.checks.infrastructure.LintDetectorTest import com.android.tools.lint.detector.api.Detector class MyKotlinLintDetectorTest : LintDetectorTest() { override fun getDetector(): Detector { return MyKotlinLintDetector() } fun testBasic() { val expected = """ src/test/pkg/AlarmTest.java:9: Warning: Value will be forced up to 5000 as of Android 5.1; don't rely on this to be exact [ShortAlarm] alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 50, 10, null); // ERROR ~~ 0 errors, 1 warnings """ lint().files( kotlin( """ fun test() { println("Value=${"$"}") } """ ), java( "src/test/pkg/AlarmTest.java", """ package test.pkg; import android.app.AlarmManager; @SuppressWarnings("ClassNameDiffersFromFileName") public class AlarmTest { public void test(AlarmManager alarmManager) { alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, 60000, null); // OK alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 6000, 70000, null); // OK alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 50, 10, null); // ERROR alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, // ERROR OtherClass.MY_INTERVAL, null); // ERROR } private static class OtherClass { public static final long MY_INTERVAL = 1000L; } } """.trimIndent() ) ).run().expect(expected) } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also visit the [source code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LintDetectorDetectorTest.kt) for the unit tests for this check to see additional scenarios. The above example was automatically extracted from the first unit test found for this lint check, `LintDetectorDetector.testProblems`. To report a problem with this extracted sample, visit https://issuetracker.google.com/issues/new?component=192708. (##) Suppressing You can suppress false positives using one of the following mechanisms: * Using a suppression annotation like this on the enclosing element: ```kt // Kotlin @Suppress("LintImplUnexpectedDomain") fun method() { expect(...) } ``` or ```java // Java @SuppressWarnings("LintImplUnexpectedDomain") void method() { expect(...); } ``` * Using a suppression comment like this on the line above: ```kt //noinspection LintImplUnexpectedDomain problematicStatement() ``` * Using a special `lint.xml` file in the source tree which turns off the check in that folder and any sub folder. A simple file might look like this: ```xml <?xml version="1.0" encoding="UTF-8"?> <lint> <issue id="LintImplUnexpectedDomain" severity="ignore" /> </lint> ``` Instead of `ignore` you can also change the severity here, for example from `error` to `warning`. You can find additional documentation on how to filter issues by path, regular expression and so on [here](https://googlesamples.github.io/android-custom-lint-rules/usage/lintxml.md.html). * In Gradle projects, using the DSL syntax to configure lint. For example, you can use something like ```gradle lintOptions { disable 'LintImplUnexpectedDomain' } ``` In Android projects this should be nested inside an `android { }` block. * For manual invocations of `lint`, using the `--ignore` flag: ``` $ lint --ignore LintImplUnexpectedDomain ...` ``` * Last, but not least, using baselines, as discussed [here](https://googlesamples.github.io/android-custom-lint-rules/usage/baselines.md.html).