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

Conditional ordering of rules #1230

Merged

Conversation

paul-dingemans
Copy link
Collaborator

Description

For backgrounds see issue: #1229.

Description below is identical to the commit messages in the order they are applied. Only the last commits is new functionality. All preceding commits are preparations.

Extract VisitorProvider from Ktlint class

The visitor is created once for each file that has to be linted/formatted. It is now divided into two distinct phases.

The setup phase is executed whenever the VisitorProvider is created. This provider is created only once per invocation of the KtLint command. In the setup phase, the rules are analysed and the order in which they have to be executed is determined. This order is identical for all nodes. As the phase is executed only once, it can now containing logging about the order in which the rules are executed. Also it opens up possibilities for more complex ordering rules using annotations and reflection (for example executing a rule after a specific rule).

The visitor creation phase actually creates the visitor. In this phase all enabled rules will be executed. Note that disabling of rules depends on the '.editorconfig' which is active for a given rootNode.

Split test into multiple tests

Split DisabledRulesTest into multiple tests.

Move sorting on ruleSetId from RuleSetsLoader to VisitorProvider

The order in which the rule sets are discovered is not relevant for the loader. Ordering rules by rule set however is important within the scope of the VisitorProvider though.

Also add order that experimental rules takes precedence over custom rules. Finally add secondary sorting on ruleId so that the order of the rules as defined by the RuleSetProviders is not relevant anymore.

Restrict usage of Rule.Modifier to initialisation phase in which the order of the rules is determined

Rule.Modifier is no longer used during actual creation of the visitor. When creating the visitor the rules are already ordered. This opens up the new possibilities to add other rule ordering mechanisms.

Note that this introduces functional changes below with regard to the order of execution of the rules:

  • Rules without Rule.Modifier and rules with Rule.Modifier.RestrictToRoot are now handled as one group instead of two distinct groups.
  • Rules with Rule.Modifier.Last and rules with Rule.Modifier.RestrictToRootLast are now handled as one group instead of two distinct groups.

Add annotations RunAsLateAsPossible and RunOnRootNodeOnly as replacements for the Rule.Modifier interfaces

Rules in the standard and experimental rule sets already are converted to use the annotations instead of the Rule.Modifier interfaces. The Rule.Modifiers interface are kept to support a grace period in which maintainers of custom rule sets should migrate as well.

Add annotation RunAfterRule and alter the rule execution order (NEW FUNCTIONALITY)

Ensures that the rule annotated with RunAfterRule appears in the rule execution order after a specified other rule

Checklist

  • tests are added
  • CHANGELOG.md is updated

Paul Dingemans added 6 commits September 11, 2021 17:59
The visitor is created once for each file that has to be linted/formatted. It is
now divided into two distinct phases.

The setup phase is executed whenever the VisitorProvider is created. This provider
is created only once per invocation of the KtLint command. In the setup phase, the
rules are analysed and the order in which they have to be executed is determined.
This order is identical for all nodes. As the phase is executed only once, it can
now containing logging about the order in which the rules are executed. Also it
opens up possibilities for more complex ordering rules using annotations and
reflection (for example executing a rule after a specific rule).

The visitor creation phase actually creates the visitor. In this phase all enabled
rules will be executed. Note that disabling of rules depends on the
'.editorconfig' which is active for a given rootNode.
The order in which the rule sets are discovered is not relevant for the
loader. Ordering rules by rule set however is important within the scope
of the VisitorProvider though.

Also add order that experimental rules takes precedence over custom rules.
Finally add secondary sorting on ruleId so that the order of the rules
as defined by the RuleSetProviders is not relevant anymore.
…order of the rules is determined

Rule.Modifier is no longer used during actual creation of the visitor. When creating the
visitor the rules are already ordered. This opens up the new possibilities to add other
rule ordering mechanisms.

Note that this introduces *functional* changes below with regard to the order of
execution of the rules:
 - Rules without Rule.Modifier and rules with Rule.Modifier.RestrictToRoot are now
   handled as one group instead of two distinct groups.
 - Rules with Rule.Modifier.Last and rules with Rule.Modifier.RestrictToRootLast are
   now handled as one group instead of two distinct groups.
…ents for the Rule.Modifier interfaces

Rules in the standard and experimental rule sets already are converted to use
the annotations instead of the Rule.Modifier interfaces. The Rule.Modifiers
interface are kept to support a grace period in which maintainers of custom
rule sets should migrate as well.
Ensures that the rule annotated with RunAfterRule appears in the rule
execution order after a specified other rule
@shashachu
Copy link
Contributor

thanks for the PR @paul-dingemans. It's an interesting idea! I feel that in theory rules should 'just work' no matter what order they're run - i.e. either they'll have work to do, or not, and if you have conflicting rules, that's sort of your problem. Similarly, you should not depend on some particular rule running before yours in order for your rule to work. But in practice maybe this ends up not being the case. I don't think I have a big problem with this PR, but I don't want to see us introducing a lot of strict ordering requirements in our standard or even experimental set of rules.

@paul-dingemans
Copy link
Collaborator Author

I do agree that dependencies between rules should be avoided when possible. But when I have too choose between a rule dependency versus implementing similar logic in multiple rules than I prefer the dependency.

For example the rule for wrapping arguments also does some indenting but it conflicts with indenting of the indent rule which is confusing when running lint without format. I think it would be better if indentation is solely reported by the the indent rule.

Within the standard and experimental rules we can be strict on using rule dependencies. With this functionality we do empower custom ruleset builders to make their own decisions. But with great power comes great responsibility 😉

@romtsn
Copy link
Collaborator

romtsn commented Sep 16, 2021

Yeah the wrapping rule should just do wrapping, and the indentation should be delegated to the indent rule. We'd need to think of a good way to have rule dependencies.

To me, the downside in this approach is that we have to introduce a ~3MB kotlin-reflect dependency, if possible it would be cool to avoid this.

Now, the kotlin-reflect dependency (3MB) is no longer needed.
@paul-dingemans
Copy link
Collaborator Author

paul-dingemans commented Sep 18, 2021

To me, the downside in this approach is that we have to introduce a ~3MB kotlin-reflect dependency, if possible it would be cool to avoid this.

I didn't thought about this. I have replaced the annotations with a new sealed class VisitorModifier. Now the kotlin-reflect is no longer needed. I did like the annotation approach a little more, but I am also happy with the new approach.

# Conflicts:
#	ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt
#	ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt
@paul-dingemans
Copy link
Collaborator Author

@romtsn Could you have another look at this PR please? The unwanted kotlin-reflect dependency was removed a while ago. If you have no further remarks, I really would be pleased if this gets merged in the next ktlint release so that I can resolve my multiline string indent rule in a separate ruleset.

# Conflicts:
#	ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt
#	ktlint/src/main/kotlin/com/pinterest/ktlint/internal/FileUtils.kt
Paul Dingemans added 3 commits January 22, 2022 14:02
…ing-of-rules

# Conflicts:
#	ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/Rule.kt
…false warning which is printer to the error log. It should be re-enabled once PR pinterest#1279 is merged to master.
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.

3 participants