Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A type with multiple annotations inside a TypeArgumentList is not formatted correctly #1725

Closed
paul-dingemans opened this issue Dec 14, 2022 · 1 comment · Fixed by #1849
Closed

Comments

@paul-dingemans
Copy link
Collaborator

Code below:

data class FooRequestDto(
    val data: List<@Valid @NotNull FooDto>,
)

is formatted as:

data class FooRequestDto(
    val data: List<@Valid @NotNull
    FooDto,>,
)

KtLint: 0.47.1

Originally posted by @Vadgyik in #673 (comment)

@paul-dingemans
Copy link
Collaborator Author

paul-dingemans commented Mar 6, 2023

When I really exaggerate the example code, it turns out that it is not so easy to find an acceptable outcome that is deterministic when formatting generic type parameters that are annotated.

Suppose I have following classes and annotations:

@Target(AnnotationTarget.TYPE)
annotation class Foo
@Target(AnnotationTarget.TYPE)
annotation class Bar1
@Target(AnnotationTarget.TYPE)
annotation class Bar2
@Target(AnnotationTarget.TYPE)
annotation class FooBarLongDescription
@Target(AnnotationTarget.TYPE)
annotation class ComplexAnnotationWithParameters(val string: String)
class SimpleDto
class LongerButReasonableDto
class ExtremeLooooooooooooooooooooooooooooongDto
class SomeCustomTypeWithGenerics<A, B, C>

Example A below shows the code in which annotations are not wrapped:

data class ExampleA(
    val atom: List<@Foo SimpleDto>,
    val simpleList: List<@Foo @FooBarLongDescription @Bar1 @Bar2 SimpleDto>,
    val moreComplexxxxxxxxxxxxMap: Map<@Foo @FooBarLongDescription @Bar1 @Bar2 SimpleDto, @Foo @FooBarLongDescription LongerButReasonableDto>,
    val veryLoooooooooooooooooooooooooongCustomType: SomeCustomTypeWithGenerics<@Foo @FooBarLongDescription @Bar1 @Bar2 SimpleDto, @Foo @FooBarLongDescription LongerButReasonableDto, @Suppress("AnnotationRule") @Foo @FooBarLongDescription ExtremeLooooooooooooooooooooooooooooongDto>,
)

This can be made more readable by wrapping each generic type having multiple annotations:

data class ExampleB(
    val atom: List<@Foo SimpleDto>,
    val simpleList:
        List<
            @Foo @FooBarLongDescription @Bar1 @Bar2 SimpleDto,
        >,
    val moreComplexxxxxxxxxxxxMap: Map<
            @Foo @FooBarLongDescription @Bar1 @Bar2 SimpleDto,
            @Foo @FooBarLongDescription LongerButReasonableDto,
        >,
    val veryLoooooooooooooooooooooooooongCustomType:
        SomeCustomTypeWithGenerics<
            @Foo @FooBarLongDescription @Bar1 @Bar2 SimpleDto,
            @Foo @FooBarLongDescription LongerButReasonableDto,
            @ComplexAnnotationWithParameters("foo bar foo bar foo bar foo bar foo bar foo bar foo bar") @Foo @FooBarLongDescription ExtremeLooooooooooooooooooooooooooooongDto,
        >,
)

A valid question with the example above is whether the same should also have be done with atom: List<@Foo SimpleDto>. The biggest downside of this approach is that formatting is not consistent with the way that annotations are formatted on normal parameters or classes.

If each annotation is wrapped to a separate line, the result would look like:

data class ExampleC(
    val atom: List<@Foo SimpleDto>,
    val simpleList: List<
        @Foo
        @FooBarLongDescription
        @Bar1
        @Bar2
        SimpleDto
    >,
    val moreComplexxxxxxxxxxxxMap: Map<
        @Foo
        @FooBarLongDescription
        @Bar1
        @Bar2
        SimpleDto,
        @Foo
        @FooBarLongDescription
        LongerButReasonableDto
    >,
    val veryLoooooooooooooooooooooooooongCustomType: SomeCustomTypeWithGenerics<
        @Foo
        @FooBarLongDescription
        @Bar1
        @Bar2
        SimpleDto,
        @Foo
        @FooBarLongDescription
        LongerButReasonableDto,
        @ComplexAnnotationWithParameters("foo bar foo bar foo bar foo bar foo bar foo bar foo bar")
        @Foo
        @FooBarLongDescription
        ExtremeLooooooooooooooooooooooooooooongDto,
    >,
)

Advantage is that this is consistent with other formatting. Disadvantage of course is that it is lengthy. Despite that, I am inclined to choose this formatting.

But when combined with the rule suppression mechanism, the developer could choose in specific case to use a manual formatting like below:

@Suppress("ktlint:annotation")
data class ExampleD(
    val atom: List<@Foo SimpleDto>,
    val simpleList: List<@Foo @FooBarLongDescription SimpleDto>,
    val moreComplexxxxxxxxxxxxMap:
        Map<
            @Foo @FooBarLongDescription
            SimpleDto,
            @Foo @FooBarLongDescription
            LongerButReasonableDto
        >,
    val veryLoooooooooooooooooooooooooongCustomType:
        SomeCustomTypeWithGenerics<
            @Foo @FooBarLongDescription SimpleDto,
            @Foo @FooBarLongDescription
            LongerButReasonableDto,
            @ComplexAnnotationWithParameters("foo bar foo bar foo bar foo bar foo bar foo bar foo bar")
            @Foo
            @FooBarLongDescription
            ExtremeLooooooooooooooooooooooooooooongDto,
        >,
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant