-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implement M2ApiDetector This implements an opt-in check that can be used to error against use of M2 APIs. This is intended to be useful for apps that have completed their M3 migrations or baseline existing M2 usages while completing their migrations. * Update docs/rules.md Co-authored-by: Chris Banes <chrisbanes@users.noreply.github.com> * Add setup instructions * Don't check imports since there's no file-level suppression * Add allow-list option * Doc cleanups * Implement M2ApiDetectorTest --------- Co-authored-by: Chris Banes <chrisbanes@users.noreply.github.com>
- Loading branch information
1 parent
6522969
commit 0bdba39
Showing
4 changed files
with
241 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
compose-lint-checks/src/main/java/slack/lint/compose/M2ApiDetector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright (C) 2022 Salesforce, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package slack.lint.compose | ||
|
||
import com.android.tools.lint.client.api.UElementHandler | ||
import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS | ||
import com.android.tools.lint.detector.api.Issue | ||
import com.android.tools.lint.detector.api.JavaContext | ||
import com.android.tools.lint.detector.api.Severity.IGNORE | ||
import com.android.tools.lint.detector.api.SourceCodeScanner | ||
import com.android.tools.lint.detector.api.StringOption | ||
import com.android.tools.lint.detector.api.TextFormat | ||
import com.intellij.psi.PsiNamedElement | ||
import org.jetbrains.uast.UCallExpression | ||
import org.jetbrains.uast.UElement | ||
import org.jetbrains.uast.UQualifiedReferenceExpression | ||
import org.jetbrains.uast.UResolvable | ||
import slack.lint.compose.util.OptionLoadingDetector | ||
import slack.lint.compose.util.Priorities.NORMAL | ||
import slack.lint.compose.util.StringSetLintOption | ||
import slack.lint.compose.util.sourceImplementation | ||
|
||
internal class M2ApiDetector | ||
@JvmOverloads | ||
constructor(private val allowList: StringSetLintOption = StringSetLintOption(ALLOW_LIST)) : | ||
OptionLoadingDetector(allowList), SourceCodeScanner { | ||
|
||
companion object { | ||
private const val M2Package = "androidx.compose.material" | ||
|
||
internal val ALLOW_LIST = | ||
StringOption( | ||
"allowed-m2-apis", | ||
"A comma-separated list of APIs in androidx.compose.material that should be allowed.", | ||
null, | ||
"This property should define a comma-separated list of APIs in androidx.compose.material that should be allowed." | ||
) | ||
|
||
val ISSUE = | ||
Issue.create( | ||
id = "ComposeM2Api", | ||
briefDescription = "Using a Compose M2 API is not recommended", | ||
explanation = | ||
""" | ||
Compose Material 2 (M2) is succeeded by Material 3 (M3). Please use M3 APIs. | ||
See https://slackhq.github.io/compose-lints/rules/#use-material-3 for more information. | ||
""", | ||
category = CORRECTNESS, | ||
priority = NORMAL, | ||
severity = IGNORE, | ||
implementation = sourceImplementation<M2ApiDetector>() | ||
) | ||
.setOptions(listOf(ALLOW_LIST)) | ||
} | ||
|
||
override fun getApplicableUastTypes() = | ||
listOf<Class<out UElement>>( | ||
UCallExpression::class.java, | ||
UQualifiedReferenceExpression::class.java, | ||
) | ||
|
||
override fun createUastHandler(context: JavaContext) = | ||
object : UElementHandler() { | ||
override fun visitCallExpression(node: UCallExpression) = checkNode(node) | ||
|
||
override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) = | ||
checkNode(node) | ||
|
||
private fun checkNode(node: UResolvable) { | ||
val resolved = node.resolve() ?: return | ||
val packageName = context.evaluator.getPackage(resolved)?.qualifiedName ?: return | ||
if (packageName == M2Package) { | ||
// Ignore any in the allow-list. | ||
if (resolved is PsiNamedElement && resolved.name in allowList.value) return | ||
context.report( | ||
issue = ISSUE, | ||
location = context.getLocation(node), | ||
message = ISSUE.getExplanation(TextFormat.TEXT), | ||
) | ||
} | ||
} | ||
} | ||
} |
121 changes: 121 additions & 0 deletions
121
compose-lint-checks/src/test/java/slack/lint/compose/M2ApiDetectorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// Copyright (C) 2023 Salesforce, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package slack.lint.compose | ||
|
||
import com.android.tools.lint.detector.api.Detector | ||
import com.android.tools.lint.detector.api.Issue | ||
import org.junit.Test | ||
|
||
class M2ApiDetectorTest : BaseSlackLintTest() { | ||
|
||
override fun getDetector(): Detector = M2ApiDetector() | ||
override fun getIssues(): List<Issue> = listOf(M2ApiDetector.ISSUE) | ||
|
||
private val Stubs = | ||
arrayOf( | ||
kotlin( | ||
""" | ||
package androidx.compose.material | ||
import androidx.compose.runtime.Composable | ||
@Composable | ||
fun Text(text: String) { | ||
// no-op | ||
} | ||
@Composable | ||
fun Surface(content: @Composable () -> Unit) { | ||
// no-op | ||
} | ||
object BottomNavigationDefaults { | ||
val Elevation = 8.dp | ||
} | ||
enum class BottomDrawerValue { | ||
Closed, | ||
Open, | ||
Expanded | ||
} | ||
""" | ||
.trimIndent() | ||
), | ||
kotlin( | ||
""" | ||
package androidx.compose.material.ripple | ||
import androidx.compose.runtime.Composable | ||
@Composable | ||
fun rememberRipple() | ||
""" | ||
.trimIndent() | ||
), | ||
) | ||
|
||
@Test | ||
fun smokeTest() { | ||
lint() | ||
.configureOption(M2ApiDetector.ALLOW_LIST, "Surface") | ||
.files( | ||
*Stubs, | ||
kotlin( | ||
""" | ||
import androidx.compose.material.BottomDrawerValue | ||
import androidx.compose.material.BottomNavigationDefaults | ||
import androidx.compose.material.Text | ||
import androidx.compose.material.ripple.rememberRipple | ||
import androidx.compose.runtime.Composable | ||
@Composable | ||
fun example() { | ||
Text("Hello, world!") | ||
} | ||
@Composable | ||
fun allowedExample() { | ||
Surface { | ||
} | ||
} | ||
@Composable | ||
fun composite() { | ||
Surface { | ||
val ripple = rememberRipple() | ||
Text("Hello, world!") | ||
val elevation = BottomNavigationDefaults.Elevation | ||
val drawerValue = BottomDrawerValue.Closed | ||
} | ||
} | ||
""" | ||
) | ||
.indented() | ||
) | ||
.allowCompilationErrors() | ||
.run() | ||
.expect( | ||
""" | ||
src/test.kt:9: Warning: Compose Material 2 (M2) is succeeded by Material 3 (M3). Please use M3 APIs. | ||
See https://slackhq.github.io/compose-lints/rules/#use-material-3 for more information. [ComposeM2Api] | ||
Text("Hello, world!") | ||
~~~~~~~~~~~~~~~~~~~~~ | ||
src/test.kt:23: Warning: Compose Material 2 (M2) is succeeded by Material 3 (M3). Please use M3 APIs. | ||
See https://slackhq.github.io/compose-lints/rules/#use-material-3 for more information. [ComposeM2Api] | ||
Text("Hello, world!") | ||
~~~~~~~~~~~~~~~~~~~~~ | ||
src/test.kt:24: Warning: Compose Material 2 (M2) is succeeded by Material 3 (M3). Please use M3 APIs. | ||
See https://slackhq.github.io/compose-lints/rules/#use-material-3 for more information. [ComposeM2Api] | ||
val elevation = BottomNavigationDefaults.Elevation | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
src/test.kt:25: Warning: Compose Material 2 (M2) is succeeded by Material 3 (M3). Please use M3 APIs. | ||
See https://slackhq.github.io/compose-lints/rules/#use-material-3 for more information. [ComposeM2Api] | ||
val drawerValue = BottomDrawerValue.Closed | ||
~~~~~~~~~~~~~~~~~~~~~~~~ | ||
0 errors, 4 warnings | ||
""" | ||
.trimIndent() | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters