Skip to content

Commit 69944c7

Browse files
authored
Publish coil-lint with coil-core. Improve coil-lint test coverage. (#3323)
1 parent 5e1ad4e commit 69944c7

File tree

4 files changed

+73
-31
lines changed

4 files changed

+73
-31
lines changed

coil-core/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,9 @@ baselineProfile {
8989
}
9090
}
9191
}
92+
93+
dependencies {
94+
lintPublish(projects.coilLint) {
95+
isTransitive = false
96+
}
97+
}

coil-lint/build.gradle.kts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import coil3.setupPublishing
22
import com.vanniktech.maven.publish.JavaLibrary
3-
import com.vanniktech.maven.publish.JavadocJar
43
import org.gradle.api.attributes.java.TargetJvmVersion
54

65
plugins {
@@ -9,11 +8,7 @@ plugins {
98
}
109

1110
setupPublishing {
12-
configure(JavaLibrary(javadocJar = JavadocJar.Empty()))
13-
}
14-
15-
kotlin {
16-
jvmToolchain(17)
11+
configure(JavaLibrary())
1712
}
1813

1914
// Lint dependencies require JVM 11+, so we must request JVM 17 compatible dependencies.
@@ -29,9 +24,9 @@ dependencies {
2924
compileOnly(libs.lint.api)
3025
compileOnly(libs.lint.checks)
3126

27+
testImplementation(libs.junit)
3228
testImplementation(libs.lint.core)
3329
testImplementation(libs.lint.tests)
34-
testImplementation(libs.junit)
3530
}
3631

3732
tasks.jar {

coil-lint/src/main/kotlin/coil3/lint/ErrorFunctionDetector.kt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ class ErrorFunctionDetector : Detector(), SourceCodeScanner {
3737
scope = node,
3838
location = context.getLocation(node),
3939
message = "Using `kotlin.error()` inside `ImageRequest.Builder`. " +
40-
"Did you mean to use Coil's `error()` extension function to set an error drawable? " +
41-
"Consider importing `coil3.request.error` instead.",
40+
"Did you mean to use Coil's `error()` extension function to set an error image? " +
41+
"If yes, import `coil3.request.error` instead.",
4242
)
4343
}
4444

@@ -72,23 +72,25 @@ class ErrorFunctionDetector : Detector(), SourceCodeScanner {
7272
val receiverType = lambdaParent.receiverType?.canonicalText
7373
val methodName = lambdaParent.methodName
7474

75-
// Check for ImageView.load { } or AsyncImage calls
76-
if (methodName == "load" || methodName == "AsyncImage" || methodName == "SubcomposeAsyncImage") {
75+
// Check for ImageView.load { }
76+
if (receiverType?.contains("ImageView") == true && methodName == "load") {
7777
return true
7878
}
7979

80-
// Check for ImageRequest.Builder { } or ImageRequest.Builder().apply { }
80+
// Check for ImageRequest.Builder { }
8181
if (receiverType?.contains("ImageRequest.Builder") == true) {
8282
return true
8383
}
84+
85+
// Check for ImageRequest.Builder().apply { }
8486
if (receiverType?.contains("ImageRequest\$Builder") == true) {
8587
return true
8688
}
8789

8890
// Check if the lambda is the trailing lambda of a function that returns/uses ImageRequest.Builder
8991
val resolvedMethod = lambdaParent.resolve()
9092
if (resolvedMethod != null) {
91-
val returnType = resolvedMethod.returnType?.canonicalText ?: ""
93+
val returnType = resolvedMethod.returnType?.canonicalText.orEmpty()
9294
if (returnType.contains("ImageRequest.Builder") || returnType.contains("ImageRequest\$Builder")) {
9395
return true
9496
}
@@ -113,17 +115,18 @@ class ErrorFunctionDetector : Detector(), SourceCodeScanner {
113115
id = "CoilErrorFunction",
114116
briefDescription = "Kotlin stdlib `error()` used inside ImageRequest.Builder",
115117
explanation = """
116-
Using Kotlin's stdlib `error()` function inside an `ImageRequest.Builder` lambda \
117-
(such as in `imageView.load { }` or `AsyncImage`) will throw an `IllegalStateException` \
118-
at runtime instead of setting an error drawable.
118+
Using Kotlin's stdlib `error()` function inside an `ImageRequest.Builder` lambda
119+
(such as in `ImageView.load { }`) will throw an `IllegalStateException`
120+
at runtime instead of setting an error image.
119121
120-
This is likely a mistake caused by IDE auto-import selecting the wrong function. \
121-
Use Coil's `error(drawable)` extension function instead to set an error placeholder.
122+
This is likely a mistake caused by not importing `coil3.request.error`.
123+
By default, the compiler will resolve the `error()`function call to
124+
`kotlin.error()` if `coil3.request.error` is not imported.
122125
123126
**Wrong:**
124127
```kotlin
125128
imageView.load(url) {
126-
error(R.drawable.error) // This is kotlin.error() - throws exception!
129+
error(R.drawable.error) // This is kotlin.error() - throws an exception!
127130
}
128131
```
129132
@@ -132,7 +135,7 @@ class ErrorFunctionDetector : Detector(), SourceCodeScanner {
132135
import coil3.request.error
133136
134137
imageView.load(url) {
135-
error(R.drawable.error) // This is coil3.request.error() - sets error drawable
138+
error(R.drawable.error) // This is coil3.request.error() - sets the error image.
136139
}
137140
```
138141
""".trimIndent(),

coil-lint/src/test/kotlin/coil3/lint/ErrorFunctionDetectorTest.kt

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import com.android.tools.lint.checks.infrastructure.TestFile
55
import com.android.tools.lint.checks.infrastructure.TestMode
66
import com.android.tools.lint.detector.api.Detector
77
import com.android.tools.lint.detector.api.Issue
8-
import org.junit.Test
98

109
class ErrorFunctionDetectorTest : LintDetectorTest() {
1110

@@ -50,7 +49,6 @@ class ErrorFunctionDetectorTest : LintDetectorTest() {
5049
""",
5150
).indented()
5251

53-
@Test
5452
fun testStdlibErrorInsideLoadBlockTriggersWarning() {
5553
lint()
5654
.allowMissingSdk()
@@ -80,7 +78,34 @@ class ErrorFunctionDetectorTest : LintDetectorTest() {
8078
.expectContains("Using kotlin.error() inside ImageRequest.Builder")
8179
}
8280

83-
@Test
81+
fun testStdlibErrorInsideLoadBlockNoImportTriggersWarning() {
82+
lint()
83+
.allowMissingSdk()
84+
.testModes(TestMode.JVM_OVERLOADS)
85+
.files(
86+
imageViewStub,
87+
coilExtensionsStub,
88+
kotlinErrorStub,
89+
kotlin(
90+
"""
91+
package test
92+
93+
import android.widget.ImageView
94+
import coil3.load
95+
96+
fun test(imageView: ImageView) {
97+
imageView.load("https://example.com/image.jpg") {
98+
error("Failed to load") // Wrong: This is kotlin.error()
99+
}
100+
}
101+
""",
102+
).indented(),
103+
)
104+
.run()
105+
.expectWarningCount(1)
106+
.expectContains("Using kotlin.error() inside ImageRequest.Builder")
107+
}
108+
84109
fun testCoilErrorExtensionDoesNotTriggerWarning() {
85110
lint()
86111
.allowMissingSdk()
@@ -108,7 +133,6 @@ class ErrorFunctionDetectorTest : LintDetectorTest() {
108133
.expectClean()
109134
}
110135

111-
@Test
112136
fun testStdlibErrorOutsideCoilContextDoesNotTriggerWarning() {
113137
lint()
114138
.allowMissingSdk()
@@ -118,11 +142,9 @@ class ErrorFunctionDetectorTest : LintDetectorTest() {
118142
"""
119143
package test
120144
121-
import kotlin.error
122-
123145
fun validateInput(input: String) {
124146
if (input.isEmpty()) {
125-
error("Input cannot be empty") // This is fine - not in Coil context
147+
error("Input cannot be empty") // This is fine - not in ImageView.load
126148
}
127149
}
128150
""",
@@ -132,7 +154,6 @@ class ErrorFunctionDetectorTest : LintDetectorTest() {
132154
.expectClean()
133155
}
134156

135-
@Test
136157
fun testStdlibErrorInRegularLambdaDoesNotTriggerWarning() {
137158
lint()
138159
.allowMissingSdk()
@@ -142,12 +163,10 @@ class ErrorFunctionDetectorTest : LintDetectorTest() {
142163
"""
143164
package test
144165
145-
import kotlin.error
146-
147166
fun process(items: List<String>) {
148167
items.forEach { item ->
149168
if (item.isEmpty()) {
150-
error("Empty item found") // This is fine - not in Coil context
169+
error("Empty item found") // This is fine - not in ImageView.load
151170
}
152171
}
153172
}
@@ -157,4 +176,23 @@ class ErrorFunctionDetectorTest : LintDetectorTest() {
157176
.run()
158177
.expectClean()
159178
}
179+
180+
fun testStdlibErrorInLoadFunctionDoesNotTriggerWarning() {
181+
lint()
182+
.allowMissingSdk()
183+
.files(
184+
kotlinErrorStub,
185+
kotlin(
186+
"""
187+
package test
188+
189+
fun load() {
190+
error("Nothing") // This is fine - not in ImageView.load
191+
}
192+
""",
193+
).indented(),
194+
)
195+
.run()
196+
.expectClean()
197+
}
160198
}

0 commit comments

Comments
 (0)