Giter Site home page Giter Site logo

twitter / compose-rules Goto Github PK

View Code? Open in Web Editor NEW
1.3K 1.3K 83.0 2.38 MB

Static checks to aid with a healthy adoption of Compose

Home Page: https://twitter.github.io/compose-rules

License: Other

Kotlin 100.00%
android jetpack-compose ktlint static-code-analysis

compose-rules's People

Contributors

afigaliyev avatar chrisbanes avatar digitalbuddha avatar jcraane avatar kenyee avatar knanao avatar kychirp avatar manuel-martos avatar mmartosdev avatar mrmans0n avatar nuhkoca avatar paulwoitaschek avatar vincentmasselis avatar zacsweers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

compose-rules's Issues

ModifierWithoutDefault for functions without a measurable size

Is your feature request related to a problem? Please describe.
The compose API guidelines say that:

Element functions without a measurable content size (e.g. Canvas, which draws arbitrary user content in the size available) MAY require the modifier parameter and omit the default value.

Describe the solution you'd like
That the rule doesn't fail for functions without a measurable size.

Describe alternatives you've considered
Suppressing the rule in situations where there's a function without a measurable size.

I'm not sure if the Layout function falls into this category or not. Maybe a separate issue is warranted, but as an aside, should this rule apply to non public functions?

Property 'TwitterCompose' is misspelled or does not exist.

When the detektPlugin applied and rules added to the config file, CI build fails with error

Run failed with 1 invalid config property. - Property 'TwitterCompose' is misspelled or does not exist.

To Reproduce
Steps to reproduce the behavior:

  1. Apply plugin
dependencies {
            detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
            detektPlugins("com.twitter.compose.rules:detekt:$twitterComposeVersion")
        }
  1. Add compose rules to bottom of the existing detekt-config.xml
TwitterCompose:
  CompositionLocalAllowlist:
    active: true
    # You can optionally define a list of CompositionLocals that are allowed here
    # allowedCompositionLocals: LocalSomething,LocalSomethingElse
  CompositionLocalNaming:
    active: true
  ContentEmitterReturningValues:
    active: true
    # You can optionally add your own composables here
    # contentEmitters: MyComposable,MyOtherComposable
  ModifierComposable:
    active: true
  ModifierMissing:
    active: true
  ModifierReused:
    active: true
  ModifierWithoutDefault:
    active: true
  MultipleEmitters:
    active: true
    # You can optionally add your own composables here
    # contentEmitters: MyComposable,MyOtherComposable
  MutableParams:
    active: true
  ComposableNaming:
    active: true
  ComposableParamOrder:
    active: true
  PreviewNaming:
    active: true
  PreviewPublic:
    active: false
  RememberMissing:
    active: true
  UnstableCollections:
    active: true
  ViewModelForwarding:
    active: true
  ViewModelInjection:
    active: true
  1. Run ./gradlew sonarqube

Expected behavior
Project compiles and CI build success

Instead it is failing on CI and locally, note that current detekt setup is working properly after deleting the TwitterCompose rule set.

Environment
Version 1.21

* What went wrong:
Execution failed for task ':sonarqube'.
> Run failed with 1 invalid config property.
  	- Property 'TwitterCompose' is misspelled or does not exist.

Allow @Preview functions to not take Modifier arguments

In general, detekt.ModifierMissing is for sure the right approach. Where it gets a little less useful though is on @Preview methods. In general, at least in my projects, these are rarely (if ever) used from inside another @Composable.

Is there an appetite for a PR adjusting this rule to not match against methods that are also annotated with @Preview?

For instance, I'd suggest that ModifierMissing not be flagged against this method:

