Skip to content

Commit c6386ad

Browse files
committed
Disable calling rules when they return .skipChildren.
Add a map `shouldSkipChildren` that tracks which rules are currently skipping children. Install handlers in visitPost for each rule that determine when we should start visiting children again (when we leave the node we're skipping).
1 parent b87fa64 commit c6386ad

File tree

3 files changed

+270
-1
lines changed

3 files changed

+270
-1
lines changed

Sources/SwiftFormat/Core/LintPipeline.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ extension LintPipeline {
2929
_ visitor: (Rule) -> (Node) -> SyntaxVisitorContinueKind, for node: Node
3030
) {
3131
guard context.isRuleEnabled(Rule.self, node: Syntax(node)) else { return }
32+
let ruleId = ObjectIdentifier(Rule.self)
33+
guard self.shouldSkipChildren[ruleId] == nil else { return }
3234
let rule = self.rule(Rule.self)
33-
_ = visitor(rule)(node)
35+
let continueKind = visitor(rule)(node)
36+
if case .skipChildren = continueKind {
37+
self.shouldSkipChildren[ruleId] = node
38+
}
3439
}
3540

3641
/// Calls the `visit` method of a rule for the given node if that rule is enabled for the node.
@@ -50,10 +55,27 @@ extension LintPipeline {
5055
// cannot currently be expressed as constraints without duplicating this function for each of
5156
// them individually.
5257
guard context.isRuleEnabled(Rule.self, node: Syntax(node)) else { return }
58+
guard self.shouldSkipChildren[ObjectIdentifier(Rule.self)] == nil else { return }
5359
let rule = self.rule(Rule.self)
5460
_ = visitor(rule)(node)
5561
}
5662

63+
/// Cleans up any state associated with `rule` when we leave syntax node `node`
64+
///
65+
/// - Parameters:
66+
/// - rule: The type of the syntax rule we're cleaning up.
67+
/// - node: The syntax node htat our traversal has left.
68+
func onVisitPost<R: Rule, Node: SyntaxProtocol>(
69+
rule: R.Type, for node: Node
70+
) {
71+
let rule = ObjectIdentifier(rule)
72+
if case .some(let skipNode) = self.shouldSkipChildren[rule] {
73+
if node.id == skipNode.id {
74+
self.shouldSkipChildren.removeValue(forKey: rule)
75+
}
76+
}
77+
}
78+
5779
/// Retrieves an instance of a lint or format rule based on its type.
5880
///
5981
/// There is at most 1 instance of each rule allocated per `LintPipeline`. This method will

0 commit comments

Comments
 (0)