(#) 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).