@Composable
@Preview(name = "Container with Single Product", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Container with Single Product", uiMode = Configuration.UI_MODE_NIGHT_YES)
fun OverviewWithSwitcherContainerWithSingleProductPreview() {
 //
} 

[Question] What's the version?

Describe the bug
What's the version? I used 0.0.1-SNAPSHOT but couldn't be found.

"com.twitter.compose.rules:detekt:0.0.1-SNAPSHOT"

Support Multipreview annotations in `ModifierMissing` rule

Is your feature request related to a problem? Please describe.

Compose now supports providing multiple preview annotations through a "proxy" annotation as documented here, and this currently trips the ModifierMissing lint since it only checks for the Preview annotation.

Describe the solution you'd like

One of these two, depending on complexity

  • For each annotation on a function, try to find the annotations present on that annotation class and search for Preview there
  • Add a Detekt input to allow providing the names of Multipreview annotations manually

Describe alternatives you've considered

🤷🏼

Additional context

Cannot choose between the following variants (runtimeElements, shadowRuntimeElements)

Describe the bug

Since library version 0.0.10, we're facing an error when running detekt through Gradle:

> Could not resolve all files for configuration ':detektPlugins'.
   > Could not resolve com.twitter.compose.rules:detekt:0.0.11.
     Required by:
         project :
      > Cannot choose between the following variants of com.twitter.compose.rules:detekt:0.0.11:
          - runtimeElements
          - shadowRuntimeElements
        All of them match the consumer attributes:
          - Variant 'runtimeElements' capability com.twitter.compose.rules:detekt:0.0.11:
              - Unmatched attributes:
                  - Provides org.gradle.category 'library' but the consumer didn't ask for it
                  - Provides org.gradle.dependency.bundling 'external' but the consumer didn't ask for it
                  - Provides org.gradle.jvm.environment 'standard-jvm' but the consumer didn't ask for it
                  - Provides org.gradle.jvm.version '11' but the consumer didn't ask for it
                  - Provides org.gradle.libraryelements 'jar' but the consumer didn't ask for it
                  - Provides org.gradle.status 'release' but the consumer didn't ask for it
                  - Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it
                  - Provides org.jetbrains.kotlin.platform.type 'jvm' but the consumer didn't ask for it
          - Variant 'shadowRuntimeElements' capability com.twitter.compose.rules:detekt:0.0.11:
              - Unmatched attributes:
                  - Provides org.gradle.category 'library' but the consumer didn't ask for it
                  - Provides org.gradle.dependency.bundling 'shadowed' but the consumer didn't ask for it
                  - Provides org.gradle.libraryelements 'jar' but the consumer didn't ask for it
                  - Provides org.gradle.status 'release' but the consumer didn't ask for it
                  - Provides org.gradle.usage 'java-runtime' but the consumer didn't ask for it

It seems like this was introduced with shadowJar. I found related issues in the ktlint (pinterest/ktlint#1114) and detekt (detekt/detekt#3743, detekt/detekt#3969, detekt/detekt#3738) repositories. I also found a temporary workaround there:

configurations.named("detektPlugins").configure { configuration ->
  configuration.attributes.attribute(
          Bundling.BUNDLING_ATTRIBUTE,
          objects.named(Bundling, Bundling.EXTERNAL)
  )
}

The detekt team solved it by not publishing the shadowRuntimeElements variant of detekt-cli anymore (detekt/detekt#3747).

To Reproduce
Steps to reproduce the behavior:

  1. use version 0.0.10/0.0.11
  2. run ./gradlew detekt
  3. see error

Fixing "PreviewPublic" reports lead to violations of Detekt's "UnusedPrivateMember" Rule

Describe the bug
The PreviewPublic rule here checks that @Preview methods are not public. This is valid, and makes sense - they shouldn't be used outside the IDE in general, so the rule makes sense.

To fix it, simply mark the @Preview method private - solved. The issue is that Detekt itself has a rule that enforces that private methods are actually used - UnusedPrivateMember (docs). That rule allows a regex to allow certain method names to pass - so we could ensure that all our preview methods start with "Preview" or similar, but since they're already annotated that seems duplicative.

Expected behavior
Ideally, we could have both rules active. In reality, I don't think we can ensure that UnusedPrivateMember isn't triggered when the fix would trigger PreviewPublic.

We could fix this in detekt by adding an annotation regex alongside the method name regex. I opened a suggestion on Detekt to solve this on their end, but I wanted to also report it here because this is the rule that's new and causing the conflict.

ComposableNaming is not configurable in Detekt

This is a followup from #104

I've tried all of these different configurations for the new functionality and none of them appear to work. It seems to me that this rule is somehow not configurable in its release. Did I miss something in my PR?

TwitterCompose:
  # Not top level
  allowedComposableFunctionNames: ['.*Presenter']
  # ComposeNaming and ComposableNaming are both used, so tried configuring both without success
  ComposeNaming:
    active: true
    allowedComposableFunctionNames: ['.*Presenter']
  ComposableNaming:
    active: true
    allowedComposableFunctionNames: ['.*Presenter']

Repro: slackhq/circuit#225

Package rules as Detekt plugin (skinny)

Is your feature request related to a problem? Please describe.
While running CI/CD, one of the steps is to download latest version of Detekt jar file (~60MB) from the official github Detekt page. This jar is used to run Detekt rules. If we want to also use compose-rules, it is needed to download another ~60MB jar file which mainly consists of already available rules from the first jar (this file cannot be run on its own, it is still provided as a plugin). So it seems that there is a download waste which could be avoided.

Describe the solution you'd like
Package rules as skinny jar, similar as detekt-formatting plugin is doing

Thanks

Publish detekt jar

Context
The project I work uses Detekt via CLI. We use the CLI because is faster when working with Git Hooks - we don't need to invoke Gradle and configure 500 modules to just analyze files statically during a commit.

Feature request
Publish detekt jar in order to allow detekt cli users to use your compose-rules plugin.

Describe the solution you'd like
Loved your compose rules and really want to use them, but I'd need a jar to call it like this:

./.detekt/detekt-cli-$VERSION/bin/detekt-cli \
    --config quality/detekt/default-config.yml \
    --plugins .detekt/detekt-formatting-$VERSION.jar,.detekt/custom-detekt-rules.jar,.detekt/compose-rules-$COMPOSE_RULE_VERSION.jar \

Our custom-detekt-rules.jar is built like this: ./gradlew :detekt:custom-detekt-rules:build, so I thought maybe you could add something like this on the workflow and publish it on the GitHub assets on the release.

Add lint target

Is your feature request related to a problem? Please describe.
Same as we support ktlint and detekt via sharing detectors that work purely at the Psi level, we could have another target/wrapper for lint.

Describe the solution you'd like
A lint artifact with the same set of rules.

Describe alternatives you've considered
N/A

Additional context
AFAICS the :rules:common code should be able to work on lint as well.

ModifierMissing with multi-previews

Describe the bug

Looks like the ModifierMissing rule is not able to detect multipreview usage, so it flags those previews as missing modifiers. If there's a technical issue with detecting it, maybe we can at least have an exclusion by function name. Maybe by regex or even just assume that previews end with "Preview".

Discussion: How should linter requests be handled that should rather be provided by Google?

I have some linter requests that should probably be implemented by Google, because they don't fall in a "best-practice" but rather in a "issue" category, such as

I currently wouldn't know how to contribute to their linter. Also, I am not sure when or if at al they will implement those linter rules. So because of that: Should this repo implement them then, or not?

Preview composables should not be public doesn't work for @preview function without @PreviewParameter

Describe the bug
According to Preview composables should not be public, a preview function should give us a lint error/warning. However, it doesn't.

To Reproduce
Steps to reproduce the behavior:

@Preview
@Composable
fun Preview() {
    MaterialTheme {
        TestScreen()
    }
}

Expected behavior
It should have a lint error/warning for a public preview function.

Screenshots
2023-05-03-3-59-17

2023-05-03-4-01-17

Environment
lib version: detekt-twitter-compose-0.0.26-all.jar
Android Studio: Android Studio Giraffe | 2022.3.1 Beta 1
OS: macOs Ventura 13.3.1

Additional context
Only a preview function with @PreviewParameter will have a lint error/warning.

https://github.com/twitter/compose-rules/blob/main/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposePreviewPublic.kt#L22-L28
Just guessing, it might have the intention to do this. According to Preview composables should not be public, it doesn't describe clearly. Either one might be wrong 🤔

ModifierMissing rule does not sees GlanceModifier

Describe the bug
ModifierMissing gives false positive alert for GlanceAppWidget

To Reproduce

class FirstGlanceWidget : GlanceAppWidget {
...
    @Composable
    fun SmallWidget(
        modifier: GlanceModifier = GlanceModifier,
    )
...
}

Expected behavior
No alert

Publish a new GitHub release only when the artifact is publicly available on MavenCentral

First of all, thank you for sharing these rules, I really appreciate it!

Is your feature request related to a problem? Please describe.
For the last couple of releases, the GitHub release entry was added before the artifact was actually available on Maven. This is a little annoying because the notification of a new version is sent by GitHub but we can't actually upgrade to it because the artifact is not there. This happened with v0.0.6 and it is still happening with v0.0.9.

Describe the solution you'd like
Would be better to publish the GitHub release only after the artifact is actually available on Maven.

Describe alternatives you've considered
The alternative solution is for me to manually check if the artifact is available and update when it is but this is a little annoying because, if the release notification would be sent after it is published, we could just upgrade without the need to any manual check.

Allow contribution of your own content emitters

Is your feature request related to a problem? Please describe.
Currently there is a hardcoded list of content emitters (from Compose, Material and a bunch of Twitter-specific ones)

Describe the solution you'd like
We should be able to provide an external list of composables via .editoconfig / detekt.yml

Describe alternatives you've considered
N/A

Additional context
Current list is here: https://github.com/twitter/compose-rules/blob/main/core-common/src/main/kotlin/com/twitter/rules/core/util/Composables.kt#L29

We should probably need two separate solutions, one for each supported linter.

A failure occurred while executing org.jlleitschuh.gradle.ktlint.worker.KtLintWorkAction > com/pinterest/ktlint/core/RuleSetProviderV2.

After adding twitter/compose-rules to the ktlint-gradle using the ktlintRuleset definition for the subprojects in the top level build.gradle.kts, while running the ./gradlew ktlintCheck, getting this issue:

A failure occurred while executing org.jlleitschuh.gradle.ktlint.worker.KtLintWorkAction
   > com/pinterest/ktlint/core/RuleSetProviderV2

Stacktrace

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:runKtlintFormatOverKotlinScripts'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:145)
        at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:143)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:131)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
        at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:402)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:389)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:382)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:368)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:61)
        at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing org.jlleitschuh.gradle.ktlint.worker.KtLintWorkAction
        at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:342)
        at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:142)
        at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:94)
        at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:80)
        at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:68)
        at org.gradle.api.internal.tasks.execution.TaskExecution$2.run(TaskExecution.java:247)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
        at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:224)
        at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:207)
        at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:190)
        at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:168)
        at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
        at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
        at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
        at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
        at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:50)
        at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:40)
        at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
        at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
        at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48)
        at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
        at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
        at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
        at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
        at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
        at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
        at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:61)
        at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:42)
        at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:60)
        at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:27)
        at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:188)
        at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:75)
        at org.gradle.internal.Either$Right.fold(Either.java:175)
        at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:59)
        at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:73)
        at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:48)
        at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:38)
        at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:27)
        at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
        at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
        at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:109)
        at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:56)
        at java.base/java.util.Optional.orElseGet(Optional.java:369)
        at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:56)
        at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38)
        at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:73)
        at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:44)
        at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
        at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
        at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:89)
        at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:50)
        at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:114)
        at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:57)
        at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:76)
        at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:50)
        at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:93)
        at java.base/java.util.Optional.orElseGet(Optional.java:369)
        at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:93)
        at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:34)
        at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
        at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:43)
        at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:31)
        at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
        at org.gradle.api.internal.tasks.execution.TaskExecution$3.withWorkspace(TaskExecution.java:284)
        at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
        at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
        at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
        at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
        at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
        at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:33)
        at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:142)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:131)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
        at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
        at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
        at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:402)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:389)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:382)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:368)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
        at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:61)
        at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.NoClassDefFoundError: com/pinterest/ktlint/core/RuleSetProviderV2
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
        at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
        at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:550)
        at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:458)
        at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:452)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:451)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.nextProviderClass(ServiceLoader.java:1210)
        at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1221)
        at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1265)
        at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1300)
        at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1385)
        at org.jlleitschuh.gradle.ktlint.worker.KtLintWorkAction.loadRuleSetsFromClasspath(KtLintWorkAction.kt:224)
        at org.jlleitschuh.gradle.ktlint.worker.KtLintWorkAction.loadRuleSetsAndFilterThem(KtLintWorkAction.kt:136)
        at org.jlleitschuh.gradle.ktlint.worker.KtLintWorkAction.execute(KtLintWorkAction.kt:35)
        at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
        at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:49)
        at org.gradle.workers.internal.AbstractClassLoaderWorker$1.create(AbstractClassLoaderWorker.java:43)
        at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:97)
        at org.gradle.workers.internal.AbstractClassLoaderWorker.executeInClassLoader(AbstractClassLoaderWorker.java:43)
        at org.gradle.workers.internal.IsolatedClassloaderWorker.run(IsolatedClassloaderWorker.java:49)
        at org.gradle.workers.internal.IsolatedClassloaderWorker.run(IsolatedClassloaderWorker.java:30)
        at org.gradle.workers.internal.WorkerDaemonServer.run(WorkerDaemonServer.java:85)
        at org.gradle.workers.internal.WorkerDaemonServer.run(WorkerDaemonServer.java:55)
        at org.gradle.process.internal.worker.request.WorkerAction$1.call(WorkerAction.java:138)
        at org.gradle.process.internal.worker.child.WorkerLogEventListener.withWorkerLoggingProtocol(WorkerLogEventListener.java:41)
        at org.gradle.process.internal.worker.request.WorkerAction.run(WorkerAction.java:135)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
        at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
        at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
        at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:61)
        ... 1 more
