Migrate lint and lint-checks to use ktfmt
See go/why-ktfmt
BUG: 319663977
Change-Id: I7cdee01ca13466c9b1f88a93c15ee36829db86a7
diff --git a/gradle.properties b/gradle.properties
index bd63277..40c0532 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -48,7 +48,7 @@
androidx.unpinComposeCompiler=false
# Prefix of projects that are opted-in to use ktfmt
-androidx.ktfmt.optin=:a,:b,:camera,:car,:collection,:concurrent,:constraintlayout,:core,:d,:e,:f,:g,:h,:i,:j,:kruth,:leanback
+androidx.ktfmt.optin=:a,:b,:camera,:car,:collection,:concurrent,:constraintlayout,:core,:d,:e,:f,:g,:h,:i,:j,:kruth,:leanback,:lint
# Disable features we do not use
android.defaults.buildfeatures.aidl=false
diff --git a/lint-checks/integration-tests/src/main/java/androidx/ComposeSample.kt b/lint-checks/integration-tests/src/main/java/androidx/ComposeSample.kt
index ca74815..f3475b7 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/ComposeSample.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/ComposeSample.kt
@@ -22,12 +22,8 @@
lambda()
}
-/**
- * This function uses an unnecessary lambda and should trigger UnnecessaryLambdaCreationDetector
- */
+/** This function uses an unnecessary lambda and should trigger UnnecessaryLambdaCreationDetector */
fun confirmCustomComposeLintChecksRun() {
val lambda = {}
- lambdaFunction {
- lambda()
- }
+ lambdaFunction { lambda() }
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageKotlin.kt
index f2b8070..807505d 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/KeepAnnotationUsageKotlin.kt
@@ -18,5 +18,4 @@
import androidx.annotation.Keep
-@Keep
-class KeepAnnotationUsageKotlin
+@Keep class KeepAnnotationUsageKotlin
diff --git a/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageKotlin.kt
index f205be3..40a92aa 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/ParcelableUsageKotlin.kt
@@ -25,8 +25,7 @@
return 0
}
- override fun writeToParcel(dest: Parcel, flags: Int) {
- }
+ override fun writeToParcel(dest: Parcel, flags: Int) {}
companion object CREATOR : Parcelable.Creator<ParcelableUsageKotlin> {
override fun createFromParcel(parcel: Parcel): ParcelableUsageKotlin {
diff --git a/lint-checks/integration-tests/src/main/java/androidx/RequiresApiKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/RequiresApiKotlin.kt
index c6de7b9..a8cf4d0 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/RequiresApiKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/RequiresApiKotlin.kt
@@ -39,7 +39,7 @@
// @RequiresApi declaration on MyStaticMethod's immediate containing class
internal class RequiresApiKotlinInner19Passes {
- @RequiresApi(19)
+ @RequiresApi(19)
object MyStaticClass {
fun MyStaticMethod(c: Char): Int {
Character.isSurrogate(c)
diff --git a/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt
index 2d1622d..57d0cb0 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt
@@ -21,12 +21,9 @@
import androidx.annotation.RestrictTo
class RestrictToTestsAnnotationUsageKotlin {
- @RestrictTo(RestrictTo.Scope.TESTS)
- fun testMethod() {}
+ @RestrictTo(RestrictTo.Scope.TESTS) fun testMethod() {}
- @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY)
- fun testMethodVarArg() {}
+ @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY) fun testMethodVarArg() {}
- @get:RestrictTo(RestrictTo.Scope.TESTS)
- val testPropertyGet = "test"
+ @get:RestrictTo(RestrictTo.Scope.TESTS) val testPropertyGet = "test"
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageKotlin.kt
index a3ef53c..f134d1b 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/TargetApiUsageKotlin.kt
@@ -22,7 +22,5 @@
@TargetApi(29)
class TargetApiUsageKotlin {
- @TargetApi(30)
- fun someMethod() {
- }
+ @TargetApi(30) fun someMethod() {}
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/VisibleForTestingUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/VisibleForTestingUsageKotlin.kt
index d09fdc81..cb5eed3 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/VisibleForTestingUsageKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/VisibleForTestingUsageKotlin.kt
@@ -23,27 +23,21 @@
@Suppress("RemoveRedundantQualifierName")
class VisibleForTestingUsageKotlin {
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
- fun testMethodPrivate() {}
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) fun testMethodPrivate() {}
@VisibleForTesting(otherwise = VisibleForTesting.Companion.PRIVATE)
fun testMethodCompanionPrivate() {}
- @VisibleForTesting(VisibleForTesting.PRIVATE)
- fun testMethodValuePrivate() {}
+ @VisibleForTesting(VisibleForTesting.PRIVATE) fun testMethodValuePrivate() {}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
fun testMethodPackagePrivate() {}
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
- fun testMethodProtected() {}
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) fun testMethodProtected() {}
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- fun testMethodPackageNone() {}
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun testMethodPackageNone() {}
- @VisibleForTesting
- fun testMethodDefault() {}
+ @VisibleForTesting fun testMethodDefault() {}
- @get:VisibleForTesting(NONE)
- val testPropertyGet = "test"
+ @get:VisibleForTesting(NONE) val testPropertyGet = "test"
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt b/lint-checks/integration-tests/src/main/java/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt
index c0862a6..451efc1 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt
@@ -27,10 +27,10 @@
class OutsideGroupExperimentalAnnotatedClass {
// b/201564937 (comments 3, 5-7) - temporarily commenting out due to import issue
-// @ExperimentalSampleAnnotation
-// fun invalidAnnotatedFunction() {
-// // Nothing to see here.
-// }
+ // @ExperimentalSampleAnnotation
+ // fun invalidAnnotatedFunction() {
+ // // Nothing to see here.
+ // }
@ExperimentalSampleAnnotationJava
fun invalidExperimentalAnnotatedMethod() {
@@ -56,10 +56,14 @@
}
/* ktlint-disable max-line-length */
- @kotlin.OptIn(RequiresOptInSampleAnnotationJava::class, RequiresOptInSampleAnnotationJavaDuplicate::class)
+ @kotlin.OptIn(
+ RequiresOptInSampleAnnotationJava::class,
+ RequiresOptInSampleAnnotationJavaDuplicate::class
+ )
fun invalidMethodWithMultipleOptInsWithoutLineBreaks() {
// Nothing to see here.
}
+
/* ktlint-enable max-line-length */
@androidx.annotation.OptIn(RequiresAndroidXOptInSampleAnnotationJava::class)
@@ -76,7 +80,10 @@
}
/* ktlint-disable max-line-length */
- @androidx.annotation.OptIn(RequiresAndroidXOptInSampleAnnotationJava::class, RequiresAndroidXOptInSampleAnnotationJavaDuplicate::class)
+ @androidx.annotation.OptIn(
+ RequiresAndroidXOptInSampleAnnotationJava::class,
+ RequiresAndroidXOptInSampleAnnotationJavaDuplicate::class
+ )
fun invalidMethodWithMultipleAndroidXOptInsWithoutLineBreaks() {
// Nothing to see here.
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKt.kt b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKt.kt
index 9fe0848..82e0878c 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKt.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKt.kt
@@ -35,35 +35,28 @@
/**
* The goal here is to get common (and correct) behavior around Activity recreation for all API
* versions up until P, where the behavior was specified to be useful and implemented to match the
- * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's
- * not in the foreground; it will be recreated when the user next interacts with it. This has a few
+ * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's not in
+ * the foreground; it will be recreated when the user next interacts with it. This has a few
* undesirable consequences:
- *
- *
* 1. It's impossible to recreate multiple activities at once, which means that activities in the
- * background will observe the new configuration before they're recreated. If we keep them on the
- * old configuration, we have two conflicting configurations active in the app, which leads to
- * logging skew.
- *
- *
+ * background will observe the new configuration before they're recreated. If we keep them on the
+ * old configuration, we have two conflicting configurations active in the app, which leads to
+ * logging skew.
* 2. Recreation occurs in the critical path of user interaction - re-inflating a bunch of views
- * isn't free, and we'd rather do it when we're in the background than when the user is staring at
- * the screen waiting to see us.
+ * isn't free, and we'd rather do it when we're in the background than when the user is staring
+ * at the screen waiting to see us.
*
- *
- * On API < 26, recreate() was implemented with a single call to a private method on
- * ActivityThread. That method still exists in 26 and 27, so we can use reflection to call it and
- * get the exact same behavior as < 26. However, that behavior has problems itself. When
- * an Activity in the background is recreated, it goes through: destroy -> create -> start ->
- * resume -> pause and doesn't stop. This is a violation of the contract for onStart/onStop,
- * but that might be palatable if it didn't also have the effect of preventing new configurations
- * from being applied - since the Activity doesn't go through onStop, our tracking of whether
- * our app is visible thinks we're always visible, and thus can't do another recreation later.
- *
+ * On API < 26, recreate() was implemented with a single call to a private method on ActivityThread.
+ * That method still exists in 26 and 27, so we can use reflection to call it and get the exact same
+ * behavior as < 26. However, that behavior has problems itself. When an Activity in the background
+ * is recreated, it goes through: destroy -> create -> start -> resume -> pause and doesn't stop.
+ * This is a violation of the contract for onStart/onStop, but that might be palatable if it didn't
+ * also have the effect of preventing new configurations from being applied - since the Activity
+ * doesn't go through onStop, our tracking of whether our app is visible thinks we're always
+ * visible, and thus can't do another recreation later.
*
* The fix for this is to add the missing onStop() call, by using reflection to call into
* ActivityThread.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal object ActivityRecreatorKt {
@@ -108,27 +101,31 @@
return false
}
return try {
- val token = tokenField!!.get(activity)
- ?: return false
- val activityThread = mainThreadField!!.get(activity)
- ?: return false
+ val token = tokenField!!.get(activity) ?: return false
+ val activityThread = mainThreadField!!.get(activity) ?: return false
val application = activity.application
val callbacks = LifecycleCheckCallbacks(activity)
application.registerActivityLifecycleCallbacks(callbacks)
/*
- * Runnables scheduled before/after recreate() will run before and after the Runnables
- * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
- * events that could be caused by recreate() run - that way we can detect onPause()
- * from the new Activity instance, and schedule onStop to run immediately after it.
- */mainHandler.post {
- callbacks.currentlyRecreatingToken = token
- }
+ * Runnables scheduled before/after recreate() will run before and after the Runnables
+ * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
+ * events that could be caused by recreate() run - that way we can detect onPause()
+ * from the new Activity instance, and schedule onStop to run immediately after it.
+ */ mainHandler.post { callbacks.currentlyRecreatingToken = token }
try {
if (needsRelaunchCall()) {
requestRelaunchActivityMethod!!.invoke(
activityThread,
- token, null, null, 0, false, null, null, false, false
+ token,
+ null,
+ null,
+ 0,
+ false,
+ null,
+ null,
+ false,
+ false
)
} else {
activity.recreate()
@@ -148,9 +145,7 @@
}
}
- /**
- * Returns true if a stop call was scheduled successfully
- */
+ /** Returns true if a stop call was scheduled successfully */
internal fun queueOnStopIfNecessary(
currentlyRecreatingToken: Any?,
currentlyRecreatingHashCode: Int,
@@ -158,8 +153,9 @@
): Boolean {
return try {
val token = tokenField!![activity]
- if (token !== currentlyRecreatingToken ||
- activity.hashCode() != currentlyRecreatingHashCode
+ if (
+ token !== currentlyRecreatingToken ||
+ activity.hashCode() != currentlyRecreatingHashCode
) {
// We're looking at a different activity, don't try to make it stop! Note that
// tokens are reused on SDK 21-23 but Activity objects (and thus hashCode, in
@@ -175,30 +171,26 @@
if (performStopActivity3ParamsMethod != null) {
performStopActivity3ParamsMethod!!.invoke(
activityThread,
- token, false, "AppCompat recreation"
+ token,
+ false,
+ "AppCompat recreation"
)
} else {
- performStopActivity2ParamsMethod!!.invoke(
- activityThread,
- token, false
- )
+ performStopActivity2ParamsMethod!!.invoke(activityThread, token, false)
}
} catch (e: RuntimeException) {
// If an Activity throws from onStop, don't swallow it
- if ((e.javaClass == RuntimeException::class.java) &&
- (e.message != null) &&
- e.message!!.startsWith("Unable to stop")
+ if (
+ (e.javaClass == RuntimeException::class.java) &&
+ (e.message != null) &&
+ e.message!!.startsWith("Unable to stop")
) {
throw e
}
// Otherwise just swallow it - we're calling random private methods,
// there's no guarantee on how they'll behave.
} catch (t: Throwable) {
- Log.e(
- LOG_TAG,
- "Exception while invoking performStopActivity",
- t
- )
+ Log.e(LOG_TAG, "Exception while invoking performStopActivity", t)
}
}
true
@@ -213,10 +205,13 @@
return null
}
return try {
- val performStop = activityThreadClass.getDeclaredMethod(
- "performStopActivity",
- IBinder::class.java, Boolean::class.javaPrimitiveType, String::class.java
- )
+ val performStop =
+ activityThreadClass.getDeclaredMethod(
+ "performStopActivity",
+ IBinder::class.java,
+ Boolean::class.javaPrimitiveType,
+ String::class.java
+ )
performStop.isAccessible = true
performStop
} catch (t: Throwable) {
@@ -229,10 +224,12 @@
return null
}
return try {
- val performStop = activityThreadClass.getDeclaredMethod(
- "performStopActivity",
- IBinder::class.java, Boolean::class.javaPrimitiveType
- )
+ val performStop =
+ activityThreadClass.getDeclaredMethod(
+ "performStopActivity",
+ IBinder::class.java,
+ Boolean::class.javaPrimitiveType
+ )
performStop.isAccessible = true
performStop
} catch (t: Throwable) {
@@ -249,18 +246,19 @@
return null
}
return try {
- val relaunch = activityThreadClass.getDeclaredMethod(
- "requestRelaunchActivity",
- IBinder::class.java,
- MutableList::class.java,
- MutableList::class.java,
- Int::class.javaPrimitiveType,
- Boolean::class.javaPrimitiveType,
- Configuration::class.java,
- Configuration::class.java,
- Boolean::class.javaPrimitiveType,
- Boolean::class.javaPrimitiveType
- )
+ val relaunch =
+ activityThreadClass.getDeclaredMethod(
+ "requestRelaunchActivity",
+ IBinder::class.java,
+ MutableList::class.java,
+ MutableList::class.java,
+ Int::class.javaPrimitiveType,
+ Boolean::class.javaPrimitiveType,
+ Configuration::class.java,
+ Configuration::class.java,
+ Boolean::class.javaPrimitiveType,
+ Boolean::class.javaPrimitiveType
+ )
relaunch.isAccessible = true
relaunch
} catch (t: Throwable) {
@@ -289,11 +287,12 @@
}
private val activityThreadClass: Class<*>?
- get() = try {
- Class.forName("android.app.ActivityThread")
- } catch (t: Throwable) {
- null
- }
+ get() =
+ try {
+ Class.forName("android.app.ActivityThread")
+ } catch (t: Throwable) {
+ null
+ }
private class LifecycleCheckCallbacks internal constructor(aboutToRecreate: Activity) :
ActivityLifecycleCallbacks {
@@ -312,7 +311,9 @@
// Whether we'll force the activity on which recreate() was called to go through an
// onStop()
private var mStopQueued = false
+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
+
override fun onActivityStarted(activity: Activity) {
// If we see a start call on the original mActivity instance, then the mActivity
// starting event executed between our call to recreate() and the actual
@@ -323,11 +324,14 @@
}
override fun onActivityResumed(activity: Activity) {}
+
override fun onActivityPaused(activity: Activity) {
- if (mDestroyed && // Original mActivity must be gone
- !mStopQueued && // Don't schedule stop twice for one recreate() call
- !mStarted && // Don't schedule stop if the original instance starting raced with...
- queueOnStopIfNecessary(currentlyRecreatingToken, mRecreatingHashCode, activity)
+ if (
+ mDestroyed && // Original mActivity must be gone
+ !mStopQueued && // Don't schedule stop twice for one recreate() call
+ !mStarted && // Don't schedule stop if the original instance starting raced
+ // with...
+ queueOnStopIfNecessary(currentlyRecreatingToken, mRecreatingHashCode, activity)
) {
mStopQueued = true
// Don't retain this object longer than necessary
@@ -336,6 +340,7 @@
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
+
override fun onActivityStopped(activity: Activity) {
// Not possible to get a start/stop pair in the same UI thread loop
}
@@ -360,11 +365,8 @@
val activityThreadClass = activityThreadClass
mainThreadField = getMainThreadField()
tokenField = getTokenField()
- performStopActivity3ParamsMethod =
- getPerformStopActivity3Params(activityThreadClass)
- performStopActivity2ParamsMethod =
- getPerformStopActivity2Params(activityThreadClass)
- requestRelaunchActivityMethod =
- getRequestRelaunchActivityMethod(activityThreadClass)
+ performStopActivity3ParamsMethod = getPerformStopActivity3Params(activityThreadClass)
+ performStopActivity2ParamsMethod = getPerformStopActivity2Params(activityThreadClass)
+ requestRelaunchActivityMethod = getRequestRelaunchActivityMethod(activityThreadClass)
}
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKtChecked.kt b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKtChecked.kt
index 1446e04..89325ab 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKtChecked.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/app/ActivityRecreatorKtChecked.kt
@@ -36,35 +36,28 @@
/**
* The goal here is to get common (and correct) behavior around Activity recreation for all API
* versions up until P, where the behavior was specified to be useful and implemented to match the
- * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's
- * not in the foreground; it will be recreated when the user next interacts with it. This has a few
+ * specification. On API 26 and 27, recreate() doesn't actually recreate the Activity if it's not in
+ * the foreground; it will be recreated when the user next interacts with it. This has a few
* undesirable consequences:
- *
- *
* 1. It's impossible to recreate multiple activities at once, which means that activities in the
- * background will observe the new configuration before they're recreated. If we keep them on the
- * old configuration, we have two conflicting configurations active in the app, which leads to
- * logging skew.
- *
- *
+ * background will observe the new configuration before they're recreated. If we keep them on the
+ * old configuration, we have two conflicting configurations active in the app, which leads to
+ * logging skew.
* 2. Recreation occurs in the critical path of user interaction - re-inflating a bunch of views
- * isn't free, and we'd rather do it when we're in the background than when the user is staring at
- * the screen waiting to see us.
+ * isn't free, and we'd rather do it when we're in the background than when the user is staring
+ * at the screen waiting to see us.
*
- *
- * On API < 26, recreate() was implemented with a single call to a private method on
- * ActivityThread. That method still exists in 26 and 27, so we can use reflection to call it and
- * get the exact same behavior as < 26. However, that behavior has problems itself. When
- * an Activity in the background is recreated, it goes through: destroy -> create -> start ->
- * resume -> pause and doesn't stop. This is a violation of the contract for onStart/onStop,
- * but that might be palatable if it didn't also have the effect of preventing new configurations
- * from being applied - since the Activity doesn't go through onStop, our tracking of whether
- * our app is visible thinks we're always visible, and thus can't do another recreation later.
- *
+ * On API < 26, recreate() was implemented with a single call to a private method on ActivityThread.
+ * That method still exists in 26 and 27, so we can use reflection to call it and get the exact same
+ * behavior as < 26. However, that behavior has problems itself. When an Activity in the background
+ * is recreated, it goes through: destroy -> create -> start -> resume -> pause and doesn't stop.
+ * This is a violation of the contract for onStart/onStop, but that might be palatable if it didn't
+ * also have the effect of preventing new configurations from being applied - since the Activity
+ * doesn't go through onStop, our tracking of whether our app is visible thinks we're always
+ * visible, and thus can't do another recreation later.
*
* The fix for this is to add the missing onStop() call, by using reflection to call into
* ActivityThread.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal object ActivityRecreatorKtChecked {
@@ -109,27 +102,31 @@
return false
}
return try {
- val token = tokenField!!.get(activity)
- ?: return false
- val activityThread = mainThreadField!!.get(activity)
- ?: return false
+ val token = tokenField!!.get(activity) ?: return false
+ val activityThread = mainThreadField!!.get(activity) ?: return false
val application = activity.application
val callbacks = LifecycleCheckCallbacks(activity)
application.registerActivityLifecycleCallbacks(callbacks)
/*
- * Runnables scheduled before/after recreate() will run before and after the Runnables
- * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
- * events that could be caused by recreate() run - that way we can detect onPause()
- * from the new Activity instance, and schedule onStop to run immediately after it.
- */mainHandler.post {
- callbacks.currentlyRecreatingToken = token
- }
+ * Runnables scheduled before/after recreate() will run before and after the Runnables
+ * scheduled by recreate(). This allows us to bound the time where mActivity lifecycle
+ * events that could be caused by recreate() run - that way we can detect onPause()
+ * from the new Activity instance, and schedule onStop to run immediately after it.
+ */ mainHandler.post { callbacks.currentlyRecreatingToken = token }
try {
if (needsRelaunchCall()) {
requestRelaunchActivityMethod!!.invoke(
activityThread,
- token, null, null, 0, false, null, null, false, false
+ token,
+ null,
+ null,
+ 0,
+ false,
+ null,
+ null,
+ false,
+ false
)
} else {
activity.recreate()
@@ -161,8 +158,9 @@
): Boolean {
return try {
val token = tokenField!![activity]
- if (token !== currentlyRecreatingToken ||
- activity.hashCode() != currentlyRecreatingHashCode
+ if (
+ token !== currentlyRecreatingToken ||
+ activity.hashCode() != currentlyRecreatingHashCode
) {
// We're looking at a different activity, don't try to make it stop! Note that
// tokens are reused on SDK 21-23 but Activity objects (and thus hashCode, in
@@ -179,31 +177,27 @@
if (performStopActivity3ParamsMethod != null) {
performStopActivity3ParamsMethod!!.invoke(
activityThread,
- token, false, "AppCompat recreation"
+ token,
+ false,
+ "AppCompat recreation"
)
} else {
- performStopActivity2ParamsMethod!!.invoke(
- activityThread,
- token, false
- )
+ performStopActivity2ParamsMethod!!.invoke(activityThread, token, false)
}
}
} catch (e: RuntimeException) {
// If an Activity throws from onStop, don't swallow it
- if ((e.javaClass == RuntimeException::class.java) &&
- (e.message != null) &&
- e.message!!.startsWith("Unable to stop")
+ if (
+ (e.javaClass == RuntimeException::class.java) &&
+ (e.message != null) &&
+ e.message!!.startsWith("Unable to stop")
) {
throw e
}
// Otherwise just swallow it - we're calling random private methods,
// there's no guarantee on how they'll behave.
} catch (t: Throwable) {
- Log.e(
- LOG_TAG,
- "Exception while invoking performStopActivity",
- t
- )
+ Log.e(LOG_TAG, "Exception while invoking performStopActivity", t)
}
}
true
@@ -218,10 +212,13 @@
return null
}
return try {
- val performStop = activityThreadClass.getDeclaredMethod(
- "performStopActivity",
- IBinder::class.java, Boolean::class.javaPrimitiveType, String::class.java
- )
+ val performStop =
+ activityThreadClass.getDeclaredMethod(
+ "performStopActivity",
+ IBinder::class.java,
+ Boolean::class.javaPrimitiveType,
+ String::class.java
+ )
performStop.isAccessible = true
performStop
} catch (t: Throwable) {
@@ -234,10 +231,12 @@
return null
}
return try {
- val performStop = activityThreadClass.getDeclaredMethod(
- "performStopActivity",
- IBinder::class.java, Boolean::class.javaPrimitiveType
- )
+ val performStop =
+ activityThreadClass.getDeclaredMethod(
+ "performStopActivity",
+ IBinder::class.java,
+ Boolean::class.javaPrimitiveType
+ )
performStop.isAccessible = true
performStop
} catch (t: Throwable) {
@@ -255,18 +254,19 @@
return null
}
return try {
- val relaunch = activityThreadClass.getDeclaredMethod(
- "requestRelaunchActivity",
- IBinder::class.java,
- MutableList::class.java,
- MutableList::class.java,
- Int::class.javaPrimitiveType,
- Boolean::class.javaPrimitiveType,
- Configuration::class.java,
- Configuration::class.java,
- Boolean::class.javaPrimitiveType,
- Boolean::class.javaPrimitiveType
- )
+ val relaunch =
+ activityThreadClass.getDeclaredMethod(
+ "requestRelaunchActivity",
+ IBinder::class.java,
+ MutableList::class.java,
+ MutableList::class.java,
+ Int::class.javaPrimitiveType,
+ Boolean::class.javaPrimitiveType,
+ Configuration::class.java,
+ Configuration::class.java,
+ Boolean::class.javaPrimitiveType,
+ Boolean::class.javaPrimitiveType
+ )
relaunch.isAccessible = true
relaunch
} catch (t: Throwable) {
@@ -295,11 +295,12 @@
}
private val activityThreadClass: Class<*>?
- get() = try {
- Class.forName("android.app.ActivityThread")
- } catch (t: Throwable) {
- null
- }
+ get() =
+ try {
+ Class.forName("android.app.ActivityThread")
+ } catch (t: Throwable) {
+ null
+ }
// Only reachable on SDK_INT < 28
private class LifecycleCheckCallbacks internal constructor(aboutToRecreate: Activity) :
@@ -319,7 +320,9 @@
// Whether we'll force the activity on which recreate() was called to go through an
// onStop()
private var mStopQueued = false
+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
+
override fun onActivityStarted(activity: Activity) {
// If we see a start call on the original mActivity instance, then the mActivity
// starting event executed between our call to recreate() and the actual
@@ -330,11 +333,14 @@
}
override fun onActivityResumed(activity: Activity) {}
+
override fun onActivityPaused(activity: Activity) {
- if (mDestroyed && // Original mActivity must be gone
- !mStopQueued && // Don't schedule stop twice for one recreate() call
- !mStarted && // Don't schedule stop if the original instance starting raced with...
- queueOnStopIfNecessary(currentlyRecreatingToken, mRecreatingHashCode, activity)
+ if (
+ mDestroyed && // Original mActivity must be gone
+ !mStopQueued && // Don't schedule stop twice for one recreate() call
+ !mStarted && // Don't schedule stop if the original instance starting raced
+ // with...
+ queueOnStopIfNecessary(currentlyRecreatingToken, mRecreatingHashCode, activity)
) {
mStopQueued = true
// Don't retain this object longer than necessary
@@ -343,6 +349,7 @@
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
+
override fun onActivityStopped(activity: Activity) {
// Not possible to get a start/stop pair in the same UI thread loop
}
@@ -367,11 +374,8 @@
val activityThreadClass = activityThreadClass
mainThreadField = getMainThreadField()
tokenField = getTokenField()
- performStopActivity3ParamsMethod =
- getPerformStopActivity3Params(activityThreadClass)
- performStopActivity2ParamsMethod =
- getPerformStopActivity2Params(activityThreadClass)
- requestRelaunchActivityMethod =
- getRequestRelaunchActivityMethod(activityThreadClass)
+ performStopActivity3ParamsMethod = getPerformStopActivity3Params(activityThreadClass)
+ performStopActivity2ParamsMethod = getPerformStopActivity2Params(activityThreadClass)
+ requestRelaunchActivityMethod = getRequestRelaunchActivityMethod(activityThreadClass)
}
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompatKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompatKotlin.kt
index f8f7f3d..d5e62a0 100644
--- a/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompatKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/androidx/sample/core/widget/ListViewCompatKotlin.kt
@@ -46,10 +46,8 @@
/**
* Check if the items in the list can be scrolled in a certain direction.
*
- * @param direction Negative to check scrolling up, positive to check
- * scrolling down.
- * @return true if the list can be scrolled in the specified direction,
- * false otherwise.
+ * @param direction Negative to check scrolling up, positive to check scrolling down.
+ * @return true if the list can be scrolled in the specified direction, false otherwise.
* @see .scrollListBy
*/
fun canScrollList(listView: ListView, direction: Int): Boolean {
diff --git a/lint-checks/integration-tests/src/main/java/replacewith/ConstructorNonStaticClassKotlin.kt b/lint-checks/integration-tests/src/main/java/replacewith/ConstructorNonStaticClassKotlin.kt
index b9200e9..dc966d0 100644
--- a/lint-checks/integration-tests/src/main/java/replacewith/ConstructorNonStaticClassKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/replacewith/ConstructorNonStaticClassKotlin.kt
@@ -15,9 +15,7 @@
*/
package replacewith
-/**
- * Usage of a static class constructor.
- */
+/** Usage of a static class constructor. */
@Suppress("unused", "deprecation")
internal class ConstructorNonStaticClassKotlin {
fun usage() {
diff --git a/lint-checks/integration-tests/src/main/java/replacewith/ConstructorStaticClassKotlin.kt b/lint-checks/integration-tests/src/main/java/replacewith/ConstructorStaticClassKotlin.kt
index 4cbdbbe..0180312 100644
--- a/lint-checks/integration-tests/src/main/java/replacewith/ConstructorStaticClassKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/replacewith/ConstructorStaticClassKotlin.kt
@@ -15,9 +15,7 @@
*/
package replacewith
-/**
- * Usage of a static class constructor.
- */
+/** Usage of a static class constructor. */
@Suppress("unused", "deprecation")
internal class ConstructorStaticClassKotlin {
fun usage() {
diff --git a/lint-checks/integration-tests/src/main/java/replacewith/ConstructorToStaticMethodKotlin.kt b/lint-checks/integration-tests/src/main/java/replacewith/ConstructorToStaticMethodKotlin.kt
index bbc7c2f..6c327c22 100644
--- a/lint-checks/integration-tests/src/main/java/replacewith/ConstructorToStaticMethodKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/replacewith/ConstructorToStaticMethodKotlin.kt
@@ -15,9 +15,7 @@
*/
package replacewith
-/**
- * Usage of a static class constructor.
- */
+/** Usage of a static class constructor. */
@Suppress("unused", "deprecation")
internal class ConstructorToStaticMethodKotlin {
fun usage() {
diff --git a/lint-checks/integration-tests/src/main/java/replacewith/MethodWithImportsKotlin.kt b/lint-checks/integration-tests/src/main/java/replacewith/MethodWithImportsKotlin.kt
index a8dccc3..dc85519 100644
--- a/lint-checks/integration-tests/src/main/java/replacewith/MethodWithImportsKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/replacewith/MethodWithImportsKotlin.kt
@@ -17,9 +17,7 @@
import androidx.annotation.AnyThread
-/**
- * Usage of replacement with imports.
- */
+/** Usage of replacement with imports. */
@Suppress("deprecation", "unused")
internal class MethodWithImportsKotlin {
@AnyThread
diff --git a/lint-checks/integration-tests/src/main/java/replacewith/MethodWithNoImportsKotlin.kt b/lint-checks/integration-tests/src/main/java/replacewith/MethodWithNoImportsKotlin.kt
index 5063755..6f8664a 100644
--- a/lint-checks/integration-tests/src/main/java/replacewith/MethodWithNoImportsKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/replacewith/MethodWithNoImportsKotlin.kt
@@ -15,9 +15,7 @@
*/
package replacewith
-/**
- * Usage of replacement with imports.
- */
+/** Usage of replacement with imports. */
@Suppress("deprecation", "unused")
internal class MethodWithNoImportsKotlin {
fun usageSingleImport() {
diff --git a/lint-checks/integration-tests/src/main/java/replacewith/ReplaceWithUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/replacewith/ReplaceWithUsageKotlin.kt
index 04f0415..341d252 100644
--- a/lint-checks/integration-tests/src/main/java/replacewith/ReplaceWithUsageKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/replacewith/ReplaceWithUsageKotlin.kt
@@ -24,10 +24,7 @@
var otherBooleanProperty: Boolean = false
var otherProperty: String = "value"
- @Deprecated(
- message = "Use [otherProperty] instead",
- replaceWith = ReplaceWith("otherProperty")
- )
+ @Deprecated(message = "Use [otherProperty] instead", replaceWith = ReplaceWith("otherProperty"))
var someProperty: String = "value"
@Deprecated(
@@ -36,10 +33,7 @@
)
var someBooleanProperty: Boolean = false
- @get:Deprecated(
- message = "Use [getMethod] instead",
- replaceWith = ReplaceWith("getMethod")
- )
+ @get:Deprecated(message = "Use [getMethod] instead", replaceWith = ReplaceWith("getMethod"))
@set:Deprecated(
message = "Use [setMethod(String)] instead",
replaceWith = ReplaceWith("setMethod(value)")
@@ -47,16 +41,17 @@
var deprecatedSetGetProperty: String = "value"
var deprecatedAccessorProperty: String
- @Deprecated(
- message = "Use [getMethod] instead",
- replaceWith = ReplaceWith("getMethod")
- )
- get() { return otherProperty }
+ @Deprecated(message = "Use [getMethod] instead", replaceWith = ReplaceWith("getMethod"))
+ get() {
+ return otherProperty
+ }
@Deprecated(
message = "Use [setMethod(String)] instead",
replaceWith = ReplaceWith("setMethod(value)")
)
- set(value) { otherProperty = value }
+ set(value) {
+ otherProperty = value
+ }
@Deprecated(
message = "Use [otherProperty] instead",
@@ -66,10 +61,7 @@
otherProperty = arg
}
- @Deprecated(
- message = "Use [otherProperty] instead",
- replaceWith = ReplaceWith("otherProperty")
- )
+ @Deprecated(message = "Use [otherProperty] instead", replaceWith = ReplaceWith("otherProperty"))
fun getMethodDeprecated(): String {
return otherProperty
}
@@ -108,9 +100,7 @@
// Stub.
}
- /**
- * Constructor.
- */
+ /** Constructor. */
constructor() {
// Stub.
}
@@ -121,15 +111,12 @@
*
* @deprecated Use [InnerClass#InnerClass()] instead.
*/
-
@Deprecated("Use [InnerClass#InnerClass()] instead.", ReplaceWith("InnerClass()"))
constructor(param: String) {
// Stub.
}
- /**
- * Constructor.
- */
+ /** Constructor. */
constructor() {
// Stub.
}
@@ -147,17 +134,13 @@
obj.toString()
}
- /**
- * Returns a new object.
- */
+ /** Returns a new object. */
@JvmStatic
fun obtain(param: Int): ReplaceWithUsageKotlin {
return ReplaceWithUsageKotlin()
}
- /**
- * String constant.
- */
+ /** String constant. */
@Deprecated(
message = "Use {@link View#AUTOFILL_HINT_NAME} directly.",
ReplaceWith(expression = "View.AUTOFILL_HINT_NAME")
diff --git a/lint-checks/integration-tests/src/main/java/replacewith/StaticKotlinMethodExplicitClassKotlin.kt b/lint-checks/integration-tests/src/main/java/replacewith/StaticKotlinMethodExplicitClassKotlin.kt
index e2bbe52..b1c21d7 100644
--- a/lint-checks/integration-tests/src/main/java/replacewith/StaticKotlinMethodExplicitClassKotlin.kt
+++ b/lint-checks/integration-tests/src/main/java/replacewith/StaticKotlinMethodExplicitClassKotlin.kt
@@ -17,9 +17,7 @@
import replacewith.ReplaceWithUsageKotlin.Companion.toString
-/**
- * Usage of a static method with an explicit class.
- */
+/** Usage of a static method with an explicit class. */
@Suppress("deprecation", "unused")
internal class StaticKotlinMethodExplicitClassKotlin {
fun main() {
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidManifestServiceExportedDetector.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidManifestServiceExportedDetector.kt
index 1283931..c64b821 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidManifestServiceExportedDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidManifestServiceExportedDetector.kt
@@ -38,26 +38,30 @@
override fun visitElement(context: XmlContext, element: Element) {
val attrExported = element.getAttribute("android:${SdkConstants.ATTR_EXPORTED}")
if (attrExported != "true") {
- val incident = Incident(context, ISSUE)
- .message("Missing exported=true in <service> tag")
- .at(element)
+ val incident =
+ Incident(context, ISSUE)
+ .message("Missing exported=true in <service> tag")
+ .at(element)
context.report(incident)
}
}
companion object {
- val ISSUE = Issue.create(
- id = "MissingServiceExportedEqualsTrue",
- briefDescription = "Missing exported=true declaration in the <service> tag inside" +
- " the library manifest",
- explanation = "Library-defined services should set the exported attribute to true.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- AndroidManifestServiceExportedDetector::class.java,
- Scope.MANIFEST_SCOPE
+ val ISSUE =
+ Issue.create(
+ id = "MissingServiceExportedEqualsTrue",
+ briefDescription =
+ "Missing exported=true declaration in the <service> tag inside" +
+ " the library manifest",
+ explanation = "Library-defined services should set the exported attribute to true.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ AndroidManifestServiceExportedDetector::class.java,
+ Scope.MANIFEST_SCOPE
+ )
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 4e7e205..a5d5286 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -26,62 +26,66 @@
class AndroidXIssueRegistry : IssueRegistry() {
override val minApi = CURRENT_API
override val api = 14
- override val issues get(): List<Issue> {
- return Issues
- }
- override val vendor = Vendor(
- feedbackUrl = "https://issuetracker.google.com/issues/new?component=1147525",
- identifier = "androidx.build",
- vendorName = "Android Open Source Project",
- )
+ override val issues
+ get(): List<Issue> {
+ return Issues
+ }
+
+ override val vendor =
+ Vendor(
+ feedbackUrl = "https://issuetracker.google.com/issues/new?component=1147525",
+ identifier = "androidx.build",
+ vendorName = "Android Open Source Project",
+ )
companion object {
- val Issues get(): List<Issue> {
- return listOf(
- AndroidManifestServiceExportedDetector.ISSUE,
- BanParcelableUsage.ISSUE,
- BanConcurrentHashMap.ISSUE,
- BanVisibilityDocTags.HIDE_ISSUE,
- BanVisibilityDocTags.SUPPRESS_ISSUE,
- BanVisibilityDocTags.REMOVED_ISSUE,
- BanInappropriateExperimentalUsage.ISSUE,
- BanInappropriateExperimentalUsage.NULL_ANNOTATION_GROUP_ISSUE,
- BanInlineOptIn.ISSUE,
- BanKeepAnnotation.ISSUE,
- BanThreadSleep.ISSUE,
- TargetApiAnnotationUsageDetector.ISSUE,
- // If you add more SampledAnnotationDetector issues here, you
- // MUST also update `buildSrc/lint_samples.xml` to ensure they
- // run against samples projects.
- SampledAnnotationDetector.OBSOLETE_SAMPLED_ANNOTATION,
- SampledAnnotationDetector.UNRESOLVED_SAMPLE_LINK,
- SampledAnnotationDetector.MULTIPLE_FUNCTIONS_FOUND,
- SampledAnnotationDetector.INVALID_SAMPLES_LOCATION,
- TestSizeAnnotationEnforcer.MISSING_TEST_SIZE_ANNOTATION,
- TestSizeAnnotationEnforcer.UNEXPECTED_TEST_SIZE_ANNOTATION,
- TestSizeAnnotationEnforcer.UNSUPPORTED_TEST_RUNNER,
- BanUncheckedReflection.ISSUE,
- ObsoleteBuildCompatUsageDetector.ISSUE,
- BanSynchronizedMethods.ISSUE,
- MetadataTagInsideApplicationTagDetector.ISSUE,
- PrivateConstructorForUtilityClassDetector.ISSUE,
- ClassVerificationFailureDetector.METHOD_CALL_ISSUE,
- ClassVerificationFailureDetector.IMPLICIT_CAST_ISSUE,
- IdeaSuppressionDetector.ISSUE,
- CameraXQuirksClassDetector.ISSUE,
- NullabilityAnnotationsDetector.ISSUE,
- IgnoreClassLevelDetector.ISSUE,
- ExperimentalPropertyAnnotationDetector.ISSUE,
- BanRestrictToTestsScope.ISSUE,
- // MissingJvmDefaultWithCompatibilityDetector is intentionally left out of the
- // registry, see comments on the class for more details.
- BanVisibleForTestingParams.ISSUE,
- PrereleaseSdkCoreDependencyDetector.ISSUE,
- DeprecationMismatchDetector.ISSUE,
- RestrictToDetector.RESTRICTED,
- ObsoleteCompatDetector.ISSUE,
- ReplaceWithDetector.ISSUE,
- )
- }
+ val Issues
+ get(): List<Issue> {
+ return listOf(
+ AndroidManifestServiceExportedDetector.ISSUE,
+ BanParcelableUsage.ISSUE,
+ BanConcurrentHashMap.ISSUE,
+ BanVisibilityDocTags.HIDE_ISSUE,
+ BanVisibilityDocTags.SUPPRESS_ISSUE,
+ BanVisibilityDocTags.REMOVED_ISSUE,
+ BanInappropriateExperimentalUsage.ISSUE,
+ BanInappropriateExperimentalUsage.NULL_ANNOTATION_GROUP_ISSUE,
+ BanInlineOptIn.ISSUE,
+ BanKeepAnnotation.ISSUE,
+ BanThreadSleep.ISSUE,
+ TargetApiAnnotationUsageDetector.ISSUE,
+ // If you add more SampledAnnotationDetector issues here, you
+ // MUST also update `buildSrc/lint_samples.xml` to ensure they
+ // run against samples projects.
+ SampledAnnotationDetector.OBSOLETE_SAMPLED_ANNOTATION,
+ SampledAnnotationDetector.UNRESOLVED_SAMPLE_LINK,
+ SampledAnnotationDetector.MULTIPLE_FUNCTIONS_FOUND,
+ SampledAnnotationDetector.INVALID_SAMPLES_LOCATION,
+ TestSizeAnnotationEnforcer.MISSING_TEST_SIZE_ANNOTATION,
+ TestSizeAnnotationEnforcer.UNEXPECTED_TEST_SIZE_ANNOTATION,
+ TestSizeAnnotationEnforcer.UNSUPPORTED_TEST_RUNNER,
+ BanUncheckedReflection.ISSUE,
+ ObsoleteBuildCompatUsageDetector.ISSUE,
+ BanSynchronizedMethods.ISSUE,
+ MetadataTagInsideApplicationTagDetector.ISSUE,
+ PrivateConstructorForUtilityClassDetector.ISSUE,
+ ClassVerificationFailureDetector.METHOD_CALL_ISSUE,
+ ClassVerificationFailureDetector.IMPLICIT_CAST_ISSUE,
+ IdeaSuppressionDetector.ISSUE,
+ CameraXQuirksClassDetector.ISSUE,
+ NullabilityAnnotationsDetector.ISSUE,
+ IgnoreClassLevelDetector.ISSUE,
+ ExperimentalPropertyAnnotationDetector.ISSUE,
+ BanRestrictToTestsScope.ISSUE,
+ // MissingJvmDefaultWithCompatibilityDetector is intentionally left out of the
+ // registry, see comments on the class for more details.
+ BanVisibleForTestingParams.ISSUE,
+ PrereleaseSdkCoreDependencyDetector.ISSUE,
+ DeprecationMismatchDetector.ISSUE,
+ RestrictToDetector.RESTRICTED,
+ ObsoleteCompatDetector.ISSUE,
+ ReplaceWithDetector.ISSUE,
+ )
+ }
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanConcurrentHashMap.kt b/lint-checks/src/main/java/androidx/build/lint/BanConcurrentHashMap.kt
index 41191a3..b736aef 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanConcurrentHashMap.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanConcurrentHashMap.kt
@@ -35,85 +35,81 @@
class BanConcurrentHashMap : Detector(), Detector.UastScanner {
- override fun getApplicableUastTypes(): List<Class<out UElement>> = listOf(
- UImportStatement::class.java,
- UQualifiedReferenceExpression::class.java
- )
+ override fun getApplicableUastTypes(): List<Class<out UElement>> =
+ listOf(UImportStatement::class.java, UQualifiedReferenceExpression::class.java)
- override fun createUastHandler(context: JavaContext): UElementHandler = object :
- UElementHandler() {
+ override fun createUastHandler(context: JavaContext): UElementHandler =
+ object : UElementHandler() {
- /**
- * Detect map construction using fully qualified reference if not imported.
- * This specifically flags the constructor, and not usages of the map after it is created.
- */
- override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
- val resolved = node.resolve()
- // In Kotlin, the resolved node will be a method with name ConcurrentHashMap
- // In Java, it will be the class itself
- if ((resolved is PsiMethod && resolved.isConcurrentHashMapConstructor()) ||
- (resolved is PsiClass && resolved.isConcurrentHashMap())) {
- reportIncidentForNode(node)
- }
- }
-
- /**
- * Detect import.
- */
- override fun visitImportStatement(node: UImportStatement) {
- if (node.importReference != null) {
- var resolved = node.resolve()
- if (resolved is PsiField) {
- resolved = resolved.containingClass
- } else if (resolved is PsiMethod) {
- resolved = resolved.containingClass
- }
-
- if (resolved is PsiClass && resolved.isConcurrentHashMap()) {
+ /**
+ * Detect map construction using fully qualified reference if not imported. This
+ * specifically flags the constructor, and not usages of the map after it is created.
+ */
+ override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
+ val resolved = node.resolve()
+ // In Kotlin, the resolved node will be a method with name ConcurrentHashMap
+ // In Java, it will be the class itself
+ if (
+ (resolved is PsiMethod && resolved.isConcurrentHashMapConstructor()) ||
+ (resolved is PsiClass && resolved.isConcurrentHashMap())
+ ) {
reportIncidentForNode(node)
}
}
- }
- /**
- * Reports an error for ConcurrentHashMap usage at the node's location.
- */
- private fun reportIncidentForNode(node: UElement) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getLocation(node))
- .message("Detected ConcurrentHashMap usage.")
- .scope(node)
- context.report(incident)
- }
+ /** Detect import. */
+ override fun visitImportStatement(node: UImportStatement) {
+ if (node.importReference != null) {
+ var resolved = node.resolve()
+ if (resolved is PsiField) {
+ resolved = resolved.containingClass
+ } else if (resolved is PsiMethod) {
+ resolved = resolved.containingClass
+ }
- /**
- * Check if the method is the constructor for ConcurrentHashMap (applicable for Kotlin).
- */
- private fun PsiMethod.isConcurrentHashMapConstructor(): Boolean {
- return name == CONCURRENT_HASHMAP && (containingClass?.isConcurrentHashMap() ?: false)
- }
+ if (resolved is PsiClass && resolved.isConcurrentHashMap()) {
+ reportIncidentForNode(node)
+ }
+ }
+ }
- /**
- * Checks if the class is ConcurrentHashMap.
- */
- private fun PsiClass.isConcurrentHashMap(): Boolean {
- return qualifiedName == CONCURRENT_HASHMAP_QUALIFIED_NAME
+ /** Reports an error for ConcurrentHashMap usage at the node's location. */
+ private fun reportIncidentForNode(node: UElement) {
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getLocation(node))
+ .message("Detected ConcurrentHashMap usage.")
+ .scope(node)
+ context.report(incident)
+ }
+
+ /**
+ * Check if the method is the constructor for ConcurrentHashMap (applicable for Kotlin).
+ */
+ private fun PsiMethod.isConcurrentHashMapConstructor(): Boolean {
+ return name == CONCURRENT_HASHMAP &&
+ (containingClass?.isConcurrentHashMap() ?: false)
+ }
+
+ /** Checks if the class is ConcurrentHashMap. */
+ private fun PsiClass.isConcurrentHashMap(): Boolean {
+ return qualifiedName == CONCURRENT_HASHMAP_QUALIFIED_NAME
+ }
}
- }
companion object {
- val ISSUE = Issue.create(
- "BanConcurrentHashMap",
- "ConcurrentHashMap usage is not allowed",
- "ConcurrentHashMap has an issue on Android’s Lollipop release that can lead to lost" +
- " updates under thread contention.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- BanConcurrentHashMap::class.java,
- Scope.JAVA_FILE_SCOPE
+ val ISSUE =
+ Issue.create(
+ "BanConcurrentHashMap",
+ "ConcurrentHashMap usage is not allowed",
+ "ConcurrentHashMap has an issue on Android’s Lollipop release that can lead to lost" +
+ " updates under thread contention.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanConcurrentHashMap::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
const val CONCURRENT_HASHMAP_QUALIFIED_NAME = "java.util.concurrent.ConcurrentHashMap"
const val CONCURRENT_HASHMAP = "ConcurrentHashMap"
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
index 092417f..276dc01 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanInappropriateExperimentalUsage.kt
@@ -44,9 +44,7 @@
import org.jetbrains.uast.resolveToUElement
import org.jetbrains.uast.toUElement
-/**
- * Prevents usage of experimental annotations outside the groups in which they were defined.
- */
+/** Prevents usage of experimental annotations outside the groups in which they were defined. */
class BanInappropriateExperimentalUsage : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
@@ -91,17 +89,18 @@
markerClasses.forEach { uElement ->
if (DEBUG) {
- println("Inspecting markerClass annotation " +
- uElement.getQualifiedName())
+ println(
+ "Inspecting markerClass annotation " + uElement.getQualifiedName()
+ )
}
inspectAnnotation(uElement, node)
}
}
/**
- * @OptIn has no effect if its markerClass isn't provided.
- * Similarly, if [getUElementsFromOptInMarkerClass] returns an empty list then
- * there isn't anything more to inspect.
+ * @OptIn has no effect if its markerClass isn't provided. Similarly, if
+ * [getUElementsFromOptInMarkerClass] returns an empty list then there isn't
+ * anything more to inspect.
*
* In both of these cases we can stop processing here.
*/
@@ -165,11 +164,12 @@
}
private fun loadAtomicLibraryGroupList(): List<String> {
- val fileStream = this::class.java.classLoader
- .getResourceAsStream(ATOMIC_LIBRARY_GROUPS_FILENAME)
- ?: throw FileNotFoundException(
- "Couldn't find atomic library group file $ATOMIC_LIBRARY_GROUPS_FILENAME" +
- " within lint-checks.jar")
+ val fileStream =
+ this::class.java.classLoader.getResourceAsStream(ATOMIC_LIBRARY_GROUPS_FILENAME)
+ ?: throw FileNotFoundException(
+ "Couldn't find atomic library group file $ATOMIC_LIBRARY_GROUPS_FILENAME" +
+ " within lint-checks.jar"
+ )
val atomicLibraryGroupsString = fileStream.bufferedReader().use { it.readText() }
if (atomicLibraryGroupsString.isEmpty()) {
@@ -226,21 +226,21 @@
/**
* Usage of experimental APIs is allowed in either of the following conditions:
- *
* - The usage is in an alpha library
- * - Both the group ID and artifact ID in `usageCoordinates` and
- * `annotationCoordinates` match
+ * - Both the group ID and artifact ID in `usageCoordinates` and `annotationCoordinates`
+ * match
* - The group IDs match, and that group ID is atomic
*/
- if (isUsedInAlpha ||
- (isUsedInSameGroup && isUsedInSameArtifact) ||
- (isUsedInSameGroup && isAtomic)) return
+ if (
+ isUsedInAlpha ||
+ (isUsedInSameGroup && isUsedInSameArtifact) ||
+ (isUsedInSameGroup && isAtomic)
+ )
+ return
// Log inappropriate experimental usage
if (DEBUG) {
- println(
- "${context.driver.mode}: report usage of $annotationGroupId in $usageGroupId"
- )
+ println("${context.driver.mode}: report usage of $annotationGroupId in $usageGroupId")
}
Incident(context)
.issue(issue)
@@ -286,81 +286,85 @@
private const val KOTLIN_REQUIRES_OPT_IN_ANNOTATION = "kotlin.RequiresOptIn"
private const val JAVA_EXPERIMENTAL_ANNOTATION =
"androidx.annotation.experimental.Experimental"
- private const val JAVA_REQUIRES_OPT_IN_ANNOTATION =
- "androidx.annotation.RequiresOptIn"
+ private const val JAVA_REQUIRES_OPT_IN_ANNOTATION = "androidx.annotation.RequiresOptIn"
- val APPLICABLE_ANNOTATIONS = listOf(
- JAVA_EXPERIMENTAL_ANNOTATION,
- KOTLIN_EXPERIMENTAL_ANNOTATION,
- JAVA_REQUIRES_OPT_IN_ANNOTATION,
- KOTLIN_REQUIRES_OPT_IN_ANNOTATION,
- )
+ val APPLICABLE_ANNOTATIONS =
+ listOf(
+ JAVA_EXPERIMENTAL_ANNOTATION,
+ KOTLIN_EXPERIMENTAL_ANNOTATION,
+ JAVA_REQUIRES_OPT_IN_ANNOTATION,
+ KOTLIN_REQUIRES_OPT_IN_ANNOTATION,
+ )
- private val APPLICATION_OPT_IN_ANNOTATIONS = listOf(
- "androidx.annotation.OptIn",
- "kotlin.OptIn",
- )
+ private val APPLICATION_OPT_IN_ANNOTATIONS =
+ listOf(
+ "androidx.annotation.OptIn",
+ "kotlin.OptIn",
+ )
// This must match the definition in ExportAtomicLibraryGroupsToTextTask
const val ATOMIC_LIBRARY_GROUPS_FILENAME = "atomic-library-groups.txt"
- val ISSUE = Issue.create(
- id = "IllegalExperimentalApiUsage",
- briefDescription = "Using experimental API from separately versioned library",
- explanation = "Annotations meta-annotated with `@RequiresOptIn` or `@Experimental` " +
- "may only be referenced from within the same-version group in which they were " +
- "defined.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- BanInappropriateExperimentalUsage::class.java,
- Scope.JAVA_FILE_SCOPE,
- ),
- )
-
- val NULL_ANNOTATION_GROUP_ISSUE = Issue.create(
- id = "NullAnnotationGroup",
- briefDescription = "Maven group associated with an annotation could not be found",
- explanation = "An annotation's group could not be found using `getProject` or " +
- "`getLibrary`.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- BanInappropriateExperimentalUsage::class.java,
- Scope.JAVA_FILE_SCOPE,
- ),
- )
-
- /**
- * Checks to see if the given annotation is always allowed for use in @OptIn.
- */
- internal fun isAnnotationAlwaysAllowed(annotation: String): Boolean {
- val allowedExperimentalAnnotations = listOf(
- Regex("com\\.google\\.devtools\\.ksp\\.KspExperimental"),
- Regex("kotlin\\..*"),
- Regex("kotlinx\\..*"),
- Regex("org.jetbrains.kotlin\\..*"),
+ val ISSUE =
+ Issue.create(
+ id = "IllegalExperimentalApiUsage",
+ briefDescription = "Using experimental API from separately versioned library",
+ explanation =
+ "Annotations meta-annotated with `@RequiresOptIn` or `@Experimental` " +
+ "may only be referenced from within the same-version group in which they were " +
+ "defined.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ BanInappropriateExperimentalUsage::class.java,
+ Scope.JAVA_FILE_SCOPE,
+ ),
)
- return allowedExperimentalAnnotations.any {
- annotation.matches(it)
- }
+
+ val NULL_ANNOTATION_GROUP_ISSUE =
+ Issue.create(
+ id = "NullAnnotationGroup",
+ briefDescription = "Maven group associated with an annotation could not be found",
+ explanation =
+ "An annotation's group could not be found using `getProject` or " +
+ "`getLibrary`.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ BanInappropriateExperimentalUsage::class.java,
+ Scope.JAVA_FILE_SCOPE,
+ ),
+ )
+
+ /** Checks to see if the given annotation is always allowed for use in @OptIn. */
+ internal fun isAnnotationAlwaysAllowed(annotation: String): Boolean {
+ val allowedExperimentalAnnotations =
+ listOf(
+ Regex("com\\.google\\.devtools\\.ksp\\.KspExperimental"),
+ Regex("kotlin\\..*"),
+ Regex("kotlinx\\..*"),
+ Regex("org.jetbrains.kotlin\\..*"),
+ )
+ return allowedExperimentalAnnotations.any { annotation.matches(it) }
}
/**
* Extracts the Maven coordinates from a given JAR path
*
- * For example: given `<checkout root>/androidx/paging/paging-common/build/libs/paging-common-3.2.0-alpha01.jar`,
- * this method will return a:
- *
+ * For example: given `<checkout
+ * root>/androidx/paging/paging-common/build/libs/paging-common-3.2.0-alpha01.jar`, this
+ * method will return a:
* - `groupId` of `androidx.paging`
* - `artifactId` of `paging-common`
* - `version` of `3.2.0-alpha01`
*
* @param jarFilePath the path to the JAR file
* @return a [LintModelMavenName] with the groupId, artifactId, and version parsed from the
- * path, or `null` if [jarFilePath] doesn't contain the string "androidx".
+ * path, or `null` if [jarFilePath] doesn't contain the string "androidx".
*/
internal fun getMavenCoordinatesFromPath(jarFilePath: String): LintModelMavenName? {
val pathParts = jarFilePath.split("/")
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanInlineOptIn.kt b/lint-checks/src/main/java/androidx/build/lint/BanInlineOptIn.kt
index 5e7fe36..ba3cf0c6 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanInlineOptIn.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanInlineOptIn.kt
@@ -41,29 +41,29 @@
val hasOptInAnnotation = context.evaluator.getAnnotation(node, "kotlin.OptIn") != null
if (context.evaluator.isInline(node) && hasOptInAnnotation) {
- val incident = Incident(context, ISSUE)
- .location(context.getNameLocation((node)))
- .message("Inline functions cannot opt into experimental APIs.")
- .scope(node)
+ val incident =
+ Incident(context, ISSUE)
+ .location(context.getNameLocation((node)))
+ .message("Inline functions cannot opt into experimental APIs.")
+ .scope(node)
context.report(incident)
}
}
}
companion object {
- val ISSUE = Issue.create(
- id = "BanInlineOptIn",
- briefDescription = "Uses @OptIn annotation on an inline function",
- explanation = "Use of the @OptIn annotation is not allowed on inline functions," +
- " as libraries using this method will inline the reference to the opted-in" +
- " class. This can potentially create a compatibility issue.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- BanInlineOptIn::class.java,
- Scope.JAVA_FILE_SCOPE
+ val ISSUE =
+ Issue.create(
+ id = "BanInlineOptIn",
+ briefDescription = "Uses @OptIn annotation on an inline function",
+ explanation =
+ "Use of the @OptIn annotation is not allowed on inline functions," +
+ " as libraries using this method will inline the reference to the opted-in" +
+ " class. This can potentially create a compatibility issue.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation = Implementation(BanInlineOptIn::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanKeepAnnotation.kt b/lint-checks/src/main/java/androidx/build/lint/BanKeepAnnotation.kt
index 6c5d65d..50a2f3e 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanKeepAnnotation.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanKeepAnnotation.kt
@@ -39,27 +39,32 @@
private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
override fun visitAnnotation(node: UAnnotation) {
- if (node.qualifiedName == "androidx.annotation.Keep" ||
- node.qualifiedName == "android.support.annotation.keep"
+ if (
+ node.qualifiedName == "androidx.annotation.Keep" ||
+ node.qualifiedName == "android.support.annotation.keep"
) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message("Uses @Keep annotation")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Uses @Keep annotation")
+ .scope(node)
context.report(incident)
}
}
}
companion object {
- val ISSUE = Issue.create(
- "BanKeepAnnotation",
- "Uses @Keep annotation",
- "Use of @Keep annotation is not allowed, please use a conditional " +
- "keep rule in proguard-rules.pro.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanKeepAnnotation::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "BanKeepAnnotation",
+ "Uses @Keep annotation",
+ "Use of @Keep annotation is not allowed, please use a conditional " +
+ "keep rule in proguard-rules.pro.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanKeepAnnotation::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanParcelableUsage.kt b/lint-checks/src/main/java/androidx/build/lint/BanParcelableUsage.kt
index b2253c69..b4bc981 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanParcelableUsage.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanParcelableUsage.kt
@@ -40,9 +40,10 @@
}
override fun visitClass(context: JavaContext, declaration: UClass) {
- if (declaration.isInterface ||
- declaration.hasModifierProperty(PsiModifier.ABSTRACT) ||
- declaration is UAnonymousClass
+ if (
+ declaration.isInterface ||
+ declaration.hasModifierProperty(PsiModifier.ABSTRACT) ||
+ declaration is UAnonymousClass
) {
return
}
@@ -50,24 +51,28 @@
// lint will also examine the entire inheritance and implementation chain.
for (superclass in declaration.uastSuperTypes) {
if (superclass.type.canonicalText == PARCELABLE_INTERFACE_CANONICAL_NAME) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(declaration))
- .message("Class implements android.os.Parcelable")
- .scope(declaration)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(declaration))
+ .message("Class implements android.os.Parcelable")
+ .scope(declaration)
context.report(incident)
}
}
}
companion object {
- val ISSUE = Issue.create(
- "BanParcelableUsage",
- "Class implements android.os.Parcelable",
- "Use of Parcelable is no longer recommended," +
- " please use VersionedParcelable instead.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanParcelableUsage::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "BanParcelableUsage",
+ "Class implements android.os.Parcelable",
+ "Use of Parcelable is no longer recommended," +
+ " please use VersionedParcelable instead.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanParcelableUsage::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt b/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt
index 5fed4c2..a09b312 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt
@@ -46,42 +46,48 @@
if (node.qualifiedName != "androidx.annotation.RestrictTo") return
// Resolve the FQN for all arguments to value parameter.
- val scopes = node.findAttributeValue("value")?.let { value ->
- if (value.isArrayInitializer()) {
- (value as? UCallExpression)?.valueArguments?.mapNotNull { arg ->
- arg as? UReferenceExpression
- } ?: emptyList()
- } else if (value is UReferenceExpression) {
- listOfNotNull(value)
- } else {
- emptyList()
- }
- }?.mapNotNull { expr ->
- expr.resolve()?.getFqName()
- } ?: emptyList()
+ val scopes =
+ node
+ .findAttributeValue("value")
+ ?.let { value ->
+ if (value.isArrayInitializer()) {
+ (value as? UCallExpression)?.valueArguments?.mapNotNull { arg ->
+ arg as? UReferenceExpression
+ } ?: emptyList()
+ } else if (value is UReferenceExpression) {
+ listOfNotNull(value)
+ } else {
+ emptyList()
+ }
+ }
+ ?.mapNotNull { expr -> expr.resolve()?.getFqName() } ?: emptyList()
if (!scopes.contains("androidx.annotation.RestrictTo.Scope.TESTS")) return
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message("Replace `@RestrictTo(TESTS)` with `@VisibleForTesting`")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Replace `@RestrictTo(TESTS)` with `@VisibleForTesting`")
+ .scope(node)
// If there's only one scope, suggest replacement.
if (scopes.size == 1) {
// Extract Kotlin use-site target, if available.
- val useSiteTarget = (node.sourcePsi as? KtAnnotationEntry)
- ?.useSiteTarget
- ?.getAnnotationUseSiteTarget()
- ?.renderName
- ?.let { "$it:" } ?: ""
+ val useSiteTarget =
+ (node.sourcePsi as? KtAnnotationEntry)
+ ?.useSiteTarget
+ ?.getAnnotationUseSiteTarget()
+ ?.renderName
+ ?.let { "$it:" } ?: ""
- val fix = fix().name("Replace with `@${useSiteTarget}VisibleForTesting`")
- .replace()
- .with("@${useSiteTarget}androidx.annotation.VisibleForTesting")
- .shortenNames()
- .build()
+ val fix =
+ fix()
+ .name("Replace with `@${useSiteTarget}VisibleForTesting`")
+ .replace()
+ .with("@${useSiteTarget}androidx.annotation.VisibleForTesting")
+ .shortenNames()
+ .build()
incident.fix(fix)
}
@@ -90,13 +96,16 @@
}
companion object {
- val ISSUE = Issue.create(
- "UsesRestrictToTestsScope",
- "Uses @RestrictTo(TESTS) restriction scope",
- "Use of @RestrictTo(TESTS) restriction scope is not allowed, use " +
- "@VisibleForTesting instead.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanRestrictToTestsScope::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "UsesRestrictToTestsScope",
+ "Uses @RestrictTo(TESTS) restriction scope",
+ "Use of @RestrictTo(TESTS) restriction scope is not allowed, use " +
+ "@VisibleForTesting instead.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanRestrictToTestsScope::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanSynchronizedMethods.kt b/lint-checks/src/main/java/androidx/build/lint/BanSynchronizedMethods.kt
index 7f50e3d..36573d3 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanSynchronizedMethods.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanSynchronizedMethods.kt
@@ -34,30 +34,35 @@
override fun getApplicableUastTypes() = listOf(UMethod::class.java)
- override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
- override fun visitMethod(node: UMethod) {
- if (node.hasModifier(JvmModifier.SYNCHRONIZED)) {
- val incident = Incident(context)
- .fix(null)
- .issue(ISSUE)
- .location(context.getLocation(node))
- .message("Use of synchronized methods is not recommended")
- .scope(node)
- context.report(incident)
+ override fun createUastHandler(context: JavaContext) =
+ object : UElementHandler() {
+ override fun visitMethod(node: UMethod) {
+ if (node.hasModifier(JvmModifier.SYNCHRONIZED)) {
+ val incident =
+ Incident(context)
+ .fix(null)
+ .issue(ISSUE)
+ .location(context.getLocation(node))
+ .message("Use of synchronized methods is not recommended")
+ .scope(node)
+ context.report(incident)
+ }
}
}
- }
companion object {
@SuppressWarnings("LintImplUnexpectedDomain")
- val ISSUE = Issue.create(
- "BanSynchronizedMethods",
- "Method is synchronized",
- "Use of synchronized methods is not recommended," +
- " please refer to https://android.googlesource.com/platform/frameworks/" +
- "support/+/androidx-main/docs/api_guidelines.md",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanSynchronizedMethods::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "BanSynchronizedMethods",
+ "Method is synchronized",
+ "Use of synchronized methods is not recommended," +
+ " please refer to https://android.googlesource.com/platform/frameworks/" +
+ "support/+/androidx-main/docs/api_guidelines.md",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanSynchronizedMethods::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanThreadSleep.kt b/lint-checks/src/main/java/androidx/build/lint/BanThreadSleep.kt
index 77d84b3..e78fee0 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanThreadSleep.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanThreadSleep.kt
@@ -35,24 +35,28 @@
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
if (context.evaluator.isMemberInClass(method, "java.lang.Thread")) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message("Uses Thread.sleep()")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Uses Thread.sleep()")
+ .scope(node)
context.report(incident)
}
}
companion object {
- val ISSUE = Issue.create(
- "BanThreadSleep",
- "Uses Thread.sleep() method",
- "Use of Thread.sleep() is not allowed, please use a callback " +
- "or another way to make more reliable code. See more details at " +
- "go/androidx/testability#calling-threadsleep-as-a-synchronization-barrier",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanThreadSleep::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "BanThreadSleep",
+ "Uses Thread.sleep() method",
+ "Use of Thread.sleep() is not allowed, please use a callback " +
+ "or another way to make more reliable code. See more details at " +
+ "go/androidx/testability#calling-threadsleep-as-a-synchronization-barrier",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanThreadSleep::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt b/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
index b448426..4ece5c1 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanUncheckedReflection.kt
@@ -39,15 +39,9 @@
class BanUncheckedReflection : Detector(), SourceCodeScanner {
- override fun getApplicableMethodNames() = listOf(
- METHOD_INVOKE_NAME
- )
+ override fun getApplicableMethodNames() = listOf(METHOD_INVOKE_NAME)
- override fun visitMethodCall(
- context: JavaContext,
- node: UCallExpression,
- method: PsiMethod
- ) {
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
// We don't care if the invocation is correct -- there's another lint for that. We're
// just enforcing the "all reflection on the platform SDK must be gated on SDK_INT checks"
// policy. Also -- since we're not actually checking whether the invocation is on the
@@ -57,46 +51,49 @@
if (!context.evaluator.isMemberInClass(method, METHOD_REFLECTION_CLASS)) return
// Flag if the call isn't inside or preceded by an SDK_INT check.
- if (!isWithinVersionCheckConditional(
+ if (
+ !isWithinVersionCheckConditional(
context,
node,
- ApiConstraint.get(HIGHEST_KNOWN_API), false
+ ApiConstraint.get(HIGHEST_KNOWN_API),
+ false
) &&
- !isWithinVersionCheckConditional(context, node, ApiConstraint.get(1), true) &&
- !isPrecededByVersionCheckExit(context, node, ApiConstraint.get(HIGHEST_KNOWN_API)) &&
- !isPrecededByVersionCheckExit(context, node, ApiConstraint.get(1)) &&
- !isWithinDeprecatedSinceApiMethod(node) &&
- !isWithinDeprecatedSinceApiClass(node)
+ !isWithinVersionCheckConditional(context, node, ApiConstraint.get(1), true) &&
+ !isPrecededByVersionCheckExit(
+ context,
+ node,
+ ApiConstraint.get(HIGHEST_KNOWN_API)
+ ) &&
+ !isPrecededByVersionCheckExit(context, node, ApiConstraint.get(1)) &&
+ !isWithinDeprecatedSinceApiMethod(node) &&
+ !isWithinDeprecatedSinceApiClass(node)
) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getLocation(node))
- .message("Method.invoke requires both an upper and lower SDK bounds checks to be" +
- " safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API.")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getLocation(node))
+ .message(
+ "Method.invoke requires both an upper and lower SDK bounds checks to be" +
+ " safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API."
+ )
+ .scope(node)
context.report(incident)
}
}
- /**
- * Checks if the expression is within a method annotated with @DeprecatedSinceApi.
- */
+ /** Checks if the expression is within a method annotated with @DeprecatedSinceApi. */
private fun isWithinDeprecatedSinceApiMethod(node: UExpression): Boolean {
val containingMethod = node.getContainingUMethod() ?: return false
return annotationsContainDeprecatedSinceApi(containingMethod.annotations)
}
- /**
- * Checks if the expression is within a class annotated with @DeprecatedSinceApi.
- */
+ /** Checks if the expression is within a class annotated with @DeprecatedSinceApi. */
private fun isWithinDeprecatedSinceApiClass(node: UExpression): Boolean {
val containingClass = node.getContainingUClass() ?: return false
return annotationsContainDeprecatedSinceApi(containingClass.annotations)
}
- /**
- * Checks if any of the annotations are @DeprecatedSinceApi.
- */
+ /** Checks if any of the annotations are @DeprecatedSinceApi. */
private fun annotationsContainDeprecatedSinceApi(annotations: Array<PsiAnnotation>): Boolean {
for (annotation in annotations) {
if (annotation.hasQualifiedName(DEPRECATED_SINCE_API_ANNOTATION)) {
@@ -107,17 +104,20 @@
}
companion object {
- val ISSUE = Issue.create(
- "BanUncheckedReflection",
- "Reflection that is not within an SDK check",
- "Jetpack policy discourages reflection. In cases where reflection is used on " +
- "platform SDK classes, it must be used within an `SDK_INT` check that delegates " +
- "to an equivalent public API on the latest version of the platform. If no " +
- "equivalent public API exists, reflection must not be used. For more " +
- "information, see go/androidx-api-guidelines#sdk-reflection.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanUncheckedReflection::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "BanUncheckedReflection",
+ "Reflection that is not within an SDK check",
+ "Jetpack policy discourages reflection. In cases where reflection is used on " +
+ "platform SDK classes, it must be used within an `SDK_INT` check that delegates " +
+ "to an equivalent public API on the latest version of the platform. If no " +
+ "equivalent public API exists, reflection must not be used. For more " +
+ "information, see go/androidx-api-guidelines#sdk-reflection.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanUncheckedReflection::class.java, Scope.JAVA_FILE_SCOPE)
+ )
const val METHOD_REFLECTION_CLASS = "java.lang.reflect.Method"
const val METHOD_INVOKE_NAME = "invoke"
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanVisibilityDocTags.kt b/lint-checks/src/main/java/androidx/build/lint/BanVisibilityDocTags.kt
index d9c7875..c9095eb 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanVisibilityDocTags.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanVisibilityDocTags.kt
@@ -29,69 +29,72 @@
@Suppress("unused")
class BanVisibilityDocTags : Detector(), Detector.UastScanner {
- private val tagToIssue = mapOf(
- "@hide" to HIDE_ISSUE,
- "@suppress" to SUPPRESS_ISSUE,
- "@removed" to REMOVED_ISSUE,
- )
+ private val tagToIssue =
+ mapOf(
+ "@hide" to HIDE_ISSUE,
+ "@suppress" to SUPPRESS_ISSUE,
+ "@removed" to REMOVED_ISSUE,
+ )
override fun getApplicableUastTypes() = listOf(UDeclaration::class.java)
- override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
+ override fun createUastHandler(context: JavaContext) =
+ object : UElementHandler() {
- override fun visitDeclaration(node: UDeclaration) {
- tagToIssue.forEach { (tag, issue) ->
- if (node.comments.any { it.text.contains(tag) }) {
- val incident = Incident(context)
- .issue(issue)
- .location(context.getNameLocation(node))
- .message("$tag is not allowed in documentation")
- .scope(node)
- context.report(incident)
+ override fun visitDeclaration(node: UDeclaration) {
+ tagToIssue.forEach { (tag, issue) ->
+ if (node.comments.any { it.text.contains(tag) }) {
+ val incident =
+ Incident(context)
+ .issue(issue)
+ .location(context.getNameLocation(node))
+ .message("$tag is not allowed in documentation")
+ .scope(node)
+ context.report(incident)
+ }
}
}
}
- }
companion object {
- val HIDE_ISSUE = Issue.create(
- id = "BanHideTag",
- briefDescription = "@hide is not allowed in Javadoc",
- explanation = "Use of the @hide annotation in Javadoc is no longer allowed." +
- " Please use @RestrictTo instead.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- BanVisibilityDocTags::class.java,
- Scope.JAVA_FILE_SCOPE
+ val HIDE_ISSUE =
+ Issue.create(
+ id = "BanHideTag",
+ briefDescription = "@hide is not allowed in Javadoc",
+ explanation =
+ "Use of the @hide annotation in Javadoc is no longer allowed." +
+ " Please use @RestrictTo instead.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(BanVisibilityDocTags::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
- val SUPPRESS_ISSUE = Issue.create(
- id = "BanSuppressTag",
- briefDescription = "@suppress is not allowed in KDoc",
- explanation = "Use of the @suppress annotation in KDoc is no longer allowed." +
- " Please use @RestrictTo instead.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- BanVisibilityDocTags::class.java,
- Scope.JAVA_FILE_SCOPE
+ val SUPPRESS_ISSUE =
+ Issue.create(
+ id = "BanSuppressTag",
+ briefDescription = "@suppress is not allowed in KDoc",
+ explanation =
+ "Use of the @suppress annotation in KDoc is no longer allowed." +
+ " Please use @RestrictTo instead.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(BanVisibilityDocTags::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
- val REMOVED_ISSUE = Issue.create(
- id = "BanRemovedTag",
- briefDescription = "@removed is not allowed in Javadoc",
- explanation = "Use of the @removed annotation in Javadoc is no longer allowed." +
- " Please use @RestrictTo(LIBRARY_GROUP_PREFIX) instead.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- BanVisibilityDocTags::class.java,
- Scope.JAVA_FILE_SCOPE
+ val REMOVED_ISSUE =
+ Issue.create(
+ id = "BanRemovedTag",
+ briefDescription = "@removed is not allowed in Javadoc",
+ explanation =
+ "Use of the @removed annotation in Javadoc is no longer allowed." +
+ " Please use @RestrictTo(LIBRARY_GROUP_PREFIX) instead.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(BanVisibilityDocTags::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanVisibleForTestingParams.kt b/lint-checks/src/main/java/androidx/build/lint/BanVisibleForTestingParams.kt
index d668982..e063418 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanVisibleForTestingParams.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/BanVisibleForTestingParams.kt
@@ -45,60 +45,64 @@
// Using "declared" to resolve an unspecified value to null, rather than the default,
// resolve the FQN for the `otherwise` attribute value and abort if it's unspecified.
- val otherwise = (node.findDeclaredAttributeValue("otherwise") as? UReferenceExpression)
- ?.resolve()
- ?.getFqName()
- ?: return // Unspecified, abort.
+ val otherwise =
+ (node.findDeclaredAttributeValue("otherwise") as? UReferenceExpression)
+ ?.resolve()
+ ?.getFqName() ?: return // Unspecified, abort.
- val fixBuilder = when (otherwise) {
- "androidx.annotation.VisibleForTesting.Companion.PRIVATE",
- "androidx.annotation.VisibleForTesting.Companion.NONE" -> {
- // Extract Kotlin use-site target, if available.
- val useSiteTarget = (node.sourcePsi as? KtAnnotationEntry)
- ?.useSiteTarget
- ?.getAnnotationUseSiteTarget()
- ?.renderName
- ?.let { "$it:" } ?: ""
+ val fixBuilder =
+ when (otherwise) {
+ "androidx.annotation.VisibleForTesting.Companion.PRIVATE",
+ "androidx.annotation.VisibleForTesting.Companion.NONE" -> {
+ // Extract Kotlin use-site target, if available.
+ val useSiteTarget =
+ (node.sourcePsi as? KtAnnotationEntry)
+ ?.useSiteTarget
+ ?.getAnnotationUseSiteTarget()
+ ?.renderName
+ ?.let { "$it:" } ?: ""
- fix().name("Remove non-default `otherwise` value")
- .replace()
- .with("@${useSiteTarget}androidx.annotation.VisibleForTesting")
+ fix()
+ .name("Remove non-default `otherwise` value")
+ .replace()
+ .with("@${useSiteTarget}androidx.annotation.VisibleForTesting")
+ }
+ "androidx.annotation.VisibleForTesting.Companion.PACKAGE_PRIVATE",
+ "androidx.annotation.VisibleForTesting.Companion.PROTECTED" -> {
+ fix().name("Remove @VisibleForTesting annotation").replace().with("")
+ }
+ else -> {
+ // This could happen if a new visibility is added in the future, in which
+ // case
+ // we'll warn about the non-default usage but we won't attempt a fix.
+ null
+ }
}
- "androidx.annotation.VisibleForTesting.Companion.PACKAGE_PRIVATE",
- "androidx.annotation.VisibleForTesting.Companion.PROTECTED" -> {
- fix().name("Remove @VisibleForTesting annotation")
- .replace()
- .with("")
- }
- else -> {
- // This could happen if a new visibility is added in the future, in which case
- // we'll warn about the non-default usage but we won't attempt a fix.
- null
- }
- }
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message("Found non-default `otherwise` value for @VisibleForTesting")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Found non-default `otherwise` value for @VisibleForTesting")
+ .scope(node)
- fixBuilder?.let {
- incident.fix(it.shortenNames().build())
- }
+ fixBuilder?.let { incident.fix(it.shortenNames().build()) }
context.report(incident)
}
}
companion object {
- val ISSUE = Issue.create(
- "UsesNonDefaultVisibleForTesting",
- "Uses non-default @VisibleForTesting visibility",
- "Use of non-default @VisibleForTesting visibility is not allowed, use the " +
- "default value instead.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanVisibleForTestingParams::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "UsesNonDefaultVisibleForTesting",
+ "Uses non-default @VisibleForTesting visibility",
+ "Use of non-default @VisibleForTesting visibility is not allowed, use the " +
+ "default value instead.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanVisibleForTestingParams::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/CameraXQuirksClassDetector.kt b/lint-checks/src/main/java/androidx/build/lint/CameraXQuirksClassDetector.kt
index 9c87cf3..24faeff 100644
--- a/lint-checks/src/main/java/androidx/build/lint/CameraXQuirksClassDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/CameraXQuirksClassDetector.kt
@@ -36,55 +36,63 @@
override fun getApplicableUastTypes() = listOf(UClass::class.java)
- override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
+ override fun createUastHandler(context: JavaContext) =
+ object : UElementHandler() {
- override fun visitClass(node: UClass) {
- val isQuirk = node.implementsList?.referenceElements?.find {
- it.referenceName!!.endsWith("Quirk")
- } != null
+ override fun visitClass(node: UClass) {
+ val isQuirk =
+ node.implementsList?.referenceElements?.find {
+ it.referenceName!!.endsWith("Quirk")
+ } != null
- if (isQuirk) {
- val comments = node.comments
- val sb = StringBuilder()
- comments.forEach { sb.append(it.text) }
- val comment = sb.append("\n").toString()
+ if (isQuirk) {
+ val comments = node.comments
+ val sb = StringBuilder()
+ comments.forEach { sb.append(it.text) }
+ val comment = sb.append("\n").toString()
- if (!comment.contains("<p>QuirkSummary") ||
- !comment.contains("Bug Id:") ||
- !comment.contains("Description:") ||
- !comment.contains("Device(s):")) {
- val implForInsertion = """
+ if (
+ !comment.contains("<p>QuirkSummary") ||
+ !comment.contains("Bug Id:") ||
+ !comment.contains("Description:") ||
+ !comment.contains("Device(s):")
+ ) {
+ val implForInsertion =
+ """
* <p>QuirkSummary
* Bug Id:
* Description:
* Device(s):
- """.trimIndent()
+ """
+ .trimIndent()
- val incident = Incident(context)
- .issue(ISSUE)
- .message("CameraX quirks should include this template in the javadoc:" +
- "\n\n$implForInsertion\n\n")
- .location(context.getNameLocation(node))
- .scope(node)
- context.report(incident)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .message(
+ "CameraX quirks should include this template in the javadoc:" +
+ "\n\n$implForInsertion\n\n"
+ )
+ .location(context.getNameLocation(node))
+ .scope(node)
+ context.report(incident)
+ }
}
}
}
- }
companion object {
- val ISSUE = Issue.create(
- id = "CameraXQuirksClassDetector",
- briefDescription = "CameraQuirks include @QuirkSummary in the javadoc",
- explanation = "CameraX quirks should include @QuirkSummary in the javadoc.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- enabledByDefault = false,
- implementation = Implementation(
- CameraXQuirksClassDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val ISSUE =
+ Issue.create(
+ id = "CameraXQuirksClassDetector",
+ briefDescription = "CameraQuirks include @QuirkSummary in the javadoc",
+ explanation = "CameraX quirks should include @QuirkSummary in the javadoc.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ enabledByDefault = false,
+ implementation =
+ Implementation(CameraXQuirksClassDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/ClassVerificationFailureDetector.kt b/lint-checks/src/main/java/androidx/build/lint/ClassVerificationFailureDetector.kt
index 9a36c5f..ef3e421 100644
--- a/lint-checks/src/main/java/androidx/build/lint/ClassVerificationFailureDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/ClassVerificationFailureDetector.kt
@@ -87,12 +87,14 @@
/**
* This check detects references to platform APIs that are likely to cause class verification
* failures.
+ *
* <p>
* Specifically, this check looks for references to APIs that were added prior to the library's
* minSdkVersion and therefore may not exist on the run-time classpath. If the class verifier
* detects such a reference, e.g. while verifying a class containing the reference, it will abort
* verification. This will prevent the class from being optimized, resulting in potentially severe
* performance losses.
+ *
* <p>
* See Chromium's excellent guide to Class Verification Failures for more information:
* https://chromium.googlesource.com/chromium/src/+/HEAD/build/android/docs/class_verification_failures.md
@@ -100,9 +102,7 @@
class ClassVerificationFailureDetector : Detector(), SourceCodeScanner {
private var apiDatabase: ApiLookup? = null
- /**
- * Copied from ApiDetector.kt
- */
+ /** Copied from ApiDetector.kt */
override fun beforeCheckRootProject(context: Context) {
if (apiDatabase == null) {
apiDatabase = ApiLookup.get(context.client, context.project.buildTarget)
@@ -113,12 +113,9 @@
}
}
- /**
- * Copied from ApiDetector.kt
- */
+ /** Copied from ApiDetector.kt */
private fun getMinSdk(context: Context): Int {
- val project = if (context.isGlobalAnalysis())
- context.mainProject else context.project
+ val project = if (context.isGlobalAnalysis()) context.mainProject else context.project
return if (!project.isAndroidProject) {
// Don't flag API checks in non-Android projects
Integer.MAX_VALUE
@@ -127,15 +124,12 @@
}
}
- /**
- * Copied from ApiDetector.kt
- */
+ /** Copied from ApiDetector.kt */
override fun createUastHandler(context: JavaContext): UElementHandler? {
if (apiDatabase == null || context.isTestSource && !context.driver.checkTestSources) {
return null
}
- val project = if (context.isGlobalAnalysis())
- context.mainProject else context.project
+ val project = if (context.isGlobalAnalysis()) context.mainProject else context.project
return if (project.isAndroidProject) {
ApiVisitor(context)
} else {
@@ -185,21 +179,25 @@
}
// This can currently only do an autofix for Java source, not Kotlin.
- val fix = if (isKotlin(node.lang)) {
- null
- } else {
- createCastFix(node, actualType, expectedType, actualTypeApi)
- }
+ val fix =
+ if (isKotlin(node.lang)) {
+ null
+ } else {
+ createCastFix(node, actualType, expectedType, actualTypeApi)
+ }
- val incident = Incident(context)
- .issue(IMPLICIT_CAST_ISSUE)
- .fix(fix)
- .location(context.getLocation(node))
- .message("This expression has type $actualTypeStr (introduced in API level " +
- "$actualTypeApi) but it used as type $expectedTypeStr (introduced in API " +
- "level $expectedTypeApi). Run-time class verification will not be able to " +
- "validate this implicit cast on devices between these API levels.")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(IMPLICIT_CAST_ISSUE)
+ .fix(fix)
+ .location(context.getLocation(node))
+ .message(
+ "This expression has type $actualTypeStr (introduced in API level " +
+ "$actualTypeApi) but it used as type $expectedTypeStr (introduced in API " +
+ "level $expectedTypeApi). Run-time class verification will not be able to " +
+ "validate this implicit cast on devices between these API levels."
+ )
+ .scope(node)
// If the actual type exists at minSdk, this isn't an invalid cast -- report the
// incident, conditional on minSdk being lower than the actual type API.
@@ -233,11 +231,7 @@
* - Removed unused values (signature, desugaring, fqcn)
* - Replaced `report` call with a call to `visitNewApiCall`
*/
- private fun visitCall(
- method: PsiMethod,
- call: UCallExpression,
- reference: UElement
- ) {
+ private fun visitCall(method: PsiMethod, call: UCallExpression, reference: UElement) {
val apiDatabase = apiDatabase ?: return
val containingClass = method.containingClass ?: return
@@ -246,8 +240,8 @@
// Change: Removed PsiCompiledElement check
val evaluator = context.evaluator
- val owner = evaluator.getQualifiedName(containingClass)
- ?: return // Couldn't resolve type
+ val owner =
+ evaluator.getQualifiedName(containingClass) ?: return // Couldn't resolve type
// Change: Removed special-casing of Support library
@@ -256,13 +250,14 @@
}
val name = getInternalMethodName(method)
- val desc = evaluator.getMethodDescription(
- method,
- includeName = false,
- includeReturn = false
- ) // Couldn't compute description of method for some reason; probably
- // failure to resolve parameter types
- ?: return
+ val desc =
+ evaluator.getMethodDescription(
+ method,
+ includeName = false,
+ includeReturn = false
+ ) // Couldn't compute description of method for some reason; probably
+ // failure to resolve parameter types
+ ?: return
// Change: Removed SimpleDateFormat and Animator checks
@Suppress("DEPRECATION") // b/262915628
@@ -275,11 +270,12 @@
return
}
- val receiver = if (call.isMethodCall()) {
- call.receiver
- } else {
- null
- }
+ val receiver =
+ if (call.isMethodCall()) {
+ call.receiver
+ } else {
+ null
+ }
// The lint API database contains two optimizations:
// First, all members that were available in API 1 are omitted from the database,
@@ -310,23 +306,22 @@
// then check the package prefix to see whether we know it's an API method whose
// members should all have been inlined.
if (call.isMethodCall()) {
- if (receiver != null &&
- receiver !is UThisExpression &&
- receiver !is PsiSuperExpression
+ if (
+ receiver != null &&
+ receiver !is UThisExpression &&
+ receiver !is PsiSuperExpression
) {
val receiverType = receiver.getExpressionType()
if (receiverType is PsiClassType) {
val containingType = context.evaluator.getClassType(containingClass)
- val inheritanceChain =
- getInheritanceChain(receiverType, containingType)
+ val inheritanceChain = getInheritanceChain(receiverType, containingType)
if (inheritanceChain != null) {
for (type in inheritanceChain) {
val expressionOwner = evaluator.getQualifiedName(type)
if (expressionOwner != null && expressionOwner != owner) {
@Suppress("DEPRECATION") // b/262915628
- val specificApi = apiDatabase.getMethodVersion(
- expressionOwner, name, desc
- )
+ val specificApi =
+ apiDatabase.getMethodVersion(expressionOwner, name, desc)
if (specificApi == -1) {
if (apiDatabase.isRelevantOwner(expressionOwner)) {
return
@@ -334,10 +329,13 @@
} else if (specificApi <= minSdk) {
return
} else {
- // For example, for Bundle#getString(String,String) the API level
- // is 12, whereas for BaseBundle#getString(String,String) the API
+ // For example, for Bundle#getString(String,String) the API
+ // level
+ // is 12, whereas for BaseBundle#getString(String,String)
+ // the API
// level is 21. If the code specified a Bundle instead of
- // a BaseBundle, reported the Bundle level in the error message
+ // a BaseBundle, reported the Bundle level in the error
+ // message
// instead.
if (specificApi < api) {
api = specificApi
@@ -372,20 +370,18 @@
var found = false
val anonymousBaseType = cls.baseClassType
val anonymousBase = anonymousBaseType.resolve()
- if (anonymousBase != null && anonymousBase.isInheritor(
- containingClass,
- true
- )
+ if (
+ anonymousBase != null &&
+ anonymousBase.isInheritor(containingClass, true)
) {
cls = anonymousBase
found = true
} else {
val surroundingBaseType =
PsiTreeUtil.getParentOfType(cls, PsiClass::class.java, true)
- if (surroundingBaseType != null && surroundingBaseType.isInheritor(
- containingClass,
- true
- )
+ if (
+ surroundingBaseType != null &&
+ surroundingBaseType.isInheritor(containingClass, true)
) {
cls = surroundingBaseType
found = true
@@ -396,16 +392,14 @@
}
}
val expressionOwner = evaluator.getQualifiedName(cls)
- if (expressionOwner == null || equivalentName(
- expressionOwner,
- "java/lang/Object"
- )
+ if (
+ expressionOwner == null ||
+ equivalentName(expressionOwner, "java/lang/Object")
) {
break
}
@Suppress("DEPRECATION") // b/262915628
- val specificApi =
- apiDatabase.getMethodVersion(expressionOwner, name, desc)
+ val specificApi = apiDatabase.getMethodVersion(expressionOwner, name, desc)
if (specificApi == -1) {
if (apiDatabase.isRelevantOwner(expressionOwner)) {
break
@@ -451,11 +445,11 @@
val methodOwner = evaluator.getQualifiedName(provider)
if (methodOwner != null) {
@Suppress("DEPRECATION") // b/262915628
- val methodApi = apiDatabase.getMethodVersion(
- methodOwner, name, desc
- )
+ val methodApi =
+ apiDatabase.getMethodVersion(methodOwner, name, desc)
if (methodApi == -1 || methodApi <= minSdk) {
- // Yes, we found another call that doesn't have an API requirement
+ // Yes, we found another call that doesn't have an API
+ // requirement
return
}
}
@@ -471,14 +465,15 @@
// calling that method locally in other contexts, but this is hopefully unlikely.)
if (receiver is USuperExpression) {
val containingMethod = call.getContainingUMethod()?.javaPsi
- if (containingMethod != null &&
- name == containingMethod.name &&
- evaluator.areSignaturesEqual(method, containingMethod) &&
- // We specifically exclude constructors from this check, because we
- // do want to flag constructors requiring the new API level; it's
- // highly likely that the constructor is called by local code so
- // you should specifically investigate this as a developer
- !method.isConstructor
+ if (
+ containingMethod != null &&
+ name == containingMethod.name &&
+ evaluator.areSignaturesEqual(method, containingMethod) &&
+ // We specifically exclude constructors from this check, because we
+ // do want to flag constructors requiring the new API level; it's
+ // highly likely that the constructor is called by local code so
+ // you should specifically investigate this as a developer
+ !method.isConstructor
) {
return
}
@@ -486,16 +481,25 @@
}
// Builtin R8 desugaring, such as rewriting compare calls (see b/36390874)
- if (owner.startsWith("java.") &&
- DesugaredMethodLookup.isDesugaredMethod(owner, name, desc, context.sourceSetType)) {
+ if (
+ owner.startsWith("java.") &&
+ DesugaredMethodLookup.isDesugaredMethod(
+ owner,
+ name,
+ desc,
+ context.sourceSetType
+ )
+ ) {
return
}
// These methods are not included in the R8 backported list so handle them manually
// the way R8 seems to
- if (api == 19 && owner == "java.lang.Throwable" &&
- (name == "addSuppressed" && desc == "(Ljava.lang.Throwable;)" ||
- name == "getSuppressed" && desc == "()")
+ if (
+ api == 19 &&
+ owner == "java.lang.Throwable" &&
+ (name == "addSuppressed" && desc == "(Ljava.lang.Throwable;)" ||
+ name == "getSuppressed" && desc == "()")
) {
if (context.project.isDesugaring(Desugaring.TRY_WITH_RESOURCES)) {
return
@@ -505,13 +509,14 @@
// Change: Removed signature generation
val nameIdentifier = call.methodIdentifier
- val location = if (call.isConstructorCall() && call.classReference != null) {
- context.getRangeLocation(call, 0, call.classReference!!, 0)
- } else if (nameIdentifier != null) {
- context.getLocation(nameIdentifier)
- } else {
- context.getLocation(reference)
- }
+ val location =
+ if (call.isConstructorCall() && call.classReference != null) {
+ context.getRangeLocation(call, 0, call.classReference!!, 0)
+ } else if (nameIdentifier != null) {
+ context.getLocation(nameIdentifier)
+ } else {
+ context.getLocation(reference)
+ }
// Change: Replaced `report` call with a call to `visitNewApiCall`
visitNewApiCall(call, method, api, reference, location)
@@ -525,9 +530,8 @@
var classUnderInspection: UClass? = this.getContainingUClass() ?: return false
while (classUnderInspection != null) {
- val potentialRequiresApiVersion = getRequiresApiFromAnnotations(
- classUnderInspection.javaPsi
- )
+ val potentialRequiresApiVersion =
+ getRequiresApiFromAnnotations(classUnderInspection.javaPsi)
if (potentialRequiresApiVersion >= minApi) {
return true
@@ -550,14 +554,17 @@
// call.getContainingUClass()!! refers to the direct parent class of this method
val containingClassName = call.getContainingUClass()!!.qualifiedName.toString()
val lintFix = createCallFix(method, call, api)
- val incident = Incident(context)
- .fix(lintFix)
- .issue(METHOD_CALL_ISSUE)
- .location(location)
- .message("This call references a method added in API level $api; however, the " +
- "containing class $containingClassName is reachable from earlier API " +
- "levels and will fail run-time class verification.")
- .scope(reference)
+ val incident =
+ Incident(context)
+ .fix(lintFix)
+ .issue(METHOD_CALL_ISSUE)
+ .location(location)
+ .message(
+ "This call references a method added in API level $api; however, the " +
+ "containing class $containingClassName is reachable from earlier API " +
+ "levels and will fail run-time class verification."
+ )
+ .scope(reference)
context.report(incident)
}
@@ -567,11 +574,7 @@
*
* @return a lint fix, or `null` if no fix could be created
*/
- private fun createCallFix(
- method: PsiMethod,
- call: UCallExpression,
- api: Int
- ): LintFix? {
+ private fun createCallFix(method: PsiMethod, call: UCallExpression, api: Int): LintFix? {
val callPsi = call.sourcePsi ?: return null
if (isKotlin(callPsi.language)) {
// We only support Java right now.
@@ -589,21 +592,23 @@
call.valueArguments.map { it.getExpressionType() }
) ?: return null
- val (wrapperClassName, insertionPoint, insertionSource) = generateInsertionSource(
- api,
- callContainingClass,
- wrapperMethodName,
- wrapperMethodParams,
- methodForInsertion
- )
+ val (wrapperClassName, insertionPoint, insertionSource) =
+ generateInsertionSource(
+ api,
+ callContainingClass,
+ wrapperMethodName,
+ wrapperMethodParams,
+ methodForInsertion
+ )
- val replacementCall = generateWrapperCall(
- method,
- call.receiver,
- call.valueArguments,
- wrapperClassName,
- wrapperMethodName
- )
+ val replacementCall =
+ generateWrapperCall(
+ method,
+ call.receiver,
+ call.valueArguments,
+ wrapperClassName,
+ wrapperMethodName
+ )
return createCompositeFix(call, replacementCall, insertionPoint, insertionSource)
}
@@ -643,35 +648,33 @@
insertionPoint: Location?,
insertionSource: String?
): LintFix {
- val fix = fix()
- .name("Extract to static inner class")
- .composite()
+ val fix = fix().name("Extract to static inner class").composite()
if (insertionPoint != null) {
- fix.add(fix()
- .replace()
- .range(insertionPoint)
- .beginning()
- .with(insertionSource)
- .shortenNames()
- .build()
+ fix.add(
+ fix()
+ .replace()
+ .range(insertionPoint)
+ .beginning()
+ .with(insertionSource)
+ .shortenNames()
+ .build()
)
}
- fix.add(fix()
- .replace()
- .range(context.getLocation(node))
- .with(replacementCall)
- .shortenNames()
- .build()
+ fix.add(
+ fix()
+ .replace()
+ .range(context.getLocation(node))
+ .with(replacementCall)
+ .shortenNames()
+ .build()
)
return fix.build()
}
- /**
- * Find what type the parent of the element is expecting the element to be.
- */
+ /** Find what type the parent of the element is expecting the element to be. */
private fun getExpectedTypeByParent(element: UElement): PsiType? {
val psi = element.sourcePsi
if (psi == null) {
@@ -701,7 +704,8 @@
// Special case the last arguments to a varargs method
return if (method.isVarArgs && paramIndex >= finalParamIndex) {
(method.parameterList.getParameter(finalParamIndex)?.type
- as? PsiEllipsisType)?.componentType
+ as? PsiEllipsisType)
+ ?.componentType
} else {
method.parameterList.getParameter(paramIndex)?.type
}
@@ -724,8 +728,9 @@
} else if (parent is KtBinaryExpression) {
// Handles the case when the expression is the right side of a Kotlin assignment.
val uParent = (parent.toUElement() as? UBinaryExpression) ?: return null
- if (uParent.operator == UastBinaryOperator.ASSIGN &&
- childOfParent == parent.right) {
+ if (
+ uParent.operator == UastBinaryOperator.ASSIGN && childOfParent == parent.right
+ ) {
return uParent.leftOperand.getExpressionType()
}
}
@@ -750,11 +755,10 @@
* Generates source code for a wrapper method and class (where applicable) and calculates
* the insertion point. If the wrapper class already exists, returns source code for the
* method body only with an insertion point at the end of the existing wrapper class body.
- * If the wrapper class and method both already exists, just returns the name of the
- * wrapper class.
+ * If the wrapper class and method both already exists, just returns the name of the wrapper
+ * class.
*
* Source code follows the general format:
- *
* ```java
* @RequiresApi(21)
* static class Api21Impl {
@@ -766,14 +770,14 @@
* @param api API level at which the platform method can be safely called
* @param callContainingClass Class containing the call to the platform method
* @param wrapperMethodName The name of the wrapper method, used to check if the wrapper
- * method already exists
+ * method already exists
* @param wrapperMethodParams List of the types of the wrapper method's parameters, used to
- * check if the wrapper method already exists
+ * check if the wrapper method already exists
* @param wrapperMethodBody Source code for the wrapper method
* @return Triple containing (1) the name of the static wrapper class, (2) the insertion
- * point for the generated source code (or null if the wrapper method already exists), and
- * (3) generated source code for a static wrapper method, including a static wrapper class
- * if necessary (or null if the wrapper method already exists)
+ * point for the generated source code (or null if the wrapper method already exists), and
+ * (3) generated source code for a static wrapper method, including a static wrapper class
+ * if necessary (or null if the wrapper method already exists)
*/
private fun generateInsertionSource(
api: Int,
@@ -786,13 +790,15 @@
val implInsertionPoint: Location?
val implForInsertion: String?
- val existingWrapperClass = callContainingClass.innerClasses.find { innerClass ->
- innerClass.name == wrapperClassName
- }
+ val existingWrapperClass =
+ callContainingClass.innerClasses.find { innerClass ->
+ innerClass.name == wrapperClassName
+ }
if (existingWrapperClass == null) {
implInsertionPoint = context.getLocation(callContainingClass.lastChild)
- implForInsertion = """
+ implForInsertion =
+ """
@androidx.annotation.RequiresApi($api)
static class $wrapperClassName {
private $wrapperClassName() {
@@ -801,12 +807,14 @@
$wrapperMethodBody
}
- """.trimIndent()
+ """
+ .trimIndent()
} else {
- val existingWrapperMethod = existingWrapperClass.methods.find { method ->
- method.name == wrapperMethodName &&
- wrapperMethodParams == getParameterTypes(method)
- }
+ val existingWrapperMethod =
+ existingWrapperClass.methods.find { method ->
+ method.name == wrapperMethodName &&
+ wrapperMethodParams == getParameterTypes(method)
+ }
if (existingWrapperMethod == null) {
implInsertionPoint = context.getLocation(existingWrapperClass.lastChild)
// Add a newline to force the `}`s for the class and method onto different lines
@@ -817,18 +825,13 @@
}
}
- return Triple(
- wrapperClassName,
- implInsertionPoint,
- implForInsertion
- )
+ return Triple(wrapperClassName, implInsertionPoint, implForInsertion)
}
/**
* Generates source code for a call to the generated wrapper method.
*
* Source code follows the general format:
- *
* ```
* WrapperClassName.wrapperMethodName(receiverVar, argumentVar)
* ```
@@ -847,29 +850,27 @@
wrapperClassName: String,
wrapperMethodName: String
): String {
- val callReceiverStr = when {
- // Static method
- context.evaluator.isStatic(method) ->
- null
- // Constructor
- method.isConstructor ->
- null
- // If there is no call receiver, and the method isn't a constructor or static,
- // it must be a call to an instance method using `this` implicitly.
- callReceiver == null ->
- "this"
- // Otherwise, use the original call receiver string (removing extra parens)
- else ->
- unwrapExpression(callReceiver).asSourceString()
- }
-
- val callValues = if (callValueArguments.isNotEmpty()) {
- callValueArguments.joinToString(separator = ", ") { argument ->
- argument.asSourceString()
+ val callReceiverStr =
+ when {
+ // Static method
+ context.evaluator.isStatic(method) -> null
+ // Constructor
+ method.isConstructor -> null
+ // If there is no call receiver, and the method isn't a constructor or static,
+ // it must be a call to an instance method using `this` implicitly.
+ callReceiver == null -> "this"
+ // Otherwise, use the original call receiver string (removing extra parens)
+ else -> unwrapExpression(callReceiver).asSourceString()
}
- } else {
- null
- }
+
+ val callValues =
+ if (callValueArguments.isNotEmpty()) {
+ callValueArguments.joinToString(separator = ", ") { argument ->
+ argument.asSourceString()
+ }
+ } else {
+ null
+ }
val replacementArgs = listOfNotNull(callReceiverStr, callValues).joinToString(", ")
@@ -894,7 +895,6 @@
* source code.
*
* Source code follows the general format:
- *
* ```
* @DoNotInline
* static ReturnType methodName(HostType hostType, ParamType paramType) {
@@ -904,7 +904,7 @@
*
* @param method Platform method which is being called
* @return Pair containing (1) the name of the static wrapper method and (2) generated
- * source code for a static wrapper around the platform method
+ * source code for a static wrapper around the platform method
*/
private fun generateWrapperMethod(
method: PsiMethod,
@@ -923,38 +923,50 @@
val hostClassName = containingClass.name ?: return null
val hostVar = hostClassName[0].lowercaseChar() + hostClassName.substring(1)
- val hostParam = if (isStatic || isConstructor) { null } else { "$hostType $hostVar" }
-
- val typeParamsStr = if (method.typeParameters.isNotEmpty()) {
- "<${method.typeParameters.joinToString(", ") { param -> "${param.name}" }}> "
- } else {
- ""
- }
-
- val paramsWithTypes = method.parameters.mapIndexed { i, param ->
- // It's possible for i to be out of bounds due to varargs
- val expectedType = expectedParamTypes.getOrNull(i)
- val actualType = (param.type as? PsiType) ?: return null
- // If the actual type isn't a PsiEllipsisType (the method is varargs) and casting
- // from the expected to the actual type would be an invalid implicit cast, use
- // the expected type. Otherwise, use the actual type.
- val typeToUse = if (expectedType != null && actualType !is PsiEllipsisType &&
- isInvalidCast(expectedType.canonicalText, actualType.canonicalText)) {
- expectedType
+ val hostParam =
+ if (isStatic || isConstructor) {
+ null
} else {
- actualType
+ "$hostType $hostVar"
}
- Pair(typeToUse, param.name)
- }
+
+ val typeParamsStr =
+ if (method.typeParameters.isNotEmpty()) {
+ "<${method.typeParameters.joinToString(", ") { param -> "${param.name}" }}> "
+ } else {
+ ""
+ }
+
+ val paramsWithTypes =
+ method.parameters.mapIndexed { i, param ->
+ // It's possible for i to be out of bounds due to varargs
+ val expectedType = expectedParamTypes.getOrNull(i)
+ val actualType = (param.type as? PsiType) ?: return null
+ // If the actual type isn't a PsiEllipsisType (the method is varargs) and
+ // casting
+ // from the expected to the actual type would be an invalid implicit cast, use
+ // the expected type. Otherwise, use the actual type.
+ val typeToUse =
+ if (
+ expectedType != null &&
+ actualType !is PsiEllipsisType &&
+ isInvalidCast(expectedType.canonicalText, actualType.canonicalText)
+ ) {
+ expectedType
+ } else {
+ actualType
+ }
+ Pair(typeToUse, param.name)
+ }
val paramStrings = paramsWithTypes.map { (type, name) -> "${type.canonicalText} $name" }
val typedParamsStr = (listOfNotNull(hostParam) + paramStrings).joinToString(", ")
- val paramTypes = listOf(PsiTypesUtil.getClassType(containingClass)) +
- paramsWithTypes.map { (type, _) -> type }
+ val paramTypes =
+ listOf(PsiTypesUtil.getClassType(containingClass)) +
+ paramsWithTypes.map { (type, _) -> type }
- val namedParamsStr = method.parameters.joinToString(separator = ", ") { param ->
- "${param.name}"
- }
+ val namedParamsStr =
+ method.parameters.joinToString(separator = ", ") { param -> "${param.name}" }
val methodName: String
var wrapperMethodName: String
@@ -984,8 +996,11 @@
if (expectedReturnType != null && expectedReturnType.canonicalText != returnTypeStr) {
returnTypeStr = expectedReturnType.canonicalText
wrapperMethodName += "Returns${expectedReturnType.presentableText}"
- } else if (expectedReturnType == null && returnTypeStr != "void" &&
- !classAvailableAtMinSdk(returnTypeStr)) {
+ } else if (
+ expectedReturnType == null &&
+ returnTypeStr != "void" &&
+ !classAvailableAtMinSdk(returnTypeStr)
+ ) {
// This method returns a value of a type that isn't available at the min SDK.
// The expected return type is null either because the returned value isn't used or
// getExpectedTypeByParent didn't know how it is used. In case it is used and is
@@ -1008,9 +1023,7 @@
)
}
- /**
- * Creates a method to cast from [fromType] to [toType].
- */
+ /** Creates a method to cast from [fromType] to [toType]. */
private fun generateCastingMethod(
fromType: PsiType,
toType: PsiType
@@ -1034,9 +1047,9 @@
* Checks if a cast from [fromType] to [toType] would be invalid, which is true when there
* is an API level where [toType] exists but [fromType] does now.
*
- * Allows optionally passing in [knownFromTypeApi] and [knownToTypeApi], the API levels
- * when [fromType] and [toType] were introduced, respectively, if that has already been
- * computed. The values will be looked up if not passed in.
+ * Allows optionally passing in [knownFromTypeApi] and [knownToTypeApi], the API levels when
+ * [fromType] and [toType] were introduced, respectively, if that has already been computed.
+ * The values will be looked up if not passed in.
*/
private fun isInvalidCast(
fromType: String,
@@ -1057,15 +1070,11 @@
return fromTypeApi > toTypeApi
}
- /**
- * Returns a list of the method's parameter types.
- */
+ /** Returns a list of the method's parameter types. */
private fun getParameterTypes(method: PsiMethod): List<PsiType> =
method.parameterList.parameters.map { it.type }
- /**
- * Check if the specified class is available at the min SDK.
- */
+ /** Check if the specified class is available at the min SDK. */
private fun classAvailableAtMinSdk(className: String): Boolean {
val apiDatabase = apiDatabase ?: return false
val minSdk = getMinSdk(context)
@@ -1074,9 +1083,7 @@
return version <= minSdk
}
- /**
- * Returns the API level this class was introduced at, or null if unknown.
- */
+ /** Returns the API level this class was introduced at, or null if unknown. */
private fun getMinApiOfClass(className: String): Int? {
val apiDatabase = apiDatabase ?: return null
if (!apiDatabase.containsClass(className)) return null
@@ -1120,14 +1127,24 @@
for (annotation in context.evaluator.getAnnotations(modifierListOwner)) {
val qualifiedName = annotation.qualifiedName
if (REQUIRES_API_ANNOTATION.isEquals(qualifiedName)) {
- var api = getLongAttribute(
- context, annotation,
- ATTR_VALUE, NO_API_REQUIREMENT.toLong()
- ).toInt()
+ var api =
+ getLongAttribute(
+ context,
+ annotation,
+ ATTR_VALUE,
+ NO_API_REQUIREMENT.toLong()
+ )
+ .toInt()
if (api <= 1) {
// @RequiresApi has two aliasing attributes: api and value
- api = getLongAttribute(
- context, annotation, "api", NO_API_REQUIREMENT.toLong()).toInt()
+ api =
+ getLongAttribute(
+ context,
+ annotation,
+ "api",
+ NO_API_REQUIREMENT.toLong()
+ )
+ .toInt()
}
return api
} else if (qualifiedName == null) {
@@ -1174,13 +1191,14 @@
companion object {
const val NO_API_REQUIREMENT = -1
- val METHOD_CALL_ISSUE = Issue.create(
- "ClassVerificationFailure",
- "Even in cases where references to new APIs are gated on SDK_INT " +
- "checks, run-time class verification will still fail on references to APIs that " +
- "may not be available at run time, including platform APIs introduced after a " +
- "library's minSdkVersion.",
- """
+ val METHOD_CALL_ISSUE =
+ Issue.create(
+ "ClassVerificationFailure",
+ "Even in cases where references to new APIs are gated on SDK_INT " +
+ "checks, run-time class verification will still fail on references to APIs that " +
+ "may not be available at run time, including platform APIs introduced after a " +
+ "library's minSdkVersion.",
+ """
References to APIs added after a library's minSdkVersion -- regardless of
any surrounding version checks -- will fail run-time class verification if
the API does not exist on the device, leading to reduced run-time performance.
@@ -1191,15 +1209,22 @@
For more details and an example, see go/androidx-api-guidelines#compat-sdk.
""",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(ClassVerificationFailureDetector::class.java, Scope.JAVA_FILE_SCOPE)
- ).setAndroidSpecific(true)
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ ClassVerificationFailureDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+ .setAndroidSpecific(true)
- val IMPLICIT_CAST_ISSUE = Issue.create(
- "ImplicitCastClassVerificationFailure",
- "Run-time class verification will fail when a type introduced at an " +
- "API level is used as another type which was introduced at a lower API level.",
- """
+ val IMPLICIT_CAST_ISSUE =
+ Issue.create(
+ "ImplicitCastClassVerificationFailure",
+ "Run-time class verification will fail when a type introduced at an " +
+ "API level is used as another type which was introduced at a lower API level.",
+ """
When a type does not exist on a device, the verifier treats the type as Object. This
is a problem if the new type is implicitly cast to a different type which does exist
on the device.
@@ -1211,8 +1236,14 @@
For some examples, see go/androidx-api-guidelines#compat-casting
""",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(ClassVerificationFailureDetector::class.java, Scope.JAVA_FILE_SCOPE)
- ).setAndroidSpecific(true)
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ ClassVerificationFailureDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+ .setAndroidSpecific(true)
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/DeprecationMismatchDetector.kt b/lint-checks/src/main/java/androidx/build/lint/DeprecationMismatchDetector.kt
index a107a58..b4f6744 100644
--- a/lint-checks/src/main/java/androidx/build/lint/DeprecationMismatchDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/DeprecationMismatchDetector.kt
@@ -60,54 +60,74 @@
if (hasDeprecatedDocTag == hasDeprecatedAnnotation) return
// Proto-generated files are not part of the public API surface
- if (node.containingFile.children.filterIsInstance<PsiComment>().any {
- it.text.contains("Generated by the protocol buffer compiler. DO NOT EDIT!")
- }) return
+ if (
+ node.containingFile.children.filterIsInstance<PsiComment>().any {
+ it.text.contains("Generated by the protocol buffer compiler. DO NOT EDIT!")
+ }
+ )
+ return
// Methods that override deprecated methods can inherit docs from the original method
- if (node is UMethod && node.hasAnnotation(OVERRIDE_ANNOTATION) &&
- (node.comments.isEmpty() ||
- node.comments.any { it.text.contains("@inheritDoc") })) return
+ if (
+ node is UMethod &&
+ node.hasAnnotation(OVERRIDE_ANNOTATION) &&
+ (node.comments.isEmpty() ||
+ node.comments.any { it.text.contains("@inheritDoc") })
+ )
+ return
// @RestrictTo elements aren't part of the public API surface
- if (node.hasAnnotation(RESTRICT_TO_ANNOTATION) ||
- node.getContainingUClass()?.hasAnnotation(RESTRICT_TO_ANNOTATION) == true) return
+ if (
+ node.hasAnnotation(RESTRICT_TO_ANNOTATION) ||
+ node.getContainingUClass()?.hasAnnotation(RESTRICT_TO_ANNOTATION) == true
+ )
+ return
// The mismatch is in a public API, report the error
- val baseIncident = Incident(context)
- .issue(ISSUE)
- .location(context.getLocation(node, LocationType.NAME))
- .scope(node)
+ val baseIncident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getLocation(node, LocationType.NAME))
+ .scope(node)
- val incident = if (hasDeprecatedAnnotation) {
- // No auto-fix for this case since developers should write a comment with details
- baseIncident
- .message("Items annotated with @Deprecated must have a @deprecated doc tag")
- } else {
- val fix = fix()
- .name("Annotate with @Deprecated")
- .annotate(DEPRECATED_ANNOTATION, context, node)
- .autoFix()
- .build()
+ val incident =
+ if (hasDeprecatedAnnotation) {
+ // No auto-fix for this case since developers should write a comment with
+ // details
+ baseIncident.message(
+ "Items annotated with @Deprecated must have a @deprecated doc tag"
+ )
+ } else {
+ val fix =
+ fix()
+ .name("Annotate with @Deprecated")
+ .annotate(DEPRECATED_ANNOTATION, context, node)
+ .autoFix()
+ .build()
- baseIncident
- .fix(fix)
- .message("Items with a @deprecated doc tag must be annotated with @Deprecated")
- }
+ baseIncident
+ .fix(fix)
+ .message(
+ "Items with a @deprecated doc tag must be annotated with @Deprecated"
+ )
+ }
context.report(incident)
}
}
companion object {
- val ISSUE = Issue.create(
- "DeprecationMismatch",
- "@Deprecated (annotation) and @deprecated (doc tag) must go together",
- "A deprecated API should both be annotated with @Deprecated and have a " +
- "@deprecated doc tag.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(DeprecationMismatchDetector::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "DeprecationMismatch",
+ "@Deprecated (annotation) and @deprecated (doc tag) must go together",
+ "A deprecated API should both be annotated with @Deprecated and have a " +
+ "@deprecated doc tag.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(DeprecationMismatchDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
private const val DEPRECATED_ANNOTATION = "java.lang.Deprecated"
private const val RESTRICT_TO_ANNOTATION = "androidx.annotation.RestrictTo"
diff --git a/lint-checks/src/main/java/androidx/build/lint/ExperimentalPropertyAnnotationDetector.kt b/lint-checks/src/main/java/androidx/build/lint/ExperimentalPropertyAnnotationDetector.kt
index f276074..be8f67a 100644
--- a/lint-checks/src/main/java/androidx/build/lint/ExperimentalPropertyAnnotationDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/ExperimentalPropertyAnnotationDetector.kt
@@ -42,139 +42,154 @@
@Suppress("UnstableApiUsage")
class ExperimentalPropertyAnnotationDetector : Detector(), Detector.UastScanner {
- override fun getApplicableUastTypes(): List<Class<out UElement>> = listOf(
- UAnnotation::class.java
- )
+ override fun getApplicableUastTypes(): List<Class<out UElement>> =
+ listOf(UAnnotation::class.java)
- override fun createUastHandler(context: JavaContext): UElementHandler = object :
- UElementHandler() {
- override fun visitAnnotation(node: UAnnotation) {
- val neededTargets = mutableSetOf(
- AnnotationUseSiteTarget.PROPERTY,
- AnnotationUseSiteTarget.PROPERTY_GETTER,
- AnnotationUseSiteTarget.PROPERTY_SETTER
- )
+ override fun createUastHandler(context: JavaContext): UElementHandler =
+ object : UElementHandler() {
+ override fun visitAnnotation(node: UAnnotation) {
+ val neededTargets =
+ mutableSetOf(
+ AnnotationUseSiteTarget.PROPERTY,
+ AnnotationUseSiteTarget.PROPERTY_GETTER,
+ AnnotationUseSiteTarget.PROPERTY_SETTER
+ )
- // If this annotation is not annotated with an experimental annotation, return
- val resolved = node.resolve()
- if (BanInappropriateExperimentalUsage.APPLICABLE_ANNOTATIONS
- .all { context.evaluator.getAnnotation(resolved, it) == null }) {
- return
- }
-
- val type = node.qualifiedName ?: return
- val source = node.sourcePsi as? KtAnnotationEntry ?: return
-
- // Check that the annotation is applied to a property
- val parent = source.parent?.parent
- if (parent !is KtProperty) return
-
- // Only applies to properties defined at the top level or in classes
- val propertyParent = parent.parent
- if ((propertyParent !is KtClassBody && propertyParent !is KtFile)) return
-
- // Don't apply the lint to private properties
- if (parent.isPrivate()) return
- // Don't apply the lint to properties in private classes
- if (propertyParent.getParentOfType<KtClass>(true)?.isPrivate() == true) return
-
- // Don't apply lint to const properties, because they are static fields in java
- if (parent.modifierList?.node?.findChildByType(KtTokens.CONST_KEYWORD) != null) return
- // Don't apply lint to @JvmField properties, because they are fields in java
- if (parent.annotationEntries.any { it.shortName.toString() == "JvmField" }) return
-
- // Don't apply lint to delegated properties
- if (parent.delegate != null) return
-
- // Annotation on setter is only needed for mutable property with non-private setter
- // Getter annotation is needed because the getter can't be private if the property isn't
- val setter = parent.setter
- if (!parent.isVar || (setter != null && setter.isPrivate())) {
- neededTargets.remove(AnnotationUseSiteTarget.PROPERTY_SETTER)
- }
-
- // Find all usages of this annotation on the property
- val existingTargets = parent.annotationEntries
- .filter { type.endsWith(it.shortName?.identifier ?: "") }
- .map { it.useSiteTarget?.getAnnotationUseSiteTarget() }
-
- val existingTargetSet = existingTargets
- // A null target means the default, which is the property target
- .map { it ?: AnnotationUseSiteTarget.PROPERTY }.toSet()
- val missingTargets = neededTargets - existingTargetSet
-
- if (missingTargets.isEmpty()) return
-
- // If not all annotations are present but more than one is, only report the error on
- // the first annotation to prevent duplicate errors
- val target = source.useSiteTarget?.getAnnotationUseSiteTarget()
- if (existingTargets.size > 1 && existingTargets.indexOf(target) != 0) return
-
- val fix = createFix(type, parent, missingTargets)
- val message = "This property does not have all required annotations to correctly mark" +
- " it as experimental."
- val location = context.getLocation(node)
- val incident = Incident(ISSUE, node, location, message, fix)
- context.report(incident)
- }
-
- private fun createFix(
- annotation: String,
- annotated: PsiElement,
- missingTargets: Set<AnnotationUseSiteTarget>
- ): LintFix {
- val fix = fix()
- .name("Add missing annotations")
- .composite()
-
- for (target in missingTargets) {
- // There's a compilation error when an experimental annotation is applied to a
- // getter: https://kotlinlang.org/docs/opt-in-requirements.html#mark-api-elements
- // Add it anyway because metalava needs it and suppress the error
- if (target == AnnotationUseSiteTarget.PROPERTY_GETTER) {
- val addSuppression = fix()
- .annotate(
- "kotlin.Suppress(\"OPT_IN_MARKER_ON_WRONG_TARGET\")",
- context,
- annotated
- )
- .build()
- fix.add(addSuppression)
+ // If this annotation is not annotated with an experimental annotation, return
+ val resolved = node.resolve()
+ if (
+ BanInappropriateExperimentalUsage.APPLICABLE_ANNOTATIONS.all {
+ context.evaluator.getAnnotation(resolved, it) == null
+ }
+ ) {
+ return
}
- val addAnnotation = fix()
- // With replace = true, the existing annotation with a different target would
- // be replaced. There shouldn't be an existing annotation with this target.
- .annotate(
- target.renderName + ":" + annotation,
- context,
- annotated,
- replace = false
- )
- .build()
- fix.add(addAnnotation)
+ val type = node.qualifiedName ?: return
+ val source = node.sourcePsi as? KtAnnotationEntry ?: return
+
+ // Check that the annotation is applied to a property
+ val parent = source.parent?.parent
+ if (parent !is KtProperty) return
+
+ // Only applies to properties defined at the top level or in classes
+ val propertyParent = parent.parent
+ if ((propertyParent !is KtClassBody && propertyParent !is KtFile)) return
+
+ // Don't apply the lint to private properties
+ if (parent.isPrivate()) return
+ // Don't apply the lint to properties in private classes
+ if (propertyParent.getParentOfType<KtClass>(true)?.isPrivate() == true) return
+
+ // Don't apply lint to const properties, because they are static fields in java
+ if (parent.modifierList?.node?.findChildByType(KtTokens.CONST_KEYWORD) != null)
+ return
+ // Don't apply lint to @JvmField properties, because they are fields in java
+ if (parent.annotationEntries.any { it.shortName.toString() == "JvmField" }) return
+
+ // Don't apply lint to delegated properties
+ if (parent.delegate != null) return
+
+ // Annotation on setter is only needed for mutable property with non-private setter
+ // Getter annotation is needed because the getter can't be private if the property
+ // isn't
+ val setter = parent.setter
+ if (!parent.isVar || (setter != null && setter.isPrivate())) {
+ neededTargets.remove(AnnotationUseSiteTarget.PROPERTY_SETTER)
+ }
+
+ // Find all usages of this annotation on the property
+ val existingTargets =
+ parent.annotationEntries
+ .filter { type.endsWith(it.shortName?.identifier ?: "") }
+ .map { it.useSiteTarget?.getAnnotationUseSiteTarget() }
+
+ val existingTargetSet =
+ existingTargets
+ // A null target means the default, which is the property target
+ .map { it ?: AnnotationUseSiteTarget.PROPERTY }
+ .toSet()
+ val missingTargets = neededTargets - existingTargetSet
+
+ if (missingTargets.isEmpty()) return
+
+ // If not all annotations are present but more than one is, only report the error on
+ // the first annotation to prevent duplicate errors
+ val target = source.useSiteTarget?.getAnnotationUseSiteTarget()
+ if (existingTargets.size > 1 && existingTargets.indexOf(target) != 0) return
+
+ val fix = createFix(type, parent, missingTargets)
+ val message =
+ "This property does not have all required annotations to correctly mark" +
+ " it as experimental."
+ val location = context.getLocation(node)
+ val incident = Incident(ISSUE, node, location, message, fix)
+ context.report(incident)
}
- return fix.build().autoFix()
+ private fun createFix(
+ annotation: String,
+ annotated: PsiElement,
+ missingTargets: Set<AnnotationUseSiteTarget>
+ ): LintFix {
+ val fix = fix().name("Add missing annotations").composite()
+
+ for (target in missingTargets) {
+ // There's a compilation error when an experimental annotation is applied to a
+ // getter:
+ // https://kotlinlang.org/docs/opt-in-requirements.html#mark-api-elements
+ // Add it anyway because metalava needs it and suppress the error
+ if (target == AnnotationUseSiteTarget.PROPERTY_GETTER) {
+ val addSuppression =
+ fix()
+ .annotate(
+ "kotlin.Suppress(\"OPT_IN_MARKER_ON_WRONG_TARGET\")",
+ context,
+ annotated
+ )
+ .build()
+ fix.add(addSuppression)
+ }
+
+ val addAnnotation =
+ fix()
+ // With replace = true, the existing annotation with a different target
+ // would
+ // be replaced. There shouldn't be an existing annotation with this
+ // target.
+ .annotate(
+ target.renderName + ":" + annotation,
+ context,
+ annotated,
+ replace = false
+ )
+ .build()
+ fix.add(addAnnotation)
+ }
+
+ return fix.build().autoFix()
+ }
}
- }
companion object {
- val ISSUE = Issue.create(
- "ExperimentalPropertyAnnotation",
- "Experimental properties need to have annotations targeting the" +
- " property, getter, and (if applicable) setter.",
- "Annotations on Kotlin properties which don't specify a use-site will " +
- "only apply to the private backing field itself, and not to the getter or setter " +
- "(see https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets). " +
- "Annotating the property use-site is required by the Kotlin compiler, the get " +
- "use-site is required by Metalava, and the set use-site is required by Java " +
- "clients, so all use-sites must be annotated.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- ExperimentalPropertyAnnotationDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val ISSUE =
+ Issue.create(
+ "ExperimentalPropertyAnnotation",
+ "Experimental properties need to have annotations targeting the" +
+ " property, getter, and (if applicable) setter.",
+ "Annotations on Kotlin properties which don't specify a use-site will " +
+ "only apply to the private backing field itself, and not to the getter or setter " +
+ "(see https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets). " +
+ "Annotating the property use-site is required by the Kotlin compiler, the get " +
+ "use-site is required by Metalava, and the set use-site is required by Java " +
+ "clients, so all use-sites must be annotated.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ ExperimentalPropertyAnnotationDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/IdeaSuppressionDetector.kt b/lint-checks/src/main/java/androidx/build/lint/IdeaSuppressionDetector.kt
index 95363c8..dfe54ee 100644
--- a/lint-checks/src/main/java/androidx/build/lint/IdeaSuppressionDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/IdeaSuppressionDetector.kt
@@ -115,9 +115,9 @@
/**
* Checks the text in [source].
*
- * If it finds matches in the string, it will report errors into the
- * given context. The associated AST [element] is used to look look
- * up suppress annotations and to find the right error range.
+ * If it finds matches in the string, it will report errors into the given context. The
+ * associated AST [element] is used to look look up suppress annotations and to find the right
+ * error range.
*/
private fun visitComment(
context: JavaContext,
@@ -128,12 +128,15 @@
val warnings = source.split(" ").drop(1).filter { JAVA_WARNINGS.contains(it) }
if (warnings.isNotEmpty()) {
val args = warnings.joinToString(", ") { "\"$it\"" }
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(element))
- .message("Uses IntelliJ-specific suppression, should use" +
- " `@SuppressWarnings($args)`")
- .scope(element)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(element))
+ .message(
+ "Uses IntelliJ-specific suppression, should use" +
+ " `@SuppressWarnings($args)`"
+ )
+ .scope(element)
context.report(incident)
}
}
@@ -141,18 +144,19 @@
companion object {
// Warnings that the Java compiler cares about and should not be suppressed inline.
- private val JAVA_WARNINGS = listOf(
- "deprecation"
- )
+ private val JAVA_WARNINGS = listOf("deprecation")
- val ISSUE = Issue.create(
- "IdeaSuppression",
- "Suppression using `//noinspection` is not supported by the Java compiler",
- "Per-line suppression using `//noinspection` is not supported by the Java compiler " +
- "and will not suppress build-time warnings. Instead, use the `@SuppressWarnings` " +
- "annotation on the containing method or class.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(IdeaSuppressionDetector::class.java, Scope.JAVA_FILE_SCOPE),
- )
+ val ISSUE =
+ Issue.create(
+ "IdeaSuppression",
+ "Suppression using `//noinspection` is not supported by the Java compiler",
+ "Per-line suppression using `//noinspection` is not supported by the Java compiler " +
+ "and will not suppress build-time warnings. Instead, use the `@SuppressWarnings` " +
+ "annotation on the containing method or class.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(IdeaSuppressionDetector::class.java, Scope.JAVA_FILE_SCOPE),
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/IgnoreClassLevelDetector.kt b/lint-checks/src/main/java/androidx/build/lint/IgnoreClassLevelDetector.kt
index c13c991..ca74b01 100644
--- a/lint-checks/src/main/java/androidx/build/lint/IgnoreClassLevelDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/IgnoreClassLevelDetector.kt
@@ -33,9 +33,7 @@
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UClass
-/**
- * Checks for usages of @org.junit.Ignore at the class level.
- */
+/** Checks for usages of @org.junit.Ignore at the class level. */
class IgnoreClassLevelDetector : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
@@ -47,12 +45,15 @@
private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
override fun visitAnnotation(node: UAnnotation) {
if (node.qualifiedName == "org.junit.Ignore" && node.uastParent is UClass) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message("@Ignore should not be used at the class level. Move the annotation " +
- "to each test individually.")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message(
+ "@Ignore should not be used at the class level. Move the annotation " +
+ "to each test individually."
+ )
+ .scope(node)
context.report(incident)
}
}
@@ -66,37 +67,42 @@
*/
@Suppress("unused")
private fun createFix(testClass: UClass, context: JavaContext, annotation: String): LintFix {
- val fix = fix()
- .name("Annotate each test method and remove the class-level annotation")
- .composite()
+ val fix =
+ fix()
+ .name("Annotate each test method and remove the class-level annotation")
+ .composite()
for (method in testClass.allMethods) {
if (method.isTestMethod()) {
- val methodFix = fix()
- // The replace param on annotate doesn't work: if @Ignore is already present on
- // the method, the annotation is added again instead of being replaced.
- .annotate("org.junit.Ignore", context, method, true)
- .build()
+ val methodFix =
+ fix()
+ // The replace param on annotate doesn't work: if @Ignore is already present
+ // on
+ // the method, the annotation is added again instead of being replaced.
+ .annotate("org.junit.Ignore", context, method, true)
+ .build()
fix.add(methodFix)
}
}
- val classFix = fix().replace()
- // This requires the exact text of the class annotation to be passed to this function.
- // This can be gotten with `node.sourcePsi?.node?.text!!`, but `text`'s doc says using
- // it should be avoided, so this isn't the best solution.
- .text(annotation)
- .with("")
- .reformat(true)
- .build()
+ val classFix =
+ fix()
+ .replace()
+ // This requires the exact text of the class annotation to be passed to this
+ // function.
+ // This can be gotten with `node.sourcePsi?.node?.text!!`, but `text`'s doc says
+ // using
+ // it should be avoided, so this isn't the best solution.
+ .text(annotation)
+ .with("")
+ .reformat(true)
+ .build()
fix.add(classFix)
return fix.build()
}
- /**
- * Checks if this PsiMethod has a @org.junit.Test annotation
- */
+ /** Checks if this PsiMethod has a @org.junit.Test annotation */
private fun PsiMethod.isTestMethod(): Boolean {
for (annotation in this.annotations) {
if (annotation.qualifiedName == "org.junit.Test") {
@@ -107,16 +113,19 @@
}
companion object {
- val ISSUE = Issue.create(
- "IgnoreClassLevelDetector",
- "@Ignore should not be used at the class level.",
- "Using @Ignore at the class level instead of annotating each individual " +
- "test causes errors in Android Test Hub.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- IgnoreClassLevelDetector::class.java,
- EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ val ISSUE =
+ Issue.create(
+ "IgnoreClassLevelDetector",
+ "@Ignore should not be used at the class level.",
+ "Using @Ignore at the class level instead of annotating each individual " +
+ "test causes errors in Android Test Hub.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ IgnoreClassLevelDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/LintUtils.kt b/lint-checks/src/main/java/androidx/build/lint/LintUtils.kt
index ab0ba70..7b48cea 100644
--- a/lint-checks/src/main/java/androidx/build/lint/LintUtils.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/LintUtils.kt
@@ -32,7 +32,6 @@
val prefix = element.containingClass?.qualifiedName
(if (prefix != null) "$prefix.$name" else name)
}
-
is KtNamedDeclaration -> element.fqName.toString()
else -> null
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/MetadataTagInsideApplicationTagDetector.kt b/lint-checks/src/main/java/androidx/build/lint/MetadataTagInsideApplicationTagDetector.kt
index 9bef39f..3bb3a31 100644
--- a/lint-checks/src/main/java/androidx/build/lint/MetadataTagInsideApplicationTagDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/MetadataTagInsideApplicationTagDetector.kt
@@ -37,29 +37,33 @@
override fun visitElement(context: XmlContext, element: Element) {
if (element.parentNode.nodeName == NODE_APPLICATION) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getLocation(element))
- .message("Detected <application>-level meta-data tag.")
- .scope(element)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getLocation(element))
+ .message("Detected <application>-level meta-data tag.")
+ .scope(element)
context.report(incident)
}
}
companion object {
- val ISSUE = Issue.create(
- "MetadataTagInsideApplicationTag",
- "Detected <application>-level <meta-data> tag in library manifest",
- "Developers should not add <application>-level <meta-data> tags to library manifests" +
- " because doing so may inadvertently cause denial-of-service attacks against" +
- " other apps. Instead, developers may consider adding <metadata> nested " +
- "inside of placeholder <service> tags.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- MetadataTagInsideApplicationTagDetector::class.java,
- Scope.MANIFEST_SCOPE
+ val ISSUE =
+ Issue.create(
+ "MetadataTagInsideApplicationTag",
+ "Detected <application>-level <meta-data> tag in library manifest",
+ "Developers should not add <application>-level <meta-data> tags to library manifests" +
+ " because doing so may inadvertently cause denial-of-service attacks against" +
+ " other apps. Instead, developers may consider adding <metadata> nested " +
+ "inside of placeholder <service> tags.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ MetadataTagInsideApplicationTagDetector::class.java,
+ Scope.MANIFEST_SCOPE
+ )
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetector.kt b/lint-checks/src/main/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetector.kt
index a773acc..f63f97d 100644
--- a/lint-checks/src/main/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetector.kt
@@ -61,24 +61,29 @@
if (!isKotlin(node.language)) return
if (!node.isInterface) return
- if (node.annotatedWithAnyOf(
+ if (
+ node.annotatedWithAnyOf(
// If the interface is not stable, it doesn't need the annotation
BanInappropriateExperimentalUsage.APPLICABLE_ANNOTATIONS +
// If the interface already has the annotation, it doesn't need it again
- JVM_DEFAULT_WITH_COMPATIBILITY)
- ) return
+ JVM_DEFAULT_WITH_COMPATIBILITY
+ )
+ )
+ return
val stableMethods = node.stableMethods()
if (stableMethods.any { it.hasDefaultImplementation() }) {
- val reason = "This interface must be annotated with @JvmDefaultWithCompatibility " +
- "because it has a stable method with a default implementation"
+ val reason =
+ "This interface must be annotated with @JvmDefaultWithCompatibility " +
+ "because it has a stable method with a default implementation"
reportIncident(node, reason)
return
}
if (stableMethods.any { it.hasParameterWithDefaultValue() }) {
- val reason = "This interface must be annotated with @JvmDefaultWithCompatibility " +
- "because it has a stable method with a parameter with a default value"
+ val reason =
+ "This interface must be annotated with @JvmDefaultWithCompatibility " +
+ "because it has a stable method with a parameter with a default value"
reportIncident(node, reason)
return
}
@@ -86,36 +91,39 @@
// This only checks the interfaces that this interface directly extends, which means if
// A extends B extends C and C is @JvmDefaultWithCompatibility, there will need to be
// two passes of running the check to annotate A and B.
- if (node.interfaces.any {
+ if (
+ node.interfaces.any {
it.annotatedWithAnyOf(listOf(JVM_DEFAULT_WITH_COMPATIBILITY))
- }) {
- val reason = "This interface must be annotated with @JvmDefaultWithCompatibility " +
- "because it implements an interface which uses this annotation"
+ }
+ ) {
+ val reason =
+ "This interface must be annotated with @JvmDefaultWithCompatibility " +
+ "because it implements an interface which uses this annotation"
reportIncident(node, reason)
return
}
}
private fun reportIncident(node: UClass, reason: String) {
- val fix = fix()
- .name("Annotate with @JvmDefaultWithCompatibility")
- .annotate(JVM_DEFAULT_WITH_COMPATIBILITY, context, node)
- .autoFix()
- .build()
+ val fix =
+ fix()
+ .name("Annotate with @JvmDefaultWithCompatibility")
+ .annotate(JVM_DEFAULT_WITH_COMPATIBILITY, context, node)
+ .autoFix()
+ .build()
- val incident = Incident(context)
- .fix(fix)
- .issue(ISSUE)
- .location(context.getLocation(node, LocationType.ALL))
- .message(reason)
- .scope(node)
+ val incident =
+ Incident(context)
+ .fix(fix)
+ .issue(ISSUE)
+ .location(context.getLocation(node, LocationType.ALL))
+ .message(reason)
+ .scope(node)
context.report(incident)
}
- /**
- * Returns a list of the class's stable methods (methods not labelled as experimental).
- */
+ /** Returns a list of the class's stable methods (methods not labelled as experimental). */
private fun UClass.stableMethods(): List<UMethod> =
methods.filter {
!it.annotatedWithAnyOf(BanInappropriateExperimentalUsage.APPLICABLE_ANNOTATIONS)
@@ -130,101 +138,105 @@
qualifiedAnnotationNames: List<String>
): Boolean = annotations.any { qualifiedAnnotationNames.contains(it.qualifiedName) }
- private fun UMethod.hasDefaultImplementation(): Boolean =
- uastBody != null
+ private fun UMethod.hasDefaultImplementation(): Boolean = uastBody != null
private fun UMethod.hasParameterWithDefaultValue(): Boolean =
uastParameters.any { param -> param.uastInitializer != null }
}
companion object {
- val ISSUE = Issue.create(
- "MissingJvmDefaultWithCompatibility",
- "The @JvmDefaultWithCompatibility needs to be used with on applicable " +
- "interfaces when `-Xjvm-default=all` is turned on to preserve compatibility.",
- "Libraries that pass `-Xjvm-default=all` to the Kotlin compiler must " +
- "use the @JvmDefaultWithCompatibility annotation on previously existing " +
- "interfaces with stable methods with default implementations or default parameter" +
- " values, and interfaces that extend other @JvmDefaultWithCompatibility " +
- "interfaces. See go/androidx-api-guidelines#kotlin-jvm-default for more details.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- MissingJvmDefaultWithCompatibilityDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val ISSUE =
+ Issue.create(
+ "MissingJvmDefaultWithCompatibility",
+ "The @JvmDefaultWithCompatibility needs to be used with on applicable " +
+ "interfaces when `-Xjvm-default=all` is turned on to preserve compatibility.",
+ "Libraries that pass `-Xjvm-default=all` to the Kotlin compiler must " +
+ "use the @JvmDefaultWithCompatibility annotation on previously existing " +
+ "interfaces with stable methods with default implementations or default parameter" +
+ " values, and interfaces that extend other @JvmDefaultWithCompatibility " +
+ "interfaces. See go/androidx-api-guidelines#kotlin-jvm-default for more details.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ MissingJvmDefaultWithCompatibilityDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
)
- )
const val JVM_DEFAULT_WITH_COMPATIBILITY = "kotlin.jvm.JvmDefaultWithCompatibility"
// This set of projects was created by running `grep "Xjvm-default=all" . -r` in the
// `frameworks/support` directory and converting the `build.gradle` files in that list to
// this format.
- private val alreadyDefaultAll = setOf(
- "androidx.room.room-compiler-processing",
- "androidx.room.room-migration",
- "androidx.room.room-testing",
- "androidx.room.room-compiler",
- "androidx.room.room-ktx",
- "androidx.room.room-common",
- "androidx.room.room-runtime",
- "androidx.compose.ui.ui",
- "androidx.compose.ui.ui-unit",
- "androidx.compose.ui.ui-tooling-preview",
- "androidx.compose.ui.ui-tooling-data",
- "androidx.compose.ui.ui-util",
- "androidx.compose.ui.ui-test",
- "androidx.compose.ui.ui-test-manifest",
- "androidx.compose.ui.ui-inspection",
- "androidx.compose.ui.ui-viewbinding",
- "androidx.compose.ui.ui-geometry",
- "androidx.compose.ui.ui-graphics",
- "androidx.compose.ui.ui-text",
- "androidx.compose.ui.ui-text-google-fonts",
- "androidx.compose.ui.ui-test-junit4",
- "androidx.compose.ui.ui-tooling",
- "androidx.compose.test-utils",
- "androidx.compose.runtime.runtime",
- "androidx.compose.runtime.runtime-livedata",
- "androidx.compose.runtime.runtime-saveable",
- "androidx.compose.runtime.runtime-rxjava2",
- "androidx.compose.runtime.runtime-tracing",
- "androidx.compose.runtime.runtime-rxjava3",
- "androidx.compose.animation.animation-tooling-internal",
- "androidx.compose.animation.animation",
- "androidx.compose.animation.animation-graphics",
- "androidx.compose.animation.animation-core",
- "androidx.compose.foundation.foundation",
- "androidx.compose.foundation.foundation-layout",
- "androidx.compose.material3.material3-window-size-class",
- "androidx.compose.material3.material3.integration-tests.material3-catalog",
- "androidx.compose.material3.material3",
- "androidx.compose.material.material-ripple",
- "androidx.lifecycle.lifecycle-viewmodel",
- "androidx.sqlite.sqlite-ktx",
- "androidx.sqlite.sqlite-framework",
- "androidx.sqlite.integration-tests.inspection-sqldelight-testapp",
- "androidx.sqlite.integration-tests.inspection-room-testapp",
- "androidx.sqlite.sqlite",
- "androidx.sqlite.sqlite-inspection",
- "androidx.tv.tv-foundation",
- "androidx.tv.tv-material",
- "androidx.window.window",
- "androidx.credentials.credentials",
- "androidx.wear.compose.compose-material",
- "androidx.wear.watchface.watchface-complications-data-source",
- "androidx.wear.watchface.watchface",
- "androidx.wear.watchface.watchface-client",
- "androidx.lifecycle.lifecycle-common",
- // These projects didn't already have "Xjvm-default=al", but the only have the error in
- // integration tests, where the annotation isn't needed.
- "androidx.annotation.annotation-experimental-lint-integration-tests",
- "androidx.annotation.annotation-experimental-lint",
- "androidx.camera.integration-tests.camera-testapp-camera2-pipe",
- "androidx.compose.integration-tests.docs-snippets",
- // These projects are excluded due to b/259578592
- "androidx.camera.camera-camera2-pipe",
- "androidx.camera.camera-camera2-pipe-integration",
- "androidx.camera.camera-camera2-pipe-testing",
- )
+ private val alreadyDefaultAll =
+ setOf(
+ "androidx.room.room-compiler-processing",
+ "androidx.room.room-migration",
+ "androidx.room.room-testing",
+ "androidx.room.room-compiler",
+ "androidx.room.room-ktx",
+ "androidx.room.room-common",
+ "androidx.room.room-runtime",
+ "androidx.compose.ui.ui",
+ "androidx.compose.ui.ui-unit",
+ "androidx.compose.ui.ui-tooling-preview",
+ "androidx.compose.ui.ui-tooling-data",
+ "androidx.compose.ui.ui-util",
+ "androidx.compose.ui.ui-test",
+ "androidx.compose.ui.ui-test-manifest",
+ "androidx.compose.ui.ui-inspection",
+ "androidx.compose.ui.ui-viewbinding",
+ "androidx.compose.ui.ui-geometry",
+ "androidx.compose.ui.ui-graphics",
+ "androidx.compose.ui.ui-text",
+ "androidx.compose.ui.ui-text-google-fonts",
+ "androidx.compose.ui.ui-test-junit4",
+ "androidx.compose.ui.ui-tooling",
+ "androidx.compose.test-utils",
+ "androidx.compose.runtime.runtime",
+ "androidx.compose.runtime.runtime-livedata",
+ "androidx.compose.runtime.runtime-saveable",
+ "androidx.compose.runtime.runtime-rxjava2",
+ "androidx.compose.runtime.runtime-tracing",
+ "androidx.compose.runtime.runtime-rxjava3",
+ "androidx.compose.animation.animation-tooling-internal",
+ "androidx.compose.animation.animation",
+ "androidx.compose.animation.animation-graphics",
+ "androidx.compose.animation.animation-core",
+ "androidx.compose.foundation.foundation",
+ "androidx.compose.foundation.foundation-layout",
+ "androidx.compose.material3.material3-window-size-class",
+ "androidx.compose.material3.material3.integration-tests.material3-catalog",
+ "androidx.compose.material3.material3",
+ "androidx.compose.material.material-ripple",
+ "androidx.lifecycle.lifecycle-viewmodel",
+ "androidx.sqlite.sqlite-ktx",
+ "androidx.sqlite.sqlite-framework",
+ "androidx.sqlite.integration-tests.inspection-sqldelight-testapp",
+ "androidx.sqlite.integration-tests.inspection-room-testapp",
+ "androidx.sqlite.sqlite",
+ "androidx.sqlite.sqlite-inspection",
+ "androidx.tv.tv-foundation",
+ "androidx.tv.tv-material",
+ "androidx.window.window",
+ "androidx.credentials.credentials",
+ "androidx.wear.compose.compose-material",
+ "androidx.wear.watchface.watchface-complications-data-source",
+ "androidx.wear.watchface.watchface",
+ "androidx.wear.watchface.watchface-client",
+ "androidx.lifecycle.lifecycle-common",
+ // These projects didn't already have "Xjvm-default=al", but the only have the error
+ // in
+ // integration tests, where the annotation isn't needed.
+ "androidx.annotation.annotation-experimental-lint-integration-tests",
+ "androidx.annotation.annotation-experimental-lint",
+ "androidx.camera.integration-tests.camera-testapp-camera2-pipe",
+ "androidx.compose.integration-tests.docs-snippets",
+ // These projects are excluded due to b/259578592
+ "androidx.camera.camera-camera2-pipe",
+ "androidx.camera.camera-camera2-pipe-integration",
+ "androidx.camera.camera-camera2-pipe-testing",
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/NullabilityAnnotationsDetector.kt b/lint-checks/src/main/java/androidx/build/lint/NullabilityAnnotationsDetector.kt
index bf0227e..37f1cad 100644
--- a/lint-checks/src/main/java/androidx/build/lint/NullabilityAnnotationsDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/NullabilityAnnotationsDetector.kt
@@ -28,9 +28,7 @@
import com.android.tools.lint.detector.api.isJava
import org.jetbrains.uast.UAnnotation
-/**
- * Checks for usages of JetBrains nullability annotations in Java code.
- */
+/** Checks for usages of JetBrains nullability annotations in Java code. */
class NullabilityAnnotationsDetector : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
@@ -61,33 +59,39 @@
val patternToReplace = "(?:org\\.jetbrains\\.annotations\\.)?$jetBrainsAnnotation"
if (node.qualifiedName == incorrectAnnotation) {
- val lintFix = fix().name("Replace with `@$replacementAnnotation`")
- .replace()
- .pattern(patternToReplace)
- .with(replacementAnnotation)
- .shortenNames()
- .autoFix(true, true)
- .build()
- val incident = Incident(context)
- .issue(ISSUE)
- .fix(lintFix)
- .location(context.getNameLocation(node))
- .message("Use `@$replacementAnnotation` instead of `@$incorrectAnnotation`")
- .scope(node)
+ val lintFix =
+ fix()
+ .name("Replace with `@$replacementAnnotation`")
+ .replace()
+ .pattern(patternToReplace)
+ .with(replacementAnnotation)
+ .shortenNames()
+ .autoFix(true, true)
+ .build()
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .fix(lintFix)
+ .location(context.getNameLocation(node))
+ .message("Use `@$replacementAnnotation` instead of `@$incorrectAnnotation`")
+ .scope(node)
context.report(incident)
}
}
}
companion object {
- val ISSUE = Issue.create(
- "NullabilityAnnotationsDetector",
- "Replace usages of JetBrains nullability annotations with androidx " +
- "versions in Java code",
- "The androidx nullability annotations should be used in androidx libraries " +
- "instead of JetBrains annotations.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(NullabilityAnnotationsDetector::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "NullabilityAnnotationsDetector",
+ "Replace usages of JetBrains nullability annotations with androidx " +
+ "versions in Java code",
+ "The androidx nullability annotations should be used in androidx libraries " +
+ "instead of JetBrains annotations.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(NullabilityAnnotationsDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/ObsoleteBuildCompatUsageDetector.kt b/lint-checks/src/main/java/androidx/build/lint/ObsoleteBuildCompatUsageDetector.kt
index d3ae2de..7c9d503 100644
--- a/lint-checks/src/main/java/androidx/build/lint/ObsoleteBuildCompatUsageDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/ObsoleteBuildCompatUsageDetector.kt
@@ -30,14 +30,15 @@
import org.jetbrains.uast.UCallExpression
class ObsoleteBuildCompatUsageDetector : Detector(), Detector.UastScanner {
- private val methodsToApiLevels = mapOf(
- "isAtLeastN" to 24,
- "isAtLeastNMR1" to 25,
- "isAtLeastO" to 26,
- "isAtLeastOMR1" to 27,
- "isAtLeastP" to 28,
- "isAtLeastQ" to 29
- )
+ private val methodsToApiLevels =
+ mapOf(
+ "isAtLeastN" to 24,
+ "isAtLeastNMR1" to 25,
+ "isAtLeastO" to 26,
+ "isAtLeastOMR1" to 27,
+ "isAtLeastP" to 28,
+ "isAtLeastQ" to 29
+ )
override fun getApplicableMethodNames() = methodsToApiLevels.keys.toList()
@@ -50,29 +51,35 @@
val target = if (node.receiver != null) node.uastParent!! else node
val apiLevel = methodsToApiLevels[node.methodName]
- val lintFix = fix().name("Use SDK_INT >= $apiLevel")
- .replace()
- .text(target.asRenderString())
- .with("Build.VERSION.SDK_INT >= $apiLevel")
- .build()
- val incident = Incident(context)
- .fix(lintFix)
- .issue(ISSUE)
- .location(context.getLocation(node))
- .message("Using deprecated BuildCompat methods")
- .scope(node)
+ val lintFix =
+ fix()
+ .name("Use SDK_INT >= $apiLevel")
+ .replace()
+ .text(target.asRenderString())
+ .with("Build.VERSION.SDK_INT >= $apiLevel")
+ .build()
+ val incident =
+ Incident(context)
+ .fix(lintFix)
+ .issue(ISSUE)
+ .location(context.getLocation(node))
+ .message("Using deprecated BuildCompat methods")
+ .scope(node)
context.report(incident)
}
companion object {
- val ISSUE = Issue.create(
- "ObsoleteBuildCompat",
- "Using deprecated BuildCompat methods",
- "BuildConfig methods should only be used prior to an API level's finalization. " +
- "Once an API level number is assigned, comparing directly with SDK_INT " +
- "is preferred as it enables other lint checks to correctly work.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(ObsoleteBuildCompatUsageDetector::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "ObsoleteBuildCompat",
+ "Using deprecated BuildCompat methods",
+ "BuildConfig methods should only be used prior to an API level's finalization. " +
+ "Once an API level number is assigned, comparing directly with SDK_INT " +
+ "is preferred as it enables other lint checks to correctly work.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(ObsoleteBuildCompatUsageDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/ObsoleteCompatDetector.kt b/lint-checks/src/main/java/androidx/build/lint/ObsoleteCompatDetector.kt
index 39652e2..8a3d477c 100644
--- a/lint-checks/src/main/java/androidx/build/lint/ObsoleteCompatDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/ObsoleteCompatDetector.kt
@@ -54,14 +54,17 @@
CompatMethodHandler(context)
companion object {
- val ISSUE = Issue.create(
- "ObsoleteCompatMethod",
- "Obsolete compatibility method can be deprecated with replacement",
- "Compatibility methods that consist of a single call to the platform SDK " +
- "should be deprecated and provide a suggestion to replace with a direct call.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(ObsoleteCompatDetector::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "ObsoleteCompatMethod",
+ "Obsolete compatibility method can be deprecated with replacement",
+ "Compatibility methods that consist of a single call to the platform SDK " +
+ "should be deprecated and provide a suggestion to replace with a direct call.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(ObsoleteCompatDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
@@ -77,63 +80,64 @@
// Does it already have @Deprecated and @ReplaceWith annotations?
val hasDeprecated = node.hasAnnotation("java.lang.Deprecated")
val hasReplaceWith = node.hasAnnotation("androidx.annotation.ReplaceWith")
- val hasDeprecatedDoc = node.comments.any { comment ->
- comment.text.contains("@deprecated ")
- }
+ val hasDeprecatedDoc =
+ node.comments.any { comment -> comment.text.contains("@deprecated ") }
if (hasDeprecated && hasReplaceWith && hasDeprecatedDoc) return
// Compat methods take the wrapped class as the first parameter.
val firstParameter = node.javaPsi.parameterList.parameters.firstOrNull() ?: return
// Ensure that we're dealing with a single-line method that operates on the wrapped class.
- val expression = (node.uastBody as? UBlockExpression)
- ?.expressions
- ?.singleOrNull()
- ?.unwrapReturnExpression()
- ?.skipParenthesizedExprDown()
- as? UQualifiedReferenceExpression
- val receiver = expression
- ?.unwrapReceiver()
+ val expression =
+ (node.uastBody as? UBlockExpression)
+ ?.expressions
+ ?.singleOrNull()
+ ?.unwrapReturnExpression()
+ ?.skipParenthesizedExprDown() as? UQualifiedReferenceExpression
+ val receiver = expression?.unwrapReceiver()
if (firstParameter != receiver) return
val lintFix = LintFix.create().composite().name("Replace obsolete compat method")
if (!hasDeprecatedDoc) {
- val docLink = when (expression.selector) {
- is UCallExpression -> {
- val methodCall = expression.selector as UCallExpression
- val className = (methodCall.receiverType as PsiClassReferenceType).className
- val methodName = methodCall.methodName
- val argTypes = methodCall.typeArguments.map { psiType ->
- (psiType as PsiClassReferenceType).className
+ val docLink =
+ when (expression.selector) {
+ is UCallExpression -> {
+ val methodCall = expression.selector as UCallExpression
+ val className = (methodCall.receiverType as PsiClassReferenceType).className
+ val methodName = methodCall.methodName
+ val argTypes =
+ methodCall.typeArguments.map { psiType ->
+ (psiType as PsiClassReferenceType).className
+ }
+ "$className#$methodName(${argTypes.joinToString(", ")})"
}
- "$className#$methodName(${argTypes.joinToString(", ")})"
+ is USimpleNameReferenceExpression -> {
+ val fieldName =
+ (expression.selector as USimpleNameReferenceExpression).resolvedName
+ val className =
+ (expression.receiver.getExpressionType() as PsiClassReferenceType)
+ .className
+ "$className#$fieldName"
+ }
+ else -> {
+ // We don't know how to handle this type of qualified reference.
+ return
+ }
}
- is USimpleNameReferenceExpression -> {
- val fieldName = (expression.selector
- as USimpleNameReferenceExpression).resolvedName
- val className = (expression.receiver.getExpressionType()
- as PsiClassReferenceType).className
- "$className#$fieldName"
- }
- else -> {
- // We don't know how to handle this type of qualified reference.
- return
- }
- }
val docText = "@deprecated Call {@link $docLink} directly."
- val javadocFix = buildInsertJavadocFix(context, node, docText)
- .autoFix()
- .shortenNames()
- .reformat(true)
- .build()
+ val javadocFix =
+ buildInsertJavadocFix(context, node, docText)
+ .autoFix()
+ .shortenNames()
+ .reformat(true)
+ .build()
lintFix.add(javadocFix)
}
if (!hasReplaceWith) {
- val replacement = expression.javaPsi!!.text!!
- .replace("\"", "\\\"")
- .replace(Regex("\n\\s*"), "")
+ val replacement =
+ expression.javaPsi!!.text!!.replace("\"", "\\\"").replace(Regex("\n\\s*"), "")
lintFix.add(
LintFix.create()
.name("Annotate with @ReplaceWith")
@@ -163,12 +167,13 @@
)
}
- val incident = Incident(context)
- .issue(ObsoleteCompatDetector.ISSUE)
- .location(context.getNameLocation(node))
- .message("Obsolete compat method should provide replacement")
- .scope(node)
- .fix(lintFix.build())
+ val incident =
+ Incident(context)
+ .issue(ObsoleteCompatDetector.ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Obsolete compat method should provide replacement")
+ .scope(node)
+ .fix(lintFix.build())
context.report(incident)
}
}
@@ -179,34 +184,18 @@
docText: String
): LintFix.ReplaceStringBuilder {
val javadocNode = node.comments.lastOrNull { it.text.startsWith("/**") }
- val javadocFix = LintFix.create()
- .name("Add @deprecated Javadoc annotation")
- .replace()
+ val javadocFix = LintFix.create().name("Add @deprecated Javadoc annotation").replace()
if (javadocNode != null) {
// Append to the existing block comment before the close.
val docEndOffset = javadocNode.text.lastIndexOf("*/")
val insertAt = context.getRangeLocation(javadocNode, docEndOffset, 2)
- val replacement = applyIndentToInsertion(
- context,
- insertAt,
- "* $docText"
- )
- javadocFix
- .range(insertAt)
- .beginning()
- .with(replacement)
+ val replacement = applyIndentToInsertion(context, insertAt, "* $docText")
+ javadocFix.range(insertAt).beginning().with(replacement)
} else {
// Insert a new comment before the declaration or any annotations.
val insertAt = context.getLocation(node.annotations.firstOrNull() ?: node.modifierList)
- val replacement = applyIndentToInsertion(
- context,
- insertAt,
- "/** $docText */"
- )
- javadocFix
- .range(insertAt)
- .beginning()
- .with(replacement)
+ val replacement = applyIndentToInsertion(context, insertAt, "/** $docText */")
+ javadocFix.range(insertAt).beginning().with(replacement)
}
return javadocFix
}
@@ -232,8 +221,9 @@
}
fun UExpression.unwrapReceiver(): PsiElement? =
- ((this as? UQualifiedReferenceExpression)
- ?.receiver?.skipParenthesizedExprDown() as? UResolvable)?.resolve()
+ ((this as? UQualifiedReferenceExpression)?.receiver?.skipParenthesizedExprDown()
+ as? UResolvable)
+ ?.resolve()
fun UExpression.unwrapReturnExpression(): UExpression =
(this as? UReturnExpression)?.returnExpression ?: this
diff --git a/lint-checks/src/main/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetector.kt b/lint-checks/src/main/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetector.kt
index 42e64c2..0e5efa3 100644
--- a/lint-checks/src/main/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetector.kt
@@ -49,14 +49,15 @@
// Check if the project is using a versioned dependency on core
val dependencies = context.project.buildVariant.artifact.dependencies.getAll()
if (dependencies.any { it.isInvalidCoreDependency() }) {
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getLocation(node))
- .message(
- "Prelease SDK check ${method.name} cannot be called as this project has " +
- "a versioned dependency on androidx.core:core"
- )
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getLocation(node))
+ .message(
+ "Prelease SDK check ${method.name} cannot be called as this project has " +
+ "a versioned dependency on androidx.core:core"
+ )
+ .scope(node)
context.report(incident)
}
}
@@ -78,11 +79,12 @@
}
companion object {
- val ISSUE = Issue.create(
- "PrereleaseSdkCoreDependency",
- "Prerelease SDK checks can only be used by projects with a TOT dependency on " +
- "androidx.core:core",
- """
+ val ISSUE =
+ Issue.create(
+ "PrereleaseSdkCoreDependency",
+ "Prerelease SDK checks can only be used by projects with a TOT dependency on " +
+ "androidx.core:core",
+ """
The implementation of a prerelease SDK check will change when the SDK is finalized,
so projects using these checks must have a tip-of-tree dependency on core to ensure
the check stays up-to-date.
@@ -92,12 +94,14 @@
See go/androidx-api-guidelines#compat-sdk for more information.
""",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- PrereleaseSdkCoreDependencyDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ PrereleaseSdkCoreDependencyDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
)
- )
private const val BUILD_COMPAT = "androidx.core.os.BuildCompat"
private const val PRERELEASE_SDK_CHECK = "$BUILD_COMPAT.PrereleaseSdkCheck"
diff --git a/lint-checks/src/main/java/androidx/build/lint/PrivateConstructorForUtilityClassDetector.kt b/lint-checks/src/main/java/androidx/build/lint/PrivateConstructorForUtilityClassDetector.kt
index e34e10b..12302fe 100644
--- a/lint-checks/src/main/java/androidx/build/lint/PrivateConstructorForUtilityClassDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/PrivateConstructorForUtilityClassDetector.kt
@@ -40,45 +40,50 @@
override fun getApplicableUastTypes() = listOf(UClass::class.java)
- override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
- override fun visitClass(node: UClass) {
- // If this doesn't look like a utility class then return.
- if (node.isInterface ||
- node.isEnum ||
- node.hasModifierProperty(PsiModifier.ABSTRACT) ||
- node is UAnonymousClass ||
- // If this is a subclass, then don't flag it.
- node.supers.any { !it.qualifiedName.equals("java.lang.Object") } ||
- // Don't run for Kotlin, for now at least
- node.containingFile.fileType == KotlinFileType.INSTANCE
- ) {
- return
- }
+ override fun createUastHandler(context: JavaContext) =
+ object : UElementHandler() {
+ override fun visitClass(node: UClass) {
+ // If this doesn't look like a utility class then return.
+ if (
+ node.isInterface ||
+ node.isEnum ||
+ node.hasModifierProperty(PsiModifier.ABSTRACT) ||
+ node is UAnonymousClass ||
+ // If this is a subclass, then don't flag it.
+ node.supers.any { !it.qualifiedName.equals("java.lang.Object") } ||
+ // Don't run for Kotlin, for now at least
+ node.containingFile.fileType == KotlinFileType.INSTANCE
+ ) {
+ return
+ }
- // If the constructors are already private or the class is private with a default
- // constructor (e.g. no constructors) then return.
- if (node.constructors.isNotEmpty() && node.constructors.all { it.isPrivate() } ||
- node.constructors.isEmpty() && node.isPrivate()
- ) {
- return
- }
+ // If the constructors are already private or the class is private with a default
+ // constructor (e.g. no constructors) then return.
+ if (
+ node.constructors.isNotEmpty() && node.constructors.all { it.isPrivate() } ||
+ node.constructors.isEmpty() && node.isPrivate()
+ ) {
+ return
+ }
- // If not all (non-constructor) members are static then return.
- if (node.methods.any { !it.isStatic && !it.isConstructor } ||
- node.methods.none { !it.isConstructor } ||
- node.fields.any { !it.isStatic }
- ) {
- return
- }
+ // If not all (non-constructor) members are static then return.
+ if (
+ node.methods.any { !it.isStatic && !it.isConstructor } ||
+ node.methods.none { !it.isConstructor } ||
+ node.fields.any { !it.isStatic }
+ ) {
+ return
+ }
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message("Utility class is missing private constructor")
- .scope(node)
- context.report(incident)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Utility class is missing private constructor")
+ .scope(node)
+ context.report(incident)
+ }
}
- }
private fun PsiModifierListOwner.isPrivateOrParameterInPrivateMethod(): Boolean {
if (hasModifier(JvmModifier.PRIVATE)) return true
@@ -86,23 +91,24 @@
return parentMethod.hasModifier(JvmModifier.PRIVATE)
}
- /**
- * Returns whether the element is private.
- */
+ /** Returns whether the element is private. */
fun PsiModifierListOwner.isPrivate(): Boolean = hasModifier(JvmModifier.PRIVATE)
companion object {
- val ISSUE = Issue.create(
- "PrivateConstructorForUtilityClass",
- "Utility classes should have a private constructor",
- "Classes which are not intended to be instantiated should be made non-instantiable " +
- "with a private constructor. This includes utility classes (classes with " +
- "only static members), and the main class.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- PrivateConstructorForUtilityClassDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val ISSUE =
+ Issue.create(
+ "PrivateConstructorForUtilityClass",
+ "Utility classes should have a private constructor",
+ "Classes which are not intended to be instantiated should be made non-instantiable " +
+ "with a private constructor. This includes utility classes (classes with " +
+ "only static members), and the main class.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ PrivateConstructorForUtilityClassDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
)
- )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/ReplaceWithDetector.kt b/lint-checks/src/main/java/androidx/build/lint/ReplaceWithDetector.kt
index 3c5bdd1..4ebdbfa 100644
--- a/lint-checks/src/main/java/androidx/build/lint/ReplaceWithDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/ReplaceWithDetector.kt
@@ -56,10 +56,11 @@
class ReplaceWithDetector : Detector(), SourceCodeScanner {
- override fun applicableAnnotations(): List<String> = listOf(
- JAVA_REPLACE_WITH_ANNOTATION,
- KOTLIN_DEPRECATED_ANNOTATION,
- )
+ override fun applicableAnnotations(): List<String> =
+ listOf(
+ JAVA_REPLACE_WITH_ANNOTATION,
+ KOTLIN_DEPRECATED_ANNOTATION,
+ )
override fun visitAnnotationUsage(
context: JavaContext,
@@ -82,24 +83,28 @@
// Don't warn for Kotlin replacement in Kotlin files -- that's the Kotlin Compiler's job.
if (qualifiedName == KOTLIN_DEPRECATED_ANNOTATION && isKotlin(usage.lang)) return
- var (expression, imports) = when (qualifiedName) {
- KOTLIN_DEPRECATED_ANNOTATION -> {
- val replaceWith = annotation.findAttributeValue("replaceWith")?.unwrap()
- as? UCallExpression ?: return
- val expression = replaceWith.valueArguments.getOrNull(0)?.parseLiteral() ?: return
- val imports = replaceWith.valueArguments.getOrNull(1)?.parseVarargLiteral()
- ?: emptyList()
- Pair(expression, imports)
+ var (expression, imports) =
+ when (qualifiedName) {
+ KOTLIN_DEPRECATED_ANNOTATION -> {
+ val replaceWith =
+ annotation.findAttributeValue("replaceWith")?.unwrap() as? UCallExpression
+ ?: return
+ val expression =
+ replaceWith.valueArguments.getOrNull(0)?.parseLiteral() ?: return
+ val imports =
+ replaceWith.valueArguments.getOrNull(1)?.parseVarargLiteral() ?: emptyList()
+ Pair(expression, imports)
+ }
+ JAVA_REPLACE_WITH_ANNOTATION -> {
+ val expression =
+ annotation.findAttributeValue("expression")?.let { expr ->
+ ConstantEvaluator.evaluate(context, expr)
+ } as? String ?: return
+ val imports = annotation.getAttributeValueVarargLiteral("imports")
+ Pair(expression, imports)
+ }
+ else -> return
}
- JAVA_REPLACE_WITH_ANNOTATION -> {
- val expression = annotation.findAttributeValue("expression")?.let { expr ->
- ConstantEvaluator.evaluate(context, expr)
- } as? String ?: return
- val imports = annotation.getAttributeValueVarargLiteral("imports")
- Pair(expression, imports)
- }
- else -> return
- }
var location = context.getLocation(usage)
val includeReceiver = Regex("^\\w+\\.\\w+.*\$").matches(expression)
@@ -109,9 +114,12 @@
// Per Kotlin documentation for ReplaceWith: For function calls, the replacement
// expression may contain argument names of the deprecated function, which will
// be substituted with actual parameters used in the call being updated.
- val argsToParams = referenced.parameters.mapIndexed { index, param ->
- param.name to usage.getArgumentForParameter(index)?.asSourceString()
- }.toMap()
+ val argsToParams =
+ referenced.parameters
+ .mapIndexed { index, param ->
+ param.name to usage.getArgumentForParameter(index)?.asSourceString()
+ }
+ .toMap()
// Tokenize the replacement expression using a regex, replacing as we go. This
// isn't the most efficient approach (e.g. trie) but it's easy to write.
@@ -128,21 +136,25 @@
}
} while (index < expression.length)
- location = when (val sourcePsi = usage.sourcePsi) {
- is PsiNewExpression -> {
- // The expression should never specify "new", but if it specifies a
- // receiver then we should replace the call to "new". For example, if
- // we're replacing `new Clazz("arg")` with `ClazzCompat.create("arg")`.
- context.getConstructorLocation(
- usage, sourcePsi, includeReceiver, includeArguments
- )
+ location =
+ when (val sourcePsi = usage.sourcePsi) {
+ is PsiNewExpression -> {
+ // The expression should never specify "new", but if it specifies a
+ // receiver then we should replace the call to "new". For example, if
+ // we're replacing `new Clazz("arg")` with `ClazzCompat.create("arg")`.
+ context.getConstructorLocation(
+ usage,
+ sourcePsi,
+ includeReceiver,
+ includeArguments
+ )
+ }
+ else -> {
+ // The expression may optionally specify a receiver or arguments, in
+ // which case we should include the originals in the replacement range.
+ context.getCallLocation(usage, includeReceiver, includeArguments)
+ }
}
- else -> {
- // The expression may optionally specify a receiver or arguments, in
- // which case we should include the originals in the replacement range.
- context.getCallLocation(usage, includeReceiver, includeArguments)
- }
- }
} else if (referenced is PsiField && usage is USimpleNameReferenceExpression) {
// The expression may optionally specify a receiver, in which case we should
// include the original in the replacement range.
@@ -165,8 +177,13 @@
expression: String,
imports: List<String>,
) {
- context.report(ISSUE, usage, location, "Replacement available",
- createLintFix(context, location, expression, imports))
+ context.report(
+ ISSUE,
+ usage,
+ location,
+ "Replacement available",
+ createLintFix(context, location, expression, imports)
+ )
}
private fun createLintFix(
@@ -177,14 +194,7 @@
): LintFix {
val name = "Replace with `$expression`"
val lintFixBuilder = fix().composite().name(name)
- lintFixBuilder.add(
- fix()
- .replace()
- .range(location)
- .name(name)
- .with(expression)
- .build()
- )
+ lintFixBuilder.add(fix().replace().range(location).name(name).with(expression).build())
if (imports.isNotEmpty()) {
lintFixBuilder.add(fix().import(context, add = imports).build())
}
@@ -202,52 +212,58 @@
): LintFix.ReplaceStringBuilder {
val isKotlin = isKotlin(context.uastFile!!.lang)
val lastImport = context.uastFile?.imports?.lastOrNull()
- val packageElem = when (val psiFile = context.psiFile) {
- is PsiJavaFile -> psiFile.packageStatement
- is KtFile -> psiFile.packageDirective?.psiOrParent
- else -> null
- }
+ val packageElem =
+ when (val psiFile = context.psiFile) {
+ is PsiJavaFile -> psiFile.packageStatement
+ is KtFile -> psiFile.packageDirective?.psiOrParent
+ else -> null
+ }
// Build the imports block. Leave any ordering or formatting up to the client.
- val prependImports = when {
- lastImport != null -> "\n"
- packageElem != null -> "\n\n"
- else -> ""
- }
- val appendImports = when {
- lastImport != null -> ""
- packageElem != null -> ""
- else -> "\n"
- }
+ val prependImports =
+ when {
+ lastImport != null -> "\n"
+ packageElem != null -> "\n\n"
+ else -> ""
+ }
+ val appendImports =
+ when {
+ lastImport != null -> ""
+ packageElem != null -> ""
+ else -> "\n"
+ }
val formattedImports = add.joinToString("\n") { "import " + if (isKotlin) it else "$it;" }
val importsText = prependImports + formattedImports + appendImports
// Append after any existing imports, after the package declaration, or at the beginning of
// the file if there are no imports and no package declaration.
- val appendLocation = (lastImport ?: packageElem) ?.let { context.getLocation(it) }
- ?: Location.create(context.file, context.getContents(), 0, 0)
+ val appendLocation =
+ (lastImport ?: packageElem)?.let { context.getLocation(it) }
+ ?: Location.create(context.file, context.getContents(), 0, 0)
val replaceBuilder = replace().range(appendLocation).end().with(importsText)
return replaceBuilder.autoFix()
}
companion object {
- private val IMPLEMENTATION = Implementation(
- ReplaceWithDetector::class.java,
- Scope.JAVA_FILE_SCOPE,
- )
+ private val IMPLEMENTATION =
+ Implementation(
+ ReplaceWithDetector::class.java,
+ Scope.JAVA_FILE_SCOPE,
+ )
const val KOTLIN_DEPRECATED_ANNOTATION = "kotlin.Deprecated"
const val JAVA_REPLACE_WITH_ANNOTATION = "androidx.annotation.ReplaceWith"
- val ISSUE = Issue.create(
- id = "ReplaceWith",
- briefDescription = "Replacement available",
- explanation = "A recommended replacement is available for this API usage.",
- category = Category.CORRECTNESS,
- priority = 4,
- severity = Severity.INFORMATIONAL,
- implementation = IMPLEMENTATION,
- )
+ val ISSUE =
+ Issue.create(
+ id = "ReplaceWith",
+ briefDescription = "Replacement available",
+ explanation = "A recommended replacement is available for this API usage.",
+ category = Category.CORRECTNESS,
+ priority = 4,
+ severity = Severity.INFORMATIONAL,
+ implementation = IMPLEMENTATION,
+ )
}
}
@@ -275,8 +291,9 @@
// Work around UAST bug where the value argument list points directly to the
// string content node instead of a node containing the opening and closing
// tokens as well. We need to include the closing tags in the range as well!
- val next = (lastArgument.sourcePsi as? KtLiteralStringTemplateEntry)
- ?.nextSibling as? TreeElement
+ val next =
+ (lastArgument.sourcePsi as? KtLiteralStringTemplateEntry)?.nextSibling
+ as? TreeElement
val delta =
if (next != null && next.elementType == KtTokens.CLOSING_QUOTE) {
next.textLength
@@ -320,7 +337,7 @@
/**
* @return the value of the specified vararg attribute as a list of String literals, or an empty
- * list if not specified
+ * list if not specified
*/
fun UAnnotation.getAttributeValueVarargLiteral(name: String): List<String> =
findDeclaredAttributeValue(name)?.parseVarargLiteral() ?: emptyList()
diff --git a/lint-checks/src/main/java/androidx/build/lint/RestrictToDetector.kt b/lint-checks/src/main/java/androidx/build/lint/RestrictToDetector.kt
index 9eaf45c..700e74d 100644
--- a/lint-checks/src/main/java/androidx/build/lint/RestrictToDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/RestrictToDetector.kt
@@ -56,9 +56,7 @@
import org.jetbrains.uast.getParentOfType
import org.jetbrains.uast.util.isArrayInitializer
-/**
- * Adapted from com/android/tools/lint/checks/RestrictToDetector.kt in Android Studio repo.
- */
+/** Adapted from com/android/tools/lint/checks/RestrictToDetector.kt in Android Studio repo. */
class RestrictToDetector : AbstractAnnotationDetector(), SourceCodeScanner {
override fun applicableAnnotations(): List<String> = listOf(RESTRICT_TO_ANNOTATION)
@@ -86,9 +84,11 @@
// here, but that points to impl classes in its hierarchy which leads to
// class loading trouble.
val sourcePsi = element.sourcePsi
- if (sourcePsi != null &&
- isKotlin(sourcePsi.language) &&
- sourcePsi.parent?.toString() == "CONSTRUCTOR_CALLEE") {
+ if (
+ sourcePsi != null &&
+ isKotlin(sourcePsi.language) &&
+ sourcePsi.parent?.toString() == "CONSTRUCTOR_CALLEE"
+ ) {
return
}
}
@@ -143,13 +143,7 @@
) {
val scope = getRestrictionScope(annotation)
if (scope != 0) {
- checkRestrictTo(
- context,
- node,
- method,
- usageInfo,
- scope
- )
+ checkRestrictTo(context, node, method, usageInfo, scope)
}
}
@@ -172,11 +166,7 @@
containingClass ?: return
- if (
- usageInfo.anyCloser {
- it.qualifiedName == RESTRICT_TO_ANNOTATION
- }
- ) {
+ if (usageInfo.anyCloser { it.qualifiedName == RESTRICT_TO_ANNOTATION }) {
return
}
@@ -210,7 +200,7 @@
val methodGroup = methodCoordinates?.groupId
if (
methodGroup != null &&
- (thisGroup == null || !sameLibraryGroupPrefix(thisGroup, methodGroup))
+ (thisGroup == null || !sameLibraryGroupPrefix(thisGroup, methodGroup))
) {
val expectedPrefix =
methodGroup.lastIndexOf('.').let {
@@ -236,11 +226,12 @@
val thisArtifact = thisCoordinates?.artifactId
val methodArtifact = methodCoordinates.artifactId
if (thisArtifact != methodArtifact) {
- val name = if (methodGroup == "__local_aars__") {
- "missing Maven coordinate due to repackaging"
- } else {
- "$methodGroup:$methodArtifact"
- }
+ val name =
+ if (methodGroup == "__local_aars__") {
+ "missing Maven coordinate due to repackaging"
+ } else {
+ "$methodGroup:$methodArtifact"
+ }
val where = "from within the same library ($name)"
reportRestriction(where, containingClass, member, context, node, usageInfo)
}
@@ -295,7 +286,12 @@
if (!isSubClass) {
reportRestriction(
- "from subclasses", containingClass, member, context, node, usageInfo
+ "from subclasses",
+ containingClass,
+ member,
+ context,
+ node,
+ usageInfo
)
}
}
@@ -316,11 +312,11 @@
member?.name ?: (containingClass.name + " constructor")
} else
//noinspection LintImplPsiEquals
- if (containingClass == member) {
- member.name ?: "class"
- } else {
- containingClass.name + "." + member.name
- }
+ if (containingClass == member) {
+ member.name ?: "class"
+ } else {
+ containingClass.name + "." + member.name
+ }
var locationNode = node
if (node is UCallExpression) {
@@ -339,8 +335,8 @@
val className = annotated.name
if (
qualifier != null &&
- className != null &&
- qualifier.asSourceString() == className
+ className != null &&
+ qualifier.asSourceString() == className
) {
locationNode = qualifier
api = className
@@ -348,7 +344,8 @@
}
}
- // If this error message changes, you need to also update ResourceTypeInspection#guessLintIssue
+ // If this error message changes, you need to also update
+ // ResourceTypeInspection#guessLintIssue
var message: String
if (where == null) {
message = "$api is marked as internal and should not be accessed from apps"
@@ -356,7 +353,8 @@
val refType = if (member is PsiMethod) "called" else "accessed"
message = "$api can only be $refType $where"
- // Most users will encounter this for the support library; let's have a clearer error message
+ // Most users will encounter this for the support library; let's have a clearer error
+ // message
// for that specific scenario
if (where == "from within the same library (groupId=com.android.support)") {
// If this error message changes, you need to also update
@@ -369,7 +367,8 @@
val location =
if (locationNode is UCallExpression) {
- context.getCallLocation(locationNode,
+ context.getCallLocation(
+ locationNode,
includeReceiver = false,
includeArguments = false
)
@@ -423,14 +422,16 @@
val resolved = expression.resolve()
if (resolved is PsiField) {
val name = resolved.name
- scope = when (name) {
- "GROUP_ID", "LIBRARY_GROUP" -> RESTRICT_TO_LIBRARY_GROUP
- "SUBCLASSES" -> RESTRICT_TO_SUBCLASSES
- "TESTS" -> RESTRICT_TO_TESTS
- "LIBRARY" -> RESTRICT_TO_LIBRARY
- "LIBRARY_GROUP_PREFIX" -> RESTRICT_TO_LIBRARY_GROUP_PREFIX
- else -> 0
- }
+ scope =
+ when (name) {
+ "GROUP_ID",
+ "LIBRARY_GROUP" -> RESTRICT_TO_LIBRARY_GROUP
+ "SUBCLASSES" -> RESTRICT_TO_SUBCLASSES
+ "TESTS" -> RESTRICT_TO_TESTS
+ "LIBRARY" -> RESTRICT_TO_LIBRARY
+ "LIBRARY_GROUP_PREFIX" -> RESTRICT_TO_LIBRARY_GROUP_PREFIX
+ else -> 0
+ }
}
} else if (expression is UastEmptyExpression) {
// See JavaUAnnotation.findDeclaredAttributeValue
@@ -486,7 +487,7 @@
id = "RestrictedApiAndroidX",
briefDescription = "Restricted API",
explanation =
- """
+ """
This API has been flagged with a restriction that has not been met.
Examples of API restrictions:
@@ -505,19 +506,18 @@
/** Attempts to find the Maven coordinate for the library containing [member]. */
private fun JavaContext.findMavenCoordinate(member: PsiMember): LintModelMavenName? {
- val mavenName = evaluator.getLibrary(member)
- ?: evaluator.getProject(member)?.mavenCoordinate
- ?: return null
+ val mavenName =
+ evaluator.getLibrary(member) ?: evaluator.getProject(member)?.mavenCoordinate ?: return null
// If the lint model is missing a Maven coordinate for this class, try to infer one from the
// JAR's owner library. If we fail, return the broken Maven name anyway.
if (mavenName == LintModelMavenName.NONE) {
- return evaluator.findJarPath(member)
+ return evaluator
+ .findJarPath(member)
?.let { jarPath ->
evaluator.findOwnerLibrary(jarPath.replace('/', File.separatorChar))
}
- ?.getMavenNameFromIdentifier()
- ?: mavenName
+ ?.getMavenNameFromIdentifier() ?: mavenName
}
// If the lint model says the class lives in a "local AAR", try a little bit harder to match
@@ -536,8 +536,7 @@
// Otherwise, try to find a dependency with a matching path and use its Maven group.
val path = artifactPath.substring(0, lastIndexOfBuild)
- return evaluator.dependencies?.getAll()
- ?.findMavenNameWithJarFileInPath(path, mavenName)
+ return evaluator.dependencies?.getAll()?.findMavenNameWithJarFileInPath(path, mavenName)
?: mavenName
}
@@ -552,22 +551,24 @@
excludeMavenName: LintModelMavenName? = null
): LintModelMavenName? {
return firstNotNullOfOrNull { library ->
- val resolvedCoordinates = when {
- library is DefaultLintModelJavaLibrary -> library.resolvedCoordinates
- library is DefaultLintModelAndroidLibrary -> library.resolvedCoordinates
- else -> null
- }
+ val resolvedCoordinates =
+ when {
+ library is DefaultLintModelJavaLibrary -> library.resolvedCoordinates
+ library is DefaultLintModelAndroidLibrary -> library.resolvedCoordinates
+ else -> null
+ }
if (resolvedCoordinates == null || resolvedCoordinates == excludeMavenName) {
return@firstNotNullOfOrNull null
}
- val hasMatchingJarFile = when {
- library == excludeMavenName -> emptyList()
- library is DefaultLintModelJavaLibrary -> library.jarFiles
- library is DefaultLintModelAndroidLibrary -> library.jarFiles
- else -> emptyList()
- }.any { jarFile -> jarFile.path.startsWith(path) }
+ val hasMatchingJarFile =
+ when {
+ library == excludeMavenName -> emptyList()
+ library is DefaultLintModelJavaLibrary -> library.jarFiles
+ library is DefaultLintModelAndroidLibrary -> library.jarFiles
+ else -> emptyList()
+ }.any { jarFile -> jarFile.path.startsWith(path) }
if (hasMatchingJarFile) {
return@firstNotNullOfOrNull resolvedCoordinates
diff --git a/lint-checks/src/main/java/androidx/build/lint/SampledAnnotationDetector.kt b/lint-checks/src/main/java/androidx/build/lint/SampledAnnotationDetector.kt
index ab50329..55966e1 100644
--- a/lint-checks/src/main/java/androidx/build/lint/SampledAnnotationDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/SampledAnnotationDetector.kt
@@ -57,26 +57,26 @@
* Detector responsible for enforcing @Sampled annotation usage
*
* This detector enforces that:
- *
* - Functions referenced with @sample are annotated with @Sampled - [UNRESOLVED_SAMPLE_LINK]
* - Functions annotated with @Sampled are referenced with @sample - [OBSOLETE_SAMPLED_ANNOTATION]
* - Functions annotated with @Sampled are inside a valid samples directory, matching module /
- * directory structure guidelines - [INVALID_SAMPLES_LOCATION]
- * - There are never multiple functions with the same fully qualified name that could be resolved
- * by an @sample link - [MULTIPLE_FUNCTIONS_FOUND]
+ * directory structure guidelines - [INVALID_SAMPLES_LOCATION]
+ * - There are never multiple functions with the same fully qualified name that could be resolved by
+ * an @sample link - [MULTIPLE_FUNCTIONS_FOUND]
*/
class SampledAnnotationDetector : Detector(), SourceCodeScanner {
override fun getApplicableUastTypes() = listOf(UDeclaration::class.java)
- override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
- override fun visitDeclaration(node: UDeclaration) {
- KDocSampleLinkHandler(context).visitDeclaration(node)
- if (node is UMethod) {
- SampledAnnotationHandler(context).visitMethod(node)
+ override fun createUastHandler(context: JavaContext) =
+ object : UElementHandler() {
+ override fun visitDeclaration(node: UDeclaration) {
+ KDocSampleLinkHandler(context).visitDeclaration(node)
+ if (node is UMethod) {
+ SampledAnnotationHandler(context).visitMethod(node)
+ }
}
}
- }
override fun checkPartialResults(context: Context, partialResults: PartialResult) {
val sampleLinks = mutableMapOf<String, MutableList<Location>>()
@@ -101,8 +101,8 @@
/**
* Returns whether this [Location] represents a file that we want to report errors for. We
* only want to report an error for files in the parent module of this samples module, to
- * avoid reporting the same errors multiple times if multiple sample modules depend
- * on a library that has @sample links.
+ * avoid reporting the same errors multiple times if multiple sample modules depend on a
+ * library that has @sample links.
*/
fun Location.shouldReport(): Boolean {
// Path of the parent module that the sample module has samples for
@@ -117,10 +117,13 @@
functionLocations == null -> {
locations.forEach { location ->
if (location.shouldReport()) {
- val incident = Incident(context)
- .issue(UNRESOLVED_SAMPLE_LINK)
- .location(location)
- .message("Couldn't find a valid @Sampled function matching $link")
+ val incident =
+ Incident(context)
+ .issue(UNRESOLVED_SAMPLE_LINK)
+ .location(location)
+ .message(
+ "Couldn't find a valid @Sampled function matching $link"
+ )
context.report(incident)
}
}
@@ -130,10 +133,11 @@
functionLocations.size > 1 -> {
locations.forEach { location ->
if (location.shouldReport()) {
- val incident = Incident(context)
- .issue(MULTIPLE_FUNCTIONS_FOUND)
- .location(location)
- .message("Found multiple functions matching $link")
+ val incident =
+ Incident(context)
+ .issue(MULTIPLE_FUNCTIONS_FOUND)
+ .location(location)
+ .message("Found multiple functions matching $link")
context.report(incident)
}
}
@@ -145,11 +149,14 @@
if (sampleLinks[link] == null) {
locations.forEach { location ->
if (location.shouldReport()) {
- val incident = Incident(context)
- .issue(OBSOLETE_SAMPLED_ANNOTATION)
- .location(location)
- .message("$link is annotated with @$SAMPLED_ANNOTATION, but is not " +
- "linked to from a @$SAMPLE_KDOC_ANNOTATION tag.")
+ val incident =
+ Incident(context)
+ .issue(OBSOLETE_SAMPLED_ANNOTATION)
+ .location(location)
+ .message(
+ "$link is annotated with @$SAMPLED_ANNOTATION, but is not " +
+ "linked to from a @$SAMPLE_KDOC_ANNOTATION tag."
+ )
context.report(incident)
}
}
@@ -169,62 +176,61 @@
const val SAMPLE_LINK_MAP = "SampleLinkMap"
const val SAMPLED_FUNCTION_MAP = "SampledFunctionMap"
- val OBSOLETE_SAMPLED_ANNOTATION = Issue.create(
- id = "ObsoleteSampledAnnotation",
- briefDescription = "Obsolete @$SAMPLED_ANNOTATION annotation",
- explanation = "This function is annotated with @$SAMPLED_ANNOTATION, but is not " +
- "linked to from a @$SAMPLE_KDOC_ANNOTATION tag. Either remove this annotation, " +
- "or add a valid @$SAMPLE_KDOC_ANNOTATION tag linking to it.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- SampledAnnotationDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val OBSOLETE_SAMPLED_ANNOTATION =
+ Issue.create(
+ id = "ObsoleteSampledAnnotation",
+ briefDescription = "Obsolete @$SAMPLED_ANNOTATION annotation",
+ explanation =
+ "This function is annotated with @$SAMPLED_ANNOTATION, but is not " +
+ "linked to from a @$SAMPLE_KDOC_ANNOTATION tag. Either remove this annotation, " +
+ "or add a valid @$SAMPLE_KDOC_ANNOTATION tag linking to it.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
- val UNRESOLVED_SAMPLE_LINK = Issue.create(
- id = "UnresolvedSampleLink",
- briefDescription = "Unresolved @$SAMPLE_KDOC_ANNOTATION annotation",
- explanation = "Couldn't find a valid @Sampled function matching the function " +
- "specified in the $SAMPLE_KDOC_ANNOTATION link. If there is a function with the " +
- "same fully qualified name, make sure it is annotated with @Sampled.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- SampledAnnotationDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val UNRESOLVED_SAMPLE_LINK =
+ Issue.create(
+ id = "UnresolvedSampleLink",
+ briefDescription = "Unresolved @$SAMPLE_KDOC_ANNOTATION annotation",
+ explanation =
+ "Couldn't find a valid @Sampled function matching the function " +
+ "specified in the $SAMPLE_KDOC_ANNOTATION link. If there is a function with the " +
+ "same fully qualified name, make sure it is annotated with @Sampled.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
- val MULTIPLE_FUNCTIONS_FOUND = Issue.create(
- id = "MultipleSampledFunctions",
- briefDescription = "Multiple matching functions found",
- explanation = "Found multiple functions matching the $SAMPLE_KDOC_ANNOTATION link.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- SampledAnnotationDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val MULTIPLE_FUNCTIONS_FOUND =
+ Issue.create(
+ id = "MultipleSampledFunctions",
+ briefDescription = "Multiple matching functions found",
+ explanation = "Found multiple functions matching the $SAMPLE_KDOC_ANNOTATION link.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
- val INVALID_SAMPLES_LOCATION = Issue.create(
- id = "InvalidSamplesLocation",
- briefDescription = "Invalid samples location",
- explanation = "This function is annotated with @$SAMPLED_ANNOTATION, but is not " +
- "inside a project/directory named $SAMPLES_DIRECTORY.",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- SampledAnnotationDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ val INVALID_SAMPLES_LOCATION =
+ Issue.create(
+ id = "InvalidSamplesLocation",
+ briefDescription = "Invalid samples location",
+ explanation =
+ "This function is annotated with @$SAMPLED_ANNOTATION, but is not " +
+ "inside a project/directory named $SAMPLES_DIRECTORY.",
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
}
}
@@ -236,13 +242,7 @@
private class KDocSampleLinkHandler(private val context: JavaContext) {
fun visitDeclaration(node: UDeclaration) {
val source = node.sourcePsi
- node.comments
- .mapNotNull {
- it.sourcePsi as? KDoc
- }
- .forEach {
- handleSampleLink(it)
- }
+ node.comments.mapNotNull { it.sourcePsi as? KDoc }.forEach { handleSampleLink(it) }
// Expect declarations are not visible in UAST, but they may have sample links on them.
// If we are looking at an actual declaration, also manually find the corresponding
// expect declaration for analysis.
@@ -255,9 +255,7 @@
// is a class with members that have documentation that we should look at - this
// will visit the declaration itself as well
declaration.forEachDescendantOfType<KtDeclaration> {
- it.docComment?.let { comment ->
- handleSampleLink(comment)
- }
+ it.docComment?.let { comment -> handleSampleLink(comment) }
}
}
}
@@ -268,27 +266,27 @@
// map of a KDocTag (which contains the location used when reporting issues) to the
// method link specified in @sample
- val sampleTags = sections.flatMap { section ->
- section.findTagsByName(SAMPLE_KDOC_ANNOTATION)
- .mapNotNull { sampleTag ->
- val linkText = sampleTag.getSubjectLink()?.getLinkText()
- if (linkText == null) {
- null
- } else {
- sampleTag to linkText
+ val sampleTags =
+ sections
+ .flatMap { section ->
+ section.findTagsByName(SAMPLE_KDOC_ANNOTATION).mapNotNull { sampleTag ->
+ val linkText = sampleTag.getSubjectLink()?.getLinkText()
+ if (linkText == null) {
+ null
+ } else {
+ sampleTag to linkText
+ }
}
}
- }.distinct()
+ .distinct()
sampleTags.forEach { (docTag, link) ->
// TODO: handle suppressions (if needed) with LintDriver.isSuppressed
- val mainLintMap =
- context.getPartialResults(UNRESOLVED_SAMPLE_LINK).map()
+ val mainLintMap = context.getPartialResults(UNRESOLVED_SAMPLE_LINK).map()
val sampleLinkLintMap =
- mainLintMap.getMap(SAMPLE_LINK_MAP) ?: LintMap().also {
- mainLintMap.put(SAMPLE_LINK_MAP, it)
- }
+ mainLintMap.getMap(SAMPLE_LINK_MAP)
+ ?: LintMap().also { mainLintMap.put(SAMPLE_LINK_MAP, it) }
// This overrides any identical links in the same project - no need to report the
// same error multiple times in different places, and it is tricky to do so in any case.
@@ -297,9 +295,7 @@
}
}
-/**
- * Handles sample functions annotated with @Sampled
- */
+/** Handles sample functions annotated with @Sampled */
private class SampledAnnotationHandler(private val context: JavaContext) {
fun visitMethod(node: UMethod) {
@@ -312,12 +308,15 @@
val currentPath = context.psiFile!!.virtualFile.path
if (SAMPLES_DIRECTORY !in currentPath) {
- val incident = Incident(context)
- .issue(INVALID_SAMPLES_LOCATION)
- .location(context.getNameLocation(node))
- .message("${node.name} is annotated with @$SAMPLED_ANNOTATION" +
- ", but is not inside a project/directory named $SAMPLES_DIRECTORY.")
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(INVALID_SAMPLES_LOCATION)
+ .location(context.getNameLocation(node))
+ .message(
+ "${node.name} is annotated with @$SAMPLED_ANNOTATION" +
+ ", but is not inside a project/directory named $SAMPLES_DIRECTORY."
+ )
+ .scope(node)
context.report(incident)
return
}
@@ -327,21 +326,20 @@
// The full name of the current function that will be referenced in a @sample tag
val fullFqName = "$parentFqName.${node.name}"
- val mainLintMap =
- context.getPartialResults(UNRESOLVED_SAMPLE_LINK).map()
+ val mainLintMap = context.getPartialResults(UNRESOLVED_SAMPLE_LINK).map()
val sampledFunctionLintMap =
- mainLintMap.getMap(SAMPLED_FUNCTION_MAP) ?: LintMap().also {
- mainLintMap.put(SAMPLED_FUNCTION_MAP, it)
- }
+ mainLintMap.getMap(SAMPLED_FUNCTION_MAP)
+ ?: LintMap().also { mainLintMap.put(SAMPLED_FUNCTION_MAP, it) }
val location = context.getNameLocation(node)
if (sampledFunctionLintMap.getLocation(fullFqName) != null) {
- val incident = Incident(context)
- .issue(MULTIPLE_FUNCTIONS_FOUND)
- .location(location)
- .message("Found multiple functions matching $fullFqName")
+ val incident =
+ Incident(context)
+ .issue(MULTIPLE_FUNCTIONS_FOUND)
+ .location(location)
+ .message("Found multiple functions matching $fullFqName")
context.report(incident)
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/TargetApiAnnotationUsageDetector.kt b/lint-checks/src/main/java/androidx/build/lint/TargetApiAnnotationUsageDetector.kt
index 048054d..2b62e86 100644
--- a/lint-checks/src/main/java/androidx/build/lint/TargetApiAnnotationUsageDetector.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/TargetApiAnnotationUsageDetector.kt
@@ -30,9 +30,7 @@
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.namePsiElement
-/**
- * Enforces policy banning use of the `@TargetApi` annotation.
- */
+/** Enforces policy banning use of the `@TargetApi` annotation. */
class TargetApiAnnotationUsageDetector : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
@@ -49,37 +47,43 @@
// instead of a constant ("TargetApi") to pass Lint's IMPORT_ALIAS test mode. In the
// case where namePsiElement returns null (which shouldn't happen), fall back to the
// RegEx check.
- val searchPattern = node.namePsiElement?.text
- ?: "(?:android\\.annotation\\.)?TargetApi"
+ val searchPattern =
+ node.namePsiElement?.text ?: "(?:android\\.annotation\\.)?TargetApi"
- val lintFix = fix().name("Replace with `@RequiresApi`")
- .replace()
- .pattern(searchPattern)
- .with("androidx.annotation.RequiresApi")
- .shortenNames()
- .autoFix(true, true)
- .build()
- val incident = Incident(context)
- .fix(lintFix)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message("Use `@RequiresApi` instead of `@TargetApi`")
- .scope(node)
+ val lintFix =
+ fix()
+ .name("Replace with `@RequiresApi`")
+ .replace()
+ .pattern(searchPattern)
+ .with("androidx.annotation.RequiresApi")
+ .shortenNames()
+ .autoFix(true, true)
+ .build()
+ val incident =
+ Incident(context)
+ .fix(lintFix)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Use `@RequiresApi` instead of `@TargetApi`")
+ .scope(node)
context.report(incident)
}
}
}
companion object {
- val ISSUE = Issue.create(
- "BanTargetApiAnnotation",
- "Replace usage of `@TargetApi` with `@RequiresApi`",
- "The `@TargetApi` annotation satisfies the `NewApi` lint check, but it does " +
- "not ensure that calls to the annotated API are correctly guarded on an `SDK_INT`" +
- " (or equivalent) check. Instead, use the `@RequiresApi` annotation to ensure " +
- "that all calls are correctly guarded.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(TargetApiAnnotationUsageDetector::class.java, Scope.JAVA_FILE_SCOPE)
- )
+ val ISSUE =
+ Issue.create(
+ "BanTargetApiAnnotation",
+ "Replace usage of `@TargetApi` with `@RequiresApi`",
+ "The `@TargetApi` annotation satisfies the `NewApi` lint check, but it does " +
+ "not ensure that calls to the annotated API are correctly guarded on an `SDK_INT`" +
+ " (or equivalent) check. Instead, use the `@RequiresApi` annotation to ensure " +
+ "that all calls are correctly guarded.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(TargetApiAnnotationUsageDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/TestSizeAnnotationEnforcer.kt b/lint-checks/src/main/java/androidx/build/lint/TestSizeAnnotationEnforcer.kt
index d068661..a38e1df 100644
--- a/lint-checks/src/main/java/androidx/build/lint/TestSizeAnnotationEnforcer.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/TestSizeAnnotationEnforcer.kt
@@ -35,12 +35,12 @@
import org.jetbrains.uast.UElement
/**
- * Lint check to enforce that every device side test (tests in the androidTest dir) has correct
- * size annotations, and a test runner that supports timeouts, so that we can correctly split up
- * test runs and enforce timeouts.
+ * Lint check to enforce that every device side test (tests in the androidTest dir) has correct size
+ * annotations, and a test runner that supports timeouts, so that we can correctly split up test
+ * runs and enforce timeouts.
*
- * Also enforces that test runners that do not support timeouts and host side tests do not
- * include test size annotations, as these are not used and can be misleading.
+ * Also enforces that test runners that do not support timeouts and host side tests do not include
+ * test size annotations, as these are not used and can be misleading.
*/
class TestSizeAnnotationEnforcer : Detector(), SourceCodeScanner {
@@ -66,16 +66,19 @@
val testRunner = runWithAnnotation.findAttributeValue(RUN_WITH_VALUE)
- val testRunnerClassName = (testRunner as? UClassLiteralExpression)
- ?.type?.canonicalText ?: return
+ val testRunnerClassName =
+ (testRunner as? UClassLiteralExpression)?.type?.canonicalText ?: return
if (testRunnerClassName !in ALLOWED_TEST_RUNNERS) {
- val incident = Incident(context)
- .issue(UNSUPPORTED_TEST_RUNNER)
- .location(context.getNameLocation(testRunner))
- .message("Unsupported test runner." +
- " Supported runners are: $ALLOWED_TEST_RUNNERS")
- .scope(testRunner)
+ val incident =
+ Incident(context)
+ .issue(UNSUPPORTED_TEST_RUNNER)
+ .location(context.getNameLocation(testRunner))
+ .message(
+ "Unsupported test runner." +
+ " Supported runners are: $ALLOWED_TEST_RUNNERS"
+ )
+ .scope(testRunner)
context.report(incident)
return
}
@@ -90,21 +93,22 @@
* size annotation.
*/
private fun enforceHasSizeAnnotations(node: UClass) {
- node.methods.filter {
- it.hasAnnotation(TEST_ANNOTATION)
- }.forEach { method ->
- val combinedAnnotations = node.uAnnotations + method.uAnnotations
- // Report an issue if neither the test method nor the surrounding class have a
- // valid test size annotation
- if (combinedAnnotations.none { it.qualifiedName in TEST_SIZE_ANNOTATIONS }) {
- val incident = Incident(context)
- .issue(MISSING_TEST_SIZE_ANNOTATION)
- .location(context.getNameLocation(method))
- .message("Missing test size annotation")
- .scope(method)
- context.report(incident)
+ node.methods
+ .filter { it.hasAnnotation(TEST_ANNOTATION) }
+ .forEach { method ->
+ val combinedAnnotations = node.uAnnotations + method.uAnnotations
+ // Report an issue if neither the test method nor the surrounding class have a
+ // valid test size annotation
+ if (combinedAnnotations.none { it.qualifiedName in TEST_SIZE_ANNOTATIONS }) {
+ val incident =
+ Incident(context)
+ .issue(MISSING_TEST_SIZE_ANNOTATION)
+ .location(context.getNameLocation(method))
+ .message("Missing test size annotation")
+ .scope(method)
+ context.report(incident)
+ }
}
- }
}
/**
@@ -115,29 +119,31 @@
node.uAnnotations
.find { it.qualifiedName in TEST_SIZE_ANNOTATIONS }
?.let { annotation ->
- val incident = Incident(context)
- .issue(UNEXPECTED_TEST_SIZE_ANNOTATION)
- .location(context.getNameLocation(annotation))
- .message("Unexpected test size annotation")
- .scope(annotation)
- context.report(incident)
- }
-
- node.methods.filter {
- it.hasAnnotation(TEST_ANNOTATION)
- }.forEach { method ->
- // Report an issue if the method has a size annotation
- method.uAnnotations
- .find { it.qualifiedName in TEST_SIZE_ANNOTATIONS }
- ?.let { annotation ->
- val incident = Incident(context)
+ val incident =
+ Incident(context)
.issue(UNEXPECTED_TEST_SIZE_ANNOTATION)
.location(context.getNameLocation(annotation))
.message("Unexpected test size annotation")
.scope(annotation)
- context.report(incident)
- }
- }
+ context.report(incident)
+ }
+
+ node.methods
+ .filter { it.hasAnnotation(TEST_ANNOTATION) }
+ .forEach { method ->
+ // Report an issue if the method has a size annotation
+ method.uAnnotations
+ .find { it.qualifiedName in TEST_SIZE_ANNOTATIONS }
+ ?.let { annotation ->
+ val incident =
+ Incident(context)
+ .issue(UNEXPECTED_TEST_SIZE_ANNOTATION)
+ .location(context.getNameLocation(annotation))
+ .message("Unexpected test size annotation")
+ .scope(annotation)
+ context.report(incident)
+ }
+ }
}
}
@@ -145,83 +151,93 @@
/**
* List of test runners that support timeouts. This is a subset of [ALLOWED_TEST_RUNNERS].
*/
- private val TIMEOUT_ENFORCED_TEST_RUNNERS = listOf(
- "androidx.test.ext.junit.runners.AndroidJUnit4"
- )
+ private val TIMEOUT_ENFORCED_TEST_RUNNERS =
+ listOf("androidx.test.ext.junit.runners.AndroidJUnit4")
/**
* Only AndroidJUnit4 enforces timeouts, so it should be used over JUnit4 / other such
- * runners. Parameterized does not enforce timeouts, but there is no equivalent that
- * does, so it is still fine to use.
+ * runners. Parameterized does not enforce timeouts, but there is no equivalent that does,
+ * so it is still fine to use.
*/
- private val ALLOWED_TEST_RUNNERS = listOf(
- "androidx.test.ext.junit.runners.AndroidJUnit4",
- "org.junit.runners.Parameterized"
- )
+ private val ALLOWED_TEST_RUNNERS =
+ listOf(
+ "androidx.test.ext.junit.runners.AndroidJUnit4",
+ "org.junit.runners.Parameterized"
+ )
private const val RUN_WITH = "org.junit.runner.RunWith"
private const val RUN_WITH_VALUE = "value"
/**
- * TODO: b/170214947
- * Directories that contain device test source. Unfortunately we don't currently have a
- * better way of figuring out what test we are analyzing, as [Scope.TEST_SOURCES]
- * includes both host and device side tests.
+ * TODO: b/170214947 Directories that contain device test source. Unfortunately we don't
+ * currently have a better way of figuring out what test we are analyzing, as
+ * [Scope.TEST_SOURCES] includes both host and device side tests.
*/
- private val ANDROID_TEST_DIRS = listOf(
- "androidTest",
- "androidInstrumentedTest",
- "androidDeviceTest",
- "androidDeviceTestDebug",
- "androidDeviceTestRelease"
- )
+ private val ANDROID_TEST_DIRS =
+ listOf(
+ "androidTest",
+ "androidInstrumentedTest",
+ "androidDeviceTest",
+ "androidDeviceTestDebug",
+ "androidDeviceTestRelease"
+ )
private const val TEST_ANNOTATION = "org.junit.Test"
- private val TEST_SIZE_ANNOTATIONS = listOf(
- "androidx.test.filters.SmallTest",
- "androidx.test.filters.MediumTest",
- "androidx.test.filters.LargeTest"
- )
-
- val UNSUPPORTED_TEST_RUNNER = Issue.create(
- "UnsupportedTestRunner",
- "Unsupported test runner",
- "Only AndroidJUnit4 supports setting a timeout for tests using the test size " +
- "annotation, so this test runner should be used instead. There is no " +
- "equivalent parameterized runner that also sets timeouts, so Parameterized is " +
- "also allowed.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- TestSizeAnnotationEnforcer::class.java,
- EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ private val TEST_SIZE_ANNOTATIONS =
+ listOf(
+ "androidx.test.filters.SmallTest",
+ "androidx.test.filters.MediumTest",
+ "androidx.test.filters.LargeTest"
)
- )
- val MISSING_TEST_SIZE_ANNOTATION = Issue.create(
- "MissingTestSizeAnnotation",
- "Missing test size annotation",
- "All tests require a valid test size annotation, on the class or per method." +
- "\nYou must use at least one of: @SmallTest, @MediumTest or @LargeTest." +
- "\nUse @SmallTest for tests that run in under 200ms, @MediumTest for tests " +
- "that run in under 1000ms, and @LargeTest for tests that run for more " +
- "than a second.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- TestSizeAnnotationEnforcer::class.java,
- EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ val UNSUPPORTED_TEST_RUNNER =
+ Issue.create(
+ "UnsupportedTestRunner",
+ "Unsupported test runner",
+ "Only AndroidJUnit4 supports setting a timeout for tests using the test size " +
+ "annotation, so this test runner should be used instead. There is no " +
+ "equivalent parameterized runner that also sets timeouts, so Parameterized is " +
+ "also allowed.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ TestSizeAnnotationEnforcer::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
)
- )
- val UNEXPECTED_TEST_SIZE_ANNOTATION = Issue.create(
- "UnexpectedTestSizeAnnotation",
- "Unexpected test size annotation",
- "Host side tests and device tests with runners that do not support timeouts " +
- "should not have any test size annotations. Host side tests all run together," +
- " and device tests with runners that do not support timeouts will be placed in" +
- " the 'large' test bucket, since we cannot enforce that they will be fast enough" +
- " to run in the 'small' / 'medium' buckets.",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- TestSizeAnnotationEnforcer::class.java,
- EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ val MISSING_TEST_SIZE_ANNOTATION =
+ Issue.create(
+ "MissingTestSizeAnnotation",
+ "Missing test size annotation",
+ "All tests require a valid test size annotation, on the class or per method." +
+ "\nYou must use at least one of: @SmallTest, @MediumTest or @LargeTest." +
+ "\nUse @SmallTest for tests that run in under 200ms, @MediumTest for tests " +
+ "that run in under 1000ms, and @LargeTest for tests that run for more " +
+ "than a second.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ TestSizeAnnotationEnforcer::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
)
- )
+
+ val UNEXPECTED_TEST_SIZE_ANNOTATION =
+ Issue.create(
+ "UnexpectedTestSizeAnnotation",
+ "Unexpected test size annotation",
+ "Host side tests and device tests with runners that do not support timeouts " +
+ "should not have any test size annotations. Host side tests all run together," +
+ " and device tests with runners that do not support timeouts will be placed in" +
+ " the 'large' test bucket, since we cannot enforce that they will be fast enough" +
+ " to run in the 'small' / 'medium' buckets.",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ TestSizeAnnotationEnforcer::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt
index 3cd0e5c..a42d0cf 100644
--- a/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/AbstractLintDetectorTest.kt
@@ -28,9 +28,7 @@
import com.android.tools.lint.detector.api.Issue
import java.io.FileNotFoundException
-/**
- * Implementation of [LintDetectorTest] that's slightly more Kotlin-friendly.
- */
+/** Implementation of [LintDetectorTest] that's slightly more Kotlin-friendly. */
abstract class AbstractLintDetectorTest(
val useDetector: Detector,
val useIssues: List<Issue>,
@@ -52,66 +50,49 @@
): TestLintResult {
// If we have stubs, push those into a virtual project and pass them through the call to
// projects(), since attempting to call files() would overwrite the call to projects().
- val projectsWithStubs = if (stubs.isNotEmpty()) {
- arrayOf(*projects, project().files(*stubs))
- } else {
- projects
- }
+ val projectsWithStubs =
+ if (stubs.isNotEmpty()) {
+ arrayOf(*projects, project().files(*stubs))
+ } else {
+ projects
+ }
- return lint()
- .projects(*projectsWithStubs)
- .testModes(testModes)
- .allowDuplicates()
- .run()
+ return lint().projects(*projectsWithStubs).testModes(testModes).allowDuplicates().run()
}
fun check(
vararg files: TestFile,
): TestLintResult {
- return lint()
- .files(
- *stubs,
- *files
- )
- .allowDuplicates()
- .run()
+ return lint().files(*stubs, *files).allowDuplicates().run()
}
}
-/**
- * Creates a new [ProjectDescription].
- */
+/** Creates a new [ProjectDescription]. */
fun project(): ProjectDescription = ProjectDescription()
-/**
- * Loads a [TestFile] from `AndroidManifest.xml` included in the JAR resources.
- */
-fun manifestSample(): TestFile = TestFiles.manifest(
- Stubs::class.java.getResource(
- "/AndroidManifest.xml"
- )?.readText() ?: throw FileNotFoundException(
- "Could not find AndroidManifest.xml in the integration test project"
+/** Loads a [TestFile] from `AndroidManifest.xml` included in the JAR resources. */
+fun manifestSample(): TestFile =
+ TestFiles.manifest(
+ Stubs::class.java.getResource("/AndroidManifest.xml")?.readText()
+ ?: throw FileNotFoundException(
+ "Could not find AndroidManifest.xml in the integration test project"
+ )
)
-)
-/**
- * Loads a [TestFile] from Java source code included in the JAR resources.
- */
-fun javaSample(className: String): TestFile = TestFiles.java(
- Stubs::class.java.getResource(
- "/java/${className.replace('.', '/')}.java"
- )?.readText() ?: throw FileNotFoundException(
- "Could not find Java sources for $className in the integration test project"
+/** Loads a [TestFile] from Java source code included in the JAR resources. */
+fun javaSample(className: String): TestFile =
+ TestFiles.java(
+ Stubs::class.java.getResource("/java/${className.replace('.', '/')}.java")?.readText()
+ ?: throw FileNotFoundException(
+ "Could not find Java sources for $className in the integration test project"
+ )
)
-)
-/**
- * Loads a [TestFile] from Kotlin source code included in the JAR resources.
- */
-fun ktSample(className: String): TestFile = TestFiles.kotlin(
- Stubs::class.java.getResource(
- "/java/${className.replace('.', '/')}.kt"
- )?.readText() ?: throw FileNotFoundException(
- "Could not find Kotlin sources for $className in the integration test project"
+/** Loads a [TestFile] from Kotlin source code included in the JAR resources. */
+fun ktSample(className: String): TestFile =
+ TestFiles.kotlin(
+ Stubs::class.java.getResource("/java/${className.replace('.', '/')}.kt")?.readText()
+ ?: throw FileNotFoundException(
+ "Could not find Kotlin sources for $className in the integration test project"
+ )
)
-)
diff --git a/lint-checks/src/test/java/androidx/build/lint/AndroidManifestServiceExportedDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/AndroidManifestServiceExportedDetectorTest.kt
index 094e9d5..1f34ae4 100644
--- a/lint-checks/src/test/java/androidx/build/lint/AndroidManifestServiceExportedDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/AndroidManifestServiceExportedDetectorTest.kt
@@ -23,24 +23,25 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class AndroidManifestServiceExportedDetectorTest : AbstractLintDetectorTest(
- useDetector = AndroidManifestServiceExportedDetector(),
- useIssues = listOf(AndroidManifestServiceExportedDetector.ISSUE),
-) {
+class AndroidManifestServiceExportedDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = AndroidManifestServiceExportedDetector(),
+ useIssues = listOf(AndroidManifestServiceExportedDetector.ISSUE),
+ ) {
@Test
fun `Detect missing exported=true declaration in service tag`() {
- val input = arrayOf(
- manifestSample()
- )
+ val input = arrayOf(manifestSample())
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
AndroidManifest.xml:21: Error: Missing exported=true in <service> tag [MissingServiceExportedEqualsTrue]
<service android:name="androidx.core.app.JobIntentService">
^
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -48,9 +49,10 @@
@Test
fun `Detect present exported=true declaration in service tag`() {
- val input = xml(
- "AndroidManifest.xml",
- """
+ val input =
+ xml(
+ "AndroidManifest.xml",
+ """
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<service
@@ -58,8 +60,9 @@
android:exported="true" />
</application>
</manifest>
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
check(input).expectClean()
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt
index 47e2510..33211a0 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanConcurrentHashMapTest.kt
@@ -24,16 +24,18 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanConcurrentHashMapTest : AbstractLintDetectorTest(
- useDetector = BanConcurrentHashMap(),
- useIssues = listOf(BanConcurrentHashMap.ISSUE),
-) {
+class BanConcurrentHashMapTest :
+ AbstractLintDetectorTest(
+ useDetector = BanConcurrentHashMap(),
+ useIssues = listOf(BanConcurrentHashMap.ISSUE),
+ ) {
@Test
fun `Detection of ConcurrentHashMap import in Java sources`() {
- val input = java(
- "src/androidx/ConcurrentHashMapImportJava.java",
- """
+ val input =
+ java(
+ "src/androidx/ConcurrentHashMapImportJava.java",
+ """
import androidx.annotation.NonNull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -45,16 +47,19 @@
return new ConcurrentHashMap<>();
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ConcurrentHashMapImportJava.java:3: Error: Detected ConcurrentHashMap usage. [BanConcurrentHashMap]
import java.util.concurrent.ConcurrentHashMap;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(input).expect(expected)
@@ -62,9 +67,10 @@
@Test
fun `Detection of ConcurrentHashMap fully-qualified usage in Java sources`() {
- val input = java(
- "src/androidx/ConcurrentHashMapUsageJava.java",
- """
+ val input =
+ java(
+ "src/androidx/ConcurrentHashMapUsageJava.java",
+ """
import androidx.annotation.NonNull;
import java.util.Map;
@@ -75,11 +81,13 @@
return new java.util.concurrent.ConcurrentHashMap<>();
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ConcurrentHashMapUsageJava.java:5: Error: Detected ConcurrentHashMap usage. [BanConcurrentHashMap]
private final Map<?, ?> mMap = new java.util.concurrent.ConcurrentHashMap<>();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -87,7 +95,8 @@
return new java.util.concurrent.ConcurrentHashMap<>();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(input).expect(expected)
@@ -95,9 +104,10 @@
@Test
fun `Detection of ConcurrentHashMap import in Kotlin sources`() {
- val input = kotlin(
- "src/androidx/ConcurrentHashMapImportKotlin.kt",
- """
+ val input =
+ kotlin(
+ "src/androidx/ConcurrentHashMapImportKotlin.kt",
+ """
package androidx
import java.util.concurrent.ConcurrentHashMap
@@ -109,23 +119,23 @@
return ConcurrentHashMap()
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ConcurrentHashMapImportKotlin.kt:3: Error: Detected ConcurrentHashMap usage. [BanConcurrentHashMap]
import java.util.concurrent.ConcurrentHashMap
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- input
- )
+ .files(*stubs, input)
// This fails in IMPORT_ALIAS mode because changing the import line changes the error.
// It fails in FULLY_QUALIFIED mode because more errors occur when the fully-qualified
// class is used. These cases are tested separately.
@@ -136,9 +146,10 @@
@Test
fun `Detection of ConcurrentHashMap fully-qualified usage in Kotlin sources`() {
- val input = kotlin(
- "src/androidx/ConcurrentHashMapUsageKotlin.kt",
- """
+ val input =
+ kotlin(
+ "src/androidx/ConcurrentHashMapUsageKotlin.kt",
+ """
package androidx
class ConcurrentHashMapUsageKotlin {
@@ -148,11 +159,13 @@
return java.util.concurrent.ConcurrentHashMap()
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ConcurrentHashMapUsageKotlin.kt:4: Error: Detected ConcurrentHashMap usage. [BanConcurrentHashMap]
private val mMap: Map<*, *> = java.util.concurrent.ConcurrentHashMap<Any, Any>()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -160,23 +173,19 @@
return java.util.concurrent.ConcurrentHashMap()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- lint()
- .files(
- *stubs,
- input
- )
- .run()
- .expect(expected)
+ lint().files(*stubs, input).run().expect(expected)
}
@Test
fun `Detection of ConcurrentHashMap import alias in Kotlin sources`() {
- val input = kotlin(
- "src/androidx/ConcurrentHashMapUsageAliasKotlin.kt",
- """
+ val input =
+ kotlin(
+ "src/androidx/ConcurrentHashMapUsageAliasKotlin.kt",
+ """
package androidx
import java.util.concurrent.ConcurrentHashMap as NewClassName
@@ -188,23 +197,23 @@
return NewClassName()
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ConcurrentHashMapUsageAliasKotlin.kt:3: Error: Detected ConcurrentHashMap usage. [BanConcurrentHashMap]
import java.util.concurrent.ConcurrentHashMap as NewClassName
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- input
- )
+ .files(*stubs, input)
// In FULLY_QUALIFIED test mode, more errors occur when the fully-qualified class is
// used. This case is tested separately.
.skipTestModes(TestMode.FULLY_QUALIFIED)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
index 425267a..93ece14 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanInappropriateExperimentalUsageTest.kt
@@ -30,20 +30,20 @@
/**
* Important:
*
- * [BanInappropriateExperimentalUsage] uses the atomic library groups list generated from
- * production data. For tests, we want to overwrite this to provide a different list of
- * atomic library groups; the file location is
- * lint-checks/src/test/resources/atomic-library-groups.txt.
+ * [BanInappropriateExperimentalUsage] uses the atomic library groups list generated from production
+ * data. For tests, we want to overwrite this to provide a different list of atomic library groups;
+ * the file location is lint-checks/src/test/resources/atomic-library-groups.txt.
*
* Note that the filename must match
* [BanInappropriateExperimentalUsage.ATOMIC_LIBRARY_GROUPS_FILENAME].
*/
@RunWith(JUnit4::class)
-class BanInappropriateExperimentalUsageTest : AbstractLintDetectorTest(
- useDetector = BanInappropriateExperimentalUsage(),
- useIssues = listOf(BanInappropriateExperimentalUsage.ISSUE),
- stubs = arrayOf(Stubs.OptIn),
-) {
+class BanInappropriateExperimentalUsageTest :
+ AbstractLintDetectorTest(
+ useDetector = BanInappropriateExperimentalUsage(),
+ useIssues = listOf(BanInappropriateExperimentalUsage.ISSUE),
+ stubs = arrayOf(Stubs.OptIn),
+ ) {
@Test
fun `Check if annotation is always allowed`() {
@@ -56,7 +56,11 @@
assertTrue(isAnnotationAlwaysAllowed("kotlin.experimental.ExperimentalTypeInference"))
assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.DelicateCoroutinesApi"))
assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.ExperimentalCoroutinesApi"))
- assertTrue(isAnnotationAlwaysAllowed("org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints"))
+ assertTrue(
+ isAnnotationAlwaysAllowed(
+ "org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints"
+ )
+ )
assertTrue(isAnnotationAlwaysAllowed("org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI"))
assertFalse(isAnnotationAlwaysAllowed("androidx.foo.bar"))
@@ -67,8 +71,14 @@
@Test
fun `getLibraryFromPath should return correct Maven coordinates`() {
/* ktlint-disable max-line-length */
- val paging = getMavenCoordinatesFromPath("/path/to/checkout/out/androidx/paging/paging-common/build/libs/paging-common-3.2.0-alpha01.jar")
- val room = getMavenCoordinatesFromPath("/path/to/checkout/out/androidx/room/room-compiler-processing/build/libs/room-compiler-processing-2.5.0-alpha02.jar")
+ val paging =
+ getMavenCoordinatesFromPath(
+ "/path/to/checkout/out/androidx/paging/paging-common/build/libs/paging-common-3.2.0-alpha01.jar"
+ )
+ val room =
+ getMavenCoordinatesFromPath(
+ "/path/to/checkout/out/androidx/room/room-compiler-processing/build/libs/room-compiler-processing-2.5.0-alpha02.jar"
+ )
/* ktlint-enable max-line-length */
assertNotNull(paging!!)
@@ -87,23 +97,27 @@
@Test
fun `Test same atomic module Experimental usage via Gradle model`() {
- val provider = project()
- .name("provider")
- .files(
- ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"),
- ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
- gradle(
- """
+ val provider =
+ project()
+ .name("provider")
+ .files(
+ ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"),
+ ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
+ gradle(
+ """
apply plugin: 'com.android.library'
group=sample.annotation.provider
"""
- ).indented(),
- )
+ )
+ .indented(),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
No warnings.
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(provider).expect(expected)
@@ -111,23 +125,27 @@
@Test
fun `Test same non-atomic module Experimental usage via Gradle model`() {
- val provider = project()
- .name("provider")
- .files(
- ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"),
- ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
- gradle(
- """
+ val provider =
+ project()
+ .name("provider")
+ .files(
+ ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"),
+ ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
+ gradle(
+ """
apply plugin: 'com.android.library'
group=sample.annotation.provider
"""
- ).indented(),
- )
+ )
+ .indented(),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
No warnings.
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(provider).expect(expected)
@@ -137,44 +155,55 @@
fun `Test cross-module Experimental usage via Gradle model`() {
/* ktlint-disable max-line-length */
- val provider = project()
- .name("provider")
- .type(ProjectDescription.Type.LIBRARY)
- .report(false)
- .files(
- JetpackRequiresOptIn,
- ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
- javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"),
- javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"),
- javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate"),
- javaSample("sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava"),
- javaSample("sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate"),
- gradle(
- """
+ val provider =
+ project()
+ .name("provider")
+ .type(ProjectDescription.Type.LIBRARY)
+ .report(false)
+ .files(
+ JetpackRequiresOptIn,
+ ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
+ javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"),
+ javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"),
+ javaSample(
+ "sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate"
+ ),
+ javaSample(
+ "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava"
+ ),
+ javaSample(
+ "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate"
+ ),
+ gradle(
+ """
apply plugin: 'com.android.library'
group=sample.annotation.provider
"""
- ).indented(),
- )
+ )
+ .indented(),
+ )
/* ktlint-enable max-line-length */
- val consumer = project()
- .name("consumer")
- .type(ProjectDescription.Type.LIBRARY)
- .dependsOn(provider)
- .files(
- JetpackOptIn,
- ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
- gradle(
- """
+ val consumer =
+ project()
+ .name("consumer")
+ .type(ProjectDescription.Type.LIBRARY)
+ .dependsOn(provider)
+ .files(
+ JetpackOptIn,
+ ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
+ gradle(
+ """
apply plugin: 'com.android.library'
group=androidx.sample.consumer
"""
- ).indented(),
- )
+ )
+ .indented(),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:35: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@ExperimentalSampleAnnotationJava
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -188,19 +217,20 @@
@kotlin.OptIn(
^
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:59: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
- @kotlin.OptIn(RequiresOptInSampleAnnotationJava::class, RequiresOptInSampleAnnotationJavaDuplicate::class)
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:65: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
+ @kotlin.OptIn(
+ ^
+../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:69: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@androidx.annotation.OptIn(RequiresAndroidXOptInSampleAnnotationJava::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:70: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
+../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:74: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@androidx.annotation.OptIn(
^
-../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:79: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
- @androidx.annotation.OptIn(RequiresAndroidXOptInSampleAnnotationJava::class, RequiresAndroidXOptInSampleAnnotationJavaDuplicate::class)
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:83: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
+ @androidx.annotation.OptIn(
+ ^
8 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(provider, consumer).expect(expected)
@@ -210,48 +240,60 @@
fun `Test cross-module Experimental usage in alpha via Gradle model`() {
/* ktlint-disable max-line-length */
- val provider = project()
- .name("provider")
- .type(ProjectDescription.Type.LIBRARY)
- .report(false)
- .files(
- JetpackRequiresOptIn,
- ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
- javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"),
- javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"),
- javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate"),
- javaSample("sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava"),
- javaSample("sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate"),
- gradle(
- """
+ val provider =
+ project()
+ .name("provider")
+ .type(ProjectDescription.Type.LIBRARY)
+ .report(false)
+ .files(
+ JetpackRequiresOptIn,
+ ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
+ javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"),
+ javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"),
+ javaSample(
+ "sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate"
+ ),
+ javaSample(
+ "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava"
+ ),
+ javaSample(
+ "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate"
+ ),
+ gradle(
+ """
apply plugin: 'com.android.library'
group=sample.annotation.provider
version=1.0.0-beta02
"""
- ).indented(),
- )
+ )
+ .indented(),
+ )
/* ktlint-enable max-line-length */
- val consumer = project()
- .name("consumer")
- .type(ProjectDescription.Type.LIBRARY)
- .dependsOn(provider)
- .files(
- JetpackOptIn,
- ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
- gradle(
- """
+ val consumer =
+ project()
+ .name("consumer")
+ .type(ProjectDescription.Type.LIBRARY)
+ .dependsOn(provider)
+ .files(
+ JetpackOptIn,
+ ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
+ gradle(
+ """
apply plugin: 'com.android.library'
group=androidx.sample.consumer
version=1.0.0-alpha01
"""
- ).indented(),
- )
+ )
+ .indented(),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
No warnings.
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(provider, consumer).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanInlineOptInTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanInlineOptInTest.kt
index e58fb16..b31f832 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanInlineOptInTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanInlineOptInTest.kt
@@ -23,30 +23,35 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanInlineOptInTest : AbstractLintDetectorTest(
- useDetector = BanInlineOptIn(),
- useIssues = listOf(BanInlineOptIn.ISSUE),
-) {
+class BanInlineOptInTest :
+ AbstractLintDetectorTest(
+ useDetector = BanInlineOptIn(),
+ useIssues = listOf(BanInlineOptIn.ISSUE),
+ ) {
@Test
fun `Detect inline function with an OptIn annotation`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
@RequiresOptIn
annotation class ExperimentalSampleAnnotation
@OptIn(ExperimentalSampleAnnotation::class)
inline fun String.myInlineFun() = this.length
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/ExperimentalSampleAnnotation.kt:5: Error: Inline functions cannot opt into experimental APIs. [BanInlineOptIn]
inline fun String.myInlineFun() = this.length
~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(input).expect(expected)
@@ -54,11 +59,13 @@
@Test
fun `Detect inline function without an OptIn annotation`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
inline fun String.myInlineFun() = this.length
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
check(input).expectClean()
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
index 5b327f1..6ec5296 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanKeepAnnotationTest.kt
@@ -23,42 +23,50 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanKeepAnnotationTest : AbstractLintDetectorTest(
- useDetector = BanKeepAnnotation(),
- useIssues = listOf(BanKeepAnnotation.ISSUE),
- stubs = arrayOf(Stubs.Keep),
-) {
+class BanKeepAnnotationTest :
+ AbstractLintDetectorTest(
+ useDetector = BanKeepAnnotation(),
+ useIssues = listOf(BanKeepAnnotation.ISSUE),
+ stubs = arrayOf(Stubs.Keep),
+ ) {
@Test
fun `Detection of Keep annotation in Java sources`() {
- val input = arrayOf(
- javaSample("androidx.KeepAnnotationUsageJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.KeepAnnotationUsageJava"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/KeepAnnotationUsageJava.java:21: Error: Uses @Keep annotation [BanKeepAnnotation]
@Keep
~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
}
+
@Test
fun `Detection of Keep annotation in Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.KeepAnnotationUsageKotlin"),
- )
+ val input =
+ arrayOf(
+ ktSample("androidx.KeepAnnotationUsageKotlin"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/KeepAnnotationUsageKotlin.kt:21: Error: Uses @Keep annotation [BanKeepAnnotation]
-@Keep
+@Keep class KeepAnnotationUsageKotlin
~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt
index 44327ba..7c0a081 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanParcelableUsageTest.kt
@@ -23,24 +23,28 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanParcelableUsageTest : AbstractLintDetectorTest(
- useDetector = BanParcelableUsage(),
- useIssues = listOf(BanParcelableUsage.ISSUE),
-) {
+class BanParcelableUsageTest :
+ AbstractLintDetectorTest(
+ useDetector = BanParcelableUsage(),
+ useIssues = listOf(BanParcelableUsage.ISSUE),
+ ) {
@Test
fun `Detection of Parcelable usage in Java sources`() {
- val input = arrayOf(
- javaSample("androidx.ParcelableUsageJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ParcelableUsageJava"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ParcelableUsageJava.java:25: Error: Class implements android.os.Parcelable [BanParcelableUsage]
public class ParcelableUsageJava implements Parcelable {
~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -48,17 +52,20 @@
@Test
fun `Detection of Parcelable usage in Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.ParcelableUsageKotlin"),
- )
+ val input =
+ arrayOf(
+ ktSample("androidx.ParcelableUsageKotlin"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ParcelableUsageKotlin.kt:23: Error: Class implements android.os.Parcelable [BanParcelableUsage]
open class ParcelableUsageKotlin protected constructor(parcel: Parcel) : Parcelable {
~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt
index 08720a0..cf84739 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt
@@ -23,20 +23,23 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanRestrictToTestsScopeTest : AbstractLintDetectorTest(
- useDetector = BanRestrictToTestsScope(),
- useIssues = listOf(BanRestrictToTestsScope.ISSUE),
- stubs = arrayOf(Stubs.RestrictTo),
-) {
+class BanRestrictToTestsScopeTest :
+ AbstractLintDetectorTest(
+ useDetector = BanRestrictToTestsScope(),
+ useIssues = listOf(BanRestrictToTestsScope.ISSUE),
+ stubs = arrayOf(Stubs.RestrictTo),
+ ) {
@Test
fun `Detection of @RestrictTo(TESTS) usage in Java sources`() {
- val input = arrayOf(
- javaSample("androidx.RestrictToTestsAnnotationUsageJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.RestrictToTestsAnnotationUsageJava"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/RestrictToTestsAnnotationUsageJava.java:26: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
@RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -53,9 +56,11 @@
@RestrictTo({Scope.TESTS, Scope.LIBRARY})
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val fixDiffs = """
+ val fixDiffs =
+ """
Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 26: Replace with `@VisibleForTesting`:
@@ -26 +26
- @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
@@ -72,47 +77,54 @@
@@ -35 +35
- @RestrictTo(TESTS)
+ @androidx.annotation.VisibleForTesting
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(fixDiffs)
+ check(*input).expect(expected).expectFixDiffs(fixDiffs)
}
+
@Test
fun `Detection of @RestrictTo(TESTS) usage in Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.RestrictToTestsAnnotationUsageKotlin"),
- )
+ val input =
+ arrayOf(
+ ktSample("androidx.RestrictToTestsAnnotationUsageKotlin"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:24: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @RestrictTo(RestrictTo.Scope.TESTS) fun testMethod() {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:27: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
- @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY)
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:26: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY) fun testMethodVarArg() {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:30: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
- @get:RestrictTo(RestrictTo.Scope.TESTS)
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:28: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @get:RestrictTo(RestrictTo.Scope.TESTS) val testPropertyGet = "test"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val fixDiffs = """
+ val fixDiffs =
+ """
Fix for src/androidx/RestrictToTestsAnnotationUsageKotlin.kt line 24: Replace with `@VisibleForTesting`:
-@@ -24 +24
-- @RestrictTo(RestrictTo.Scope.TESTS)
-+ @androidx.annotation.VisibleForTesting
-Fix for src/androidx/RestrictToTestsAnnotationUsageKotlin.kt line 30: Replace with `@get:VisibleForTesting`:
-@@ -30 +30
-- @get:RestrictTo(RestrictTo.Scope.TESTS)
-+ @get:androidx.annotation.VisibleForTesting
- """.trimIndent()
+@@ -22 +22
++ import androidx.annotation.VisibleForTesting
+@@ -24 +25
+- @RestrictTo(RestrictTo.Scope.TESTS) fun testMethod() {}
++ @VisibleForTesting fun testMethod() {}
+Fix for src/androidx/RestrictToTestsAnnotationUsageKotlin.kt line 28: Replace with `@get:VisibleForTesting`:
+@@ -22 +22
++ import androidx.annotation.VisibleForTesting
+@@ -28 +29
+- @get:RestrictTo(RestrictTo.Scope.TESTS) val testPropertyGet = "test"
++ @get:VisibleForTesting val testPropertyGet = "test"
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(fixDiffs)
+ check(*input).expect(expected).expectFixDiffs(fixDiffs)
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt
index c71e791..d14fb6f 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanSynchronizedMethodsTest.kt
@@ -23,31 +23,36 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanSynchronizedMethodsTest : AbstractLintDetectorTest(
- useDetector = BanSynchronizedMethods(),
- useIssues = listOf(BanSynchronizedMethods.ISSUE),
-) {
+class BanSynchronizedMethodsTest :
+ AbstractLintDetectorTest(
+ useDetector = BanSynchronizedMethods(),
+ useIssues = listOf(BanSynchronizedMethods.ISSUE),
+ ) {
@Test
fun `Detection of synchronized methods in Java sources`() {
- val input = java(
- "src/androidx/SynchronizedMethodJava.java",
- """
+ val input =
+ java(
+ "src/androidx/SynchronizedMethodJava.java",
+ """
public class SynchronizedMethodJava {
public synchronized void someMethod() {
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/SynchronizedMethodJava.java:3: Error: Use of synchronized methods is not recommended [BanSynchronizedMethods]
public synchronized void someMethod() {
^
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(input).expect(expected)
@@ -55,24 +60,28 @@
@Test
fun `Detection of synchronized methods in Kotlin sources`() {
- val input = kotlin(
- "src/androidx/SynchronizedMethodKotlin.kt",
- """
+ val input =
+ kotlin(
+ "src/androidx/SynchronizedMethodKotlin.kt",
+ """
class SynchronizedMethodKotlin {
@Synchronized
fun someMethod() {}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/SynchronizedMethodKotlin.kt:3: Error: Use of synchronized methods is not recommended [BanSynchronizedMethods]
@Synchronized
^
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanThreadSleepTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanThreadSleepTest.kt
index f8a9a1b..b27181f 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanThreadSleepTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanThreadSleepTest.kt
@@ -23,25 +23,29 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanThreadSleepTest : AbstractLintDetectorTest(
- useDetector = BanThreadSleep(),
- useIssues = listOf(BanThreadSleep.ISSUE),
- stubs = arrayOf(Stubs.Keep),
-) {
+class BanThreadSleepTest :
+ AbstractLintDetectorTest(
+ useDetector = BanThreadSleep(),
+ useIssues = listOf(BanThreadSleep.ISSUE),
+ stubs = arrayOf(Stubs.Keep),
+ ) {
@Test
fun `Detection of Thread#sleep in Java sources`() {
- val input = arrayOf(
- javaSample("androidx.ThreadSleepUsageJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ThreadSleepUsageJava"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ThreadSleepUsageJava.java:21: Error: Uses Thread.sleep() [BanThreadSleep]
Thread.sleep(1000);
~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -49,17 +53,20 @@
@Test
fun `Detection of Thread#sleep in Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.ThreadSleepUsageKotlin"),
- )
+ val input =
+ arrayOf(
+ ktSample("androidx.ThreadSleepUsageKotlin"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ThreadSleepUsageKotlin.kt:21: Error: Uses Thread.sleep() [BanThreadSleep]
Thread.sleep(1000)
~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt
index 130f667..780ea88 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanUncheckedReflectionTest.kt
@@ -24,21 +24,20 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanUncheckedReflectionTest : AbstractLintDetectorTest(
- useDetector = BanUncheckedReflection(),
- useIssues = listOf(BanUncheckedReflection.ISSUE),
- stubs = arrayOf(Stubs.ChecksSdkIntAtLeast),
-) {
+class BanUncheckedReflectionTest :
+ AbstractLintDetectorTest(
+ useDetector = BanUncheckedReflection(),
+ useIssues = listOf(BanUncheckedReflection.ISSUE),
+ stubs = arrayOf(Stubs.ChecksSdkIntAtLeast),
+ ) {
@Test
fun `Detection of unchecked reflection in real-world Java sources`() {
- val input = arrayOf(
- javaSample("androidx.sample.core.app.ActivityRecreator"),
- RestrictTo
- )
+ val input = arrayOf(javaSample("androidx.sample.core.app.ActivityRecreator"), RestrictTo)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/sample/core/app/ActivityRecreator.java:261: Error: Method.invoke requires both an upper and lower SDK bounds checks to be safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API. [BanUncheckedReflection]
performStopActivity3ParamsMethod.invoke(activityThread,
^
@@ -46,7 +45,8 @@
performStopActivity2ParamsMethod.invoke(activityThread,
^
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -54,40 +54,36 @@
@Test
fun `Detection of unchecked reflection in real-world Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.sample.core.app.ActivityRecreatorKt"),
- RestrictTo
- )
+ val input = arrayOf(ktSample("androidx.sample.core.app.ActivityRecreatorKt"), RestrictTo)
/* ktlint-disable max-line-length */
- val expected = """
-src/androidx/sample/core/app/ActivityRecreatorKt.kt:176: Error: Method.invoke requires both an upper and lower SDK bounds checks to be safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API. [BanUncheckedReflection]
+ val expected =
+ """
+src/androidx/sample/core/app/ActivityRecreatorKt.kt:172: Error: Method.invoke requires both an upper and lower SDK bounds checks to be safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API. [BanUncheckedReflection]
performStopActivity3ParamsMethod!!.invoke(
^
-src/androidx/sample/core/app/ActivityRecreatorKt.kt:181: Error: Method.invoke requires both an upper and lower SDK bounds checks to be safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API. [BanUncheckedReflection]
- performStopActivity2ParamsMethod!!.invoke(
- ^
+src/androidx/sample/core/app/ActivityRecreatorKt.kt:179: Error: Method.invoke requires both an upper and lower SDK bounds checks to be safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API. [BanUncheckedReflection]
+ performStopActivity2ParamsMethod!!.invoke(activityThread, token, false)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- lint()
- .files(*input)
- .run()
- .expect(expected)
+ lint().files(*input).run().expect(expected)
}
@Test
fun `Checked reflection in real-world Java sources`() {
- val input = arrayOf(
- javaSample("androidx.sample.core.app.ActivityRecreatorChecked"),
- RestrictTo
- )
+ val input =
+ arrayOf(javaSample("androidx.sample.core.app.ActivityRecreatorChecked"), RestrictTo)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
No warnings.
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -95,17 +91,17 @@
@Test
fun `Checked reflection in real-world Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.sample.core.app.ActivityRecreatorKtChecked"),
- RestrictTo
- )
+ val input =
+ arrayOf(ktSample("androidx.sample.core.app.ActivityRecreatorKtChecked"), RestrictTo)
check(*input).expectClean()
}
@Test
fun `Checked reflection using preceding if with return`() {
- val input = kotlin("""
+ val input =
+ kotlin(
+ """
package androidx.foo
import android.os.Build
@@ -119,14 +115,19 @@
)
method.invoke(null, true)
}
- """.trimIndent())
+ """
+ .trimIndent()
+ )
check(input).expectClean()
}
@Test
fun `Checked reflection using @DeprecatedSinceApi method`() {
- val input = arrayOf(kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package androidx.foo
import android.os.Build
@@ -140,16 +141,21 @@
)
method.invoke(null, true)
}
- """.trimIndent()),
- Stubs.DeprecatedSinceApi
- )
+ """
+ .trimIndent()
+ ),
+ Stubs.DeprecatedSinceApi
+ )
check(*input).expectClean()
}
@Test
fun `Checked reflection using @DeprecatedSinceApi class`() {
- val input = arrayOf(java("""
+ val input =
+ arrayOf(
+ java(
+ """
package androidx.foo;
import android.os.Build;
@@ -172,17 +178,21 @@
}
}
}
- """.trimIndent()),
- Stubs.DeprecatedSinceApi
- )
+ """
+ .trimIndent()
+ ),
+ Stubs.DeprecatedSinceApi
+ )
check(*input).expectClean()
}
@Test
fun `Checked reflection using Kotlin range check`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package androidx.foo
import android.os.Build
@@ -196,8 +206,10 @@
method.invoke(null, true)
}
}
- """.trimIndent())
- )
+ """
+ .trimIndent()
+ )
+ )
check(*input).expectClean()
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanVisibilityDocTagsTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanVisibilityDocTagsTest.kt
index c8c7827..080a19d 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanVisibilityDocTagsTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanVisibilityDocTagsTest.kt
@@ -21,17 +21,20 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanVisibilityDocTagsTest : AbstractLintDetectorTest(
- useDetector = BanVisibilityDocTags(),
- useIssues = listOf(
- BanVisibilityDocTags.HIDE_ISSUE,
- BanVisibilityDocTags.SUPPRESS_ISSUE,
- BanVisibilityDocTags.REMOVED_ISSUE,
- ),
-) {
+class BanVisibilityDocTagsTest :
+ AbstractLintDetectorTest(
+ useDetector = BanVisibilityDocTags(),
+ useIssues =
+ listOf(
+ BanVisibilityDocTags.HIDE_ISSUE,
+ BanVisibilityDocTags.SUPPRESS_ISSUE,
+ BanVisibilityDocTags.REMOVED_ISSUE,
+ ),
+ ) {
- private val fileWithHideInJavadoc = java(
- """
+ private val fileWithHideInJavadoc =
+ java(
+ """
/**
* @hide
*/
@@ -47,15 +50,17 @@
*/
public static void hide() {}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
@Test
fun `Detection of Hide tag in Javadoc`() {
val input = arrayOf(fileWithHideInJavadoc)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/HideClass.java:4: Error: @hide is not allowed in documentation [BanHideTag]
public class HideClass {
~~~~~~~~~
@@ -66,14 +71,16 @@
public static void hide() {}
~~~~
3 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
}
- private val fileWithSuppressInKdoc = kotlin(
- """
+ private val fileWithSuppressInKdoc =
+ kotlin(
+ """
/**
* @suppress
*/
@@ -88,15 +95,17 @@
*/
public val suppressedProperty = 1
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
@Test
fun `Detection of Suppress tag in Kdoc`() {
val input = arrayOf(fileWithSuppressInKdoc)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/SuppressClass.kt:4: Error: @suppress is not allowed in documentation [BanSuppressTag]
public class SuppressClass {
~~~~~~~~~~~~~
@@ -107,7 +116,8 @@
public val suppressedProperty = 1
~~~~~~~~~~~~~~~~~~
3 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -115,9 +125,10 @@
@Test
fun `Detection of removed tag`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
class Foo {
/**
* A previously useful function.
@@ -125,10 +136,11 @@
**/
fun foo() = Unit
}
- """.trimIndent()
- ),
- java(
"""
+ .trimIndent()
+ ),
+ java(
+ """
/**
* Bar class
* @removed don't use this
@@ -137,11 +149,13 @@
/** @removed */
public void bar() {}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
- val expected = """
+ val expected =
+ """
src/Bar.java:5: Error: @removed is not allowed in documentation [BanRemovedTag]
public class Bar {
~~~
@@ -152,7 +166,8 @@
fun foo() = Unit
~~~
3 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(*input).expect(expected)
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanVisibleForTestingParamsTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanVisibleForTestingParamsTest.kt
index a413a88..339dc19 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanVisibleForTestingParamsTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/BanVisibleForTestingParamsTest.kt
@@ -23,20 +23,23 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanVisibleForTestingParamsTest : AbstractLintDetectorTest(
- useDetector = BanVisibleForTestingParams(),
- useIssues = listOf(BanVisibleForTestingParams.ISSUE),
- stubs = arrayOf(Stubs.VisibleForTesting),
-) {
+class BanVisibleForTestingParamsTest :
+ AbstractLintDetectorTest(
+ useDetector = BanVisibleForTestingParams(),
+ useIssues = listOf(BanVisibleForTestingParams.ISSUE),
+ stubs = arrayOf(Stubs.VisibleForTesting),
+ ) {
@Test
fun `Detection of @VisibleForTesting usage in Java sources`() {
- val input = arrayOf(
- javaSample("androidx.VisibleForTestingUsageJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.VisibleForTestingUsageJava"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/VisibleForTestingUsageJava.java:23: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -50,9 +53,11 @@
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val fixDiffs = """
+ val fixDiffs =
+ """
Fix for src/androidx/VisibleForTestingUsageJava.java line 23: Remove non-default `otherwise` value:
@@ -23 +23
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -67,77 +72,81 @@
@@ -32 +32
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ @VisibleForTesting
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(fixDiffs)
+ check(*input).expect(expected).expectFixDiffs(fixDiffs)
}
+
@Test
fun `Detection of @VisibleForTesting usage in Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.VisibleForTestingUsageKotlin"),
- )
+ val input =
+ arrayOf(
+ ktSample("androidx.VisibleForTestingUsageKotlin"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/VisibleForTestingUsageKotlin.kt:26: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) fun testMethodPrivate() {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/VisibleForTestingUsageKotlin.kt:29: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
+src/androidx/VisibleForTestingUsageKotlin.kt:28: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
@VisibleForTesting(otherwise = VisibleForTesting.Companion.PRIVATE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/VisibleForTestingUsageKotlin.kt:32: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
- @VisibleForTesting(VisibleForTesting.PRIVATE)
+src/androidx/VisibleForTestingUsageKotlin.kt:31: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
+ @VisibleForTesting(VisibleForTesting.PRIVATE) fun testMethodValuePrivate() {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/VisibleForTestingUsageKotlin.kt:35: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
+src/androidx/VisibleForTestingUsageKotlin.kt:33: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/VisibleForTestingUsageKotlin.kt:38: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+src/androidx/VisibleForTestingUsageKotlin.kt:36: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) fun testMethodProtected() {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/VisibleForTestingUsageKotlin.kt:41: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+src/androidx/VisibleForTestingUsageKotlin.kt:38: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun testMethodPackageNone() {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/androidx/VisibleForTestingUsageKotlin.kt:47: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
- @get:VisibleForTesting(NONE)
+src/androidx/VisibleForTestingUsageKotlin.kt:42: Error: Found non-default otherwise value for @VisibleForTesting [UsesNonDefaultVisibleForTesting]
+ @get:VisibleForTesting(NONE) val testPropertyGet = "test"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val fixDiffs = """
+ val fixDiffs =
+ """
Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 26: Remove non-default `otherwise` value:
@@ -26 +26
-- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-+ @VisibleForTesting
-Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 29: Remove non-default `otherwise` value:
-@@ -29 +29
+- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) fun testMethodPrivate() {}
++ @VisibleForTesting fun testMethodPrivate() {}
+Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 28: Remove non-default `otherwise` value:
+@@ -28 +28
- @VisibleForTesting(otherwise = VisibleForTesting.Companion.PRIVATE)
+ @VisibleForTesting
-Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 32: Remove non-default `otherwise` value:
-@@ -32 +32
-- @VisibleForTesting(VisibleForTesting.PRIVATE)
-+ @VisibleForTesting
-Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 35: Remove @VisibleForTesting annotation:
-@@ -35 +35
+Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 31: Remove non-default `otherwise` value:
+@@ -31 +31
+- @VisibleForTesting(VisibleForTesting.PRIVATE) fun testMethodValuePrivate() {}
++ @VisibleForTesting fun testMethodValuePrivate() {}
+Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 33: Remove @VisibleForTesting annotation:
+@@ -33 +33
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 38: Remove @VisibleForTesting annotation:
+Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 36: Remove @VisibleForTesting annotation:
+@@ -36 +36
+- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) fun testMethodProtected() {}
++ fun testMethodProtected() {}
+Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 38: Remove non-default `otherwise` value:
@@ -38 +38
-- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
-Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 41: Remove non-default `otherwise` value:
-@@ -41 +41
-- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
-+ @VisibleForTesting
-Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 47: Remove non-default `otherwise` value:
-@@ -47 +47
-- @get:VisibleForTesting(NONE)
-+ @get:VisibleForTesting
- """.trimIndent()
+- @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun testMethodPackageNone() {}
++ @VisibleForTesting fun testMethodPackageNone() {}
+Fix for src/androidx/VisibleForTestingUsageKotlin.kt line 42: Remove non-default `otherwise` value:
+@@ -42 +42
+- @get:VisibleForTesting(NONE) val testPropertyGet = "test"
++ @get:VisibleForTesting val testPropertyGet = "test"
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(fixDiffs)
+ check(*input).expect(expected).expectFixDiffs(fixDiffs)
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/CameraXQuirksClassDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/CameraXQuirksClassDetectorTest.kt
index 77438db..87698c3 100644
--- a/lint-checks/src/test/java/androidx/build/lint/CameraXQuirksClassDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/CameraXQuirksClassDetectorTest.kt
@@ -24,19 +24,19 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class CameraXQuirksClassDetectorTest : AbstractLintDetectorTest(
- useDetector = CameraXQuirksClassDetector(),
- useIssues = listOf(CameraXQuirksClassDetector.ISSUE)
-) {
+class CameraXQuirksClassDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = CameraXQuirksClassDetector(),
+ useIssues = listOf(CameraXQuirksClassDetector.ISSUE)
+ ) {
@Test
fun `Detection of CameraX Quirks in Java`() {
- val input = arrayOf(
- javaSample("androidx.CameraXMissingQuirkSummaryJava")
- )
+ val input = arrayOf(javaSample("androidx.CameraXMissingQuirkSummaryJava"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/CameraXMissingQuirkSummaryJava.java:22: Error: CameraX quirks should include this template in the javadoc:
* <p>QuirkSummary
@@ -47,16 +47,15 @@
public class CameraXMissingQuirkSummaryJava implements Quirk {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.allowDuplicates()
.skipTestModes(PARTIAL) // b/324629808
- .run().expect(expected)
+ .run()
+ .expect(expected)
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
index 781c1fd..32b9a74 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ClassVerificationFailureDetectorTest.kt
@@ -26,25 +26,25 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class ClassVerificationFailureDetectorTest : AbstractLintDetectorTest(
- useDetector = ClassVerificationFailureDetector(),
- useIssues = listOf(ClassVerificationFailureDetector.METHOD_CALL_ISSUE),
- stubs = arrayOf(
- // AndroidManifest with minSdkVersion=14
- manifest().minSdk(14),
- ),
-) {
+class ClassVerificationFailureDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = ClassVerificationFailureDetector(),
+ useIssues = listOf(ClassVerificationFailureDetector.METHOD_CALL_ISSUE),
+ stubs =
+ arrayOf(
+ // AndroidManifest with minSdkVersion=14
+ manifest().minSdk(14),
+ ),
+ ) {
@Test
fun `Detection of unsafe references in Java sources`() {
- val input = arrayOf(
- javaSample("androidx.ClassVerificationFailureFromJava"),
- RequiresApi,
- IntRange
- )
+ val input =
+ arrayOf(javaSample("androidx.ClassVerificationFailureFromJava"), RequiresApi, IntRange)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ClassVerificationFailureFromJava.java:37: Error: This call references a method added in API level 21; however, the containing class androidx.ClassVerificationFailureFromJava is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
view.setBackgroundTintList(tint);
~~~~~~~~~~~~~~~~~~~~~
@@ -55,7 +55,8 @@
return view.getAccessibilityClassName();
~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -63,12 +64,14 @@
@Test
fun `Detection and auto-fix of unsafe references in real-world Java sources`() {
- val input = arrayOf(
- javaSample("androidx.sample.core.widget.ListViewCompat"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.sample.core.widget.ListViewCompat"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/sample/core/widget/ListViewCompat.java:39: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
listView.scrollListBy(y);
~~~~~~~~~~~~
@@ -76,9 +79,11 @@
return listView.canScrollList(direction);
~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/sample/core/widget/ListViewCompat.java line 39: Extract to static inner class:
@@ -39 +39
- listView.scrollListBy(y);
@@ -115,7 +120,8 @@
+
@@ -92 +103
+ }
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFix)
@@ -123,20 +129,23 @@
@Test
fun `Detection and auto-fix of unsafe references in real-world Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.sample.core.widget.ListViewCompatKotlin"),
- )
+ val input =
+ arrayOf(
+ ktSample("androidx.sample.core.widget.ListViewCompatKotlin"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/sample/core/widget/ListViewCompatKotlin.kt:33: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompatKotlin is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
listView.scrollListBy(y)
~~~~~~~~~~~~
-src/androidx/sample/core/widget/ListViewCompatKotlin.kt:58: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompatKotlin is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
+src/androidx/sample/core/widget/ListViewCompatKotlin.kt:56: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompatKotlin is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
listView.canScrollList(direction)
~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -144,15 +153,14 @@
@Test
fun `Detection of RequiresApi annotation in outer class in Java source`() {
- val input = arrayOf(
- javaSample("androidx.RequiresApiJava"),
- RequiresApi
- )
+ val input = arrayOf(javaSample("androidx.RequiresApiJava"), RequiresApi)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
No warnings.
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -160,13 +168,11 @@
@Test
fun `Detection of RequiresApi annotation in outer class in Kotlin source`() {
- val input = arrayOf(
- ktSample("androidx.RequiresApiKotlin"),
- RequiresApi
- )
+ val input = arrayOf(ktSample("androidx.RequiresApiKotlin"), RequiresApi)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/RequiresApiKotlinOuter19Passes.kt:67: Error: This call references a method added in API level 19; however, the containing class androidx.RequiresApiKotlinNoAnnotationFails.MyStaticClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
Character.isSurrogate(c)
~~~~~~~~~~~
@@ -180,7 +186,8 @@
Character.isSurrogate(c)
~~~~~~~~~~~
4 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -188,12 +195,14 @@
@Test
fun `Auto-fix unsafe void-type method reference in Java source`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeVoidMethodReferenceJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.AutofixUnsafeVoidMethodReferenceJava"),
+ )
/* ktlint-disable max-line-length */
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeVoidMethodReferenceJava.java line 34: Extract to static inner class:
@@ -34 +34
- view.setBackgroundTintList(new ColorStateList(null, null));
@@ -212,7 +221,8 @@
+
@@ -38 +49
+ }
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expectFixDiffs(expectedFix)
@@ -220,12 +230,14 @@
@Test
fun `Auto-fix unsafe constructor reference in Java source`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeConstructorReferenceJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.AutofixUnsafeConstructorReferenceJava"),
+ )
/* ktlint-disable max-line-length */
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeConstructorReferenceJava.java line 35: Extract to static inner class:
@@ -35 +35
- AccessibilityNodeInfo node = new AccessibilityNodeInfo(new View(context), 1);
@@ -244,7 +256,8 @@
+
@@ -39 +50
+ }
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expectFixDiffs(expectedFix)
@@ -252,12 +265,14 @@
@Test
fun `Auto-fix unsafe static method reference in Java source`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeStaticMethodReferenceJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.AutofixUnsafeStaticMethodReferenceJava"),
+ )
/* ktlint-disable max-line-length */
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeStaticMethodReferenceJava.java line 33: Extract to static inner class:
@@ -33 +33
- return View.generateViewId();
@@ -276,7 +291,8 @@
+
@@ -38 +49
+ }
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expectFixDiffs(expectedFix)
@@ -284,12 +300,14 @@
@Test
fun `Auto-fix unsafe generic-type method reference in Java source`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeGenericMethodReferenceJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.AutofixUnsafeGenericMethodReferenceJava"),
+ )
/* ktlint-disable max-line-length */
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeGenericMethodReferenceJava.java line 34: Extract to static inner class:
@@ -34 +34
- return context.getSystemService(serviceClass);
@@ -308,7 +326,8 @@
+
@@ -39 +50
+ }
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expectFixDiffs(expectedFix)
@@ -316,13 +335,12 @@
@Test
fun `Auto-fix unsafe reference in Java source with existing inner class`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeReferenceWithExistingClassJava"),
- RequiresApi
- )
+ val input =
+ arrayOf(javaSample("androidx.AutofixUnsafeReferenceWithExistingClassJava"), RequiresApi)
/* ktlint-disable max-line-length */
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeReferenceWithExistingClassJava.java line 36: Extract to static inner class:
@@ -36 +36
- view.setBackgroundTintList(new ColorStateList(null, null));
@@ -341,7 +359,8 @@
+
@@ -47 +58
+ }
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expectFixDiffs(expectedFix)
@@ -349,14 +368,16 @@
@Test
fun `Auto-fix unsafe reference in Java source when the fix code already exists`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeReferenceWithExistingFix"),
- RequiresApi,
- DoNotInline
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.AutofixUnsafeReferenceWithExistingFix"),
+ RequiresApi,
+ DoNotInline
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixUnsafeReferenceWithExistingFix.java:37: Error: This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeReferenceWithExistingFix is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
view.setBackgroundTintList(new ColorStateList(null, null));
~~~~~~~~~~~~~~~~~~~~~
@@ -366,7 +387,8 @@
2 errors, 0 warnings
"""
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeReferenceWithExistingFix.java line 37: Extract to static inner class:
@@ -37 +37
- view.setBackgroundTintList(new ColorStateList(null, null));
@@ -391,13 +413,15 @@
@Test
fun `Detection and auto-fix for qualified expressions (issue 205026874)`() {
- val input = arrayOf(
- javaSample("androidx.sample.appcompat.widget.ActionBarBackgroundDrawable"),
- RequiresApi
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.sample.appcompat.widget.ActionBarBackgroundDrawable"),
+ RequiresApi
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java:71: Error: This call references a method added in API level 21; however, the containing class androidx.sample.appcompat.widget.ActionBarBackgroundDrawable is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
mContainer.mSplitBackground.getOutline(outline);
~~~~~~~~~~
@@ -405,9 +429,11 @@
mContainer.mBackground.getOutline(outline);
~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java line 71: Extract to static inner class:
@@ -71 +71
- mContainer.mSplitBackground.getOutline(outline);
@@ -444,7 +470,8 @@
+
@@ -91 +102
+ }
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFix)
@@ -452,20 +479,20 @@
@Test
fun `Auto-fix includes fully qualified class name (issue 205035683, 236721202)`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeMethodWithQualifiedClass"),
- RequiresApi
- )
+ val input =
+ arrayOf(javaSample("androidx.AutofixUnsafeMethodWithQualifiedClass"), RequiresApi)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixUnsafeMethodWithQualifiedClass.java:40: Error: This call references a method added in API level 19; however, the containing class androidx.AutofixUnsafeMethodWithQualifiedClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return builder.setMediaSize(mediaSize);
~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/androidx/AutofixUnsafeMethodWithQualifiedClass.java line 40: Extract to static inner class:
@@ -40 +40
+ return Api19Impl.setMediaSize(builder, mediaSize);
@@ -490,12 +517,11 @@
@Test
fun `Auto-fix for unsafe method call on this`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeCallToThis")
- )
+ val input = arrayOf(javaSample("androidx.AutofixUnsafeCallToThis"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixUnsafeCallToThis.java:39: Error: This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeCallToThis is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
getClipToPadding();
~~~~~~~~~~~~~~~~
@@ -508,7 +534,8 @@
3 errors, 0 warnings
"""
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeCallToThis.java line 39: Extract to static inner class:
@@ -39 +39
- getClipToPadding();
@@ -571,19 +598,19 @@
@Test
fun `Auto-fix for unsafe method call on cast object (issue 206111383)`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeCallOnCast")
- )
+ val input = arrayOf(javaSample("androidx.AutofixUnsafeCallOnCast"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixUnsafeCallOnCast.java:32: Error: This call references a method added in API level 28; however, the containing class androidx.AutofixUnsafeCallOnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
((DisplayCutout) secretDisplayCutout).getSafeInsetTop();
~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeCallOnCast.java line 32: Extract to static inner class:
@@ -32 +32
- ((DisplayCutout) secretDisplayCutout).getSafeInsetTop();
@@ -610,13 +637,12 @@
@Test
fun `Auto-fix with implicit class cast from new return type (issue 214389795)`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeCallWithImplicitReturnCast"),
- RequiresApi
- )
+ val input =
+ arrayOf(javaSample("androidx.AutofixUnsafeCallWithImplicitReturnCast"), RequiresApi)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java:36: Error: This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return new AdaptiveIconDrawable(null, null);
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -635,7 +661,8 @@
5 errors, 0 warnings
"""
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java line 36: Extract to static inner class:
@@ -36 +36
- return new AdaptiveIconDrawable(null, null);
@@ -734,20 +761,20 @@
@Test
fun `Auto-fix for constructor needs qualified class name (issue 244714253)`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeConstructorQualifiedClass"),
- RequiresApi
- )
+ val input =
+ arrayOf(javaSample("androidx.AutofixUnsafeConstructorQualifiedClass"), RequiresApi)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixUnsafeConstructorQualifiedClass.java:32: Error: This call references a method added in API level 24; however, the containing class androidx.AutofixUnsafeConstructorQualifiedClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return new Notification.DecoratedCustomViewStyle();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeConstructorQualifiedClass.java line 32: Extract to static inner class:
@@ -32 +32
+ return Api24Impl.createDecoratedCustomViewStyle();
@@ -772,13 +799,12 @@
@Test
fun `Auto-fix with implicit class cast from new parameter type (issue 266845827)`() {
- val input = arrayOf(
- javaSample("androidx.AutofixUnsafeCallWithImplicitParamCast"),
- RequiresApi
- )
+ val input =
+ arrayOf(javaSample("androidx.AutofixUnsafeCallWithImplicitParamCast"), RequiresApi)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixUnsafeCallWithImplicitParamCast.java:34: Error: This call references a method added in API level 16; however, the containing class androidx.AutofixUnsafeCallWithImplicitParamCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
style.setBuilder(builder);
~~~~~~~~~~
@@ -788,7 +814,8 @@
2 errors, 0 warnings
"""
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixUnsafeCallWithImplicitParamCast.java line 34: Extract to static inner class:
@@ -34 +34
- style.setBuilder(builder);
@@ -833,13 +860,12 @@
@Test
fun `Auto-fix for method with varargs that are implicitly cast (issue 266845827)`() {
- val input = arrayOf(
- javaSample("androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast"),
- RequiresApi
- )
+ val input =
+ arrayOf(javaSample("androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast"), RequiresApi)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java:35: Error: This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
adapter.setAutofillOptions();
~~~~~~~~~~~~~~~~~~
@@ -852,7 +878,8 @@
3 errors, 0 warnings
"""
- val expectedFix = """
+ val expectedFix =
+ """
Fix for src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java line 35: Extract to static inner class:
@@ -35 +35
- adapter.setAutofillOptions();
diff --git a/lint-checks/src/test/java/androidx/build/lint/DeprecationMismatchDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/DeprecationMismatchDetectorTest.kt
index 78c96a9..bc97ee9 100644
--- a/lint-checks/src/test/java/androidx/build/lint/DeprecationMismatchDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/DeprecationMismatchDetectorTest.kt
@@ -21,15 +21,17 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class DeprecationMismatchDetectorTest : AbstractLintDetectorTest(
- useDetector = DeprecationMismatchDetector(),
- useIssues = listOf(DeprecationMismatchDetector.ISSUE),
-) {
+class DeprecationMismatchDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = DeprecationMismatchDetector(),
+ useIssues = listOf(DeprecationMismatchDetector.ISSUE),
+ ) {
@Test
fun `Test correctly matched @deprecated and @Deprecated`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
/**
@@ -55,18 +57,20 @@
@Deprecated
public interface InnerFoo {}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test @deprecated missing @Deprecated`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
/**
@@ -88,12 +92,14 @@
*/
public interface InnerFoo {}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/Foo.java:6: Error: Items with a @deprecated doc tag must be annotated with @Deprecated [DeprecationMismatch]
public class Foo {
~~~
@@ -107,9 +113,11 @@
public interface InnerFoo {}
~~~~~~~~
4 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Autofix for src/java/androidx/Foo.java line 6: Annotate with @Deprecated:
@@ -6 +6
+ @Deprecated
@@ -122,17 +130,19 @@
Autofix for src/java/androidx/Foo.java line 20: Annotate with @Deprecated:
@@ -20 +20
+ @Deprecated
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test @Deprecated missing @deprecated`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
@Deprecated
@@ -146,12 +156,14 @@
@Deprecated
public interface InnerFoo {}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/Foo.java:4: Error: Items annotated with @Deprecated must have a @deprecated doc tag [DeprecationMismatch]
public class Foo {
~~~
@@ -165,7 +177,8 @@
public interface InnerFoo {}
~~~~~~~~
4 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -173,9 +186,10 @@
@Test
fun `Test @deprecated not required for private APIs`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
@Deprecated
@@ -189,17 +203,19 @@
@Deprecated
private interface InnerFoo {}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test @deprecated not required for proto-generated APIs`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
// Generated by the protocol buffer compiler. DO NOT EDIT!
package java.androidx.proto;
@@ -214,17 +230,19 @@
@Deprecated
public interface InnerFoo {}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test anonymous classes don't need @deprecated`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
/**
@@ -238,10 +256,11 @@
@Deprecated
public void foo();
}
- """.trimIndent()
- ),
- java(
"""
+ .trimIndent()
+ ),
+ java(
+ """
package java.androidx;
public class Bar {
@@ -252,18 +271,20 @@
}.foo();
}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test @RestrictTo APIs don't need @deprecated`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import androidx.annotation.RestrictTo;
@@ -280,18 +301,20 @@
@Deprecated
private interface InnerFoo {}
}
- """.trimIndent()
- ),
- Stubs.RestrictTo
- )
+ """
+ .trimIndent()
+ ),
+ Stubs.RestrictTo
+ )
check(*input).expectClean()
}
@Test
fun `Test overriding methods don't need @deprecated`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidX;
public interface MyInterface {
@@ -308,9 +331,9 @@
void inheritedWithInheritDoc();
}
""",
- ),
- java(
- """
+ ),
+ java(
+ """
package test.pkg;
public class MyClass implements MyInterface {
@@ -329,8 +352,8 @@
public void inheritedWithInheritDoc() {}
}
"""
+ )
)
- )
check(*input).expectClean()
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ExperimentalPropertyAnnotationDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ExperimentalPropertyAnnotationDetectorTest.kt
index 498bd10..eca2896 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ExperimentalPropertyAnnotationDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ExperimentalPropertyAnnotationDetectorTest.kt
@@ -22,22 +22,29 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class ExperimentalPropertyAnnotationDetectorTest : AbstractLintDetectorTest(
- useDetector = ExperimentalPropertyAnnotationDetector(),
- useIssues = listOf(ExperimentalPropertyAnnotationDetector.ISSUE),
- stubs = arrayOf(kotlin("""
+class ExperimentalPropertyAnnotationDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = ExperimentalPropertyAnnotationDetector(),
+ useIssues = listOf(ExperimentalPropertyAnnotationDetector.ISSUE),
+ stubs =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
@Retention(AnnotationRetention.BINARY)
annotation class ExperimentalKotlinAnnotation
- """))
-) {
+ """
+ )
+ )
+ ) {
@Test
fun `Test correctly annotated var properties`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -52,17 +59,18 @@
var correctlyAnnotatedWithDefault: Int = 3
}
"""
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test var properties annotated with one target`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -79,11 +87,12 @@
var annotatedWithDefault = 3
}
"""
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/AnnotatedProperty.kt:5: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@get:ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -99,7 +108,8 @@
4 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/AnnotatedProperty.kt line 5: Add missing annotations:
@@ -5 +5
+ @set:ExperimentalKotlinAnnotation
@@ -123,10 +133,7 @@
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.skipTestModes(TestMode.SUPPRESSIBLE) // b/257294309
.run()
.expect(expected)
@@ -135,9 +142,10 @@
@Test
fun `Test var property annotated with two targets`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -162,11 +170,12 @@
var annotatedWithSetAndProperty = 3
}
"""
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/AnnotatedProperty.kt:5: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@get:ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -185,7 +194,8 @@
5 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/AnnotatedProperty.kt line 5: Add missing annotations:
@@ -5 +5
+ @set:ExperimentalKotlinAnnotation
@@ -207,10 +217,7 @@
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.skipTestModes(TestMode.SUPPRESSIBLE) // b/257294309
.run()
.expect(expected)
@@ -219,9 +226,10 @@
@Test
fun `Test correctly annotated val property`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -234,18 +242,18 @@
val correctlyAnnotatedWithProperty: Int = 3
}
"""
+ )
)
- )
- check(*input)
- .expectClean()
+ check(*input).expectClean()
}
@Test
fun `Test val properties annotated with one target`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -259,11 +267,12 @@
val annotatedWithDefault = 3
}
"""
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/AnnotatedProperty.kt:5: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@get:ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -276,7 +285,8 @@
3 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/AnnotatedProperty.kt line 5: Add missing annotations:
@@ -5 +5
+ @property:ExperimentalKotlinAnnotation
@@ -292,10 +302,7 @@
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.skipTestModes(TestMode.SUPPRESSIBLE) // b/257294309
.run()
.expect(expected)
@@ -304,9 +311,10 @@
@Test
fun `Test property annotated with non-experimental annotation`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -314,25 +322,25 @@
var correctlyAnnotated: Int = 3
}
"""
- ),
- kotlin(
- """
+ ),
+ kotlin(
+ """
package java.androidx
annotation class NonExperimentalAnnotation
"""
+ )
)
- )
- check(*input)
- .expectClean()
+ check(*input).expectClean()
}
@Test
fun `Test property using Java defined annotation`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
import java.androidx.ExperimentalJavaAnnotation
@@ -342,8 +350,9 @@
var annotatedWithGet = 3
}
"""
- ),
- java("""
+ ),
+ java(
+ """
package java.androidx;
import static androidx.annotation.RequiresOptIn.Level.ERROR;
@@ -352,9 +361,11 @@
@RequiresOptIn(level = ERROR)
public @interface ExperimentalJavaAnnotation {}
- """.trimIndent()),
- kotlin(
- """
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package androidx.annotation
import kotlin.annotation.Retention
@@ -370,19 +381,22 @@
ERROR
}
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/AnnotatedProperty.kt:7: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@get:ExperimentalJavaAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/AnnotatedProperty.kt line 7: Add missing annotations:
@@ -7 +7
+ @set:ExperimentalJavaAnnotation
@@ -391,10 +405,7 @@
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.skipTestModes(TestMode.SUPPRESSIBLE) // b/257294309
.run()
.expect(expected)
@@ -403,26 +414,29 @@
@Test
fun `Test property defined at top-level`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
@get:ExperimentalKotlinAnnotation
var annotatedWithGet = 3
"""
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/test.kt:4: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@get:ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/test.kt line 4: Add missing annotations:
@@ -4 +4
+ @set:ExperimentalKotlinAnnotation
@@ -431,10 +445,7 @@
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.skipTestModes(TestMode.SUPPRESSIBLE) // b/257294309
.run()
.expect(expected)
@@ -443,9 +454,10 @@
@Test
fun `Test property defined in companion object`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -455,18 +467,20 @@
}
}
"""
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/AnnotatedProperty.kt:6: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@get:ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/AnnotatedProperty.kt line 6: Add missing annotations:
@@ -6 +6
+ @set:ExperimentalKotlinAnnotation
@@ -475,10 +489,7 @@
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.skipTestModes(TestMode.SUPPRESSIBLE) // b/257294309
.run()
.expect(expected)
@@ -487,9 +498,10 @@
@Test
fun `Test property defined in interface`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
interface AnnotatedProperty {
@@ -497,18 +509,20 @@
val annotatedWithGet: Int
}
"""
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/AnnotatedProperty.kt:5: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@get:ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/AnnotatedProperty.kt line 5: Add missing annotations:
@@ -5 +5
+ @property:ExperimentalKotlinAnnotation
@@ -516,10 +530,7 @@
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- *input
- )
+ .files(*stubs, *input)
.skipTestModes(TestMode.SUPPRESSIBLE) // b/257294309
.run()
.expect(expected)
@@ -528,9 +539,10 @@
@Test
fun `Test experimental annotations on non-properties don't trigger lint`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
@file:ExperimentalKotlinAnnotation
@@ -541,17 +553,18 @@
fun experimentalFunction() {}
}
"""
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test property annotated with JvmField doesn't trigger lint`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedWithJvmField {
@@ -560,67 +573,72 @@
var experimentalProperty = 3
}
"""
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test const property doesn't trigger lint`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
@ExperimentalKotlinAnnotation
const val EXPERIMENTAL_CONST = 3
"""
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test property with delegate doesn't trigger lint`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
@ExperimentalKotlinAnnotation
var experimentalProperty by mutableStateOf(0L)
"""
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test property within function doesn't trigger lint`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
fun functionWithProperty() {
@ExperimentalKotlinAnnotation
val experimentalProperty = 3
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test private property doesn't trigger lint but other non-public properties do`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
class AnnotatedProperty {
@@ -633,12 +651,14 @@
@ExperimentalKotlinAnnotation
internal var internalProperty = 3
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/AnnotatedProperty.kt:7: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -648,7 +668,8 @@
2 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/AnnotatedProperty.kt line 7: Add missing annotations:
@@ -7 +7
+ @set:ExperimentalKotlinAnnotation
@@ -662,52 +683,56 @@
"""
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(expectedFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test property in private class doesn't trigger lint`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
private class AnnotatedProperty {
@ExperimentalKotlinAnnotation
var experimentalProperty = 3
}
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
check(*input).expectClean()
}
@Test
fun `Test property with private setter only has get annotation added`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
@ExperimentalKotlinAnnotation
var experimentalProperty = 3
private set
- """.trimIndent()
+ """
+ .trimIndent()
+ )
)
- )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/test.kt:3: Error: This property does not have all required annotations to correctly mark it as experimental. [ExperimentalPropertyAnnotation]
@ExperimentalKotlinAnnotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/test.kt line 3: Add missing annotations:
@@ -3 +3
+ @get:ExperimentalKotlinAnnotation
@@ -715,8 +740,6 @@
"""
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(expectedFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
index 1a2f2c0..2949d44 100644
--- a/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/IdeaSuppressionDetectorTest.kt
@@ -24,16 +24,18 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class IdeaSuppressionDetectorTest : AbstractLintDetectorTest(
- useDetector = IdeaSuppressionDetector(),
- useIssues = listOf(IdeaSuppressionDetector.ISSUE),
-) {
+class IdeaSuppressionDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = IdeaSuppressionDetector(),
+ useIssues = listOf(IdeaSuppressionDetector.ISSUE),
+ ) {
@Test
fun `Detection of IDEA-specific suppression in Java sources`() {
- val input = java(
- "src/androidx/IdeaSuppressionJava.java",
- """
+ val input =
+ java(
+ "src/androidx/IdeaSuppressionJava.java",
+ """
public class IdeaSuppressionJava {
// Call to a deprecated method with an inline suppression.
@@ -49,23 +51,23 @@
public void notDeprecatedMethod() {}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/IdeaSuppressionJava.java:5: Error: Uses IntelliJ-specific suppression, should use @SuppressWarnings("deprecation") [IdeaSuppression]
//noinspection deprecation
~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- input
- )
+ .files(*stubs, input)
.allowDuplicates()
.skipTestModes(TestMode.SUPPRESSIBLE)
.run()
@@ -74,9 +76,10 @@
@Test
fun `Detection of IDEA-specific suppression in Kotlin sources`() {
- val input = kotlin(
- "src/androidx/IdeaSuppressionKotlin.kt",
- """
+ val input =
+ kotlin(
+ "src/androidx/IdeaSuppressionKotlin.kt",
+ """
class IdeaSuppressionKotlin {
// Call to a deprecated method with an inline suppression.
@@ -92,22 +95,22 @@
fun notDeprecatedMethod() {}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/IdeaSuppressionKotlin.kt:5: Error: Uses IntelliJ-specific suppression, should use @SuppressWarnings("deprecation") [IdeaSuppression]
//noinspection deprecation
~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
lint()
- .files(
- *stubs,
- input
- )
+ .files(*stubs, input)
.allowDuplicates()
.skipTestModes(TestMode.SUPPRESSIBLE)
.run()
diff --git a/lint-checks/src/test/java/androidx/build/lint/IgnoreClassLevelDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/IgnoreClassLevelDetectorTest.kt
index 55abe22..3e9e805 100644
--- a/lint-checks/src/test/java/androidx/build/lint/IgnoreClassLevelDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/IgnoreClassLevelDetectorTest.kt
@@ -21,14 +21,17 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class IgnoreClassLevelDetectorTest : AbstractLintDetectorTest(
- useDetector = IgnoreClassLevelDetector(),
- useIssues = listOf(IgnoreClassLevelDetector.ISSUE),
+class IgnoreClassLevelDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = IgnoreClassLevelDetector(),
+ useIssues = listOf(IgnoreClassLevelDetector.ISSUE),
) {
@Test
fun `Detection of class level ignore in Kotlin sources`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
import org.junit.Ignore
@@ -43,13 +46,15 @@
@Ignore
fun twoTest() {}
}
- """),
- Stubs.IgnoreAnnotation,
- Stubs.TestAnnotation
- )
+ """
+ ),
+ Stubs.IgnoreAnnotation,
+ Stubs.TestAnnotation
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/TestClass.kt:7: Error: @Ignore should not be used at the class level. Move the annotation to each test individually. [IgnoreClassLevelDetector]
@Ignore("Class")
~~~~~~~~~~~~~~~~
@@ -62,9 +67,10 @@
@Test
fun `Detection of class level ignore in Java sources`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import org.junit.Ignore;
@@ -80,13 +86,14 @@
public void twoTest() {}
}
"""
- ),
- Stubs.IgnoreAnnotation,
- Stubs.TestAnnotation
- )
+ ),
+ Stubs.IgnoreAnnotation,
+ Stubs.TestAnnotation
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/TestClass.java:7: Error: @Ignore should not be used at the class level. Move the annotation to each test individually. [IgnoreClassLevelDetector]
@Ignore
~~~~~~~
@@ -99,9 +106,10 @@
@Test
fun `Test level ignore allowed in Kotlin sources`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
import org.junit.Ignore
@@ -116,19 +124,20 @@
fun twoTest() {}
}
"""
- ),
- Stubs.IgnoreAnnotation,
- Stubs.TestAnnotation
- )
+ ),
+ Stubs.IgnoreAnnotation,
+ Stubs.TestAnnotation
+ )
check(*input).expectClean()
}
@Test
fun `Test level ignore allowed in Java sources`() {
- val input = arrayOf(
- java(
- """
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import org.junit.Ignore;
@@ -142,10 +151,11 @@
@Test
public void twoTest() {}
}
- """),
- Stubs.IgnoreAnnotation,
- Stubs.TestAnnotation
- )
+ """
+ ),
+ Stubs.IgnoreAnnotation,
+ Stubs.TestAnnotation
+ )
check(*input).expectClean()
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ImplicitCastVerificationFailureDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ImplicitCastVerificationFailureDetectorTest.kt
index 1b0b889..379e981 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ImplicitCastVerificationFailureDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ImplicitCastVerificationFailureDetectorTest.kt
@@ -23,20 +23,24 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class ImplicitCastVerificationFailureDetectorTest : AbstractLintDetectorTest(
- useDetector = ClassVerificationFailureDetector(),
- useIssues = listOf(ClassVerificationFailureDetector.IMPLICIT_CAST_ISSUE),
- stubs = arrayOf(
- // AndroidManifest with minSdkVersion=14
- manifest().minSdk(14),
- RequiresApi,
- DoNotInline,
- ),
-) {
+class ImplicitCastVerificationFailureDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = ClassVerificationFailureDetector(),
+ useIssues = listOf(ClassVerificationFailureDetector.IMPLICIT_CAST_ISSUE),
+ stubs =
+ arrayOf(
+ // AndroidManifest with minSdkVersion=14
+ manifest().minSdk(14),
+ RequiresApi,
+ DoNotInline,
+ ),
+ ) {
@Test
fun `Unsafe implicit cast for method argument`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Notification;
@@ -60,8 +64,11 @@
}
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Notification
@@ -84,11 +91,14 @@
}
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/UnsafeImplicitCastAsMethodArgumentJava.java:11: Error: This expression has type android.app.Notification.MessagingStyle (introduced in API level 24) but it used as type android.app.Notification.Style (introduced in API level 16). Run-time class verification will not be able to validate this implicit cast on devices between these API levels. [ImplicitCastClassVerificationFailure]
Api16Impl.setBuilder(style, builder);
~~~~~
@@ -98,7 +108,8 @@
2 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/UnsafeImplicitCastAsMethodArgumentJava.java line 11: Extract to static inner class:
@@ -11 +11
- Api16Impl.setBuilder(style, builder);
@@ -125,8 +136,10 @@
@Test
fun `Unsafe implicit cast within catch block`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Presentation;
@@ -154,8 +167,11 @@
}
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Presentation
@@ -183,11 +199,14 @@
}
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/UnsafeImplicitCastInCatchBlockJava.java:15: Error: This expression has type android.view.WindowManager.InvalidDisplayException (introduced in API level 17) but it used as type java.lang.Throwable (introduced in API level 1). Run-time class verification will not be able to validate this implicit cast on devices between these API levels. [ImplicitCastClassVerificationFailure]
Log.w("Error", "Couldn't show presentation!", e);
~
@@ -197,7 +216,8 @@
2 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/UnsafeImplicitCastInCatchBlockJava.java line 15: Extract to static inner class:
@@ -15 +15
- Log.w("Error", "Couldn't show presentation!", e);
@@ -218,8 +238,10 @@
@Test
fun `Unsafe implicit cast in assignment statement`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Notification;
@@ -233,8 +255,11 @@
style = messagingStyle;
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Notification
@@ -248,11 +273,14 @@
style = messagingStyle
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/UnsafeImplicitCastInAssignmentJava.java:11: Error: This expression has type android.app.Notification.MessagingStyle (introduced in API level 24) but it used as type android.app.Notification.Style (introduced in API level 16). Run-time class verification will not be able to validate this implicit cast on devices between these API levels. [ImplicitCastClassVerificationFailure]
style = messagingStyle;
~~~~~~~~~~~~~~
@@ -262,7 +290,8 @@
2 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/UnsafeImplicitCastInAssignmentJava.java line 11: Extract to static inner class:
@@ -11 +11
- style = messagingStyle;
@@ -289,8 +318,10 @@
@Test
fun `Unsafe implicit cast on return`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Notification;
@@ -302,8 +333,11 @@
return style;
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Notification
@@ -315,11 +349,14 @@
return style
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/ImplicitCastOnReturnJava.java:9: Error: This expression has type android.app.Notification.MessagingStyle (introduced in API level 24) but it used as type android.app.Notification.Style (introduced in API level 16). Run-time class verification will not be able to validate this implicit cast on devices between these API levels. [ImplicitCastClassVerificationFailure]
return style;
~~~~~
@@ -329,7 +366,8 @@
2 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/ImplicitCastOnReturnJava.java line 9: Extract to static inner class:
@@ -9 +9
- return style;
@@ -356,8 +394,10 @@
@Test
fun `Unsafe implicit cast of method call result`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.graphics.drawable.AdaptiveIconDrawable;
@@ -381,8 +421,11 @@
}
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.graphics.drawable.AdaptiveIconDrawable
@@ -405,11 +448,14 @@
}
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/ImplicitCastOfMethodCallResultJava.java:11: Error: This expression has type android.graphics.drawable.AdaptiveIconDrawable (introduced in API level 26) but it used as type android.graphics.drawable.Drawable (introduced in API level 1). Run-time class verification will not be able to validate this implicit cast on devices between these API levels. [ImplicitCastClassVerificationFailure]
return Api26Impl.createAdaptiveIconDrawable(null, null);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -419,7 +465,8 @@
2 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/ImplicitCastOfMethodCallResultJava.java line 11: Extract to static inner class:
@@ -11 +11
- return Api26Impl.createAdaptiveIconDrawable(null, null);
@@ -440,8 +487,10 @@
@Test
fun `Safe implicit cast to object`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Notification;
@@ -455,8 +504,11 @@
style = messagingStyle;
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Notification
@@ -470,16 +522,20 @@
style = messagingStyle
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
@Test
fun `Safe explicit cast`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Notification;
@@ -493,8 +549,11 @@
style = (Notification.Style) messagingStyle;
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Notification
@@ -508,16 +567,20 @@
style = messagingStyle as Notification.Style
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
@Test
fun `Safe implicit cast between classes from the same API level`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.media.tv.BroadcastInfoResponse;
@@ -532,8 +595,11 @@
response = pesResponse;
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.media.tv.BroadcastInfoResponse
@@ -548,16 +614,20 @@
response = pesResponse
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
@Test
fun `Safe implicit cast within @RequiresApi class`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Notification;
@@ -574,8 +644,11 @@
builder.extend(extender);
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Notification
@@ -590,16 +663,20 @@
builder.extend(extender)
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
@Test
fun `Safe implicit cast from null value`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.Notification;
@@ -611,8 +688,11 @@
return null;
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.Notification
@@ -624,16 +704,20 @@
return null
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
@Test
fun `Safe implicit cast from type introduced earlier than the minSdk`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.app.FragmentBreadCrumbs;
@@ -647,8 +731,11 @@
viewGroup = breadCrumbs;
}
}
- """.trimIndent()),
- kotlin("""
+ """
+ .trimIndent()
+ ),
+ kotlin(
+ """
package java.androidx
import android.app.FragmentBreadCrumbs
@@ -662,16 +749,20 @@
viewGroup = breadCrumbs
}
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
@Test
fun `Unsafe implicit cast to varargs method`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
import android.icu.number.FormattedNumber;
@@ -694,11 +785,14 @@
}
}
}
- """.trimIndent())
- )
+ """
+ .trimIndent()
+ )
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/UnsafeCastToVarargs.java:11: Error: This expression has type android.icu.number.FormattedNumber (introduced in API level 30) but it used as type java.lang.CharSequence (introduced in API level 1). Run-time class verification will not be able to validate this implicit cast on devices between these API levels. [ImplicitCastClassVerificationFailure]
Api27Impl.setAutofillOptions(adapter, vararg1, vararg2, vararg3);
~~~~~~~
@@ -710,7 +804,8 @@
~~~~~~~
3 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/java/androidx/UnsafeCastToVarargs.java line 11: Extract to static inner class:
@@ -11 +11
- Api27Impl.setAutofillOptions(adapter, vararg1, vararg2, vararg3);
diff --git a/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt
index 62d45d5..60eec26 100644
--- a/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/MetadataTagInsideApplicationTagDetectorTest.kt
@@ -23,24 +23,25 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class MetadataTagInsideApplicationTagDetectorTest : AbstractLintDetectorTest(
- useDetector = MetadataTagInsideApplicationTagDetector(),
- useIssues = listOf(MetadataTagInsideApplicationTagDetector.ISSUE),
-) {
+class MetadataTagInsideApplicationTagDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = MetadataTagInsideApplicationTagDetector(),
+ useIssues = listOf(MetadataTagInsideApplicationTagDetector.ISSUE),
+ ) {
@Test
fun `Detect usage of metadata tag inside application tag`() {
- val input = arrayOf(
- manifestSample()
- )
+ val input = arrayOf(manifestSample())
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
AndroidManifest.xml:19: Error: Detected <application>-level meta-data tag. [MetadataTagInsideApplicationTag]
<meta-data android:name="name" android:value="value" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetectorTest.kt
index fb54974..17c805f 100644
--- a/lint-checks/src/test/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/MissingJvmDefaultWithCompatibilityDetectorTest.kt
@@ -21,80 +21,88 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class MissingJvmDefaultWithCompatibilityDetectorTest : AbstractLintDetectorTest(
- useDetector = MissingJvmDefaultWithCompatibilityDetector(),
- useIssues = listOf(MissingJvmDefaultWithCompatibilityDetector.ISSUE),
-
-) {
+class MissingJvmDefaultWithCompatibilityDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = MissingJvmDefaultWithCompatibilityDetector(),
+ useIssues = listOf(MissingJvmDefaultWithCompatibilityDetector.ISSUE),
+ ) {
@Test
fun `Test lint for interface with stable default method`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
interface InterfaceWithDefaultMethod {
fun methodWithoutDefaultImplementation(foo: Int): String
fun methodWithDefaultImplementation(): Int = 3
}
- """)
- )
+ """
+ )
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/InterfaceWithDefaultMethod.kt:4: Error: This interface must be annotated with @JvmDefaultWithCompatibility because it has a stable method with a default implementation [MissingJvmDefaultWithCompatibility]
interface InterfaceWithDefaultMethod {
^
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Autofix for src/java/androidx/InterfaceWithDefaultMethod.kt line 4: Annotate with @JvmDefaultWithCompatibility:
@@ -4 +4
+ @JvmDefaultWithCompatibility
"""
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(expectedFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test lint for interface with stable method with default parameter`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
interface InterfaceWithMethodWithDefaultParameterValue {
fun methodWithDefaultParameterValue(foo: Int = 3): Int
}
- """)
- )
+ """
+ )
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/InterfaceWithMethodWithDefaultParameterValue.kt:4: Error: This interface must be annotated with @JvmDefaultWithCompatibility because it has a stable method with a parameter with a default value [MissingJvmDefaultWithCompatibility]
interface InterfaceWithMethodWithDefaultParameterValue {
^
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Autofix for src/java/androidx/InterfaceWithMethodWithDefaultParameterValue.kt line 4: Annotate with @JvmDefaultWithCompatibility:
@@ -4 +4
+ @JvmDefaultWithCompatibility
"""
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(expectedFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test lint for interface implementing @JvmDefaultWithCompatibility interface`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
import kotlin.jvm.JvmDefaultWithCompatibility
@@ -103,41 +111,47 @@
interface InterfaceWithAnnotation {
fun foo(bar: Int = 3): Int
}
- """),
- kotlin("""
+ """
+ ),
+ kotlin(
+ """
package java.androidx
interface InterfaceWithoutAnnotation: InterfaceWithAnnotation {
fun baz(): Int
}
- """),
- Stubs.JvmDefaultWithCompatibility
- )
+ """
+ ),
+ Stubs.JvmDefaultWithCompatibility
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/InterfaceWithoutAnnotation.kt:4: Error: This interface must be annotated with @JvmDefaultWithCompatibility because it implements an interface which uses this annotation [MissingJvmDefaultWithCompatibility]
interface InterfaceWithoutAnnotation: InterfaceWithAnnotation {
^
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Autofix for src/java/androidx/InterfaceWithoutAnnotation.kt line 4: Annotate with @JvmDefaultWithCompatibility:
@@ -4 +4
+ @JvmDefaultWithCompatibility
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(expectedFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test lint does not apply to interface implementing @JvmDefaultWithCompatibility`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
import kotlin.jvm.JvmDefaultWithCompatibility
@@ -146,54 +160,60 @@
interface InterfaceWithAnnotation {
fun foo(bar: Int = 3): Int = 4
}
- """),
- Stubs.JvmDefaultWithCompatibility
- )
+ """
+ ),
+ Stubs.JvmDefaultWithCompatibility
+ )
- check(*input)
- .expectClean()
+ check(*input).expectClean()
}
@Test
fun `Test lint does not apply to unstable interface`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
@RequiresOptIn
interface UnstableInterface {
fun foo(bar: Int = 3): Int = 4
}
- """),
- Stubs.OptIn
- )
+ """
+ ),
+ Stubs.OptIn
+ )
- check(*input)
- .expectClean()
+ check(*input).expectClean()
}
@Test
fun `Test lint does not apply to interface with no stable methods`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
interface InterfaceWithoutStableMethods {
@RequiresOptIn
fun unstableMethod(foo: Int = 3): Int = 4
}
- """),
- Stubs.OptIn
- )
+ """
+ ),
+ Stubs.OptIn
+ )
- check(*input)
- .expectClean()
+ check(*input).expectClean()
}
@Test
fun `Test lint does apply to interface with one unstable method and one stable method`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
interface InterfaceWithStableAndUnstableMethods {
@@ -201,50 +221,56 @@
fun unstableMethod(foo: Int = 3): Int = 4
fun stableMethod(foo: Int = 3): Int = 4
}
- """),
- Stubs.OptIn
- )
+ """
+ ),
+ Stubs.OptIn
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/java/androidx/InterfaceWithStableAndUnstableMethods.kt:4: Error: This interface must be annotated with @JvmDefaultWithCompatibility because it has a stable method with a default implementation [MissingJvmDefaultWithCompatibility]
interface InterfaceWithStableAndUnstableMethods {
^
1 errors, 0 warnings
"""
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Autofix for src/java/androidx/InterfaceWithStableAndUnstableMethods.kt line 4: Annotate with @JvmDefaultWithCompatibility:
@@ -4 +4
+ @JvmDefaultWithCompatibility
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(expectedFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test lint does not apply to interface with no default methods`() {
- val input = arrayOf(
- kotlin("""
+ val input =
+ arrayOf(
+ kotlin(
+ """
package java.androidx
interface InterfaceWithoutDefaults {
fun methodWithoutDefaults(foo: Int): Int
}
- """)
- )
+ """
+ )
+ )
- check(*input)
- .expectClean()
+ check(*input).expectClean()
}
@Test
fun `Test lint does not apply to Java file`() {
- val input = arrayOf(
- java("""
+ val input =
+ arrayOf(
+ java(
+ """
package java.androidx;
interface JavaInterface {
@@ -252,10 +278,10 @@
return 3;
}
}
- """)
- )
+ """
+ )
+ )
- check(*input)
- .expectClean()
+ check(*input).expectClean()
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/NullabilityAnnotationsDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/NullabilityAnnotationsDetectorTest.kt
index 681f5ba..ceef49d 100644
--- a/lint-checks/src/test/java/androidx/build/lint/NullabilityAnnotationsDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/NullabilityAnnotationsDetectorTest.kt
@@ -22,15 +22,17 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class NullabilityAnnotationsDetectorTest : AbstractLintDetectorTest(
- useDetector = NullabilityAnnotationsDetector(),
- useIssues = listOf(NullabilityAnnotationsDetector.ISSUE),
-) {
+class NullabilityAnnotationsDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = NullabilityAnnotationsDetector(),
+ useIssues = listOf(NullabilityAnnotationsDetector.ISSUE),
+ ) {
@Test
fun `Detection of Jetbrains nullability usage in Java sources`() {
- val source = java(
- "src/androidx/sample/NullabilityAnnotationsJava.java",
- """
+ val source =
+ java(
+ "src/androidx/sample/NullabilityAnnotationsJava.java",
+ """
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,16 +43,15 @@
private void method2(@Nullable String arg) {
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val input = arrayOf(
- source,
- JetBrainsAnnotations
- )
+ val input = arrayOf(source, JetBrainsAnnotations)
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/sample/NullabilityAnnotationsJava.java:5: Error: Use @androidx.annotation.NonNull instead of @org.jetbrains.annotations.NotNull [NullabilityAnnotationsDetector]
private void method1(@NotNull String arg) {
~~~~~~~~
@@ -58,9 +59,11 @@
private void method2(@Nullable String arg) {
~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectFixDiffs = """
+ val expectFixDiffs =
+ """
Autofix for src/androidx/sample/NullabilityAnnotationsJava.java line 5: Replace with `@androidx.annotation.NonNull`:
@@ -5 +5
- private void method1(@NotNull String arg) {
@@ -69,18 +72,18 @@
@@ -8 +8
- private void method2(@Nullable String arg) {
+ private void method2(@androidx.annotation.Nullable String arg) {
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- check(*input)
- .expect(expected)
- .expectFixDiffs(expectFixDiffs)
+ check(*input).expect(expected).expectFixDiffs(expectFixDiffs)
}
@Test
fun `JetBrains annotations allowed in Kotlin sources`() {
- val source = kotlin(
- """
+ val source =
+ kotlin(
+ """
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
@@ -89,13 +92,11 @@
private fun method2(@Nullable arg: String?) {}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val input = arrayOf(
- source,
- JetBrainsAnnotations
- )
+ val input = arrayOf(source, JetBrainsAnnotations)
check(*input).expectClean()
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
index 7834027..3b905d1 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
@@ -24,17 +24,19 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class ObsoleteBuildCompatUsageDetectorTest : AbstractLintDetectorTest(
- useDetector = ObsoleteBuildCompatUsageDetector(),
- useIssues = listOf(ObsoleteBuildCompatUsageDetector.ISSUE),
- stubs = arrayOf(BuildCompat),
-) {
+class ObsoleteBuildCompatUsageDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = ObsoleteBuildCompatUsageDetector(),
+ useIssues = listOf(ObsoleteBuildCompatUsageDetector.ISSUE),
+ stubs = arrayOf(BuildCompat),
+ ) {
@Test
@Ignore("ANDROID_HOME not available on CI")
fun isAtLeastN() {
- val input = java(
- """
+ val input =
+ java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -44,18 +46,21 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastN()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedDiff = """
+ val expectedDiff =
+ """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 24:
@@ -5 +5
- if (BuildCompat.isAtLeastN()) {
@@ -63,16 +68,15 @@
"""
/* ktlint-enable max-line-length */
- check(input)
- .expect(expected.trimIndent())
- .expectFixDiffs(expectedDiff.trimIndent())
+ check(input).expect(expected.trimIndent()).expectFixDiffs(expectedDiff.trimIndent())
}
@Test
@Ignore("ANDROID_HOME not available on CI")
fun isAtLeastNStaticImport() {
- val input = java(
- """
+ val input =
+ java(
+ """
package foo;
import static androidx.core.os.BuildCompat.isAtLeastN;
public class Example {
@@ -82,18 +86,21 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (isAtLeastN()) {
~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedDiff = """
+ val expectedDiff =
+ """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 24:
@@ -5 +5
- if (isAtLeastN()) {
@@ -101,16 +108,15 @@
"""
/* ktlint-enable max-line-length */
- check(input)
- .expect(expected.trimIndent())
- .expectFixDiffs(expectedDiff.trimIndent())
+ check(input).expect(expected.trimIndent()).expectFixDiffs(expectedDiff.trimIndent())
}
@Test
@Ignore("ANDROID_HOME not available on CI")
fun isAtLeastNMR1() {
- val input = java(
- """
+ val input =
+ java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -120,18 +126,21 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastNMR1()) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedDiff = """
+ val expectedDiff =
+ """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 25:
@@ -5 +5
- if (BuildCompat.isAtLeastNMR1()) {
@@ -139,16 +148,15 @@
"""
/* ktlint-enable max-line-length */
- check(input)
- .expect(expected.trimIndent())
- .expectFixDiffs(expectedDiff.trimIndent())
+ check(input).expect(expected.trimIndent()).expectFixDiffs(expectedDiff.trimIndent())
}
@Test
@Ignore("ANDROID_HOME not available on CI")
fun isAtLeastO() {
- val input = java(
- """
+ val input =
+ java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -158,18 +166,21 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastO()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedDiff = """
+ val expectedDiff =
+ """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 26:
@@ -5 +5
- if (BuildCompat.isAtLeastO()) {
@@ -177,16 +188,15 @@
"""
/* ktlint-enable max-line-length */
- check(input)
- .expect(expected.trimIndent())
- .expectFixDiffs(expectedDiff.trimIndent())
+ check(input).expect(expected.trimIndent()).expectFixDiffs(expectedDiff.trimIndent())
}
@Test
@Ignore("ANDROID_HOME not available on CI")
fun isAtLeastOMR1() {
- val input = java(
- """
+ val input =
+ java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -196,18 +206,21 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastOMR1()) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedDiff = """
+ val expectedDiff =
+ """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 27:
@@ -5 +5
- if (BuildCompat.isAtLeastOMR1()) {
@@ -215,16 +228,15 @@
"""
/* ktlint-enable max-line-length */
- check(input)
- .expect(expected.trimIndent())
- .expectFixDiffs(expectedDiff.trimIndent())
+ check(input).expect(expected.trimIndent()).expectFixDiffs(expectedDiff.trimIndent())
}
@Test
@Ignore("ANDROID_HOME not available on CI")
fun isAtLeastP() {
- val input = java(
- """
+ val input =
+ java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -234,18 +246,21 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastP()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedDiff = """
+ val expectedDiff =
+ """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 28:
@@ -5 +5
- if (BuildCompat.isAtLeastP()) {
@@ -253,16 +268,15 @@
"""
/* ktlint-enable max-line-length */
- check(input)
- .expect(expected.trimIndent())
- .expectFixDiffs(expectedDiff.trimIndent())
+ check(input).expect(expected.trimIndent()).expectFixDiffs(expectedDiff.trimIndent())
}
@Test
@Ignore("ANDROID_HOME not available on CI")
fun isAtLeastQ() {
- val input = java(
- """
+ val input =
+ java(
+ """
package foo;
import androidx.core.os.BuildCompat;
public class Example {
@@ -272,18 +286,21 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/foo/Example.java:5: Error: Using deprecated BuildCompat methods [ObsoleteBuildCompat]
if (BuildCompat.isAtLeastQ()) {
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
- val expectedDiff = """
+ val expectedDiff =
+ """
Fix for src/foo/Example.java line 5: Use SDK_INT >= 29:
@@ -5 +5
- if (BuildCompat.isAtLeastQ()) {
@@ -291,14 +308,13 @@
"""
/* ktlint-enable max-line-length */
- check(input)
- .expect(expected.trimIndent())
- .expectFixDiffs(expectedDiff.trimIndent())
+ check(input).expect(expected.trimIndent()).expectFixDiffs(expectedDiff.trimIndent())
}
companion object {
- private val BuildCompat = java(
- """
+ private val BuildCompat =
+ java(
+ """
package androidx.core.os;
public class BuildCompat {
public static boolean isAtLeastN() { return false; }
@@ -308,7 +324,8 @@
public static boolean isAtLeastP() { return false; }
public static boolean isAtLeastQ() { return false; }
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ObsoleteCompatDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/ObsoleteCompatDetectorTest.kt
index c71313d..2a558b7 100644
--- a/lint-checks/src/test/java/androidx/build/lint/ObsoleteCompatDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/ObsoleteCompatDetectorTest.kt
@@ -23,10 +23,14 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class ObsoleteCompatDetectorTest : AbstractLintDetectorTest(
- useDetector = ObsoleteCompatDetector(),
- useIssues = listOf(ObsoleteCompatDetector.ISSUE),
- stubs = arrayOf(java("""
+class ObsoleteCompatDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = ObsoleteCompatDetector(),
+ useIssues = listOf(ObsoleteCompatDetector.ISSUE),
+ stubs =
+ arrayOf(
+ java(
+ """
package androidx.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
@@ -39,17 +43,21 @@
public @interface ReplaceWith {
String expression();
}
- """))
-) {
+ """
+ )
+ )
+ ) {
@Test
fun `Obsolete compat method`() {
- val input = arrayOf(
- javaSample("androidx.ObsoleteCompatMethod"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ObsoleteCompatMethod"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ObsoleteCompatMethod.java:33: Error: Obsolete compat method should provide replacement [ObsoleteCompatMethod]
public static long hashCode(Object obj) {
~~~~~~~~
@@ -57,9 +65,11 @@
public static long hashCodeNoDoc(Object obj) {
~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedAutoFix = """
+ val expectedAutoFix =
+ """
Fix for src/androidx/ObsoleteCompatMethod.java line 33: Replace obsolete compat method:
@@ -20 +20
+ import androidx.annotation.ReplaceWith;
@@ -75,7 +85,8 @@
+ @Deprecated
+ @ReplaceWith(expression = "obj.hashCode()")
+ /** @deprecated Call {@link Object#hashCode()} directly. */
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedAutoFix)
@@ -83,25 +94,30 @@
@Test
fun `Obsolete compat method missing @ReplaceWith`() {
- val input = arrayOf(
- javaSample("androidx.ObsoleteCompatMethodMissingReplaceWith"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ObsoleteCompatMethodMissingReplaceWith"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ObsoleteCompatMethodMissingReplaceWith.java:32: Error: Obsolete compat method should provide replacement [ObsoleteCompatMethod]
public static long hashCode(Object obj) {
~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedAutoFix = """
+ val expectedAutoFix =
+ """
Autofix for src/androidx/ObsoleteCompatMethodMissingReplaceWith.java line 32: Replace obsolete compat method:
@@ -18 +18
+ import androidx.annotation.ReplaceWith;
@@ -31 +32
+ @ReplaceWith(expression = "obj.hashCode()")
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedAutoFix)
@@ -109,25 +125,30 @@
@Test
fun `Obsolete compat method missing multi-line @ReplaceWith`() {
- val input = arrayOf(
- javaSample("androidx.ObsoleteCompatMethodMissingMultiLineReplaceWith"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ObsoleteCompatMethodMissingMultiLineReplaceWith"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ObsoleteCompatMethodMissingMultiLineReplaceWith.java:32: Error: Obsolete compat method should provide replacement [ObsoleteCompatMethod]
public static long hashCode(Object obj) {
~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedAutoFix = """
+ val expectedAutoFix =
+ """
Autofix for src/androidx/ObsoleteCompatMethodMissingMultiLineReplaceWith.java line 32: Replace obsolete compat method:
@@ -18 +18
+ import androidx.annotation.ReplaceWith;
@@ -31 +32
+ @ReplaceWith(expression = "obj.hashCode()")
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedAutoFix)
@@ -135,23 +156,28 @@
@Test
fun `Obsolete compat methods missing @Deprecated`() {
- val input = arrayOf(
- javaSample("androidx.ObsoleteCompatMethodMissingDeprecated"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ObsoleteCompatMethodMissingDeprecated"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ObsoleteCompatMethodMissingDeprecated.java:37: Error: Obsolete compat method should provide replacement [ObsoleteCompatMethod]
public static long hashCode(Object obj) {
~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedAutoFix = """
+ val expectedAutoFix =
+ """
Autofix for src/androidx/ObsoleteCompatMethodMissingDeprecated.java line 37: Replace obsolete compat method:
@@ -36 +36
+ @Deprecated
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedAutoFix)
@@ -159,12 +185,14 @@
@Test
fun `Obsolete compat methods missing javadoc`() {
- val input = arrayOf(
- javaSample("androidx.ObsoleteCompatMethodMissingJavadoc"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ObsoleteCompatMethodMissingJavadoc"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ObsoleteCompatMethodMissingJavadoc.java:37: Error: Obsolete compat method should provide replacement [ObsoleteCompatMethod]
public static long hashCode(Object obj) {
~~~~~~~~
@@ -172,16 +200,19 @@
public static long hashCodeNoDoc(Object obj) {
~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedAutoFix = """
+ val expectedAutoFix =
+ """
Autofix for src/androidx/ObsoleteCompatMethodMissingJavadoc.java line 37: Replace obsolete compat method:
@@ -34 +34
+ * @deprecated Call {@link Object#hashCode()} directly.
Autofix for src/androidx/ObsoleteCompatMethodMissingJavadoc.java line 44: Replace obsolete compat method:
@@ -42 +42
+ /** @deprecated Call {@link Object#hashCode()} directly. */
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedAutoFix)
@@ -189,12 +220,14 @@
@Test
fun `Obsolete compat methods missing Deprecated and javadoc`() {
- val input = arrayOf(
- javaSample("androidx.ObsoleteCompatMethodMissingDeprecatedAndJavadoc"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.ObsoleteCompatMethodMissingDeprecatedAndJavadoc"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/ObsoleteCompatMethodMissingDeprecatedAndJavadoc.java:36: Error: Obsolete compat method should provide replacement [ObsoleteCompatMethod]
public static long hashCode(Object obj) {
~~~~~~~~
@@ -202,9 +235,11 @@
public static long hashCodeNoDoc(Object obj) {
~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedAutoFix = """
+ val expectedAutoFix =
+ """
Fix for src/androidx/ObsoleteCompatMethodMissingDeprecatedAndJavadoc.java line 36: Replace obsolete compat method:
@@ -34 +34
+ * @deprecated Call {@link Object#hashCode()} directly.
@@ -214,7 +249,8 @@
@@ -41 +41
+ @Deprecated
+ /** @deprecated Call {@link Object#hashCode()} directly. */
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedAutoFix)
diff --git a/lint-checks/src/test/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetectorTest.kt
index b6d3422..ce9e5f6 100644
--- a/lint-checks/src/test/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/PrereleaseSdkCoreDependencyDetectorTest.kt
@@ -21,21 +21,24 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class PrereleaseSdkCoreDependencyDetectorTest : AbstractLintDetectorTest(
- useDetector = PrereleaseSdkCoreDependencyDetector(),
- useIssues = listOf(PrereleaseSdkCoreDependencyDetector.ISSUE),
- stubs = arrayOf(
- Stubs.BuildCompat,
- Stubs.ChecksSdkIntAtLeast,
- Stubs.JetpackRequiresOptIn,
- Stubs.RestrictTo
- )
-) {
+class PrereleaseSdkCoreDependencyDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = PrereleaseSdkCoreDependencyDetector(),
+ useIssues = listOf(PrereleaseSdkCoreDependencyDetector.ISSUE),
+ stubs =
+ arrayOf(
+ Stubs.BuildCompat,
+ Stubs.ChecksSdkIntAtLeast,
+ Stubs.JetpackRequiresOptIn,
+ Stubs.RestrictTo
+ )
+ ) {
@Test
fun `Versioned dependency with isAtLeastU is flagged`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package androidx.test
import androidx.core.os.BuildCompat
@@ -43,22 +46,28 @@
fun callIsAtLeastU() {
return BuildCompat.isAtLeastU()
}
- """.trimIndent()
- ),
- gradle("""
+ """
+ .trimIndent()
+ ),
+ gradle(
+ """
dependencies {
implementation("androidx.core:core:1.9.0")
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/main/kotlin/androidx/test/test.kt:6: Error: Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core [PrereleaseSdkCoreDependency]
return BuildCompat.isAtLeastU()
~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -66,9 +75,10 @@
@Test
fun `Tip-of-tree dependency with isAtLeastU is not flagged`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package androidx.test
import androidx.core.os.BuildCompat
@@ -76,23 +86,28 @@
fun callIsAtLeastU() {
return BuildCompat.isAtLeastU()
}
- """.trimIndent()
- ),
- gradle("""
+ """
+ .trimIndent()
+ ),
+ gradle(
+ """
dependencies {
implementation(project(":core:core"))
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
@Test
fun `Versioned dependency with isAtLeastSv2 is flagged`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package androidx.test
import androidx.core.os.BuildCompat
@@ -100,22 +115,28 @@
fun callIsAtLeastSv2() {
return BuildCompat.isAtLeastSv2()
}
- """.trimIndent()
- ),
- gradle("""
+ """
+ .trimIndent()
+ ),
+ gradle(
+ """
dependencies {
implementation("androidx.core:core:1.9.0")
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/main/kotlin/androidx/test/test.kt:6: Error: Prelease SDK check isAtLeastSv2 cannot be called as this project has a versioned dependency on androidx.core:core [PrereleaseSdkCoreDependency]
return BuildCompat.isAtLeastSv2()
~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -123,9 +144,10 @@
@Test
fun `Versioned dependency with non-annotated isAtLeastN is not flagged`() {
- val input = arrayOf(
- kotlin(
- """
+ val input =
+ arrayOf(
+ kotlin(
+ """
package androidx.test
import androidx.core.os.BuildCompat
@@ -133,14 +155,18 @@
fun callIsAtLeastN() {
return BuildCompat.isAtLeastN()
}
- """.trimIndent()
- ),
- gradle("""
+ """
+ .trimIndent()
+ ),
+ gradle(
+ """
dependencies {
implementation("androidx.core:core:1.9.0")
}
- """.trimIndent()),
- )
+ """
+ .trimIndent()
+ ),
+ )
check(*input).expectClean()
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
index 7b98a82..34e2adb 100644
--- a/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/PrivateConstructorForUtilityClassDetectorTest.kt
@@ -23,16 +23,18 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class PrivateConstructorForUtilityClassDetectorTest : AbstractLintDetectorTest(
- useDetector = PrivateConstructorForUtilityClassDetector(),
- useIssues = listOf(PrivateConstructorForUtilityClassDetector.ISSUE),
-) {
+class PrivateConstructorForUtilityClassDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = PrivateConstructorForUtilityClassDetector(),
+ useIssues = listOf(PrivateConstructorForUtilityClassDetector.ISSUE),
+ ) {
@Test
fun testInnerClassVisibilityJava() {
- val input = java(
- "src/androidx/PrivateConstructorForUtilityClassJava.java",
- """
+ val input =
+ java(
+ "src/androidx/PrivateConstructorForUtilityClassJava.java",
+ """
public class PrivateConstructorForUtilityClassJava {
// This class has a default private constructor, which is allowed.
@@ -55,11 +57,13 @@
static void method() { }
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/PrivateConstructorForUtilityClassJava.java:9: Error: Utility class is missing private constructor [PrivateConstructorForUtilityClass]
static class DefaultInnerClass {
~~~~~~~~~~~~~~~~~
@@ -70,7 +74,8 @@
public static class PublicInnerClass {
~~~~~~~~~~~~~~~~
3 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/RestrictToDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/RestrictToDetectorTest.kt
index 8b703e9..44e589f 100644
--- a/lint-checks/src/test/java/androidx/build/lint/RestrictToDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/RestrictToDetectorTest.kt
@@ -49,7 +49,7 @@
lint()
.files(
java(
- """
+ """
package test.pkg;
import androidx.annotation.RestrictTo;
@@ -81,7 +81,7 @@
}
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -94,7 +94,7 @@
project()
.files(
java(
- """
+ """
package test.pkg;
import library.pkg.internal.InternalClass;
import library.pkg.Library;
@@ -115,11 +115,11 @@
}
}
"""
- )
+ )
.indented(),
java(
- "src/test/java/test/pkg/UnitTestLibrary.java",
- """
+ "src/test/java/test/pkg/UnitTestLibrary.java",
+ """
package test.pkg;
import library.pkg.PrivateClass;
@@ -130,18 +130,18 @@
}
}
"""
- )
+ )
.indented(),
library,
gradle(
- """
+ """
apply plugin: 'com.android.application'
dependencies {
compile 'my.group.id:mylib:25.0.0-SNAPSHOT'
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -171,7 +171,7 @@
project()
.files(
java(
- """
+ """
package com.example.mylibrary;
import androidx.annotation.RestrictTo;
@@ -205,10 +205,10 @@
}
}
"""
- )
+ )
.indented(),
java(
- """
+ """
package test.pkg;
import com.example.mylibrary.LibraryCode;
@@ -225,7 +225,7 @@
}
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -236,7 +236,7 @@
project()
.files(
kotlin(
- """
+ """
package com.example.myapplication
import com.example.mylibrary.LibraryCode
@@ -250,7 +250,7 @@
val f3 = LibraryCode.FIELD3
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -278,7 +278,7 @@
project()
.files(
java(
- """
+ """
package test.pkg;
import library.pkg.PrivateClass;
@@ -290,10 +290,10 @@
}
}
"""
- )
+ )
.indented(),
java(
- """
+ """
package test.pkg;
import library.pkg.PrivateClass;
@@ -301,10 +301,10 @@
public class TestLibrary2 extends PrivateClass {
}
"""
- )
+ )
.indented(),
java(
- """
+ """
package test.pkg;
@SuppressWarnings("ClassNameDiffersFromFileName")
@@ -314,10 +314,10 @@
}
}
"""
- )
+ )
.indented(),
java(
- """
+ """
package test.pkg;
@SuppressWarnings("ClassNameDiffersFromFileName")
@@ -327,18 +327,18 @@
}
}
"""
- )
+ )
.indented(),
library,
gradle(
- """
+ """
apply plugin: 'com.android.application'
dependencies {
compile 'my.group.id:mylib:25.0.0-SNAPSHOT'
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -446,28 +446,28 @@
"""
),
java(
- """
+ """
package test.pkg;
@SuppressWarnings("ClassNameDiffersFromFileName")
public class MyJavaClass extends RestrictedClass implements RestrictedInterface {
}
"""
- )
+ )
.indented(),
java(
- "src/androidTest/java/test/pkg/MyTestJavaClass.java",
- """
+ "src/androidTest/java/test/pkg/MyTestJavaClass.java",
+ """
package test.pkg;
@SuppressWarnings("ClassNameDiffersFromFileName")
public class MyTestJavaClass extends RestrictedClass {
}
"""
- )
+ )
.indented(),
kotlin(
- """
+ """
package test.pkg
import androidx.annotation.RestrictTo
@@ -475,10 +475,10 @@
@RestrictTo(RestrictTo.Scope.TESTS)
open class RestrictedClass
"""
- )
+ )
.indented(),
kotlin(
- """
+ """
package test.pkg
import androidx.annotation.RestrictTo
@@ -486,17 +486,17 @@
@RestrictTo(RestrictTo.Scope.TESTS)
interface RestrictedInterface
"""
- )
+ )
.indented(),
gradle(
- """
+ """
android {
lintOptions {
checkTestSources true
}
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -570,7 +570,7 @@
lint()
.files(
kotlin(
- """
+ """
package test.pkg
import androidx.annotation.RestrictTo
@@ -583,7 +583,7 @@
@RestrictTo(RestrictTo.Scope.SUBCLASSES)
val foo = Foo()
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -604,7 +604,7 @@
lint()
.files(
kotlin(
- """
+ """
package test.pkg
import androidx.annotation.RestrictTo
@@ -630,10 +630,10 @@
var foo4: Foo? = Foo()
}
"""
- )
+ )
.indented(),
kotlin(
- """
+ """
package test.pkg
class Sub : Bar() {
fun test() {
@@ -658,7 +658,7 @@
}
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -685,34 +685,32 @@
fun testGetMavenCoordinateFromIdentifier() {
assertEquals(
"androidx.wear.tiles:tiles-material:",
- DefaultLintModelModuleLibrary(
- ":@@:wear:tiles:tiles-material", "", null, false
- ).getMavenNameFromIdentifier()!!.toString()
+ DefaultLintModelModuleLibrary(":@@:wear:tiles:tiles-material", "", null, false)
+ .getMavenNameFromIdentifier()!!
+ .toString()
)
assertEquals(
"androidx.wear.tiles:tiles-material:",
- DefaultLintModelModuleLibrary(
- ":@@:wear:tiles:tiles-material::debug", "", null, false
- ).getMavenNameFromIdentifier()!!.toString()
+ DefaultLintModelModuleLibrary(":@@:wear:tiles:tiles-material::debug", "", null, false)
+ .getMavenNameFromIdentifier()!!
+ .toString()
)
assertEquals(
null,
- DefaultLintModelModuleLibrary(
- "", "", null, false
- ).getMavenNameFromIdentifier()
+ DefaultLintModelModuleLibrary("", "", null, false).getMavenNameFromIdentifier()
)
}
fun testFindMavenNameWithJarFileInPath() {
val mavenName = DefaultLintModelMavenName("", "")
val path = "/media/nvme/android/androidx-main/out/androidx/health/connect/connect-client"
- val filePath = "$path/build/.transforms/7940653434057db25345237f4ed56def/transformed/out" +
- "/jars/libs/repackaged.jar"
- val library = DefaultLintModelJavaLibrary(
- "", listOf(File(filePath)), mavenName, false, null
- )
+ val filePath =
+ "$path/build/.transforms/7940653434057db25345237f4ed56def/transformed/out" +
+ "/jars/libs/repackaged.jar"
+ val library =
+ DefaultLintModelJavaLibrary("", listOf(File(filePath)), mavenName, false, null)
assertEquals(mavenName, listOf(library).findMavenNameWithJarFileInPath(path))
}
@@ -722,9 +720,9 @@
mavenLibrary(
"my.group.id:mylib:25.0.0-SNAPSHOT",
stubSources =
- listOf(
- java(
- """
+ listOf(
+ java(
+ """
package library.pkg;
import androidx.annotation.RestrictTo;
@@ -738,10 +736,10 @@
}
}
"""
- )
- .indented(),
- java(
- """
+ )
+ .indented(),
+ java(
+ """
package library.pkg;
import androidx.annotation.RestrictTo;
@@ -752,10 +750,10 @@
}
}
"""
- )
- .indented(),
- java(
- """
+ )
+ .indented(),
+ java(
+ """
package library.pkg.internal;
public class InternalClass {
@@ -763,18 +761,18 @@
}
}
"""
- )
- .indented(),
- java(
- """
+ )
+ .indented(),
+ java(
+ """
@RestrictTo(RestrictTo.Scope.GROUP_ID)
package library.pkg.internal;
import androidx.annotation.RestrictTo;
"""
- )
- .indented()
- ),
+ )
+ .indented()
+ ),
compileOnly = listOf(SUPPORT_ANNOTATIONS_JAR)
)
}
@@ -784,7 +782,7 @@
project()
.files(
java(
- """
+ """
package com.example.mylibrary;
import androidx.annotation.RestrictTo;
@@ -825,10 +823,10 @@
}
}
"""
- )
+ )
.indented(),
java(
- """
+ """
package test.pkg;
import com.example.mylibrary.LibraryCode;
@@ -847,14 +845,14 @@
}
}
"""
- )
+ )
.indented(),
gradle(
- """
+ """
apply plugin: 'com.android.library'
group=test.pkg.library
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -865,7 +863,7 @@
project()
.files(
java(
- """
+ """
package com.example.dotless;
import androidx.annotation.RestrictTo;
@@ -876,14 +874,14 @@
}
}
"""
- )
+ )
.indented(),
gradle(
- """
+ """
apply plugin: 'com.android.library'
group=dotless
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
@@ -893,7 +891,7 @@
project()
.files(
kotlin(
- """
+ """
package com.example.myapplication
import com.example.mylibrary.LibraryCode
@@ -911,14 +909,14 @@
DotlessCode.method() // ERROR
}
"""
- )
+ )
.indented(),
gradle(
- """
+ """
apply plugin: 'com.android.library'
group=other.app
"""
- )
+ )
.indented()
)
.name("lib2")
@@ -1007,7 +1005,7 @@
lint()
.files(
kotlin(
- """
+ """
package test.pkg
import library.pkg.PrivateKotlinClass
@@ -1016,14 +1014,14 @@
override fun method() {}
}
"""
- )
+ )
.indented(),
mavenLibrary(
"my.group.id:myklib:25.0.0-SNAPSHOT",
stubSources =
- listOf(
- kotlin(
- """
+ listOf(
+ kotlin(
+ """
package library.pkg
import androidx.annotation.RestrictTo
@@ -1033,20 +1031,20 @@
open fun method() {}
}
"""
- )
- .indented(),
- ),
+ )
+ .indented(),
+ ),
compileOnly = listOf(SUPPORT_ANNOTATIONS_JAR)
),
gradle(
- """
+ """
apply plugin: 'com.android.application'
dependencies {
compile 'my.group.id:myklib:25.0.0-SNAPSHOT'
}
"""
- )
+ )
.indented(),
SUPPORT_ANNOTATIONS_JAR
)
diff --git a/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationDetectorTest.kt
index 02a9935..cfb4891 100644
--- a/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationDetectorTest.kt
@@ -33,20 +33,21 @@
*
* This tests the following module setup:
*
- * Module 'foo', which lives in foo
- * Module 'foo:samples', which lives in foo/samples, and depends on 'foo'
+ * Module 'foo', which lives in foo Module 'foo:samples', which lives in foo/samples, and depends on
+ * 'foo'
*/
@RunWith(JUnit4::class)
class SampledAnnotationDetectorTest : LintDetectorTest() {
override fun getDetector() = SampledAnnotationDetector()
- override fun getIssues() = mutableListOf(
- SampledAnnotationDetector.OBSOLETE_SAMPLED_ANNOTATION,
- SampledAnnotationDetector.UNRESOLVED_SAMPLE_LINK,
- SampledAnnotationDetector.MULTIPLE_FUNCTIONS_FOUND,
- SampledAnnotationDetector.INVALID_SAMPLES_LOCATION
- )
+ override fun getIssues() =
+ mutableListOf(
+ SampledAnnotationDetector.OBSOLETE_SAMPLED_ANNOTATION,
+ SampledAnnotationDetector.UNRESOLVED_SAMPLE_LINK,
+ SampledAnnotationDetector.MULTIPLE_FUNCTIONS_FOUND,
+ SampledAnnotationDetector.INVALID_SAMPLES_LOCATION
+ )
private val fooModuleName = "foo"
private val sampleModuleName = "samples"
@@ -54,24 +55,26 @@
private val barFilePath = "src/foo/Bar.kt"
private val sampleFilePath = "samples/src/foo/samples/test.kt"
- private val sampledStub = compiled(
- "libs/sampled.jar",
- kotlin(
- """
+ private val sampledStub =
+ compiled(
+ "libs/sampled.jar",
+ kotlin(
+ """
package androidx.annotation
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class Sampled
"""
- ).to("androidx/annotation/Sampled.kt"),
- 0x9dd4162c,
-"""
+ )
+ .to("androidx/annotation/Sampled.kt"),
+ 0x9dd4162c,
+ """
META-INF/main.kotlin_module:
H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM3AJcrFnZafr1ecmFuQk1osxBaSWlzi
XaLEoMUAAClM+YMvAAAA
""",
- """
+ """
androidx/annotation/Sampled.class:
H4sIAAAAAAAAAIVSyU4CQRB9NYggbriDuGv0Jki8cXLBSKJiEL1wapmOGWlm
CNOg3rj5Tx4M8ehHGatdgMNE+1D9qvq96qrqfv94eQWwj01CSrh203Psx7Rw
@@ -84,24 +87,27 @@
JMYxgclvGGc4ZeAXZxozmGXVXAWhAuYLWGCLhDHJAhaRYpaPJSxXYPlY8bH6
CeBn7eLsAgAA
"""
- )
+ )
- private val emptySampleFile = kotlin(
- """
+ private val emptySampleFile =
+ kotlin(
+ """
package foo.samples
"""
- )
+ )
- private val unannotatedSampleFile = kotlin(
- """
+ private val unannotatedSampleFile =
+ kotlin(
+ """
package foo.samples
fun sampleBar() {}
"""
- )
+ )
- private val multipleMatchingSampleFile = kotlin(
- """
+ private val multipleMatchingSampleFile =
+ kotlin(
+ """
package foo.samples
import androidx.annotation.Sampled
@@ -112,10 +118,11 @@
@Sampled
fun sampleBar(param: Boolean = false) {}
"""
- )
+ )
- private val correctlyAnnotatedSampleFile = kotlin(
- """
+ private val correctlyAnnotatedSampleFile =
+ kotlin(
+ """
package foo.samples
import androidx.annotation.Sampled
@@ -123,7 +130,7 @@
@Sampled
fun sampleBar() {}
"""
- )
+ )
private fun checkKotlin(
fooFile: TestFile? = null,
@@ -132,20 +139,22 @@
requiresPartialAnalysis: Boolean = true
) {
val projectDescriptions = mutableListOf<ProjectDescription>()
- val fooProject = ProjectDescription().apply {
- name = fooModuleName
- type = ProjectDescription.Type.LIBRARY
- fooFile?.let { addFile(fooFile) }
- }
+ val fooProject =
+ ProjectDescription().apply {
+ name = fooModuleName
+ type = ProjectDescription.Type.LIBRARY
+ fooFile?.let { addFile(fooFile) }
+ }
projectDescriptions += fooProject
- val sampleProject = ProjectDescription().apply {
- name = sampleModuleName
- type = ProjectDescription.Type.LIBRARY
- under = fooProject
- files = arrayOf(sampleFile, sampledStub)
- dependsOn(fooProject)
- }
+ val sampleProject =
+ ProjectDescription().apply {
+ name = sampleModuleName
+ type = ProjectDescription.Type.LIBRARY
+ under = fooProject
+ files = arrayOf(sampleFile, sampledStub)
+ dependsOn(fooProject)
+ }
projectDescriptions += sampleProject
lint().run {
@@ -173,8 +182,9 @@
@Test
fun unresolvedSampleLink_Function() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -184,7 +194,7 @@
fun bar() {}
}
"""
- )
+ )
val sampleFile = emptySampleFile
@@ -200,8 +210,9 @@
@Test
fun unannotatedSampleFunction_Function() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -211,7 +222,7 @@
fun bar() {}
}
"""
- )
+ )
val sampleFile = unannotatedSampleFile
@@ -227,8 +238,9 @@
@Test
fun multipleMatchingSampleFunctions_Function() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -238,7 +250,7 @@
fun bar() {}
}
"""
- )
+ )
val sampleFile = multipleMatchingSampleFile
@@ -261,8 +273,9 @@
@Test
fun correctlyAnnotatedSampleFunction_Function() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -272,7 +285,7 @@
fun bar() {}
}
"""
- )
+ )
val sampleFile = correctlyAnnotatedSampleFile
@@ -281,8 +294,9 @@
@Test
fun unresolvedSampleLink_Class() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
/**
@@ -290,7 +304,7 @@
*/
class Bar
"""
- )
+ )
val sampleFile = emptySampleFile
@@ -306,8 +320,9 @@
@Test
fun unannotatedSampleFunction_Class() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
/**
@@ -315,7 +330,7 @@
*/
class Bar
"""
- )
+ )
val sampleFile = unannotatedSampleFile
@@ -331,8 +346,9 @@
@Test
fun multipleMatchingSampleFunctions_Class() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
/**
@@ -340,7 +356,7 @@
*/
class Bar
"""
- )
+ )
val sampleFile = multipleMatchingSampleFile
@@ -363,8 +379,9 @@
@Test
fun correctlyAnnotatedSampleFunction_Class() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
/**
@@ -372,7 +389,7 @@
*/
class Bar
"""
- )
+ )
val sampleFile = correctlyAnnotatedSampleFile
@@ -381,8 +398,9 @@
@Test
fun unresolvedSampleLink_Field() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -392,7 +410,7 @@
const val bar = 0
}
"""
- )
+ )
val sampleFile = emptySampleFile
@@ -408,8 +426,9 @@
@Test
fun unannotatedSampleFunction_Field() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -419,7 +438,7 @@
const val bar = 0
}
"""
- )
+ )
val sampleFile = unannotatedSampleFile
@@ -435,8 +454,9 @@
@Test
fun multipleMatchingSampleFunctions_Field() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -446,7 +466,7 @@
const val bar = 0
}
"""
- )
+ )
val sampleFile = multipleMatchingSampleFile
@@ -469,8 +489,9 @@
@Test
fun correctlyAnnotatedSampleFunction_Field() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -480,7 +501,7 @@
const val bar = 0
}
"""
- )
+ )
val sampleFile = correctlyAnnotatedSampleFile
@@ -489,8 +510,9 @@
@Test
fun unresolvedSampleLink_PropertyWithGetter() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -500,7 +522,7 @@
val bar get() = 0
}
"""
- )
+ )
val sampleFile = emptySampleFile
@@ -516,8 +538,9 @@
@Test
fun unannotatedSampleFunction_PropertyWithGetter() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -527,7 +550,7 @@
val bar get() = 0
}
"""
- )
+ )
val sampleFile = unannotatedSampleFile
@@ -543,8 +566,9 @@
@Test
fun multipleMatchingSampleFunctions_PropertyWithGetter() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -554,7 +578,7 @@
val bar get() = 0
}
"""
- )
+ )
val sampleFile = multipleMatchingSampleFile
@@ -577,8 +601,9 @@
@Test
fun correctlyAnnotatedSampleFunction_PropertyWithGetter() {
- val fooFile = kotlin(
- """
+ val fooFile =
+ kotlin(
+ """
package foo
class Bar {
@@ -588,7 +613,7 @@
val bar get() = 0
}
"""
- )
+ )
val sampleFile = correctlyAnnotatedSampleFile
@@ -612,8 +637,9 @@
@Test
fun invalidSampleLocation() {
- val sampleFile = kotlin(
- """
+ val sampleFile =
+ kotlin(
+ """
package foo.wrong.location
import androidx.annotation.Sampled
@@ -621,7 +647,7 @@
@Sampled
fun sampleBar() {}
"""
- )
+ )
val expected =
"""samples/src/foo/wrong/location/test.kt:7: Error: foo.wrong.location.sampleBar is annotated with @Sampled, but is not linked to from a @sample tag. [ObsoleteSampledAnnotation]
diff --git a/lint-checks/src/test/java/androidx/build/lint/Stubs.kt b/lint-checks/src/test/java/androidx/build/lint/Stubs.kt
index 08b9ba1..5382118 100644
--- a/lint-checks/src/test/java/androidx/build/lint/Stubs.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/Stubs.kt
@@ -28,67 +28,72 @@
/* ktlint-disable max-line-length */
- /**
- * [TestFile] containing Keep.java from the annotation library.
- */
- val Keep = TestFiles.java(
- """
+ /** [TestFile] containing Keep.java from the annotation library. */
+ val Keep =
+ TestFiles.java(
+ """
package androidx.annotation;
public @interface Keep {
}
"""
- )
+ )
- val RunWith = TestFiles.kotlin(
- """
+ val RunWith =
+ TestFiles.kotlin(
+ """
package org.junit.runner
annotation class RunWith(val value: KClass<*>)
"""
- )
+ )
- val JUnit4Runner = TestFiles.kotlin(
- """
+ val JUnit4Runner =
+ TestFiles.kotlin(
+ """
package org.junit.runners
class JUnit4
"""
- )
+ )
- val ParameterizedRunner = TestFiles.kotlin(
- """
+ val ParameterizedRunner =
+ TestFiles.kotlin(
+ """
package org.junit.runners
class Parameterized
"""
- )
+ )
- val AndroidJUnit4Runner = TestFiles.kotlin(
- """
+ val AndroidJUnit4Runner =
+ TestFiles.kotlin(
+ """
package androidx.test.ext.junit.runners
class AndroidJUnit4
"""
- )
+ )
- val TestSizeAnnotations = TestFiles.kotlin(
- """
+ val TestSizeAnnotations =
+ TestFiles.kotlin(
+ """
package androidx.test.filters
annotation class SmallTest
annotation class MediumTest
annotation class LargeTest
"""
- )
+ )
- val TestAnnotation = TestFiles.kotlin(
- """
+ val TestAnnotation =
+ TestFiles.kotlin(
+ """
package org.junit
annotation class Test
"""
- )
+ )
/**
* [TestFile] containing OptIn.kt from the Kotlin standard library.
@@ -96,8 +101,9 @@
* This is a workaround for the Kotlin standard library used by the Lint test harness not
* including the Experimental annotation by default.
*/
- val OptIn = TestFiles.kotlin(
- """
+ val OptIn =
+ TestFiles.kotlin(
+ """
package kotlin
import kotlin.annotation.AnnotationRetention.BINARY
@@ -131,13 +137,12 @@
vararg val markerClass: KClass<out Annotation>
)
"""
- )
+ )
- /**
- * [TestFile] containing ChecksSdkIntAtLeast.java from the annotation library.
- */
- val ChecksSdkIntAtLeast = TestFiles.java(
- """
+ /** [TestFile] containing ChecksSdkIntAtLeast.java from the annotation library. */
+ val ChecksSdkIntAtLeast =
+ TestFiles.java(
+ """
package androidx.annotation;
import static java.lang.annotation.ElementType.FIELD;
@@ -158,10 +163,11 @@
int lambda() default -1;
}
"""
- )
+ )
- val RequiresApi = TestFiles.java(
- """
+ val RequiresApi =
+ TestFiles.java(
+ """
package androidx.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
@@ -185,10 +191,11 @@
int api() default 1;
}
"""
- )
+ )
- val IntRange = TestFiles.java(
- """
+ val IntRange =
+ TestFiles.java(
+ """
package androidx.annotation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
@@ -210,10 +217,11 @@
long to() default Long.MAX_VALUE;
}
"""
- )
+ )
- val RestrictTo = TestFiles.java(
- """
+ val RestrictTo =
+ TestFiles.java(
+ """
package androidx.annotation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
@@ -245,27 +253,30 @@
}
}
"""
- )
+ )
- val JetBrainsAnnotations = TestFiles.kotlin(
- """
+ val JetBrainsAnnotations =
+ TestFiles.kotlin(
+ """
package org.jetbrains.annotations
annotation class NotNull
annotation class Nullable
"""
- )
+ )
- val IgnoreAnnotation = TestFiles.kotlin(
- """
+ val IgnoreAnnotation =
+ TestFiles.kotlin(
+ """
package org.junit
annotation class Ignore
"""
- )
+ )
- val DoNotInline = TestFiles.java(
- """
+ val DoNotInline =
+ TestFiles.java(
+ """
package androidx.annotation;
import static java.lang.annotation.ElementType.METHOD;
@@ -279,10 +290,11 @@
public @interface DoNotInline {
}
"""
- )
+ )
- val DeprecatedSinceApi = TestFiles.kotlin(
- """
+ val DeprecatedSinceApi =
+ TestFiles.kotlin(
+ """
package androidx.annotation
import kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS
@@ -300,23 +312,24 @@
val message: String = ""
)
"""
- )
+ )
- val JvmDefaultWithCompatibility = TestFiles.kotlin(
- """
+ val JvmDefaultWithCompatibility =
+ TestFiles.kotlin(
+ """
package kotlin.jvm
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class JvmDefaultWithCompatibility
- """.trimIndent()
- )
-
- /**
- * [TestFile] containing OptIn.kt from the AndroidX experimental annotation library.
- */
- val JetpackOptIn: TestFile = LintDetectorTest.kotlin(
"""
+ .trimIndent()
+ )
+
+ /** [TestFile] containing OptIn.kt from the AndroidX experimental annotation library. */
+ val JetpackOptIn: TestFile =
+ LintDetectorTest.kotlin(
+ """
package androidx.annotation
import kotlin.annotation.Retention
@@ -340,14 +353,16 @@
@get:Suppress("ArrayReturn")
vararg val markerClass: KClass<out Annotation>
)
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/**
* [TestFile] containing RequiresOptIn.kt from the AndroidX experimental annotation library.
*/
- val JetpackRequiresOptIn: TestFile = LintDetectorTest.kotlin(
- """
+ val JetpackRequiresOptIn: TestFile =
+ LintDetectorTest.kotlin(
+ """
package androidx.annotation
import kotlin.annotation.Retention
@@ -363,14 +378,14 @@
ERROR
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- /**
- * [TestFile] containing VisibleForTesting.kt from the AndroidX annotation library.
- */
- val VisibleForTesting: TestFile = LintDetectorTest.kotlin(
- """
+ /** [TestFile] containing VisibleForTesting.kt from the AndroidX annotation library. */
+ val VisibleForTesting: TestFile =
+ LintDetectorTest.kotlin(
+ """
package androidx.annotation
@MustBeDocumented
@@ -385,13 +400,14 @@
public const val NONE: Int = 5
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- /**
- * Contains only a few of the isAtLeastX implementations from BuildCompat for testing
- */
- val BuildCompat: TestFile = LintDetectorTest.java("""
+ /** Contains only a few of the isAtLeastX implementations from BuildCompat for testing */
+ val BuildCompat: TestFile =
+ LintDetectorTest.java(
+ """
package androidx.core.os;
import android.os.Build;
@@ -439,7 +455,9 @@
@RequiresOptIn
public @interface PrereleaseSdkCheck { }
}
- """.trimIndent())
+ """
+ .trimIndent()
+ )
/* ktlint-enable max-line-length */
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationDetectorTest.kt
index 99837b9c2..1a22ce7 100644
--- a/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationDetectorTest.kt
@@ -23,19 +23,22 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class TargetApiAnnotationDetectorTest : AbstractLintDetectorTest(
- useDetector = TargetApiAnnotationUsageDetector(),
- useIssues = listOf(TargetApiAnnotationUsageDetector.ISSUE),
-) {
+class TargetApiAnnotationDetectorTest :
+ AbstractLintDetectorTest(
+ useDetector = TargetApiAnnotationUsageDetector(),
+ useIssues = listOf(TargetApiAnnotationUsageDetector.ISSUE),
+ ) {
@Test
fun `Detection of TargetApi usage in Java sources`() {
- val input = arrayOf(
- javaSample("androidx.TargetApiUsageJava"),
- )
+ val input =
+ arrayOf(
+ javaSample("androidx.TargetApiUsageJava"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/TargetApiUsageJava.java:22: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
@TargetApi(29)
~~~~~~~~~~~~~~
@@ -43,7 +46,8 @@
@TargetApi(30)
~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
@@ -51,20 +55,23 @@
@Test
fun `Detection of TargetApi usage in Kotlin sources`() {
- val input = arrayOf(
- ktSample("androidx.TargetApiUsageKotlin"),
- )
+ val input =
+ arrayOf(
+ ktSample("androidx.TargetApiUsageKotlin"),
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/TargetApiUsageKotlin.kt:22: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
@TargetApi(29)
~~~~~~~~~~~~~~
src/androidx/TargetApiUsageKotlin.kt:25: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
- @TargetApi(30)
+ @TargetApi(30) fun someMethod() {}
~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationUsageDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationUsageDetectorTest.kt
index 2a77039..55b568b 100644
--- a/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationUsageDetectorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationUsageDetectorTest.kt
@@ -28,18 +28,14 @@
class TargetApiAnnotationUsageDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = TargetApiAnnotationUsageDetector()
- override fun getIssues(): List<Issue> = listOf(
- TargetApiAnnotationUsageDetector.ISSUE
- )
+ override fun getIssues(): List<Issue> = listOf(TargetApiAnnotationUsageDetector.ISSUE)
private fun checkTask(testFile: TestFile): TestLintTask {
- return lint().files(
- java(annotationSource),
- testFile
- )
+ return lint().files(java(annotationSource), testFile)
}
- private val annotationSource = """
+ private val annotationSource =
+ """
package android.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
@@ -55,12 +51,14 @@
public @interface TargetApi {
int value();
}
- """.trimIndent()
+ """
+ .trimIndent()
@Test
fun testAnnotationUsageJava() {
- val input = java(
- """
+ val input =
+ java(
+ """
package androidx.sample;
import android.annotation.TargetApi;
@@ -72,11 +70,13 @@
// Stub
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/sample/SampleClass.java:5: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
@TargetApi(24)
~~~~~~~~~~~~~~
@@ -84,11 +84,13 @@
@TargetApi(15)
~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
/* ktlint-disable max-line-length */
- val expectFixDiffs = """
+ val expectFixDiffs =
+ """
Fix for src/androidx/sample/SampleClass.java line 5: Replace with `@RequiresApi`:
@@ -5 +5
- @TargetApi(24)
@@ -97,19 +99,18 @@
@@ -7 +7
- @TargetApi(15)
+ @androidx.annotation.RequiresApi(15)
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- checkTask(input)
- .run()
- .expect(expected)
- .expectFixDiffs(expectFixDiffs)
+ checkTask(input).run().expect(expected).expectFixDiffs(expectFixDiffs)
}
@Test
fun testAnnotationUsageKt() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
package androidx.sample
import android.annotation.TargetApi
@@ -121,11 +122,13 @@
// Stub
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/androidx/sample/SampleClass.kt:5: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
@TargetApi(24)
~~~~~~~~~~~~~~
@@ -133,11 +136,13 @@
@TargetApi(15)
~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
/* ktlint-disable max-line-length */
- val expectFixDiffs = """
+ val expectFixDiffs =
+ """
Fix for src/androidx/sample/SampleClass.kt line 5: Replace with `@RequiresApi`:
@@ -5 +5
- @TargetApi(24)
@@ -146,12 +151,10 @@
@@ -7 +7
- @TargetApi(15)
+ @androidx.annotation.RequiresApi(15)
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
- checkTask(input)
- .run()
- .expect(expected)
- .expectFixDiffs(expectFixDiffs)
+ checkTask(input).run().expect(expected).expectFixDiffs(expectFixDiffs)
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt b/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
index f2bd9a4..1be9b28 100644
--- a/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/TestSizeAnnotationEnforcerTest.kt
@@ -29,17 +29,19 @@
class TestSizeAnnotationEnforcerTest : LintDetectorTest() {
override fun getDetector(): Detector = TestSizeAnnotationEnforcer()
- override fun getIssues(): List<Issue> = listOf(
- TestSizeAnnotationEnforcer.MISSING_TEST_SIZE_ANNOTATION,
- TestSizeAnnotationEnforcer.UNEXPECTED_TEST_SIZE_ANNOTATION,
- TestSizeAnnotationEnforcer.UNSUPPORTED_TEST_RUNNER
- )
+ override fun getIssues(): List<Issue> =
+ listOf(
+ TestSizeAnnotationEnforcer.MISSING_TEST_SIZE_ANNOTATION,
+ TestSizeAnnotationEnforcer.UNEXPECTED_TEST_SIZE_ANNOTATION,
+ TestSizeAnnotationEnforcer.UNSUPPORTED_TEST_RUNNER
+ )
@Test
fun allowJUnit4ForHostSideTests() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import org.junit.runner.RunWith
@@ -51,18 +53,20 @@
fun aTest() {}
}
"""
- ).within("src/test"),
- *StubClasses
- )
+ )
+ .within("src/test"),
+ *StubClasses
+ )
.run()
.expectClean()
}
@Test
fun noTestSizeAnnotationsForHostSideTests() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import androidx.test.filters.MediumTest
@@ -83,13 +87,14 @@
fun anotherTest() {}
}
"""
- ).within("src/test"),
- *StubClasses
- )
+ )
+ .within("src/test"),
+ *StubClasses
+ )
.run()
.expect(
/* ktlint-disable max-line-length */
-"""
+ """
src/test/androidx/foo/Test.kt:8: Error: Unexpected test size annotation [UnexpectedTestSizeAnnotation]
@MediumTest
~~~~~~~~~~~
@@ -101,9 +106,10 @@
@Test
fun failsForUnsupportedTestRunner() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import org.junit.runner.RunWith
@@ -112,9 +118,10 @@
@RunWith(JUnit4::class)
class Test
"""
- ).within("src/androidTest"),
- *StubClasses
- )
+ )
+ .within("src/androidTest"),
+ *StubClasses
+ )
.run()
.expect(
/* ktlint-disable max-line-length */
@@ -130,9 +137,10 @@
@Test
fun allowsAndroidJUnit4() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -146,18 +154,20 @@
fun test() {}
}
"""
- ).within("src/androidTest"),
- *StubClasses
- )
+ )
+ .within("src/androidTest"),
+ *StubClasses
+ )
.run()
.expectClean()
}
@Test
fun allowsParameterized() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import org.junit.runner.RunWith
@@ -169,18 +179,20 @@
fun test() {}
}
"""
- ).within("src/androidTest"),
- *StubClasses
- )
+ )
+ .within("src/androidTest"),
+ *StubClasses
+ )
.run()
.expectClean()
}
@Test
fun ignoresMissingRunWith() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
class Test {
@@ -188,17 +200,19 @@
fun test() {}
}
"""
- ).within("src/androidTest")
- )
+ )
+ .within("src/androidTest")
+ )
.run()
.expectClean()
}
@Test
fun testSizeAnnotationOnClass() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -216,18 +230,20 @@
fun notATest() {}
}
"""
- ).within("src/androidTest"),
- *StubClasses
- )
+ )
+ .within("src/androidTest"),
+ *StubClasses
+ )
.run()
.expectClean()
}
@Test
fun failsForTestMethodMissingAnnotation() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -247,9 +263,10 @@
fun notATest() {}
}
"""
- ).within("src/androidTest"),
- *StubClasses
- )
+ )
+ .within("src/androidTest"),
+ *StubClasses
+ )
.run()
.expect(
/* ktlint-disable max-line-length */
@@ -265,9 +282,10 @@
@Test
fun failsIfNoAnnotations() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -285,9 +303,10 @@
fun notATest() {}
}
"""
- ).within("src/androidTest"),
- *StubClasses
- )
+ )
+ .within("src/androidTest"),
+ *StubClasses
+ )
.run()
.expect(
/* ktlint-disable max-line-length */
@@ -306,9 +325,10 @@
@Test
fun ignoresSizeAnnotationsForParameterizedTests() {
- lint().files(
- kotlin(
- """
+ lint()
+ .files(
+ kotlin(
+ """
package androidx.foo
import androidx.test.filters.MediumTest
@@ -330,19 +350,21 @@
fun anotherTest() {}
}
"""
- ).within("src/androidTest"),
- *StubClasses
- )
+ )
+ .within("src/androidTest"),
+ *StubClasses
+ )
.run()
.expectClean()
}
- private val StubClasses = arrayOf(
- Stubs.RunWith,
- Stubs.JUnit4Runner,
- Stubs.ParameterizedRunner,
- Stubs.AndroidJUnit4Runner,
- Stubs.TestSizeAnnotations,
- Stubs.TestAnnotation
- )
+ private val StubClasses =
+ arrayOf(
+ Stubs.RunWith,
+ Stubs.JUnit4Runner,
+ Stubs.ParameterizedRunner,
+ Stubs.AndroidJUnit4Runner,
+ Stubs.TestSizeAnnotations,
+ Stubs.TestAnnotation
+ )
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorConstructorTest.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorConstructorTest.kt
index 9c66fa9..f7d2efa 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorConstructorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorConstructorTest.kt
@@ -25,25 +25,30 @@
@Test
fun constructorStaticClass() {
- val input = arrayOf(
+ val input =
+ arrayOf(
javaSample("replacewith.ReplaceWithUsageJava"),
javaSample("replacewith.ConstructorStaticClass")
- )
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/ConstructorStaticClass.java:25: Information: Replacement available [ReplaceWith]
new ReplaceWithUsageJava("parameter");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/ConstructorStaticClass.java line 25: Replace with `StringBuffer("parameter")`:
@@ -25 +25
- new ReplaceWithUsageJava("parameter");
+ new StringBuffer("parameter");
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -51,25 +56,30 @@
@Test
fun constructorNonStaticClass() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- javaSample("replacewith.ConstructorNonStaticClass")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ javaSample("replacewith.ConstructorNonStaticClass")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/ConstructorNonStaticClass.java:25: Information: Replacement available [ReplaceWith]
new ReplaceWithUsageJava().new InnerClass("param");
~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/ConstructorNonStaticClass.java line 25: Replace with `InnerClass()`:
@@ -25 +25
- new ReplaceWithUsageJava().new InnerClass("param");
+ new ReplaceWithUsageJava().new InnerClass();
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -77,25 +87,30 @@
@Test
fun constructorToStaticMethod() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- javaSample("replacewith.ConstructorToStaticMethod")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ javaSample("replacewith.ConstructorToStaticMethod")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/ConstructorToStaticMethod.java:25: Information: Replacement available [ReplaceWith]
new ReplaceWithUsageJava(10000);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/ConstructorToStaticMethod.java line 25: Replace with `ReplaceWithUsageJava.newInstance(10000)`:
@@ -25 +25
- new ReplaceWithUsageJava(10000);
+ ReplaceWithUsageJava.newInstance(10000);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -103,25 +118,30 @@
@Test
fun constructorStaticClassKotlin() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- ktSample("replacewith.ConstructorStaticClassKotlin")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ ktSample("replacewith.ConstructorStaticClassKotlin")
+ )
/* ktlint-disable max-line-length */
- val expected = """
-src/replacewith/ConstructorStaticClassKotlin.kt:24: Information: Replacement available [ReplaceWith]
+ val expected =
+ """
+src/replacewith/ConstructorStaticClassKotlin.kt:22: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava("parameter")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
-Fix for src/replacewith/ConstructorStaticClassKotlin.kt line 24: Replace with `StringBuffer("parameter")`:
-@@ -24 +24
+ val expectedFixDiffs =
+ """
+Fix for src/replacewith/ConstructorStaticClassKotlin.kt line 22: Replace with `StringBuffer("parameter")`:
+@@ -22 +22
- ReplaceWithUsageJava("parameter")
+ StringBuffer("parameter")
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -129,25 +149,30 @@
@Test
fun constructorNonStaticClassKotlin() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- ktSample("replacewith.ConstructorNonStaticClassKotlin")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ ktSample("replacewith.ConstructorNonStaticClassKotlin")
+ )
/* ktlint-disable max-line-length */
- val expected = """
-src/replacewith/ConstructorNonStaticClassKotlin.kt:24: Information: Replacement available [ReplaceWith]
+ val expected =
+ """
+src/replacewith/ConstructorNonStaticClassKotlin.kt:22: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava().InnerClass("param")
~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
-Fix for src/replacewith/ConstructorNonStaticClassKotlin.kt line 24: Replace with `InnerClass()`:
-@@ -24 +24
+ val expectedFixDiffs =
+ """
+Fix for src/replacewith/ConstructorNonStaticClassKotlin.kt line 22: Replace with `InnerClass()`:
+@@ -22 +22
- ReplaceWithUsageJava().InnerClass("param")
+ ReplaceWithUsageJava().InnerClass()
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -155,25 +180,30 @@
@Test
fun constructorToStaticMethodKotlin() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- ktSample("replacewith.ConstructorToStaticMethodKotlin")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ ktSample("replacewith.ConstructorToStaticMethodKotlin")
+ )
/* ktlint-disable max-line-length */
- val expected = """
-src/replacewith/ConstructorToStaticMethodKotlin.kt:24: Information: Replacement available [ReplaceWith]
+ val expected =
+ """
+src/replacewith/ConstructorToStaticMethodKotlin.kt:22: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava(10000)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
-Fix for src/replacewith/ConstructorToStaticMethodKotlin.kt line 24: Replace with `ReplaceWithUsageJava.newInstance(10000)`:
-@@ -24 +24
+ val expectedFixDiffs =
+ """
+Fix for src/replacewith/ConstructorToStaticMethodKotlin.kt line 22: Replace with `ReplaceWithUsageJava.newInstance(10000)`:
+@@ -22 +22
- ReplaceWithUsageJava(10000)
+ ReplaceWithUsageJava.newInstance(10000)
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorFieldTest.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorFieldTest.kt
index 61474a2..5e60b13 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorFieldTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorFieldTest.kt
@@ -16,7 +16,6 @@
package androidx.build.lint.replacewith
-import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -26,25 +25,30 @@
@Test
fun staticFieldExplicitClass() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- javaSample("replacewith.StaticFieldExplicitClass")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ javaSample("replacewith.StaticFieldExplicitClass")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/StaticFieldExplicitClass.java:25: Information: Replacement available [ReplaceWith]
System.out.println(ReplaceWithUsageJava.AUTOFILL_HINT_NAME);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/StaticFieldExplicitClass.java line 25: Replace with `View.AUTOFILL_HINT_NAME`:
@@ -25 +25
- System.out.println(ReplaceWithUsageJava.AUTOFILL_HINT_NAME);
+ System.out.println(View.AUTOFILL_HINT_NAME);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -52,25 +56,30 @@
@Test
fun staticFieldImplicitClass() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- javaSample("replacewith.StaticFieldImplicitClass")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ javaSample("replacewith.StaticFieldImplicitClass")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/StaticFieldImplicitClass.java:27: Information: Replacement available [ReplaceWith]
System.out.println(AUTOFILL_HINT_NAME);
~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/StaticFieldImplicitClass.java line 27: Replace with `View.AUTOFILL_HINT_NAME`:
@@ -27 +27
- System.out.println(AUTOFILL_HINT_NAME);
+ System.out.println(View.AUTOFILL_HINT_NAME);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorImportsTest.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorImportsTest.kt
index b705402..47eb584 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorImportsTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorImportsTest.kt
@@ -25,12 +25,11 @@
@Test
fun methodWithImportsJava() {
- val input = arrayOf(
- javaSample("replacewith.MethodWithImportsJava")
- )
+ val input = arrayOf(javaSample("replacewith.MethodWithImportsJava"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/MethodWithImportsJava.java:38: Information: Replacement available [ReplaceWith]
oldMethodSingleImport(null);
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -38,9 +37,11 @@
oldMethodMultiImport(null);
~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/MethodWithImportsJava.java line 38: Replace with `newMethod(null)`:
@@ -20 +20
+ import androidx.annotation.Deprecated;
@@ -54,7 +55,8 @@
@@ -42 +44
- oldMethodMultiImport(null);
+ newMethod(null);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -62,37 +64,42 @@
@Test
fun methodWithImportsKotlin() {
- val input = arrayOf(
- ktSample("replacewith.MethodWithImportsKotlin"),
- javaSample("replacewith.ReplaceWithUsageJava")
- )
+ val input =
+ arrayOf(
+ ktSample("replacewith.MethodWithImportsKotlin"),
+ javaSample("replacewith.ReplaceWithUsageJava")
+ )
/* ktlint-disable max-line-length */
- val expected = """
-src/replacewith/MethodWithImportsKotlin.kt:27: Information: Replacement available [ReplaceWith]
+ val expected =
+ """
+src/replacewith/MethodWithImportsKotlin.kt:25: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava.toStringWithImport("hello")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/replacewith/MethodWithImportsKotlin.kt:32: Information: Replacement available [ReplaceWith]
+src/replacewith/MethodWithImportsKotlin.kt:30: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava.toStringWithImports("world")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
-Fix for src/replacewith/MethodWithImportsKotlin.kt line 27: Replace with `"hello".toString()`:
+ val expectedFixDiffs =
+ """
+Fix for src/replacewith/MethodWithImportsKotlin.kt line 25: Replace with `"hello".toString()`:
@@ -19 +19
+ import androidx.annotation.Deprecated
-@@ -27 +28
+@@ -25 +26
- ReplaceWithUsageJava.toStringWithImport("hello")
+ "hello".toString()
-Fix for src/replacewith/MethodWithImportsKotlin.kt line 32: Replace with `"world".toString()`:
+Fix for src/replacewith/MethodWithImportsKotlin.kt line 30: Replace with `"world".toString()`:
@@ -19 +19
+ import androidx.annotation.Deprecated
+ import androidx.annotation.NonNull
-@@ -32 +34
+@@ -30 +32
- ReplaceWithUsageJava.toStringWithImports("world")
+ "world".toString()
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -100,12 +107,11 @@
@Test
fun methodWithNoImportsJava() {
- val input = arrayOf(
- javaSample("replacewith.MethodWithNoImportsJava")
- )
+ val input = arrayOf(javaSample("replacewith.MethodWithNoImportsJava"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/MethodWithNoImportsJava.java:37: Information: Replacement available [ReplaceWith]
oldMethodSingleImport(null);
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -113,9 +119,11 @@
oldMethodMultiImport(null);
~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/MethodWithNoImportsJava.java line 37: Replace with `newMethod(null)`:
@@ -19 +19
+ import androidx.annotation.Deprecated;
@@ -131,7 +139,8 @@
@@ -41 +44
- oldMethodMultiImport(null);
+ newMethod(null);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -139,39 +148,44 @@
@Test
fun methodWithNoImportsKotlin() {
- val input = arrayOf(
- ktSample("replacewith.MethodWithNoImportsKotlin"),
- javaSample("replacewith.ReplaceWithUsageJava")
- )
+ val input =
+ arrayOf(
+ ktSample("replacewith.MethodWithNoImportsKotlin"),
+ javaSample("replacewith.ReplaceWithUsageJava")
+ )
/* ktlint-disable max-line-length */
- val expected = """
-src/replacewith/MethodWithNoImportsKotlin.kt:24: Information: Replacement available [ReplaceWith]
+ val expected =
+ """
+src/replacewith/MethodWithNoImportsKotlin.kt:22: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava.toStringWithImport("hello")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/replacewith/MethodWithNoImportsKotlin.kt:28: Information: Replacement available [ReplaceWith]
+src/replacewith/MethodWithNoImportsKotlin.kt:26: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava.toStringWithImports("world")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
-Fix for src/replacewith/MethodWithNoImportsKotlin.kt line 24: Replace with `"hello".toString()`:
+ val expectedFixDiffs =
+ """
+Fix for src/replacewith/MethodWithNoImportsKotlin.kt line 22: Replace with `"hello".toString()`:
@@ -18 +18
+ import androidx.annotation.Deprecated
+
-@@ -24 +26
+@@ -22 +24
- ReplaceWithUsageJava.toStringWithImport("hello")
+ "hello".toString()
-Fix for src/replacewith/MethodWithNoImportsKotlin.kt line 28: Replace with `"world".toString()`:
+Fix for src/replacewith/MethodWithNoImportsKotlin.kt line 26: Replace with `"world".toString()`:
@@ -18 +18
+ import androidx.annotation.Deprecated
+ import androidx.annotation.NonNull
+
-@@ -28 +31
+@@ -26 +29
- ReplaceWithUsageJava.toStringWithImports("world")
+ "world".toString()
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -179,12 +193,11 @@
@Test
fun methodWithNoImportsOrPackage() {
- val input = arrayOf(
- javaSample("replacewith.MethodWithNoImportsOrPackage")
- )
+ val input = arrayOf(javaSample("replacewith.MethodWithNoImportsOrPackage"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/MethodWithNoImportsOrPackage.java:35: Information: Replacement available [ReplaceWith]
oldMethodSingleImport(null);
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -192,9 +205,11 @@
oldMethodMultiImport(null);
~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/MethodWithNoImportsOrPackage.java line 35: Replace with `newMethod(null)`:
@@ -35 +35
- oldMethodSingleImport(null);
@@ -208,7 +223,8 @@
@@ -42 +42
+ import androidx.annotation.Deprecated;
+ import androidx.annotation.NonNull;
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinConstructorTest.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinConstructorTest.kt
index 56df687..e6fcd8d 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinConstructorTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinConstructorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.build.lint.replacewith;
+package androidx.build.lint.replacewith
import org.junit.Test
import org.junit.runner.RunWith
@@ -25,20 +25,24 @@
@Test
fun constructorStaticClass() {
- val input = arrayOf(
+ val input =
+ arrayOf(
ktSample("replacewith.ReplaceWithUsageKotlin"),
javaSample("replacewith.ConstructorKotlinStaticClass")
- )
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/ConstructorKotlinStaticClass.java:25: Information: Replacement available [ReplaceWith]
new ReplaceWithUsageKotlin("parameter");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/ConstructorKotlinStaticClass.java line 25: Replace with `StringBuffer("parameter")`:
@@ -19 +19
+ import java.lang.StringBuffer;
@@ -46,7 +50,8 @@
@@ -25 +27
- new ReplaceWithUsageKotlin("parameter");
+ new StringBuffer("parameter");
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -54,25 +59,30 @@
@Test
fun constructorNonStaticClass() {
- val input = arrayOf(
- ktSample("replacewith.ReplaceWithUsageKotlin"),
- javaSample("replacewith.ConstructorKotlinNonStaticClass")
- )
+ val input =
+ arrayOf(
+ ktSample("replacewith.ReplaceWithUsageKotlin"),
+ javaSample("replacewith.ConstructorKotlinNonStaticClass")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/ConstructorKotlinNonStaticClass.java:25: Information: Replacement available [ReplaceWith]
new ReplaceWithUsageKotlin().new InnerClass("param");
~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/ConstructorKotlinNonStaticClass.java line 25: Replace with `InnerClass()`:
@@ -25 +25
- new ReplaceWithUsageKotlin().new InnerClass("param");
+ new ReplaceWithUsageKotlin().new InnerClass();
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -80,25 +90,30 @@
@Test
fun constructorToStaticMethod() {
- val input = arrayOf(
- ktSample("replacewith.ReplaceWithUsageKotlin"),
- javaSample("replacewith.ConstructorKotlinToStaticMethod")
- )
+ val input =
+ arrayOf(
+ ktSample("replacewith.ReplaceWithUsageKotlin"),
+ javaSample("replacewith.ConstructorKotlinToStaticMethod")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/ConstructorKotlinToStaticMethod.java:25: Information: Replacement available [ReplaceWith]
new ReplaceWithUsageKotlin(10000);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/ConstructorKotlinToStaticMethod.java line 25: Replace with `ReplaceWithUsageKotlin.obtain(10000)`:
@@ -25 +25
- new ReplaceWithUsageKotlin(10000);
+ ReplaceWithUsageKotlin.obtain(10000);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinMethodTest.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinMethodTest.kt
index 078eb2f..85882f4 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinMethodTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorKotlinMethodTest.kt
@@ -25,25 +25,30 @@
@Test
fun staticMethodExplicitClass() {
- val input = arrayOf(
- ktSample("replacewith.ReplaceWithUsageKotlin"),
- javaSample("replacewith.StaticKotlinMethodExplicitClassJava")
- )
+ val input =
+ arrayOf(
+ ktSample("replacewith.ReplaceWithUsageKotlin"),
+ javaSample("replacewith.StaticKotlinMethodExplicitClassJava")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/StaticKotlinMethodExplicitClassJava.java:25: Information: Replacement available [ReplaceWith]
ReplaceWithUsageKotlin.toString(this);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/StaticKotlinMethodExplicitClassJava.java line 25: Replace with `this.toString()`:
@@ -25 +25
- ReplaceWithUsageKotlin.toString(this);
+ this.toString();
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -51,15 +56,18 @@
@Test
fun staticMethodExplicitClass_withKotlinSource_hasNoWarnings() {
- val input = arrayOf(
- ktSample("replacewith.ReplaceWithUsageKotlin"),
- ktSample("replacewith.StaticKotlinMethodExplicitClassKotlin")
- )
+ val input =
+ arrayOf(
+ ktSample("replacewith.ReplaceWithUsageKotlin"),
+ ktSample("replacewith.StaticKotlinMethodExplicitClassKotlin")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
No warnings.
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected)
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorMethodTest.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorMethodTest.kt
index 33d5845..6211c62 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorMethodTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorMethodTest.kt
@@ -25,25 +25,30 @@
@Test
fun staticMethodExplicitClass() {
- val input = arrayOf(
- javaSample("replacewith.ReplaceWithUsageJava"),
- javaSample("replacewith.StaticMethodExplicitClass")
- )
+ val input =
+ arrayOf(
+ javaSample("replacewith.ReplaceWithUsageJava"),
+ javaSample("replacewith.StaticMethodExplicitClass")
+ )
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/StaticMethodExplicitClass.java:25: Information: Replacement available [ReplaceWith]
ReplaceWithUsageJava.toString(this);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/StaticMethodExplicitClass.java line 25: Replace with `this.toString()`:
@@ -25 +25
- ReplaceWithUsageJava.toString(this);
+ this.toString();
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -51,24 +56,26 @@
@Test
fun methodImplicitThis() {
- val input = arrayOf(
- javaSample("replacewith.MethodImplicitThis")
- )
+ val input = arrayOf(javaSample("replacewith.MethodImplicitThis"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/MethodImplicitThis.java:33: Information: Replacement available [ReplaceWith]
oldMethod(null);
~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/MethodImplicitThis.java line 33: Replace with `newMethod(null)`:
@@ -33 +33
- oldMethod(null);
+ newMethod(null);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
@@ -76,24 +83,26 @@
@Test
fun methodExplicitThis() {
- val input = arrayOf(
- javaSample("replacewith.MethodExplicitThis")
- )
+ val input = arrayOf(javaSample("replacewith.MethodExplicitThis"))
/* ktlint-disable max-line-length */
- val expected = """
+ val expected =
+ """
src/replacewith/MethodExplicitThis.java:33: Information: Replacement available [ReplaceWith]
this.oldMethod(null);
~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/MethodExplicitThis.java line 33: Replace with `newMethod(null)`:
@@ -33 +33
- this.oldMethod(null);
+ this.newMethod(null);
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorPropertyTest.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorPropertyTest.kt
index 7d032e1..4ac1ff0 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorPropertyTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/ReplaceWithDetectorPropertyTest.kt
@@ -25,15 +25,17 @@
@Test
fun propertyUsage_isIgnored() {
- val input = arrayOf(
- ktSample("replacewith.ReplaceWithUsageKotlin"),
- javaSample("replacewith.PropertyJava")
- )
+ val input =
+ arrayOf(
+ ktSample("replacewith.ReplaceWithUsageKotlin"),
+ javaSample("replacewith.PropertyJava")
+ )
/* ktlint-disable max-line-length */
// TODO(b/323214452): This is incomplete, but we have explicitly suppressed replacement of
// Kotlin property accessors until we can properly convert the expressions to Java.
- val expected = """
+ val expected =
+ """
src/replacewith/PropertyJava.java:42: Information: Replacement available [ReplaceWith]
clazz.setMethodDeprecated("value");
~~~~~~~~~~~~~~~~~~~
@@ -41,11 +43,13 @@
clazz.getMethodDeprecated();
~~~~~~~~~~~~~~~~~~~
0 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
// TODO(b/323214452): These are incorrect, but we can't fix them unless we parse the
// expression as a property reference and (a) convert to Java or (b) ignore them.
- val expectedFixDiffs = """
+ val expectedFixDiffs =
+ """
Fix for src/replacewith/PropertyJava.java line 42: Replace with `otherProperty = "value"`:
@@ -42 +42
- clazz.setMethodDeprecated("value");
@@ -54,7 +58,8 @@
@@ -43 +43
- clazz.getMethodDeprecated();
+ clazz.otherProperty();
- """.trimIndent()
+ """
+ .trimIndent()
/* ktlint-enable max-line-length */
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
diff --git a/lint-checks/src/test/java/androidx/build/lint/replacewith/TestUtils.kt b/lint-checks/src/test/java/androidx/build/lint/replacewith/TestUtils.kt
index ff498bb..fa99813 100644
--- a/lint-checks/src/test/java/androidx/build/lint/replacewith/TestUtils.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/replacewith/TestUtils.kt
@@ -24,45 +24,40 @@
fun check(vararg testFiles: TestFile): TestLintResult {
return TestLintTask.lint()
- .files(
- ANDROIDX_REPLACE_WITH_KT,
- ANDROIDX_ANY_THREAD_KT,
- *testFiles
- )
+ .files(ANDROIDX_REPLACE_WITH_KT, ANDROIDX_ANY_THREAD_KT, *testFiles)
.issues(ReplaceWithDetector.ISSUE)
.run()
}
-/**
- * Loads a [TestFile] from Java source code included in the JAR resources.
- */
+/** Loads a [TestFile] from Java source code included in the JAR resources. */
fun javaSample(className: String): TestFile {
return TestFiles.java(
- ReplaceWithDetectorMethodTest::class.java.getResource(
- "/java/${className.replace('.', '/')}.java"
- )!!.readText()
+ ReplaceWithDetectorMethodTest::class
+ .java
+ .getResource("/java/${className.replace('.', '/')}.java")!!
+ .readText()
)
}
-/**
- * Loads a [TestFile] from Kotlin source code included in the JAR resources.
- */
+/** Loads a [TestFile] from Kotlin source code included in the JAR resources. */
fun ktSample(className: String): TestFile {
return TestFiles.kotlin(
- ReplaceWithDetectorMethodTest::class.java.getResource(
- "/java/${className.replace('.', '/')}.kt"
- )!!.readText()
+ ReplaceWithDetectorMethodTest::class
+ .java
+ .getResource("/java/${className.replace('.', '/')}.kt")!!
+ .readText()
)
}
/**
* [TestFile] containing ReplaceWith.kt from the Annotation library.
*
- * This is a workaround for IntelliJ failing to recognize source files if they are also
- * included as resources.
+ * This is a workaround for IntelliJ failing to recognize source files if they are also included as
+ * resources.
*/
-val ANDROIDX_REPLACE_WITH_KT: TestFile = TestFiles.kotlin(
- """
+val ANDROIDX_REPLACE_WITH_KT: TestFile =
+ TestFiles.kotlin(
+ """
package androidx.annotation
@Retention(AnnotationRetention.BINARY)
@@ -86,14 +81,14 @@
val expression: String,
vararg val imports: String
)
- """.trimIndent()
-)
+ """
+ .trimIndent()
+ )
-/**
- * [TestFile] containing AnyThread.kt from the Annotation library.
- */
-val ANDROIDX_ANY_THREAD_KT: TestFile = TestFiles.kotlin(
- """
+/** [TestFile] containing AnyThread.kt from the Annotation library. */
+val ANDROIDX_ANY_THREAD_KT: TestFile =
+ TestFiles.kotlin(
+ """
package androidx.annotation
@MustBeDocumented
@@ -108,5 +103,6 @@
AnnotationTarget.VALUE_PARAMETER
)
annotation class AnyThread
- """.trimIndent()
-)
+ """
+ .trimIndent()
+ )
diff --git a/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt b/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt
index b944fbb..369f671 100644
--- a/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt
+++ b/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt
@@ -31,47 +31,53 @@
import org.jetbrains.uast.UElement
/**
- * Checks for usages of [eager APIs](https://docs.gradle.org/current/userguide/task_configuration_avoidance.html)
- * and [project isolation unsafe APIs](https://docs.gradle.org/nightly/userguide/isolated_projects.html)
+ * Checks for usages of
+ * [eager APIs](https://docs.gradle.org/current/userguide/task_configuration_avoidance.html) and
+ * [project isolation unsafe APIs](https://docs.gradle.org/nightly/userguide/isolated_projects.html)
*/
class DiscouragedGradleMethodDetector : Detector(), Detector.UastScanner {
- override fun getApplicableUastTypes(): List<Class<out UElement>> = listOf(
- UCallExpression::class.java
- )
+ override fun getApplicableUastTypes(): List<Class<out UElement>> =
+ listOf(UCallExpression::class.java)
- override fun createUastHandler(context: JavaContext): UElementHandler = object :
- UElementHandler() {
- override fun visitCallExpression(node: UCallExpression) {
- val methodName = node.methodName
- val (containingClassName, replacementMethod, issue) = REPLACEMENTS[methodName] ?: return
- val containingClass = (node.receiverType as? PsiClassType)?.resolve() ?: return
- // Check that the called method is from the expected class (or a child class) and not an
- // unrelated method with the same name).
- if (!containingClass.isInstanceOf(containingClassName)) return
+ override fun createUastHandler(context: JavaContext): UElementHandler =
+ object : UElementHandler() {
+ override fun visitCallExpression(node: UCallExpression) {
+ val methodName = node.methodName
+ val (containingClassName, replacementMethod, issue) =
+ REPLACEMENTS[methodName] ?: return
+ val containingClass = (node.receiverType as? PsiClassType)?.resolve() ?: return
+ // Check that the called method is from the expected class (or a child class) and
+ // not an
+ // unrelated method with the same name).
+ if (!containingClass.isInstanceOf(containingClassName)) return
- val fix = replacementMethod?.let {
- fix()
- .replace()
- .with(it)
- .reformat(true)
- // Don't auto-fix from the command line because the replacement methods don't
- // have the same return types, so the fixed code likely won't compile.
- .autoFix(robot = false, independent = false)
- .build()
+ val fix =
+ replacementMethod?.let {
+ fix()
+ .replace()
+ .with(it)
+ .reformat(true)
+ // Don't auto-fix from the command line because the replacement methods
+ // don't
+ // have the same return types, so the fixed code likely won't compile.
+ .autoFix(robot = false, independent = false)
+ .build()
+ }
+ val message =
+ replacementMethod?.let { "Use $it instead of $methodName" }
+ ?: "Avoid using method $methodName"
+
+ val incident =
+ Incident(context)
+ .issue(issue)
+ .location(context.getNameLocation(node))
+ .message(message)
+ .fix(fix)
+ .scope(node)
+ context.report(incident)
}
- val message = replacementMethod?.let { "Use $it instead of $methodName" }
- ?: "Avoid using method $methodName"
-
- val incident = Incident(context)
- .issue(issue)
- .location(context.getNameLocation(node))
- .message(message)
- .fix(fix)
- .scope(node)
- context.report(incident)
}
- }
/** Checks if the class is [qualifiedName] or has [qualifiedName] as a super type. */
fun PsiClass.isInstanceOf(qualifiedName: String): Boolean =
@@ -87,65 +93,74 @@
private const val NAMED_DOMAIN_OBJECT_COLLECTION =
"org.gradle.api.NamedDomainObjectCollection"
- val EAGER_CONFIGURATION_ISSUE = Issue.create(
- "EagerGradleConfiguration",
- "Avoid using eager task APIs",
- """
+ val EAGER_CONFIGURATION_ISSUE =
+ Issue.create(
+ "EagerGradleConfiguration",
+ "Avoid using eager task APIs",
+ """
Lazy APIs defer creating and configuring objects until they are needed instead of
doing unnecessary work in the configuration phase.
See https://docs.gradle.org/current/userguide/task_configuration_avoidance.html for
more details.
""",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- DiscouragedGradleMethodDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(DiscouragedGradleMethodDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
- val PROJECT_ISOLATION_ISSUE = Issue.create(
- "GradleProjectIsolation",
- "Avoid using APIs that are not project isolation safe",
- """
+ val PROJECT_ISOLATION_ISSUE =
+ Issue.create(
+ "GradleProjectIsolation",
+ "Avoid using APIs that are not project isolation safe",
+ """
Using APIs that reach out cross projects makes it not safe for Gradle project
isolation.
See https://docs.gradle.org/nightly/userguide/isolated_projects.html for
more details.
""",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- DiscouragedGradleMethodDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(DiscouragedGradleMethodDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
// A map from eager method name to the containing class of the method and the name of the
// replacement method, if there is a direct equivalent.
- private val REPLACEMENTS = mapOf(
- "all" to
- Replacement(DOMAIN_OBJECT_COLLECTION, "configureEach", EAGER_CONFIGURATION_ISSUE),
- "create" to Replacement(TASK_CONTAINER, "register", EAGER_CONFIGURATION_ISSUE),
- "findAll" to
- Replacement(NAMED_DOMAIN_OBJECT_COLLECTION, null, EAGER_CONFIGURATION_ISSUE),
- "findByName" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
- "findByPath" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
- "findProperty" to
- Replacement(PROJECT, "providers.gradleProperty", PROJECT_ISOLATION_ISSUE),
- "property" to
- Replacement(PROJECT, "providers.gradleProperty", PROJECT_ISOLATION_ISSUE),
- "iterator" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
- "get" to Replacement(TASK_PROVIDER, null, EAGER_CONFIGURATION_ISSUE),
- "getAt" to Replacement(TASK_COLLECTION, "named", EAGER_CONFIGURATION_ISSUE),
- "getByPath" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
- "getByName" to Replacement(TASK_CONTAINER, "named", EAGER_CONFIGURATION_ISSUE),
- "matching" to Replacement(TASK_COLLECTION, null, EAGER_CONFIGURATION_ISSUE),
- "replace" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
- "remove" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
- "whenTaskAdded" to
- Replacement(TASK_CONTAINER, "configureEach", EAGER_CONFIGURATION_ISSUE),
- "whenObjectAdded" to
- Replacement(DOMAIN_OBJECT_COLLECTION, "configureEach", EAGER_CONFIGURATION_ISSUE),
- )
+ private val REPLACEMENTS =
+ mapOf(
+ "all" to
+ Replacement(
+ DOMAIN_OBJECT_COLLECTION,
+ "configureEach",
+ EAGER_CONFIGURATION_ISSUE
+ ),
+ "create" to Replacement(TASK_CONTAINER, "register", EAGER_CONFIGURATION_ISSUE),
+ "findAll" to
+ Replacement(NAMED_DOMAIN_OBJECT_COLLECTION, null, EAGER_CONFIGURATION_ISSUE),
+ "findByName" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
+ "findByPath" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
+ "findProperty" to
+ Replacement(PROJECT, "providers.gradleProperty", PROJECT_ISOLATION_ISSUE),
+ "property" to
+ Replacement(PROJECT, "providers.gradleProperty", PROJECT_ISOLATION_ISSUE),
+ "iterator" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
+ "get" to Replacement(TASK_PROVIDER, null, EAGER_CONFIGURATION_ISSUE),
+ "getAt" to Replacement(TASK_COLLECTION, "named", EAGER_CONFIGURATION_ISSUE),
+ "getByPath" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
+ "getByName" to Replacement(TASK_CONTAINER, "named", EAGER_CONFIGURATION_ISSUE),
+ "matching" to Replacement(TASK_COLLECTION, null, EAGER_CONFIGURATION_ISSUE),
+ "replace" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
+ "remove" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
+ "whenTaskAdded" to
+ Replacement(TASK_CONTAINER, "configureEach", EAGER_CONFIGURATION_ISSUE),
+ "whenObjectAdded" to
+ Replacement(
+ DOMAIN_OBJECT_COLLECTION,
+ "configureEach",
+ EAGER_CONFIGURATION_ISSUE
+ ),
+ )
}
}
diff --git a/lint/lint-gradle/src/main/java/androidx/lint/gradle/GradleIssueRegistry.kt b/lint/lint-gradle/src/main/java/androidx/lint/gradle/GradleIssueRegistry.kt
index 1a5ca3e..843ef6f 100644
--- a/lint/lint-gradle/src/main/java/androidx/lint/gradle/GradleIssueRegistry.kt
+++ b/lint/lint-gradle/src/main/java/androidx/lint/gradle/GradleIssueRegistry.kt
@@ -20,25 +20,25 @@
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
-/**
- * Collection of Gradle lint check issues.
- */
+/** Collection of Gradle lint check issues. */
class GradleIssueRegistry : IssueRegistry() {
override val api = CURRENT_API
- override val issues = listOf(
- DiscouragedGradleMethodDetector.EAGER_CONFIGURATION_ISSUE,
- DiscouragedGradleMethodDetector.PROJECT_ISOLATION_ISSUE,
- InternalApiUsageDetector.INTERNAL_GRADLE_ISSUE,
- InternalApiUsageDetector.INTERNAL_AGP_ISSUE,
- WithPluginClasspathUsageDetector.ISSUE,
- WithTypeWithoutConfigureEachUsageDetector.ISSUE,
- )
+ override val issues =
+ listOf(
+ DiscouragedGradleMethodDetector.EAGER_CONFIGURATION_ISSUE,
+ DiscouragedGradleMethodDetector.PROJECT_ISOLATION_ISSUE,
+ InternalApiUsageDetector.INTERNAL_GRADLE_ISSUE,
+ InternalApiUsageDetector.INTERNAL_AGP_ISSUE,
+ WithPluginClasspathUsageDetector.ISSUE,
+ WithTypeWithoutConfigureEachUsageDetector.ISSUE,
+ )
- override val vendor = Vendor(
- // TODO: Update component (or the issue template)
- feedbackUrl = "https://issuetracker.google.com/issues/new?component=1147525",
- identifier = "androidx.lint:lint-gradle",
- vendorName = "Android Open Source Project",
- )
+ override val vendor =
+ Vendor(
+ // TODO: Update component (or the issue template)
+ feedbackUrl = "https://issuetracker.google.com/issues/new?component=1147525",
+ identifier = "androidx.lint:lint-gradle",
+ vendorName = "Android Open Source Project",
+ )
}
diff --git a/lint/lint-gradle/src/main/java/androidx/lint/gradle/InternalApiUsageDetector.kt b/lint/lint-gradle/src/main/java/androidx/lint/gradle/InternalApiUsageDetector.kt
index 5b1d9cf..91b8417 100644
--- a/lint/lint-gradle/src/main/java/androidx/lint/gradle/InternalApiUsageDetector.kt
+++ b/lint/lint-gradle/src/main/java/androidx/lint/gradle/InternalApiUsageDetector.kt
@@ -32,87 +32,90 @@
import org.jetbrains.uast.UImportStatement
class InternalApiUsageDetector : Detector(), Detector.UastScanner {
- override fun getApplicableUastTypes(): List<Class<out UElement>> = listOf(
- UImportStatement::class.java,
- )
+ override fun getApplicableUastTypes(): List<Class<out UElement>> =
+ listOf(
+ UImportStatement::class.java,
+ )
- override fun createUastHandler(context: JavaContext): UElementHandler = object :
- UElementHandler() {
- override fun visitImportStatement(node: UImportStatement) {
- if (node.importReference != null) {
- var resolved = node.resolve()
- if (resolved is PsiField) {
- resolved = resolved.containingClass
- } else if (resolved is PsiMethod) {
- resolved = resolved.containingClass
- }
+ override fun createUastHandler(context: JavaContext): UElementHandler =
+ object : UElementHandler() {
+ override fun visitImportStatement(node: UImportStatement) {
+ if (node.importReference != null) {
+ var resolved = node.resolve()
+ if (resolved is PsiField) {
+ resolved = resolved.containingClass
+ } else if (resolved is PsiMethod) {
+ resolved = resolved.containingClass
+ }
- if (resolved is PsiClass) {
- if (resolved.isInternalGradleApi()) {
- reportIncidentForNode(
- INTERNAL_GRADLE_ISSUE,
- node,
- "Avoid using internal Gradle APIs"
- )
- } else if (resolved.isInternalAgpApi()) {
- reportIncidentForNode(
- INTERNAL_AGP_ISSUE,
- node,
- "Avoid using internal Android Gradle Plugin APIs"
- )
+ if (resolved is PsiClass) {
+ if (resolved.isInternalGradleApi()) {
+ reportIncidentForNode(
+ INTERNAL_GRADLE_ISSUE,
+ node,
+ "Avoid using internal Gradle APIs"
+ )
+ } else if (resolved.isInternalAgpApi()) {
+ reportIncidentForNode(
+ INTERNAL_AGP_ISSUE,
+ node,
+ "Avoid using internal Android Gradle Plugin APIs"
+ )
+ }
}
}
}
- }
- private fun reportIncidentForNode(issue: Issue, node: UElement, message: String) {
- val incident = Incident(context)
- .issue(issue)
- .location(context.getLocation(node))
- .message(message)
- .scope(node)
- context.report(incident)
- }
+ private fun reportIncidentForNode(issue: Issue, node: UElement, message: String) {
+ val incident =
+ Incident(context)
+ .issue(issue)
+ .location(context.getLocation(node))
+ .message(message)
+ .scope(node)
+ context.report(incident)
+ }
- private fun PsiClass.isInternalGradleApi(): Boolean {
- val className = qualifiedName ?: return false
- return className.startsWith("org.gradle.") && className.contains(".internal.")
- }
+ private fun PsiClass.isInternalGradleApi(): Boolean {
+ val className = qualifiedName ?: return false
+ return className.startsWith("org.gradle.") && className.contains(".internal.")
+ }
- private fun PsiClass.isInternalAgpApi(): Boolean {
- val className = qualifiedName ?: return false
- return className.startsWith("com.android.build.") && className.contains(".internal.")
+ private fun PsiClass.isInternalAgpApi(): Boolean {
+ val className = qualifiedName ?: return false
+ return className.startsWith("com.android.build.") &&
+ className.contains(".internal.")
+ }
}
- }
companion object {
- val INTERNAL_GRADLE_ISSUE = Issue.create(
- "InternalGradleApiUsage",
- "Avoid using internal Gradle APIs",
- """
+ val INTERNAL_GRADLE_ISSUE =
+ Issue.create(
+ "InternalGradleApiUsage",
+ "Avoid using internal Gradle APIs",
+ """
Using internal APIs results in fragile plugin behavior as these types have no binary
compatibility guarantees. It is best to create a feature request to open up these
APIs if you find them useful.
""",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- InternalApiUsageDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(InternalApiUsageDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
- val INTERNAL_AGP_ISSUE = Issue.create(
- "InternalAgpApiUsage",
- "Avoid using internal Android Gradle Plugin APIs",
- """
+ val INTERNAL_AGP_ISSUE =
+ Issue.create(
+ "InternalAgpApiUsage",
+ "Avoid using internal Android Gradle Plugin APIs",
+ """
Using internal APIs results in fragile plugin behavior as these types have no binary
compatibility guarantees. It is best to create a feature request to open up these
APIs if you find them useful.
""",
- Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(
- InternalApiUsageDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(InternalApiUsageDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
- )
}
}
diff --git a/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithPluginClasspathUsageDetector.kt b/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithPluginClasspathUsageDetector.kt
index 22bcafb..2ae80ce 100644
--- a/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithPluginClasspathUsageDetector.kt
+++ b/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithPluginClasspathUsageDetector.kt
@@ -35,15 +35,17 @@
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
val evaluator = context.evaluator
- val message = "Avoid usage of GradleRunner#withPluginClasspath, which is broken. " +
- "Instead use something like https://github.com/autonomousapps/" +
- "dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
+ val message =
+ "Avoid usage of GradleRunner#withPluginClasspath, which is broken. " +
+ "Instead use something like https://github.com/autonomousapps/" +
+ "dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message(message)
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message(message)
+ .scope(node)
if (evaluator.isMemberInClass(node.resolve(), "org.gradle.testkit.runner.GradleRunner")) {
context.report(incident)
@@ -51,20 +53,23 @@
}
companion object {
- val ISSUE: Issue = Issue.create(
- id = "WithPluginClasspathUsage",
- briefDescription = "Flags usage of GradleRunner#withPluginClasspath",
- explanation = """
+ val ISSUE: Issue =
+ Issue.create(
+ id = "WithPluginClasspathUsage",
+ briefDescription = "Flags usage of GradleRunner#withPluginClasspath",
+ explanation =
+ """
This check flags usage of `GradleRunner#withPluginClasspath` in tests,
as it might lead to potential issues or it is discouraged in certain contexts.
""",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- WithPluginClasspathUsageDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ WithPluginClasspathUsageDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
)
- )
}
}
diff --git a/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetector.kt b/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetector.kt
index 614b012..3bec922 100644
--- a/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetector.kt
+++ b/lint/lint-gradle/src/main/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetector.kt
@@ -29,17 +29,21 @@
class WithTypeWithoutConfigureEachUsageDetector : Detector(), Detector.UastScanner {
override fun getApplicableMethodNames(): List<String> = listOf("withType")
+
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
val evaluator = context.evaluator
val message = "Avoid passing a closure to withType, use withType().configureEach instead"
- val incident = Incident(context)
- .issue(ISSUE)
- .location(context.getNameLocation(node))
- .message(message)
- .scope(node)
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message(message)
+ .scope(node)
- if (evaluator.isMemberInClass(node.resolve(), DOMAIN_OBJECT_COLLECTION) &&
- node.valueArgumentCount != 1) {
+ if (
+ evaluator.isMemberInClass(node.resolve(), DOMAIN_OBJECT_COLLECTION) &&
+ node.valueArgumentCount != 1
+ ) {
context.report(incident)
}
}
@@ -47,20 +51,24 @@
companion object {
private const val DOMAIN_OBJECT_COLLECTION = "org.gradle.api.DomainObjectCollection"
- val ISSUE = Issue.create(
- id = "WithTypeWithoutConfigureEach",
- briefDescription = "Flags usage of withType with a closure instead of configureEach",
- explanation = """
+ val ISSUE =
+ Issue.create(
+ id = "WithTypeWithoutConfigureEach",
+ briefDescription =
+ "Flags usage of withType with a closure instead of configureEach",
+ explanation =
+ """
Using withType with a closure directly eagerly creates task.
Using configureEach defers the creation of tasks.
""",
- category = Category.CORRECTNESS,
- priority = 5,
- severity = Severity.ERROR,
- implementation = Implementation(
- WithTypeWithoutConfigureEachUsageDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ WithTypeWithoutConfigureEachUsageDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
)
- )
}
}
diff --git a/lint/lint-gradle/src/test/java/androidx/lint/gradle/EagerConfigurationIssueTest.kt b/lint/lint-gradle/src/test/java/androidx/lint/gradle/EagerConfigurationIssueTest.kt
index 1d2864f..9f08534 100644
--- a/lint/lint-gradle/src/test/java/androidx/lint/gradle/EagerConfigurationIssueTest.kt
+++ b/lint/lint-gradle/src/test/java/androidx/lint/gradle/EagerConfigurationIssueTest.kt
@@ -21,42 +21,50 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class EagerConfigurationIssueTest : GradleLintDetectorTest(
- detector = DiscouragedGradleMethodDetector(),
- issues = listOf(DiscouragedGradleMethodDetector.EAGER_CONFIGURATION_ISSUE)
-) {
+class EagerConfigurationIssueTest :
+ GradleLintDetectorTest(
+ detector = DiscouragedGradleMethodDetector(),
+ issues = listOf(DiscouragedGradleMethodDetector.EAGER_CONFIGURATION_ISSUE)
+ ) {
@Test
fun `Test usage of TaskContainer#create`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.create("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Use register instead of create [EagerGradleConfiguration]
project.tasks.create("example")
~~~~~~
1 errors, 0 warnings
- """.trimIndent()
- val expectedFixDiffs = """
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
Fix for src/test.kt line 4: Replace with register:
@@ -4 +4
- project.tasks.create("example")
+ project.tasks.register("example")
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test usage of unrelated create method`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
interface Bar
class Foo : Bar {
fun create() = Unit
@@ -64,43 +72,51 @@
fun foo() {
Foo().create()
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
check(input).expectClean()
}
@Test
fun `Test usage of TaskContainer#getByName`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.getByName("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Use named instead of getByName [EagerGradleConfiguration]
project.tasks.getByName("example")
~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
- val expectedFixDiffs = """
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
Fix for src/test.kt line 4: Replace with named:
@@ -4 +4
- project.tasks.getByName("example")
+ project.tasks.named("example")
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test usage of DomainObjectCollection#all`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
@@ -108,29 +124,35 @@
fun configure(project: Project, action: Action<Task>) {
project.tasks.all(action)
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:6: Error: Use configureEach instead of all [EagerGradleConfiguration]
project.tasks.all(action)
~~~
1 errors, 0 warnings
- """.trimIndent()
- val expectedFixDiffs = """
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
Fix for src/test.kt line 6: Replace with configureEach:
@@ -6 +6
- project.tasks.all(action)
+ project.tasks.configureEach(action)
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test usage of TaskContainer#whenTaskAdded`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
@@ -138,29 +160,35 @@
fun configure(project: Project, action: Action<Task>) {
project.tasks.whenTaskAdded(action)
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:6: Error: Use configureEach instead of whenTaskAdded [EagerGradleConfiguration]
project.tasks.whenTaskAdded(action)
~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
- val expectedFixDiffs = """
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
Fix for src/test.kt line 6: Replace with configureEach:
@@ -6 +6
- project.tasks.whenTaskAdded(action)
+ project.tasks.configureEach(action)
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test usage of DomainObjectCollection#whenObjectAdded`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
@@ -168,249 +196,296 @@
fun configure(project: Project, action: Action<Task>) {
project.tasks.whenObjectAdded(action)
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:6: Error: Use configureEach instead of whenObjectAdded [EagerGradleConfiguration]
project.tasks.whenObjectAdded(action)
~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
- val expectedFixDiffs = """
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
Fix for src/test.kt line 6: Replace with configureEach:
@@ -6 +6
- project.tasks.whenObjectAdded(action)
+ project.tasks.configureEach(action)
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test usage of TaskCollection#getAt`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.getAt("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Use named instead of getAt [EagerGradleConfiguration]
project.tasks.getAt("example")
~~~~~
1 errors, 0 warnings
- """.trimIndent()
- val expectedFixDiffs = """
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
Fix for src/test.kt line 4: Replace with named:
@@ -4 +4
- project.tasks.getAt("example")
+ project.tasks.named("example")
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Test usage of TaskContainer#getByPath`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.getByPath("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Avoid using method getByPath [EagerGradleConfiguration]
project.tasks.getByPath("example")
~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of TaskContainer#findByName`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.findByName("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Avoid using method findByName [EagerGradleConfiguration]
project.tasks.findByName("example")
~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of TaskContainer#findByPath`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.findByPath("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Avoid using method findByPath [EagerGradleConfiguration]
project.tasks.findByPath("example")
~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of TaskContainer#replace`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.replace("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Avoid using method replace [EagerGradleConfiguration]
project.tasks.replace("example")
~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of TaskContainer#remove`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project, task: Task) {
project.tasks.remove(task)
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Avoid using method remove [EagerGradleConfiguration]
project.tasks.remove(task)
~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of TaskContainer#iterator`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.findByPath("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Avoid using method findByPath [EagerGradleConfiguration]
project.tasks.findByPath("example")
~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of NamedDomainObjectCollection#findAll`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import groovy.lang.Closure
import org.gradle.api.Project
fun configure(project: Project, closure: Closure) {
project.tasks.findAll(closure)
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:5: Error: Avoid using method findAll [EagerGradleConfiguration]
project.tasks.findAll(closure)
~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of TaskCollection#matching`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import groovy.lang.Closure
import org.gradle.api.Project
fun configure(project: Project, closure: Closure) {
project.tasks.matching(closure)
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:5: Error: Avoid using method matching [EagerGradleConfiguration]
project.tasks.matching(closure)
~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@Test
fun `Test usage of TaskProvider#get`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.register("example").get()
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Avoid using method get [EagerGradleConfiguration]
project.tasks.register("example").get()
~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
diff --git a/lint/lint-gradle/src/test/java/androidx/lint/gradle/GradleLintDetectorTest.kt b/lint/lint-gradle/src/test/java/androidx/lint/gradle/GradleLintDetectorTest.kt
index 64dff60..d8ba341 100644
--- a/lint/lint-gradle/src/test/java/androidx/lint/gradle/GradleLintDetectorTest.kt
+++ b/lint/lint-gradle/src/test/java/androidx/lint/gradle/GradleLintDetectorTest.kt
@@ -28,6 +28,7 @@
private val issues: List<Issue>
) : LintDetectorTest() {
override fun getDetector(): Detector = detector
+
override fun getIssues(): List<Issue> = issues
/** Convenience method for running a lint test over the input [files] and [STUBS]. */
diff --git a/lint/lint-gradle/src/test/java/androidx/lint/gradle/InternalApiUsageDetectorTest.kt b/lint/lint-gradle/src/test/java/androidx/lint/gradle/InternalApiUsageDetectorTest.kt
index 8f3a2e5..bd3c431 100644
--- a/lint/lint-gradle/src/test/java/androidx/lint/gradle/InternalApiUsageDetectorTest.kt
+++ b/lint/lint-gradle/src/test/java/androidx/lint/gradle/InternalApiUsageDetectorTest.kt
@@ -22,17 +22,20 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class InternalApiUsageDetectorTest : GradleLintDetectorTest(
- detector = InternalApiUsageDetector(),
- issues = listOf(
- InternalApiUsageDetector.INTERNAL_GRADLE_ISSUE,
- InternalApiUsageDetector.INTERNAL_AGP_ISSUE,
- )
-) {
+class InternalApiUsageDetectorTest :
+ GradleLintDetectorTest(
+ detector = InternalApiUsageDetector(),
+ issues =
+ listOf(
+ InternalApiUsageDetector.INTERNAL_GRADLE_ISSUE,
+ InternalApiUsageDetector.INTERNAL_AGP_ISSUE,
+ )
+ ) {
@Test
fun `Test usage of internal Gradle API`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.component.SoftwareComponent
import org.gradle.api.internal.component.SoftwareComponentInternal
@@ -43,8 +46,9 @@
}
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
lint()
.files(*STUBS, input)
@@ -57,7 +61,8 @@
import org.gradle.api.internal.component.SoftwareComponentInternal
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
)
lint()
@@ -74,17 +79,20 @@
import org.gradle.api.internal.component.SoftwareComponentInternal as IMPORT_ALIAS_2_SOFTWARECOMPONENTINTERNAL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
)
}
@Test
fun `Test usage of internal Android Gradle API`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import com.android.build.gradle.internal.lint.VariantInputs
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
lint()
.files(*STUBS, input)
@@ -97,14 +105,16 @@
import com.android.build.gradle.internal.lint.VariantInputs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
)
}
@Test
fun `Test usage of Internal annotation`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import java.io.File
import org.gradle.api.Task
import org.gradle.api.tasks.Internal
@@ -113,8 +123,9 @@
@get:Internal
val notInput: File
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
check(input).expectClean()
}
}
diff --git a/lint/lint-gradle/src/test/java/androidx/lint/gradle/ProjectIsolationIssueTest.kt b/lint/lint-gradle/src/test/java/androidx/lint/gradle/ProjectIsolationIssueTest.kt
index e2f90c0..889fcd1 100644
--- a/lint/lint-gradle/src/test/java/androidx/lint/gradle/ProjectIsolationIssueTest.kt
+++ b/lint/lint-gradle/src/test/java/androidx/lint/gradle/ProjectIsolationIssueTest.kt
@@ -21,34 +21,41 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class ProjectIsolationIssueTest : GradleLintDetectorTest(
- detector = DiscouragedGradleMethodDetector(),
- issues = listOf(DiscouragedGradleMethodDetector.PROJECT_ISOLATION_ISSUE)
-) {
+class ProjectIsolationIssueTest :
+ GradleLintDetectorTest(
+ detector = DiscouragedGradleMethodDetector(),
+ issues = listOf(DiscouragedGradleMethodDetector.PROJECT_ISOLATION_ISSUE)
+ ) {
@Test
fun `Test usage of TaskContainer#create`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.findProperty("example")
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: Use providers.gradleProperty instead of findProperty [GradleProjectIsolation]
project.findProperty("example")
~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
- val expectedFixDiffs = """
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
Fix for src/test.kt line 4: Replace with providers.gradleProperty:
@@ -4 +4
- project.findProperty("example")
+ project.providers.gradleProperty("example")
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
diff --git a/lint/lint-gradle/src/test/java/androidx/lint/gradle/Stubs.kt b/lint/lint-gradle/src/test/java/androidx/lint/gradle/Stubs.kt
index 18eb6cf..643a812 100644
--- a/lint/lint-gradle/src/test/java/androidx/lint/gradle/Stubs.kt
+++ b/lint/lint-gradle/src/test/java/androidx/lint/gradle/Stubs.kt
@@ -60,7 +60,8 @@
val value: String = ""
)
- """.trimIndent()
+ """
+ .trimIndent()
),
kotlin(
"""
@@ -68,7 +69,8 @@
interface Provider<T> {
fun get() : T
}
- """.trimIndent()
+ """
+ .trimIndent()
),
kotlin(
"""
@@ -98,21 +100,24 @@
interface Action<T>
interface Task
- """.trimIndent()
+ """
+ .trimIndent()
),
kotlin(
"""
package groovy.lang
class Closure
- """.trimIndent()
+ """
+ .trimIndent()
),
kotlin(
"""
package org.gradle.api.component
interface SoftwareComponent
- """.trimIndent()
+ """
+ .trimIndent()
),
kotlin(
"""
@@ -125,13 +130,15 @@
}
interface UsageContext
- """.trimIndent()
+ """
+ .trimIndent()
),
kotlin(
"""
package com.android.build.gradle.internal.lint
abstract class VariantInputs
- """.trimIndent()
+ """
+ .trimIndent()
),
kotlin(
"""
@@ -145,6 +152,7 @@
fun withPluginClasspath(): GradleRunner = this
fun build(): org.gradle.testkit.runner.BuildResult = TODO()
}
- """.trimIndent()
+ """
+ .trimIndent()
)
)
diff --git a/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithPluginClasspathUsageDetectorTest.kt b/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithPluginClasspathUsageDetectorTest.kt
index b3a1c1e..d489c4b 100644
--- a/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithPluginClasspathUsageDetectorTest.kt
+++ b/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithPluginClasspathUsageDetectorTest.kt
@@ -21,15 +21,17 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class WithPluginClasspathUsageDetectorTest : GradleLintDetectorTest(
- detector = WithPluginClasspathUsageDetector(),
- issues = listOf(WithPluginClasspathUsageDetector.ISSUE)
-) {
+class WithPluginClasspathUsageDetectorTest :
+ GradleLintDetectorTest(
+ detector = WithPluginClasspathUsageDetector(),
+ issues = listOf(WithPluginClasspathUsageDetector.ISSUE)
+ ) {
@Test
fun `Test withPluginClassPath usage`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.testkit.runner.GradleRunner
import java.io.File
@@ -41,19 +43,23 @@
.build()
}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
- val message = "Avoid usage of GradleRunner#withPluginClasspath, which is broken. " +
- "Instead use something like https://github.com/autonomousapps/" +
- "dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
+ val message =
+ "Avoid usage of GradleRunner#withPluginClasspath, which is broken. " +
+ "Instead use something like https://github.com/autonomousapps/" +
+ "dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
- val expected = """
+ val expected =
+ """
src/TestClass.kt:8: Error: $message [WithPluginClasspathUsage]
.withPluginClasspath()
~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
diff --git a/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetectorTest.kt b/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetectorTest.kt
index 455ad21..996f13f 100644
--- a/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetectorTest.kt
+++ b/lint/lint-gradle/src/test/java/androidx/lint/gradle/WithTypeWithoutConfigureEachUsageDetectorTest.kt
@@ -21,32 +21,36 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class WithTypeWithoutConfigureEachUsageDetectorTest : GradleLintDetectorTest(
- detector = WithTypeWithoutConfigureEachUsageDetector(),
- issues = listOf(WithTypeWithoutConfigureEachUsageDetector.ISSUE)
-
-) {
+class WithTypeWithoutConfigureEachUsageDetectorTest :
+ GradleLintDetectorTest(
+ detector = WithTypeWithoutConfigureEachUsageDetector(),
+ issues = listOf(WithTypeWithoutConfigureEachUsageDetector.ISSUE)
+ ) {
@Test
fun `Test withType Without ConfigureEach usage`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.withType(Example::class.java) {}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
val message = "Avoid passing a closure to withType, use withType().configureEach instead"
- val expected = """
+ val expected =
+ """
src/test.kt:4: Error: $message [WithTypeWithoutConfigureEach]
project.tasks.withType(Example::class.java) {}
~~~~~~~~
1 errors, 0 warnings
- """.trimIndent()
+ """
+ .trimIndent()
check(input).expect(expected)
}
@@ -54,15 +58,17 @@
@Test
fun `Test withType With ConfigureEach usage`() {
- val input = kotlin(
- """
+ val input =
+ kotlin(
+ """
import org.gradle.api.Project
fun configure(project: Project) {
project.tasks.withType(Example::class.java).configureEach {}
}
- """.trimIndent()
- )
+ """
+ .trimIndent()
+ )
check(input).expectClean()
}
}