Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ declaring done.
(same rule as `lint-baseline.xml`). To grandfather something deliberately:
`make detekt-baseline MODULE=:foo`, or dispatch `detekt_baseline.yml` against the branch — the
committed diff is the review. Test and `androidTest` sources are scanned too.
Compose-specific rules (`Compose:` block in `config/detekt/detekt.yml`) come from
[`mrmans0n/compose-rules`](https://github.com/mrmans0n/compose-rules), the actively
maintained continuation of `twitter/compose-rules` (frozen since 2023 — `slackhq/compose-lints`
is a different project, an Android Lint port, not a Detekt ruleset). Pinned to `0.4.x` because
`0.5.0+` requires Detekt 2.0.0-alpha, which would invalidate every `detekt-baseline.xml`.
`UnstableCollections`, `Material2`, `ComposableNestingDepth`, and `PreviewNaming` are
deliberately off; `ViewModelInjection` is repointed at `metroViewModel`/`assistedMetroViewModel`
since this project is on Metro, not Hilt.
- `make android-lint` is **Android Lint** (`:app:lintDebug`, checkDependencies across the whole
graph), gated by `app/lint-baseline.xml`. A string added to `values/strings.xml` without its
`values-fr` / `values-es` siblings passes every other rung and fails CI with
Expand Down
2 changes: 2 additions & 0 deletions app/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
<CurrentIssues>
<ID>EmptyFunctionBlock:FakeSplitInstallManager.kt$FakeSplitInstallManager${}</ID>
<ID>MatchingDeclarationName:LocalDataSourceTest2.kt$LocalDataSourceTest</ID>
<ID>MutableParams:DynamicFeatureNavigator.kt$backStack</ID>
<ID>RememberMissing:DynamicFeatureNavigator.kt$mutableStateOf&lt;DynamicFeatureKey?&gt;(null)</ID>
</CurrentIssues>
</SmellBaseline>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ plugins {

val libs = the<LibrariesForLibs>()

dependencies {
"detektPlugins"(libs.compose.rules.detekt)
}

configure<DetektExtension> {
toolVersion = libs.versions.detekt.get()
// Test sources are scanned too. They were excluded, so a third of the repo's Kotlin - the part
Expand Down
1 change: 1 addition & 0 deletions catalog/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ModifierMissing:MainActivity.kt$CatalogApp</ID>
<ID>SwallowedException:MainActivity.kt$e: Throwable</ID>
<ID>TooGenericExceptionCaught:MainActivity.kt$e: Throwable</ID>
</CurrentIssues>
Expand Down
89 changes: 89 additions & 0 deletions config/detekt/detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,95 @@ complexity:
LongMethod:
ignoreAnnotated: ['Composable']

Compose:
# Rules ported from twitter/compose-rules, continued at mrmans0n/compose-rules since Twitter
# stopped maintaining the original. Version pinned to the last release built against Detekt
# 1.23.x (0.5.0+ requires Detekt 2.0.0-alpha, which we are not adopting - it would invalidate
# every per-module detekt-baseline.xml).
ComposableAnnotationNaming:
active: true
ComposableNaming:
active: true
ComposableNestingDepth:
active: false # Opt-in; we don't enforce a max nesting depth today.
ComposableParamOrder:
active: true
CompositionLocalAllowlist:
active: true
allowedCompositionLocals: LocalSpacing,LocalColors,LocalTypography,LocalDebugDrawerToggle,LocalSplitInstallManager
CompositionLocalNaming:
active: true
ConditionHoist:
active: true
ContentEmitterReturningValues:
active: true
ContentTrailingLambda:
active: true
ContentSlotReused:
active: true
DefaultsVisibility:
active: true
InvalidReadOnlyComposable:
active: true
LambdaParameterEventTrailing:
active: true
LambdaParameterInRestartableEffect:
active: true
Material2:
active: false # Opt-in; the design system deliberately still uses material.icons.core (M2 icons).
MissingReadOnlyComposable:
active: true
ModifierClickableOrder:
active: true
ModifierComposed:
active: true
ModifierMissing:
active: true
ModifierNaming:
active: true
ModifierNotUsedAtRoot:
active: true
ModifierReused:
active: true
ModifierWithoutDefault:
active: true
MultipleEmitters:
active: true
MutableParams:
active: true
MutableStateAutoboxing:
active: true
MutableStateParam:
active: true
ParameterNaming:
active: true
PreviewAnnotationNaming:
active: true
PreviewNaming:
active: false # Opt-in; @LightDarkPreviews (see :core:designsystem) doesn't follow the suffix convention this expects.
PreviewPublic:
active: true
RememberContentMissing:
active: true
RememberMissing:
active: true
StaleRememberUpdatedStateInRemember:
active: true
StateParam:
active: true
UnnecessaryComposable:
active: true
UnstableCollections:
active: false # Opt-in; only relevant with strong skipping disabled, and compose-stability.conf already covers our stability gaps.
VarsWithoutStateBacking:
active: true
ViewModelForwarding:
active: true
ViewModelInjection:
active: true
# Default factory names are Hilt-shaped (hiltViewModel); this project is on Metro DI.
viewModelFactories: metroViewModel,assistedMetroViewModel

style:
WildcardImport:
excludeImports:
Expand Down
6 changes: 6 additions & 0 deletions core/designsystem/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
<ID>MagicNumber:DialogWithProgressBar.kt$500</ID>
<ID>MagicNumber:DialogWithProgressBar.kt$DialogProgressProvider$0.5f</ID>
<ID>MatchingDeclarationName:Spacing.kt$BillionBeersSpacing</ID>
<ID>ModifierComposed:ComposeExtensions.kt$noRippleClickable</ID>
<ID>ModifierMissing:CatalogDemos.kt$ColorCatalogDemo</ID>
<ID>ModifierMissing:CatalogDemos.kt$TypographyCatalogDemo</ID>
<ID>ModifierMissing:DialogWithProgressBar.kt$DialogWithProgressBarDemo</ID>
<ID>MutableStateAutoboxing:DialogWithProgressBar.kt$mutableStateOf(number)</ID>
<ID>PreviewPublic:DialogWithProgressBar.kt$DialogContentPreview</ID>
<ID>SwallowedException:ComposeExtensions.kt$e: Exception</ID>
<ID>TooGenericExceptionCaught:ComposeExtensions.kt$e: Exception</ID>
</CurrentIssues>
Expand Down
4 changes: 4 additions & 0 deletions feature/beerbrowse/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@
<ID>LongParameterList:BrowseBeersScreen.kt$( title: String, viewState: CommonUiState&lt;PagedListUiModel&lt;Beer&gt;&gt;, onBack: () -&gt; Unit, onBeerClick: (Beer) -&gt; Unit, onScrollToBottom: () -&gt; Unit, onRetryLoadMore: () -&gt; Unit, // Serves both first-page reloads: the full-screen error retry and the pull-to-refresh gesture. onRetryFirstPage: () -&gt; Unit, )</ID>
<ID>LongParameterList:BrowseHomeScreen.kt$( styles: CommonUiState&lt;List&lt;BeerStyle&gt;&gt;, breweries: CommonUiState&lt;List&lt;Brewery&gt;&gt;, selectedTab: Int, onTabSelected: (Int) -&gt; Unit, onStyleClick: (BeerStyle) -&gt; Unit, onBreweryClick: (Brewery) -&gt; Unit, onBack: () -&gt; Unit, onRetryStyles: () -&gt; Unit, onRetryBreweries: () -&gt; Unit, )</ID>
<ID>MatchingDeclarationName:BeerBrowseScreenImpl.kt$BrowseSelection</ID>
<ID>ParameterNaming:BrowseHomeScreen.kt$onTabSelected</ID>
<ID>PreviewPublic:BrowseBeersScreen.kt$BrowseBeersScreenPreview</ID>
<ID>PreviewPublic:BrowseHomeScreen.kt$BrowseHomeScreenPreview</ID>
<ID>ViewModelInjection:BrowseBeersScreen.kt$viewModel</ID>
</CurrentIssues>
</SmellBaseline>
13 changes: 13 additions & 0 deletions feature/beerdetail/detekt-baseline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ModifierMissing:BeerDetailScreen.kt$BeerDetailScreenImpl</ID>
<ID>ModifierMissing:ComposeBeerDetail.kt$ComposeBeerDetail</ID>
<ID>ModifierMissing:ComposeBeerDetail.kt$StatCard</ID>
<ID>MultipleEmitters:ComposeBeerDetail.kt$BeerDetailBulletSection</ID>
<ID>PreviewPublic:ComposeBeerDetail.kt$ComposeBeerDetailPreview</ID>
<ID>PreviewPublic:ComposeBeerDetail.kt$ComposeBeerDetailWithoutEnrichedFieldsPreview</ID>
<ID>ViewModelInjection:BeerDetailScreen.kt$viewModel</ID>
</CurrentIssues>
</SmellBaseline>
2 changes: 2 additions & 0 deletions feature/beersearch/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
<CurrentIssues>
<ID>EmptyFunctionBlock:BeersSearchViewModelTest.kt$BeersSearchViewModelTest${}</ID>
<ID>LongParameterList:BeersSearchScreen.kt$( viewState: CommonUiState&lt;PagedListUiModel&lt;Beer&gt;&gt;, query: String, onQueryChange: (String) -&gt; Unit, onBeerClick: (Beer) -&gt; Unit, onBack: () -&gt; Unit, onScrollToBottom: () -&gt; Unit, onRetryLoadMore: () -&gt; Unit, onRetrySearch: () -&gt; Unit, autoFocus: Boolean = true, )</ID>
<ID>ModifierMissing:BeersSearchScreen.kt$BeersSearchContent</ID>
<ID>PreviewPublic:BeersSearchScreen.kt$BeersSearchScreenPreview</ID>
</CurrentIssues>
</SmellBaseline>
3 changes: 3 additions & 0 deletions feature/beerslist/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>LongParameterList:BeersListScreen.kt$( viewState: CommonUiState&lt;PagedListUiModel&lt;Beer&gt;&gt;, onBeerClick: (Beer) -&gt; Unit, onSearchClick: () -&gt; Unit, onBrowseClick: () -&gt; Unit, onScrollToBottom: () -&gt; Unit, onRefresh: () -&gt; Unit, onRetry: () -&gt; Unit, onRetryLoadMore: () -&gt; Unit, )</ID>
<ID>ModifierMissing:BeersListScreen.kt$BeersListContent</ID>
<ID>ModifierMissing:BeersListScreen.kt$BeersListItemSkeleton</ID>
<ID>PreviewPublic:BeersListScreen.kt$BeersListScreenPreview</ID>
</CurrentIssues>
</SmellBaseline>
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ kotlinx-serialization-json = "1.11.0"
# Tools
spotless = "8.9.0"
detekt = "1.23.8"
compose-rules = "0.4.23"
dependency-guard = "0.5.0"
kotlinpoet = "2.3.0"

Expand Down Expand Up @@ -166,6 +167,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin
ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "com-google-devtools-ksp" }
spotless-gradlePlugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
detekt-gradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
compose-rules-detekt = { module = "io.nlopez.compose.rules:detekt", version.ref = "compose-rules" }
android-junit5-plugin = { module = "de.mannodermaus.gradle.plugins:android-junit5", version.ref = "androidJunit5" }
paparazzi-plugin = { module = "app.cash.paparazzi:paparazzi-gradle-plugin", version.ref = "paparazzi" }

Expand Down
34 changes: 34 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5891,6 +5891,22 @@
<sha256 value="15bc25b67ff0a49272b270ef2b8cffd620057ca16cb29dff7ad5661adca2ae12" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.nlopez.compose.rules" name="common-detekt" version="0.4.23">
<artifact name="common-detekt-0.4.23.jar">
<sha256 value="9e31c8c50be620f5df5b20f3ea45863d386cad79da1409e22ca2b335319bcb29" origin="Generated by Gradle"/>
</artifact>
<artifact name="common-detekt-0.4.23.module">
<sha256 value="b704954b4748cf7cdd509ee9f8705e90d476e51f2174c3d070a37f5aa5d422b5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.nlopez.compose.rules" name="detekt" version="0.4.23">
<artifact name="detekt-0.4.23.jar">
<sha256 value="e2368e9360d9daa347c09f718619d526ac9e2f3741237e1a37dffb9b6d2b7ee1" origin="Generated by Gradle"/>
</artifact>
<artifact name="detekt-0.4.23.module">
<sha256 value="06ea3832507bc2a1510c21e0fa7e619df24b1859bab7a493ec5ac6407f62d6c8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.opencensus" name="opencensus-api" version="0.31.0">
<artifact name="opencensus-api-0.31.0.jar">
<sha256 value="702ba55d78f39d55195dcf041fdfaab7a7490a9ac45013542487ed9e4d3a4d23" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -7352,6 +7368,11 @@
<sha256 value="ee66a676860eb3c713470d7df644c9e8b8d8b6c67a389eb2b9a69573eb1189f4" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-compiler-embeddable" version="2.0.20">
<artifact name="kotlin-compiler-embeddable-2.0.20.pom">
<sha256 value="597043fb8c6527f42999ca04ed352963922cd16e6988aaa52d3db32da1db859d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-compiler-embeddable" version="2.0.21">
<artifact name="kotlin-compiler-embeddable-2.0.21.jar">
<sha256 value="9fa8cdd1de0dccffe154c997d423ec6b5f53cd6d9177e3a77a9b0de03fb1bc81" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -8103,6 +8124,14 @@
<sha256 value="81fd6d181012487ee3246eff4e2bacb64b58c46e5b5aa72971a4ddf1bd1541ed" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="2.1.20">
<artifact name="kotlin-stdlib-2.1.20.jar">
<sha256 value="1bcc74e8ce84e2c25eaafde10f1248349cce3062b6e36978cbeec610db1e930a" origin="Generated by Gradle"/>
</artifact>
<artifact name="kotlin-stdlib-2.1.20.module">
<sha256 value="55d296e45445f6c88699b0896706ea9550af87ad94873dc13b65bebbd5660a6f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="2.2.0">
<artifact name="kotlin-stdlib-2.2.0.jar">
<sha256 value="65d12d85a3b865c160db9147851712a64b10dadd68b22eea22a95bf8a8670dca" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -8203,6 +8232,11 @@
<sha256 value="6f5df8af633600a6b9cfb0fcc764af3d5119f376677677b91b6aee816b1d30ab" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="2.1.20">
<artifact name="kotlin-stdlib-common-2.1.20.module">
<sha256 value="94d0ad2b2319b85c40351cf9ee7077da12dd3850392e6cf10a3fe8159fbc73f7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="2.2.10">
<artifact name="kotlin-stdlib-common-2.2.10.module">
<sha256 value="336afb512852028b1da5d5d07a6fb833cf9a797ee4b33519694d312f683227c9" origin="Generated by Gradle"/>
Expand Down
12 changes: 12 additions & 0 deletions presentation_utils/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ComposableParamOrder:ComposeErrorView.kt$ComposeErrorView</ID>
<ID>LambdaParameterInRestartableEffect:DynamicFeatureLoader.kt$onCancelled</ID>
<ID>LambdaParameterInRestartableEffect:InfiniteListHandler.kt$onLoadMore</ID>
<ID>LongParameterList:DynamicFeatureInstallerTest.kt$DynamicFeatureInstallerTest$( status: Int, sessionId: Int = SESSION_ID, errorCode: Int = SplitInstallErrorCode.NO_ERROR, downloaded: Long = 0, total: Long = 0, modules: List&lt;String&gt; = listOf(MODULE), )</ID>
<ID>MatchingDeclarationName:InfiniteListHandler.kt$ListPosition</ID>
<ID>ModifierMissing:ComposeBeersListItem.kt$BeerChip</ID>
<ID>ModifierMissing:ComposeBeersListItem.kt$BeerImage</ID>
<ID>ModifierMissing:ComposeBeersListItem.kt$ComposeBeersListItem</ID>
<ID>ModifierMissing:ComposeTitle.kt$ComposeTitle</ID>
<ID>ParameterNaming:DynamicFeatureLoader.kt$onCancelled</ID>
<ID>PreviewPublic:ComposeBeersListItem.kt$ComposeBeersListItemPreview</ID>
<ID>PreviewPublic:ComposeErrorView.kt$ComposeErrorViewPreview</ID>
<ID>PreviewPublic:ComposeTitle.kt$ComposeTitlePreview</ID>
<ID>PreviewPublic:DynamicFeatureLoader.kt$InstallFailedContentPreview</ID>
</CurrentIssues>
</SmellBaseline>
Loading