Caused by: java.lang.ClassNotFoundException: com.pinterest.ktlint.core.RuleSetProviderV2
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        ... 47 more

Additional context

  1. Got the same issue raised in JLLeitschuh/ktlint-gradle as well.

ViewModelForwarding: false positive when using a VM as key for LaunchedEffect?

Describe the bug
I'm not 100% sure but I think I'm getting a false positive if I use a VM as key of a LaunchedEffect or DisposableEffect.

To Reproduce

@Composable
fun MyComposable(viewModel: MyComposableViewModel = hiltViewModel()) {
    LaunchedEffect(viewModel) {
        viewModel.onUiEvent(Event.OnViewAttached)
    }
}

Expected behavior
No ViewModelForwarding violation is raised when using a VM as parameter for LaunchedEffect() or DisposableEffect.

Additional context
I'm getting this with v0.0.5 (the latest v0.0.6 is not available on maven yet).

Artifact requires JDK 17

I tried to apply this artifact as follows

buildscript {
    dependencies {
        classpath("com.twitter.compose.rules:ktlint:0.0.9")
    }
}

because I'm using the Kotlinter Gradle plugin. It seems the artifact requires JDK 17, however in this Android project I use JDK 11.

A problem occurred configuring root project 'android'.
> Could not resolve all files for configuration ':classpath'.
   > Could not resolve com.twitter.compose.rules:ktlint:0.0.9.
     Required by:
         project :
      > No matching variant of com.twitter.compose.rules:ktlint:0.0.9 was found. The consumer was configured to find a runtime of a library compatible with Java 11, packaged as a jar, and its dependencies declared externally, as well as attribute 'org.gradle.plugin.api-version' with value '7.5.1' but:
          - Variant 'apiElements' capability com.twitter.compose.rules:ktlint:0.0.9 declares a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares an API of a component compatible with Java 17 and the consumer needed a runtime of a component compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '7.5.1')
          - Variant 'runtimeElements' capability com.twitter.compose.rules:ktlint:0.0.9 declares a runtime of a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component compatible with Java 17 and the consumer needed a component compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '7.5.1')

