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

Add rule to migrate away from ktlint-disable directives #2068

Merged
merged 28 commits into from
Jun 27, 2023

Conversation

paul-dingemans
Copy link
Collaborator

@paul-dingemans paul-dingemans commented Jun 5, 2023

Description

Add rule to migrate ktlint-disable directives from comments to @Suppress or @SuppressWarnings annotations

Note that the SuppressionLocatorBuilder never ignore this rule as otherwise the suppression hint to ignore all rules would also ignore the rule which has to migrate this suppression hint.

REVIEW suggestion:

Closes #1947

Checklist

  • PR description added
  • tests are added
  • KtLint has been applied on source code itself and violations are fixed
  • documentation is updated
  • CHANGELOG.md is updated

In case of adding a new rule:

…itted

The reports print the lint violations in which they occur in the source code. This is not necessarily the order in which they are detected and fixed. For debugging some type of problems, the latter order is important.
 or @SuppressWarnings annotations

Note that the SuppressionLocatorBuilder never ignore this rule as otherwise the suppression hint to ignore *all* rules would also ignore the rule which has to migrate this suppression hint.

Closes #1947
@paul-dingemans
Copy link
Collaborator Author

@atulgpt I you also want to contribute by reviewing changes, you're welcome to review this PR. You can review it in detail or just briefly. Normally, I would just merge the change as there are almost no regular contributors on the project that would like to do this. I you don't want to review, just let me know.

REVIEW suggestion:

  • The functional change for is contained in this commit
  • Other commits are supportive to the change

@atulgpt
Copy link
Contributor

atulgpt commented Jun 5, 2023

@atulgpt I you also want to contribute by reviewing changes, you're welcome to review this PR. You can review it in detail or just briefly. Normally, I would just merge the change as there are almost no regular contributors on the project that would like to do this. I you don't want to review, just let me know.

REVIEW suggestion:

* The functional change for is contained in [this commit](https://github.com/pinterest/ktlint/pull/2068/commits/428d52e637d931754b3756f8912f64cea884f8d5)

* Other commits are supportive to the change

Hi, @paul-dingemans I have added a few more TC suggestions. Will take a look in the logic as well

@paul-dingemans
Copy link
Collaborator Author

@paul-dingemans
Copy link
Collaborator Author

@atulgpt I you also want to contribute by reviewing changes, you're welcome to review this PR. You can review it in detail or just briefly. Normally, I would just merge the change as there are almost no regular contributors on the project that would like to do this. I you don't want to review, just let me know.
REVIEW suggestion:

* The functional change for is contained in [this commit](https://github.com/pinterest/ktlint/pull/2068/commits/428d52e637d931754b3756f8912f64cea884f8d5)

* Other commits are supportive to the change

Hi, @paul-dingemans I have added a few more TC suggestions. Will take a look in the logic as well

Wow, I am really amazed at the good quality of the review comments. Tnx! I have left some review comments unanswered. I have to think about them.

@paul-dingemans
Copy link
Collaborator Author

Can you please "resolve" the remarks on which I have responded and on which you agree? In that way, it is more clear for both of us what still needs to be fixed or discussed. Tnx. You're doing really great as reviewer!

@atulgpt
Copy link
Contributor

atulgpt commented Jun 7, 2023

Can you please "resolve" the remarks on which I have responded and on which you agree? In that way, it is more clear for both of us what still needs to be fixed or discussed. Tnx. You're doing really great as a reviewer!

Screenshot 2023-06-08 at 1 38 49 AM

Somehow it doesn't give me that option

@paul-dingemans
Copy link
Collaborator Author

Hmm, I can not change that. If you leave a thumbs up or 'ok' message then I will resolve them.

- Merge rule suppressions that propagate to the same declaration annotation
…o a file annotation. This also applies to directives which are placed as last comment before a top level class, fun or property (the kotlin embeddable compiler places such comments inside the ASTNode of the class, fun, property respectively.

- Convert a block comment with a ktlint-disable directive to the first parent declaration or expression whenever a matching ktlint-enable directive is found as sibling inside the same parent node.
- Do not autocorrect other block comments containing a ktlint-disable directive
…eset. In this way, it can be guaranteed that the rule runs for all API consumers, even the consumers that do not pass the standard rule providers

- Do not process the ktlint-directives in the SuppressionLocatorBuilder, MaxLineLengthRule, NoSingleLineBlockCommentRule anymore
- Fix offset problem with suppression annotation (see unit test MaxLineLengthRuleTest)
- Fix problems in unit tests that referred to ktlint-directives which are no longer processed
- Add "libs.logback" as runtime only dependency so that logging is visible in unit tests
- Move checking of unknown rule ids from SuppressionLocatorBuilder to KtlintSuppressionRule so that normal lint violations are emitted instead of log message being printed.
@paul-dingemans
Copy link
Collaborator Author

Hi @atulgpt ,
I hope you can find some time for review of logic.

@atulgpt
Copy link
Contributor

atulgpt commented Jun 14, 2023

Hi @paul-dingemans somehow the below TC is failing(it is adding multiple annotations)

val code =
    """
    class Foo() {
        val foo: Int
            get() = 1
            set(value: Int) { // ktlint-disable standard:foo
                field = value // ktlint-disable standard:foo
            }
    }
    """.trimIndent()
val formattedCode =
    """
    class Foo() {
        val foo: Int
            get() = 1
            @Suppress("ktlint:standard:foo")
            set(value: Int) {
                field = value
            }
    }
    """.trimIndent()

@atulgpt
Copy link
Contributor

atulgpt commented Jun 14, 2023

class Foo() {
    var foo: Int = 0
        get() = 1
        @Suppress(names = ["abc"])
        set(value: Int) {
            field = value // ktlint-disable standard:foo
        }
}

this is also failing(seems to be an issue with named parameter)

@atulgpt
Copy link
Contributor

atulgpt commented Jun 14, 2023

I think currently we are also not handling type aliases like below

typealias A = Int // ktlint-disable standard:foo

@atulgpt
Copy link
Contributor

atulgpt commented Jun 14, 2023

And below code behaves strangely(maybe I am not setting the TC correctly)

val code =
    """
    private val a by lazy(LazyThreadSafetyMode.PUBLICATION) { // ktlint-disable standard:foo
        val t = 0 // ktlint-disable standard:foo
        2
    }
    """.trimIndent()
val formattedCode =
    """
    private val a by lazy(LazyThreadSafetyMode.PUBLICATION) { // ktlint-disable standard:foo
        val t = 0 // ktlint-disable standard:foo
        2
    }
    """.trimIndent()

above code doesn't show any violations but if I change the code like below

val code =
    """
    private val a by lazy(LazyThreadSafetyMode.PUBLICATION) { // ktlint-disable standard:foo
        val t = 0 // ktlint-disable standard:foo
        2
    }
    fun t() { // ktlint-disable standard:foo

    }
    """.trimIndent()

it gets itself corrected as below code

private val a by lazy(LazyThreadSafetyMode.PUBLICATION) @Suppress("ktlint:standard:foo")
  {
      @Suppress("ktlint:standard:foo")
      val t = 0
      2
  }
  @Suppress("ktlint:standard:foo")
  fun t() {
  
  }

@paul-dingemans
Copy link
Collaborator Author

Hi @paul-dingemans somehow the below TC is failing(it is adding multiple annotations)

val code =
    """
    class Foo() {
        val foo: Int
            get() = 1
            set(value: Int) { // ktlint-disable standard:foo
                field = value // ktlint-disable standard:foo
            }
    }
    """.trimIndent()
val formattedCode =
    """
    class Foo() {
        val foo: Int
            get() = 1
            @Suppress("ktlint:standard:foo")
            set(value: Int) {
                field = value
            }
    }
    """.trimIndent()

Fixed

@paul-dingemans
Copy link
Collaborator Author

class Foo() {
var foo: Int = 0
get() = 1
@Suppress(names = ["abc"])
set(value: Int) {
field = value // ktlint-disable standard:foo
}
}

I have simplified your example to:

            @Suppress(names = ["unused"])
            val foo = "foo" // ktlint-disable standard:foo

Fixed. When merging the annotations, I have chosen to drop the usage of the name argument for the sake of simplicity. So the result of above is:

            @Suppress("ktlint:standard:foo", "unused")
            val foo = "foo"

@paul-dingemans
Copy link
Collaborator Author

I think currently we are also not handling type aliases like below

typealias A = Int // ktlint-disable standard:foo

Type aliases are handled. No fix required.

…amed argument

The named argument is replaced with the vararg notation in which the existing and new suppressions are merged
@paul-dingemans
Copy link
Collaborator Author

And below code behaves strangely(maybe I am not setting the TC correctly)

val code =
    """
    private val a by lazy(LazyThreadSafetyMode.PUBLICATION) { // ktlint-disable standard:foo
        val t = 0 // ktlint-disable standard:foo
        2
    }
    """.trimIndent()
val formattedCode =
    """
    private val a by lazy(LazyThreadSafetyMode.PUBLICATION) { // ktlint-disable standard:foo
        val t = 0 // ktlint-disable standard:foo
        2
    }
    """.trimIndent()

above code doesn't show any violations but if I change the code like below

val code =
    """
    private val a by lazy(LazyThreadSafetyMode.PUBLICATION) { // ktlint-disable standard:foo
        val t = 0 // ktlint-disable standard:foo
        2
    }
    fun t() { // ktlint-disable standard:foo

    }
    """.trimIndent()

it gets itself corrected as below code

private val a by lazy(LazyThreadSafetyMode.PUBLICATION) @Suppress("ktlint:standard:foo")
  {
      @Suppress("ktlint:standard:foo")
      val t = 0
      2
  }
  @Suppress("ktlint:standard:foo")
  fun t() {
  
  }

Nice TC. I have use a simplified example to reproduce:

val foo by lazy(LazyThreadSafetyMode.PUBLICATION) { // ktlint-disable standard:indent
    // do something
}

which is now formatted as:

val foo by @Suppress("ktlint:standard:indent")
    lazy(LazyThreadSafetyMode.PUBLICATION) {
        // do something
    }

This example also learned that in case of nested expression, the annotation was not created at the outer most expression which resulted in code which could not be compiled. For example when using FUNCTION_LITERAL or LAMBDA_EXPRESSION:

val foo =
    setOf("a")
        .map {
            bar(it) // ktlint-disable standard:indent
        }

val foo = foo() {
    bar(it) // ktlint-disable standard:indent
}

This is also fixed.

ktlint-disable directives have to be replaced with annotations before process other rules because the SuppressionLocatorBuilder is no longer processing those directives.
EMPTY_EDITOR_CONFIG_OVERRIDE
.plus(
STANDARD_NO_FOO_IDENTIFIER_RULE_ID.createRuleExecutionEditorConfigProperty() to RuleExecution.enabled,
ruleProviders =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paul-dingemans under which rule this got formatted?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure whether I understand.

But the IndentationRule would format:

                return mapOf(
                       1 to "   1 ms",
                      10 to "  10 ms",
                     999 to " 999 ms",
                    1000 to "   1 sec",
                )

to

                return mapOf(
                    1 to "   1 ms",
                    10 to "  10 ms",
                    999 to " 999 ms",
                    1000 to "   1 sec",
                )

if the rule would not be suppressed.

Does that answer the question?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I am wondering why can't ruleProviders = setOf( be on the same line? Is this coz ktlint official recommends to always(in case of multiline chain) to newline after =

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, see https://pinterest.github.io/ktlint/0.49.1/rules/experimental/#multiline-expression-wrapping. Rationale is that when using long identifiers (or small screens) that the first part of the expression gets disconnected from the other lines. So it is still allowed to write rulesproviders = setOf(....), if its fits on a single line.

# Conflicts:
#	CHANGELOG.md
#	ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt
@paul-dingemans paul-dingemans merged commit 4f6ed15 into master Jun 27, 2023
16 checks passed
@paul-dingemans paul-dingemans deleted the 1947-ktlint-disable branch June 27, 2023 08:40
@paul-dingemans paul-dingemans added this to the 0.50.0 milestone Jun 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Drop support for ktlint-enable / ktlint-disable directive
2 participants