pinterest / ktlint

An anti-bikeshedding Kotlin linter with built-in formatter
https://pinterest.github.io/ktlint/
MIT License
6.06k stars 504 forks source link

Unstable format results with anonymous function #2694

Closed flaviut closed 5 days ago

flaviut commented 3 weeks ago

Expected Behavior

Formatting this code:

val authorizor =
    AuthorizationManager(
        fun(
            _: Supplier<Authentication>,
            obj: MessageAuthorizationContext<*>,
        ): AuthorizationDecision = AuthorizationDecision(false),
    )

should end up in some sort of stable state. It doesn't really matter to me what it looks like, as long as it's consistent.

Observed Behavior

fails with "Format was not able to resolve all violations which (theoretically) can be autocorrected in file …/TestFile.kt in 3 consecutive runs of format."

10:10:36.500 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered RuleSetProviderV3 with id 'standard' in ktlint JAR
10:10:36.653 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered RuleSetProviderV3 with id 'standard' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'baseline' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'plain' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'checkstyle' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'json' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'format' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'html' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'plain-summary' in ktlint JAR
10:10:36.663 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintServiceLoader -- Discovered ReporterProviderV2 with id 'sarif' in ktlint JAR
10:10:36.664 [main] DEBUG com.pinterest.ktlint.cli.internal.ReporterAggregator -- Initializing "plain" reporter with {plain=true, color=false, color_name=DARK_GRAY, format=true}
10:10:36.678 [pool-1-thread-1] DEBUG com.pinterest.ktlint.rule.engine.internal.CodeFormatter -- Starting with processing file 'TestFile.kt'
10:10:36.937 [pool-1-thread-1] DEBUG com.pinterest.ktlint.rule.engine.internal.EditorConfigLoader -- Effective editorconfig properties for file '…/TestFile.kt':
    ktlint_standard_no-wildcard-imports: disabled
10:10:36.954 [pool-1-thread-1] DEBUG com.pinterest.ktlint.rule.engine.internal.RuleProviderSorter -- Rules will be executed in order below:
           - internal:ktlint-suppression, 
           - standard:annotation-spacing, 
           - standard:backing-property-naming, 
           - standard:binary-expression-wrapping, 
           - standard:blank-line-before-declaration, 
           - standard:chain-wrapping, 
           - standard:class-naming, 
           - standard:colon-spacing, 
           - standard:comma-spacing, 
           - standard:comment-spacing, 
           - standard:comment-wrapping, 
           - standard:condition-wrapping, 
           - standard:context-receiver-wrapping, 
           - standard:curly-spacing, 
           - standard:discouraged-comment-location, 
           - standard:dot-spacing, 
           - standard:double-colon-spacing, 
           - standard:enum-entry-name-case, 
           - standard:enum-wrapping, 
           - standard:filename, 
           - standard:final-newline, 
           - standard:fun-keyword-spacing, 
           - standard:function-expression-body, 
           - standard:function-naming, 
           - standard:function-return-type-spacing, 
           - standard:function-start-of-body-spacing, 
           - standard:function-type-modifier-spacing, 
           - standard:function-type-reference-spacing, 
           - standard:if-else-bracing, 
           - standard:if-else-wrapping, 
           - standard:import-ordering, 
           - standard:kdoc-wrapping, 
           - standard:keyword-spacing, 
           - standard:modifier-list-spacing, 
           - standard:modifier-order, 
           - standard:multiline-expression-wrapping, 
           - standard:multiline-if-else, 
           - standard:multiline-loop, 
           - standard:no-blank-line-before-rbrace, 
           - standard:no-blank-line-in-list, 
           - standard:no-blank-lines-in-chained-method-calls, 
           - standard:no-consecutive-blank-lines, 
           - standard:no-consecutive-comments, 
           - standard:no-empty-class-body, 
           - standard:no-empty-file, 
           - standard:no-empty-first-line-in-class-body, 
           - standard:no-empty-first-line-in-method-block, 
           - standard:no-line-break-after-else, 
           - standard:no-line-break-before-assignment, 
           - standard:no-multi-spaces, 
           - standard:no-trailing-spaces, 
           - standard:no-unit-return, 
           - standard:no-unused-imports, 
           - standard:nullable-type-spacing, 
           - standard:op-spacing, 
           - standard:package-name, 
           - standard:parameter-list-spacing, 
           - standard:parameter-list-wrapping, 
           - standard:parameter-wrapping, 
           - standard:paren-spacing, 
           - standard:property-naming, 
           - standard:property-wrapping, 
           - standard:range-spacing, 
           - standard:spacing-around-angle-brackets, 
           - standard:spacing-between-declarations-with-annotations, 
           - standard:spacing-between-declarations-with-comments, 
           - standard:spacing-between-function-name-and-opening-parenthesis, 
           - standard:statement-wrapping, 
           - standard:string-template, 
           - standard:try-catch-finally-spacing, 
           - standard:type-argument-comment, 
           - standard:type-argument-list-spacing, 
           - standard:type-parameter-comment, 
           - standard:type-parameter-list-spacing, 
           - standard:unary-op-spacing, 
           - standard:unnecessary-parentheses-before-trailing-lambda, 
           - standard:value-argument-comment, 
           - standard:value-parameter-comment, 
           - standard:annotation, 
           - standard:no-single-line-block-comment, 
           - standard:wrapping, 
           - standard:no-semi, 
           - standard:class-signature, 
           - standard:function-signature, 
           - standard:argument-list-wrapping, 
           - standard:chain-method-continuation, 
           - standard:function-literal, 
           - standard:trailing-comma-on-call-site, 
           - standard:trailing-comma-on-declaration-site, 
           - standard:indent, 
           - standard:block-comment-initial-star-alignment, 
           - standard:string-template-indent, 
           - standard:max-line-length
10:10:37.032 [pool-1-thread-1] WARN com.pinterest.ktlint.rule.engine.internal.CodeFormatter -- Format was not able to resolve all violations which (theoretically) can be autocorrected in file …/TestFile.kt in 3 consecutive runs of format.
10:10:37.032 [pool-1-thread-1] DEBUG com.pinterest.ktlint.rule.engine.internal.CodeFormatter -- line separator: lf --> LF
10:10:37.034 [pool-1-thread-1] DEBUG com.pinterest.ktlint.rule.engine.internal.CodeFormatter -- Finished with processing file 'TestFile.kt'
10:10:37.035 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintCommandLine -- Finished processing in 540ms / 1 file(s) scanned / 6 error(s) found
10:10:37.035 [main] DEBUG com.pinterest.ktlint.cli.internal.KtlintCommandLine -- Exit ktlint with exit code: 0

Steps to Reproduce

$ cat TestFile.kt
val authorizor =
    AuthorizationManager(
        fun(
            _: Supplier<Authentication>,
            obj: MessageAuthorizationContext<*>,
        ): AuthorizationDecision = AuthorizationDecision(false),
    )

Your Environment

flaviut commented 3 weeks ago

Workaround:

val authorizor = AuthorizationManager<MessageAuthorizationContext<*>> { _, obj ->
    AuthorizationDecision(false)
}
paul-dingemans commented 2 weeks ago

val authorizor = AuthorizationManager( fun( _: Supplier, obj: MessageAuthorizationContext<*>, ): AuthorizationDecision = AuthorizationDecision(false), )

I have never seen this construct with an anonymous function being passed in this way. The blank-line-before-declaration should not treat this argument for the AuthorizationManager as a declaration, and as of that not enforce a blank line above it.