When running the project with JDK 17 everything works fine. However since Java 11 is currently the highest supported Java language version for Android development, I assume a lot of Android devs use JDK 11 at most.

It would be nice if this artifact works with JDK 11 🙏🏼 Thank you 😃

Could not found required artefacts

Describe the bug
When declaring a dependency on detektPlugins "com.twitter.compose.rules:detekt:0.0.2", the following error occurs after running 'gradle app:detekt':

`
Execution failed for task ':app:detekt'.

Could not resolve all files for configuration ':app:detektPlugins'.
Could not find com.twitter.compose.rules.core:common:0.0.2.
Searched in the following locations:
- https://dl.google.com/dl/android/maven2/com/twitter/compose/rules/core/common/0.0.2/common-0.0.2.pom
- https://repo.maven.apache.org/maven2/com/twitter/compose/rules/core/common/0.0.2/common-0.0.2.pom
- Output omitted
`

Expected behavior
Detekt runs with the Twitter compose rules applied.

False positives on expect / actual composable functions

I'm currently developing a Kotlin Multiplatform application with a shared Compose UI for the Android and JVM platforms. Of course several Composables require platform-specific code so I'm using the expect / actual mechanism here. Some rules don't handle this correctly at the moment.

@Composable
expect fun PlatformSpecifcComposable(modifier: Modifier = Modifier)

@Composable
actual fun PlatformSpecifcComposable(modifier: Modifier) {
}

For instance on actual fun PlatformSpecifcComposable(modifier: Modifier) I now get the error

[twitter-compose:modifier-without-default-check] This @Composable function has a modifier parameter but it doesn't have a default value

Default values can only be specified at the expect level. Maybe other rules are also affected?

Twitter Compose Rules version 0.0.14.

modifier-missing-check: Wrong report for fake content emitters

Currently, the modifier-missing-check rule shows an error for things that are technically content emitters but don't emit real content in terms that the content is a different window.

In the following example it does not really make sense to supply a modifier because the composable is really encapsulating it's contents.

@Composable
public fun MyDialog() {
  AlertDialog(
    onDismissRequest = { /*TODO*/ },
    buttons = { Text(text = "Button") },
    text = { Text(text = "Body") },
  )
}

The same applies for composables like ModalBottomSheetLayout.

One solution could be be to extend the current rule logic and check if the function has a single direct content emitter which is one of these window-style content emitters.

Tested with 0.0.22

ModifierWithoutDefault and overriden functions

I've seen that composable functions with Modifier param in interface doesn't need default value, but any code that extends that interface is flagged by this rule. For instance:

interface Bleh {
    @Composable
    fun Something(modifier: Modifier)
}

class BlehImpl : Bleh {
    @Composable
    override fun Something(modifier: Modifier) {}
}

is flagged and the only solution for it is to add an exception in detekt baseline file. Shouldn't be nice to add and exception for this scenario?

lint worker execution error

Trying to apply rule set with kotlinter (latest 3.13.0).

While running check I'm getting following errors:

lint worker execution error
java.lang.NoClassDefFoundError: Could not initialize class org.jmailen.gradle.kotlinter.support.RuleSetsKt
	at org.jmailen.gradle.kotlinter.tasks.lint.LintWorkerAction.execute(LintWorkerAction.kt:43)
...

This does not happen while using non-forked original classpath "com.twitter.compose.rules:ktlint:<version>"

Maven doesn't contains any artifact for the version 0.0.11

Describe the bug
I'm unable to perform a gradle sync for the version 0.0.11 while 0.0.10 works well

To Reproduce
Add this line detektPlugins "com.twitter.compose.rules:detekt:0.0.11" into your dependencies lamba

Expected behavior
A successful gradle sync

Stacktrace

10: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':core:test:detekt'.
> Could not resolve all files for configuration ':core:test:detektPlugins'.
   > Could not find com.twitter.compose.rules:detekt:0.0.11.
     Searched in the following locations:
       - https://dl.google.com/dl/android/maven2/com/twitter/compose/rules/detekt/0.0.11/detekt-0.0.11.pom
       - https://repo.maven.apache.org/maven2/com/twitter/compose/rules/detekt/0.0.11/detekt-0.0.11.pom
     Required by:
         project :core:test

Environment

  • Gradle 7.5
  • AGP 7.3.0
  • Kotlin 1.7.10
  • Compose 1.2.1
  • Compose compiler 1.3.0

Allow reporting PreviewPublic for @Preview Composables without any param

Currently the PreviewPublic rule only reports Composable functions that are annotated with @Preview and have preview params.
In our codebase we always wrap the actual Composables into separate Preview Composables (e.g. to set a specific theme). Therefore Composables annotated with@Preview are solely used as previews and should never be used in actual UI. Currently this rule won't report these functions.

Would be nice to allow configuring this rule, so that also Composable functions annotated with @Preview and without any params are reported.

Feature Request: Allow specifying an optional pattern for `ComposableNaming`

Is your feature request related to a problem? Please describe.
In our architecture we can define presenter functions that return State objects. These tend to be define similarly to how Molecule does it, where the function acts as business logic. This fails the naming check however.

