twitter / compose-rules Goto Github PK
View Code? Open in Web Editor NEWStatic checks to aid with a healthy adoption of Compose
Home Page: https://twitter.github.io/compose-rules
License: Other
Static checks to aid with a healthy adoption of Compose
Home Page: https://twitter.github.io/compose-rules
License: Other
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?
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:
dependencies {
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
detektPlugins("com.twitter.compose.rules:detekt:$twitterComposeVersion")
}
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
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.
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() {
//
}
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"
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
Preview
thereDescribe alternatives you've considered
🤷🏼
Additional context
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:
./gradlew detekt
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.
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
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
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.
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.
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".
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?
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.
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 🤔
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
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.
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.
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
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).
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 😃
Describe the bug
Cannot send a post
To Reproduce
Steps to reproduce the behavior:
Expected behavior
I can send the post.
Environment
see :https://www.whatsmybrowser.org/b/JKH29
Additional context
na
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.
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
.
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
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?
Describe the bug
Code like this gets flagged by ModifierMissing rule:
@Preview
@Composable
fun PreviewSomething() {
LazyColumn {
// ... whatever
}
}
Wouldn't it be nice to don't have this false issue flagged by this rule?
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>"
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
Given the current situation at Twitter, can we expect this library to still be maintained?
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.
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 👍
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
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
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
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}")
}
}
}
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.