From 698cb914c8b887e004deb5ae6732dd46d11c45f4 Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:54:06 +0100 Subject: [PATCH 1/2] Fix plugin application in lint module --- build.gradle.kts | 3 --- stream-android-core-lint/build.gradle.kts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d81c14f..35bbde5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,9 +27,6 @@ streamProject { coverage { includedModules = setOf("stream-android-core") - sonarCoverageExclusions = setOf( - "**/lint/**", - ) } } diff --git a/stream-android-core-lint/build.gradle.kts b/stream-android-core-lint/build.gradle.kts index 6285265..4c1fd51 100644 --- a/stream-android-core-lint/build.gradle.kts +++ b/stream-android-core-lint/build.gradle.kts @@ -2,7 +2,7 @@ import com.vanniktech.maven.publish.JavadocJar import com.vanniktech.maven.publish.KotlinJvm plugins { - libs.plugins.stream.java.library + alias(libs.plugins.stream.java.library) alias(libs.plugins.jetbrains.kotlin.jvm) alias(libs.plugins.maven.publish) alias(libs.plugins.dokka) From 0e3d1c5ddf43f56f42295f41b83fd0e11210f75f Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:04:24 +0100 Subject: [PATCH 2/2] Apply spotless --- stream-android-core-lint/build.gradle.kts | 5 +- .../android/core/lint/StreamIssueRegistry.kt | 1 + .../detectors/ExposeAsStateFlowDetector.kt | 29 +++++---- .../lint/detectors/KeepInstanceDetector.kt | 65 ++++++++++--------- .../lint/detectors/MustBeInternalDetector.kt | 13 ++-- .../StreamApiExplicitMarkerDetector.kt | 23 +++---- .../detectors/SuspendRunCatchingDetector.kt | 11 ++-- 7 files changed, 76 insertions(+), 71 deletions(-) diff --git a/stream-android-core-lint/build.gradle.kts b/stream-android-core-lint/build.gradle.kts index 4c1fd51..f33779d 100644 --- a/stream-android-core-lint/build.gradle.kts +++ b/stream-android-core-lint/build.gradle.kts @@ -8,7 +8,6 @@ plugins { alias(libs.plugins.dokka) } - dependencies { compileOnly(libs.lint.api) compileOnly(libs.lint.checks) @@ -28,12 +27,12 @@ mavenPublishing { coordinates( groupId = io.getstream.core.Configuration.artifactGroup, artifactId = "stream-android-core-lint", - version = rootProject.version.toString() + version = rootProject.version.toString(), ) configure( KotlinJvm( javadocJar = JavadocJar.Dokka("dokkaJavadoc"), sourcesJar = true, - ) + ), ) } diff --git a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/StreamIssueRegistry.kt b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/StreamIssueRegistry.kt index ed45173..ad6da4c 100644 --- a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/StreamIssueRegistry.kt +++ b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/StreamIssueRegistry.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.getstream.android.core.lint import com.android.tools.lint.client.api.IssueRegistry diff --git a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/ExposeAsStateFlowDetector.kt b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/ExposeAsStateFlowDetector.kt index e1a27fa..434f24d 100644 --- a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/ExposeAsStateFlowDetector.kt +++ b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/ExposeAsStateFlowDetector.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.getstream.android.core.lint.detectors import com.android.tools.lint.client.api.UElementHandler @@ -229,20 +230,20 @@ class ExposeAsStateFlowDetector : Detector(), Detector.UastScanner { briefDescription = "Expose MutableStateFlow as read-only via asStateFlow()", explanation = """ - Exposing a MutableStateFlow directly (even when typed as StateFlow) allows consumers to downcast - and mutate your internal state. Wrap the internal MutableStateFlow with asStateFlow() when you - expose it. - - Problematic: - private val _state = MutableStateFlow(initial) - val state: StateFlow = _state - val state: StateFlow get() = _state - - Correct: - private val _state = MutableStateFlow(initial) - val state: StateFlow = _state.asStateFlow() - val state: StateFlow get() = _state.asStateFlow() - """ + Exposing a MutableStateFlow directly (even when typed as StateFlow) allows consumers to downcast + and mutate your internal state. Wrap the internal MutableStateFlow with asStateFlow() when you + expose it. + + Problematic: + private val _state = MutableStateFlow(initial) + val state: StateFlow = _state + val state: StateFlow get() = _state + + Correct: + private val _state = MutableStateFlow(initial) + val state: StateFlow = _state.asStateFlow() + val state: StateFlow get() = _state.asStateFlow() + """ .trimIndent(), category = Category.CORRECTNESS, priority = 7, diff --git a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/KeepInstanceDetector.kt b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/KeepInstanceDetector.kt index 100ae69..1294215 100644 --- a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/KeepInstanceDetector.kt +++ b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/KeepInstanceDetector.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.getstream.android.core.lint.detectors import com.android.tools.lint.client.api.UElementHandler @@ -373,13 +374,13 @@ class KeepInstanceDetector : Detector(), Detector.UastScanner { "Comma-separated FQNs of stateful types that must not be used as temporaries", explanation = """ - Any constructor/factory call whose expression type is the same as, or a subtype of, one of these - fully-qualified names will be treated as a stateful instance that needs to be kept (stored or returned), - not created and immediately used or discarded. + Any constructor/factory call whose expression type is the same as, or a subtype of, one of these + fully-qualified names will be treated as a stateful instance that needs to be kept (stored or returned), + not created and immediately used or discarded. - Example: - com.example.SomeType, io.getstream.video.android.core.StreamClient - """ + Example: + com.example.SomeType, io.getstream.video.android.core.StreamClient + """ .trimIndent(), ) @@ -391,32 +392,32 @@ class KeepInstanceDetector : Detector(), Detector.UastScanner { "Don’t use stateful instances as temporaries or discard them", explanation = """ - Types that expose observable or long-lived state (for example, via StateFlow) must be kept. - Creating an instance inline and immediately chaining a call, or creating it as a standalone - expression and discarding the value, can stop updates from being delivered and may leak resources. - - Flags: - • SomeType(...).state.collect { ... } // calling members on a temporary instance - • SomeType(...) // created and discarded - - How to fix: - • Store the instance in a variable and use it, or - • Return the instance from the current function. - - Correct: - val client = StreamClient(config) - client.connectionState.collect { /* ... */ } - - Problematic: - StreamClient(config).connectionState.collect { /* ... */ } - - Configuration: - You can configure which types are considered stateful via lint.xml: - - - """ + Types that expose observable or long-lived state (for example, via StateFlow) must be kept. + Creating an instance inline and immediately chaining a call, or creating it as a standalone + expression and discarding the value, can stop updates from being delivered and may leak resources. + + Flags: + • SomeType(...).state.collect { ... } // calling members on a temporary instance + • SomeType(...) // created and discarded + + How to fix: + • Store the instance in a variable and use it, or + • Return the instance from the current function. + + Correct: + val client = StreamClient(config) + client.connectionState.collect { /* ... */ } + + Problematic: + StreamClient(config).connectionState.collect { /* ... */ } + + Configuration: + You can configure which types are considered stateful via lint.xml: + + + """ .trimIndent(), category = Category.CORRECTNESS, priority = 7, diff --git a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/MustBeInternalDetector.kt b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/MustBeInternalDetector.kt index ef81c5f..a88bc91 100644 --- a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/MustBeInternalDetector.kt +++ b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/MustBeInternalDetector.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.getstream.android.core.lint.detectors import com.android.tools.lint.client.api.UElementHandler @@ -195,9 +196,9 @@ class MustBeInternalDetector : Detector(), Detector.UastScanner { "Comma-separated package **glob** patterns that represent internal packages; declarations here must not be public.", explanation = """ - Supports wildcards: '*' matches any sequence (including dots), '?' matches a single char. " + - Examples: 'io.getstream.core.internal', 'io.getstream.*.internal', 'com.example.internal*'." - """ + Supports wildcards: '*' matches any sequence (including dots), '?' matches a single char. " + + Examples: 'io.getstream.core.internal', 'io.getstream.*.internal', 'com.example.internal*'." + """ .trimIndent(), ) @@ -208,9 +209,9 @@ class MustBeInternalDetector : Detector(), Detector.UastScanner { briefDescription = "Disallow `public` in internal packages", explanation = """ - Declarations located in packages marked as internal must not be `public`. \ - Use `internal` (preferred) or `private`. - """ + Declarations located in packages marked as internal must not be `public`. \ + Use `internal` (preferred) or `private`. + """ .trimIndent(), category = Category.CORRECTNESS, priority = 7, diff --git a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/StreamApiExplicitMarkerDetector.kt b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/StreamApiExplicitMarkerDetector.kt index a396762..98384e2 100644 --- a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/StreamApiExplicitMarkerDetector.kt +++ b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/StreamApiExplicitMarkerDetector.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.getstream.android.core.lint.detectors import com.android.tools.lint.client.api.UElementHandler @@ -230,12 +231,12 @@ class StreamApiExplicitMarkerDetector : Detector(), Detector.UastScanner { description = "Comma-separated package **glob** patterns where the rule applies.", explanation = """ - Supports wildcards: '*' (any sequence) and '?' (single char). - Examples: - - 'io.getstream.android.core.api' - - 'io.getstream.android.core.*.api' - - 'io.getstream.android.*' - """ + Supports wildcards: '*' (any sequence) and '?' (single char). + Examples: + - 'io.getstream.android.core.api' + - 'io.getstream.android.core.*.api' + - 'io.getstream.android.*' + """ .trimIndent(), ) @@ -245,8 +246,8 @@ class StreamApiExplicitMarkerDetector : Detector(), Detector.UastScanner { description = "Comma-separated package **glob** patterns to exclude from the rule.", explanation = """ - Same glob syntax as 'packages'. Evaluated after includes. - """ + Same glob syntax as 'packages'. Evaluated after includes. + """ .trimIndent(), ) @@ -259,9 +260,9 @@ class StreamApiExplicitMarkerDetector : Detector(), Detector.UastScanner { "StreamApiExplicitMarkerMissing", "Public API must be explicitly marked", """ - To prevent accidental exposure, all top-level public declarations must be explicitly \ - marked as @StreamPublishedApi (allowed to leak) or @StreamInternalApi (not allowed to leak). - """ + To prevent accidental exposure, all top-level public declarations must be explicitly \ + marked as @StreamPublishedApi (allowed to leak) or @StreamInternalApi (not allowed to leak). + """ .trimIndent(), Category.CORRECTNESS, 7, diff --git a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/SuspendRunCatchingDetector.kt b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/SuspendRunCatchingDetector.kt index 00e1bbc..939f4aa 100644 --- a/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/SuspendRunCatchingDetector.kt +++ b/stream-android-core-lint/src/main/java/io/getstream/android/core/lint/detectors/SuspendRunCatchingDetector.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package io.getstream.android.core.lint.detectors import com.android.tools.lint.client.api.UElementHandler @@ -116,11 +117,11 @@ class SuspendRunCatchingDetector : Detector(), Detector.UastScanner { briefDescription = "Use runCatchingCancellable in suspend contexts", explanation = """ - Using `kotlin.runCatching { ... }` inside a suspend \ - function or a suspend lambda, cancellation is not propagated as expected. \ - Prefer `runCatchingCancellable { ... }`, which rethrows `CancellationException` while \ - still returning `Result` for other failures. - """ + Using `kotlin.runCatching { ... }` inside a suspend \ + function or a suspend lambda, cancellation is not propagated as expected. \ + Prefer `runCatchingCancellable { ... }`, which rethrows `CancellationException` while \ + still returning `Result` for other failures. + """ .trimIndent(), category = Category.CORRECTNESS, priority = 7,