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

Support file-level ignore directive for specific rules #950

Merged
merged 1 commit into from
Mar 6, 2025

Conversation

TTOzzi
Copy link
Member

@TTOzzi TTOzzi commented Mar 1, 2025

Resolve #927

I have modified the function that disables specific rules in node-level directive so that it can also be reused for file-level directive, ensuring both follow the same logic.

Previously, if a file-level directive appeared in the topmost comment, we would always skip visiting nodes. However, with this change, we now visit child nodes when only specific rules are disabled. As a result, this caused an unintended side effect in cases where the OrderedImports rule was enabled. Specifically, a directive included in the leadingTrivia of a sorted import statement was being reordered along with the imports.
To resolve this, I modified the OrderedImports rule to treat the swift-format-ignore-file directive as part of the file header.

Additionally, to avoid simple string comparisons like:

if line.description.contains("swift-format-ignore-file") {

I have created an IgnoreDirective enum to handle ignore directives more cleanly.

For easier review, I have split the commits.
Once the review is complete, I will squash and force push them before merging. 🙂

@ahoppen
Copy link
Member

ahoppen commented Mar 3, 2025

@TTOzzi The Windows CI failures should be addressed by #951. Could you rebase your changes on top of main so that we can pick up that fix for CI?

@TTOzzi
Copy link
Member Author

TTOzzi commented Mar 3, 2025

@TTOzzi The Windows CI failures should be addressed by #951. Could you rebase your changes on top of main so that we can pick up that fix for CI?

Oh, thank you for identifying the cause of the failure 😃
I’ve rebased it.

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Thank you and thanks for remembering to not re-order the ignore directive in ordered imports. I’ve got a couple of thoughts inline.

@@ -651,4 +651,36 @@ final class OrderedImportsTests: LintOrFormatRuleTestCase {
]
)
}

func testImportsOrderWithFileIgnoreDirective() {
assertFormatting(
Copy link
Member

Choose a reason for hiding this comment

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

Does this also end up adding a newline after // swift-format-ignore-file if no re-ordering is necessary? Eg. for

// swift-format-ignore-file
import LibA
import LibB

To be clear, I’m fine with that, just want to check and record the behavior in a test case.


One thing I did notice is that I think this flips comments order around if you have eg.

// We need to ignore this file because it is generated
// swift-format-ignore-line

import Foundation

Maybe swift-format-ignore-line just needs to flush the commentBuffer into the headerBuffer similar to what a blank line does.

Copy link
Member Author

@TTOzzi TTOzzi Mar 6, 2025

Choose a reason for hiding this comment

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

Does this also end up adding a newline after // swift-format-ignore-file if no re-ordering is necessary?

Yes, that's right. In the current implementation, if the ignore directive does not disable OrderedImports,

// swift-format-ignore-file: DoNotUseSemicolons
import LibA
import LibB

is formatted as

// swift-format-ignore-file: DoNotUseSemicolons

import LibA
import LibB

because the // swift-format-ignore-file: DoNotUseSemicolons comment is treated as part of the file header by the OrderedImports rule and is separated accordingly.
However, if the ignore directive includes OrderedImports, such as // swift-format-ignore-file or // swift-format-ignore-file: OrderedImports, the original code is preserved.

Additionally, I have fixed the issue where the comment order could be flipped when// swift-format-ignore-line appeared after the first line, and I have added a test case for it.

Thanks for the feedback!

Comment on lines 116 to 123
private var pattern: String {
return #"^\s*\/\/\s*"# + description + #"((:\s+(([A-z0-9]+[,\s]*)+))?$|\s+$)"#
}

/// Rule ignore regex object.
fileprivate var regex: NSRegularExpression {
return try! NSRegularExpression(pattern: pattern, options: [])
}
Copy link
Member

Choose a reason for hiding this comment

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

Since you are working on this already, I’ve got a few thoughts:

  • The regex restriction after the : seems a little restrictive to me. Eg. I imagine it would be fine to try and debug why a future rule that contains a _ cannot be ignored using a directive. Maybe we can just make the regex a prefix match. @allevato Do you remember why the regex is this specific?
  • This re-compiles the regex every time regex is accessed. Compiling regular expressions is fairly expensive, so I’d prefer to cache that.
  • Could we use Swift’s Regex instead of NSRegularExpression?
  • Or, alternatively, would it be easier to write a simple line matcher and avoid going through regex matching at all?

All except for avoiding to re-compile the regex don’t need to be in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

@allevato Do you remember why the regex is this specific?

Not off the top of my head. It may have just been to capture precisely what we expected rule names to look like at the time. I think it would be fine to be a little less prescriptive in the regex itself, if it simplifies things.

Could we use Swift’s Regex instead of NSRegularExpression?

We'd have to raise our minimum deployment version, which is macOS 12.0 right now, since Regex requires 13.0. If that aligns with other binaries in the Swift toolchain, I'd love to drop NSRegularExpression (holistically, in a separate PR).

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I have updated the implementation to cache the regex instead of recompiling it each time.

And as for other suggestions, would it be a good idea to create a separate issue for these?

  • Relaxing the regex restrictions
    (If we can raise the minimum deployment version, migrating from NSRegularExpression to Swift’s Regex)
  • Alternatively, parsing ignore directives using string matching instead of regex

Copy link
Member

Choose a reason for hiding this comment

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

Issues (or PRs 😉) for those items would be good, yes. Also, I am happy to raise the deployment target to macOS 13. SourceKit-LSP, for example, already requires macOS 13.0.

Copy link
Member Author

Choose a reason for hiding this comment

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

Got it! I'll work on a follow-up PR for those items 😁

@TTOzzi TTOzzi force-pushed the ignore-specific-rules-in-file branch from ab12d60 to 79fd962 Compare March 6, 2025 10:37
@TTOzzi TTOzzi force-pushed the ignore-specific-rules-in-file branch from 79fd962 to 35e6d73 Compare March 6, 2025 10:43
Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Thanks. Looks good to me 👍🏽

@ahoppen ahoppen merged commit b251238 into swiftlang:main Mar 6, 2025
19 checks passed
@TTOzzi TTOzzi deleted the ignore-specific-rules-in-file branch March 7, 2025 00:33
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.

No syntax to disable rule for file
3 participants