@Composable
fun ProfilePresenter(
  userFlow: Flow<User>,
  balanceFlow: Flow<Long>,
): ProfileModel {
  val user by userFlow.collectAsState(null)
  val balance by balanceFlow.collectAsState(0L)

  return if (user == null) {
    Loading
  } else {
    Data(user.name, balance)
  }
}

Describe the solution you'd like
It would be convenient if we could have a matcher config to ignore these types.

TwitterCompose:
  ComposableNaming:
    active: false
    functionNameExcludes: ['**Presenter']

Describe alternatives you've considered
The only option right now is to suppress or disable the rule entirely.

Additional context
Happy to send a PR if you're open to it 👍

Github action error

Hello, I am adding the rules in a github action but it is generating an error, maybe it will happen to someone else

I'm using this action

https://github.com/alaegin/Detect-Action

action error

java -jar /opt/detekt.jar --config config/detekt/detekt.yml --report xml:detekt_report.xml --excludes **/build/**,**/.idea/** --plugins /opt/detekt-formatting.jar,config/detekt/detekt-0.0.3.jar, config/detekt/common-0.0.3.jar 
Was passed main parameter 'config/detekt/common-0.0.3.jar' but no main parameter was defined in your arg class

PreviewPublic rule flags valid code

Describe the bug
This code gets flagged by PreviewPublic rule:

@Preview
@Composable
private fun MyComposable(@PreviewParameter(User::class) user: User) {
   ...
}

Seems that skip logic should be more explicit about function visibility.

Additional context
https://github.com/twitter/compose-rules/blob/main/rules/common/src/main/kotlin/com/twitter/compose/rules/ComposePreviewPublic.kt

Suggested changes
I would suggest to change this line:

if (function.valueParameters.none { it.isPreviewParameter }) return

by the below code snippet:

if (function.isPublic && function.valueParameters.none { it.isPreviewParameter }) return
if (!function.isPublic && function.valueParameters.all { it.isPreviewParameter }) return

PreviewNaming does not follow the official AndroidX naming convention for Preview

Describe the bug
I know that these are Twitter's Jetpack Compose Rules and not Google's Jetpack Compose Rules but I want to point out that the official AndroidX Preview annotations use Preview as prefix and not suffix.

/**
 * A MultiPreview annotation for displaying a @[Composable] method using the screen sizes of five different reference devices.
 */
@Retention(AnnotationRetention.BINARY)
@Target(
        AnnotationTarget.ANNOTATION_CLASS,
        AnnotationTarget.FUNCTION
)
@Preview(name = "Phone", device = PHONE, showSystemUi = true)
@Preview(name = "Phone - Landscape",
         device = "spec:width = 411dp, height = 891dp, orientation = landscape, dpi = 420",
         showSystemUi = true)
@Preview(name = "Unfolded Foldable", device = FOLDABLE, showSystemUi = true)
@Preview(name = "Tablet", device = TABLET, showSystemUi = true)
@Preview(name = "Desktop", device = DESKTOP, showSystemUi = true)
annotation class PreviewScreenSizes

/**
 * A MultiPreview annotation for desplaying a @[Composable] method using seven standard font sizes.
 */
@Retention(AnnotationRetention.BINARY)
@Target(
        AnnotationTarget.ANNOTATION_CLASS,
        AnnotationTarget.FUNCTION
)
@Preview(name = "85%", fontScale = 0.85f)
@Preview(name = "100%", fontScale = 1.0f)
@Preview(name = "115%", fontScale = 1.15f)
@Preview(name = "130%", fontScale = 1.3f)
@Preview(name = "150%", fontScale = 1.5f)
@Preview(name = "180%", fontScale = 1.8f)
@Preview(name = "200%", fontScale = 2f)
annotation class PreviewFontScale

/**
 * A MultiPreview annotation for desplaying a @[Composable] method using light and dark themes.
 *
 * Note that the app theme should support dark and light modes for these previews to be different.
 */
@Retention(AnnotationRetention.BINARY)
@Target(
        AnnotationTarget.ANNOTATION_CLASS,
        AnnotationTarget.FUNCTION
)
@Preview(name = "Light")
@Preview(name = "Dark", uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL)
annotation class PreviewLightDark

/**
 * A MultiPreview annotation for desplaying a @[Composable] method using four different wallpaper colors.
 *
 * Note that the app should use a dynamic theme for these previews to be different.
 */
@Retention(AnnotationRetention.BINARY)
@Target(
        AnnotationTarget.ANNOTATION_CLASS,
        AnnotationTarget.FUNCTION
)
@Preview(name = "Red", wallpaper = RED_DOMINATED_EXAMPLE)
@Preview(name = "Blue", wallpaper = BLUE_DOMINATED_EXAMPLE)
@Preview(name = "Green", wallpaper = GREEN_DOMINATED_EXAMPLE)
@Preview(name = "Yellow", wallpaper = YELLOW_DOMINATED_EXAMPLE)
annotation class PreviewDynamicColors

Rule feature request: Using derivedStateOf without any State object in calculation

Currently there is no (lint) warning when using derivedStateOf without accessing any State object in its calculation, which can lead to unexpected behaviour. Since no State object is read, the derived value will never update which however might not be obvious just by looking at the code.

Let's take the following Composable for example:

@Composable
fun Amount(
    amount: Int,
    modifier: Modifier = Modifier,
) {
    val isZeroAmount by remember { derivedStateOf { amount == 0 } }

    Box(modifier = modifier) {
        if (isZeroAmount) {
            Text("Amount: Is zero")
        } else {
            Text("Amount: $amount")
        }
    }
}

If amount is 1 initially and becomes 0 on the next recomposition, the Composable will incorrectly display Amount: 0 instead of Amount: Is zero.

This might for instance happen when a State variable with by delegation is changed to a plain type.
The correct code for this case would be:

@Composable
fun Amount(
    amount: State<Int>,
    modifier: Modifier = Modifier,
) {
    val isZeroAmount by remember { derivedStateOf { amount.value == 0 } }

    Box(modifier = modifier) {
        if (isZeroAmount) {
            Text("Amount: Is zero")
        } else {
            Text("Amount: ${amount.value}")
        }
    }
}

High severity issues in security dependency check - False Positive

ktlint ruleset shows up as high severity issue when running a dependency check.
pinterest/ktlint#512

The reason is that the ktlint module packages as ktlint.jar and ktlint-core.jar, which results in a false positive because it thinks version 0.0.5 (also 0.0.12) is below ktlints fix version 0.30.0 if I am not mistaken.

To Reproduce
Run plugin org.owasp.dependencycheck 7.1.1 and see it fail

Expected behavior
It shouldn't show as high severity issue which is a false-positive

Additional information
core-ktlint-0.0.5.jar | NVDCVE-2019-1010260 | High | CWE-319
File Path /var/lib/jenkins/.gradle/caches/modules-2/files-2.1/com.twitter.compose.rules/core-ktlint/0.0.5/f9f346f5a1fd509f84e53775ec52f18514c4ee42/core-ktlint-0.0.5.jar
SHA-1 f9f346f5a1fd509f84e53775ec52f18514c4ee42
SHA-256 5c8976a039ecedeb10de5fa44b56e1014b71badbcd1404c89c9643221f173462
Description Using ktlint to download and execute custom rulesets can result in arbitrary code execution as the served jars can be compromised by a MITM. This attack is exploitable via Man in the Middle of the HTTP connection to the artifact servers. This vulnerability appears to have been fixed in 0.30.0 and later; after commit 5e547b287d6c260d328a2cb658dbe6b7a7ff2261.

ktlint-0.0.5.jar | NVDCVE-2019-1010260 | High | CWE-319
File Path | /var/lib/jenkins/.gradle/caches/modules-2/files-2.1/com.twitter.compose.rules/ktlint/0.0.5/7954c9ff6e47f94dce73bc6f534c22f66bdb34fb/ktlint-0.0.5.jar
SHA-1 7954c9ff6e47f94dce73bc6f534c22f66bdb34fb
SHA-256 7e57dc0e98863516afacac94b6ffdea50b6226e1fae5f280581da642b2c6d7b0
Description Using ktlint to download and execute custom rulesets can result in arbitrary code execution as the served jars can be compromised by a MITM. This attack is exploitable via Man in the Middle of the HTTP connection to the artifact servers. This vulnerability appears to have been fixed in 0.30.0 and later; after commit 5e547b287d6c260d328a2cb658dbe6b7a7ff2261.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.