Skip to content

Commit 40bab4e

Browse files
committed
Merge pull request #219 from dylansturg/optional_rules
Support opt-in rules using an `isOptIn` class property.
1 parent 8423f76 commit 40bab4e

File tree

7 files changed

+40
-10
lines changed

7 files changed

+40
-10
lines changed

Package.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,14 @@ let package = Package(
6161
"SwiftSyntax",
6262
]
6363
),
64-
.target(name: "generate-pipeline", dependencies: ["SwiftSyntax"]),
64+
.target(
65+
name: "generate-pipeline",
66+
dependencies: [
67+
"SwiftFormatCore",
68+
"SwiftFormatRules",
69+
"SwiftSyntax",
70+
]
71+
),
6572
.target(
6673
name: "swift-format",
6774
dependencies: [

Sources/SwiftFormatCore/Rule.swift

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ public protocol Rule {
1818
/// The human-readable name of the rule. This defaults to the class name.
1919
static var ruleName: String { get }
2020

21+
/// Whether this rule is opt-in, meaning it is disabled by default.
22+
static var isOptIn: Bool { get }
23+
2124
/// Creates a new Rule in a given context.
2225
init(context: Context)
2326
}

Sources/SwiftFormatCore/SyntaxFormatRule.swift

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ import SwiftSyntax
1414

1515
/// A rule that both formats and lints a given file.
1616
open class SyntaxFormatRule: SyntaxRewriter, Rule {
17+
/// Whether this rule is opt-in, meaning it's disabled by default. Rules are opt-out unless they
18+
/// override this property.
19+
open class var isOptIn: Bool {
20+
return false
21+
}
22+
1723
/// The context in which the rule is executed.
1824
public let context: Context
1925

Sources/SwiftFormatCore/SyntaxLintRule.swift

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import SwiftSyntax
1515

1616
/// A rule that lints a given file.
1717
open class SyntaxLintRule: SyntaxVisitor, Rule {
18+
/// Whether this rule is opt-in, meaning it's disabled by default. Rules are opt-out unless they
19+
/// override this property.
20+
open class var isOptIn: Bool {
21+
return false
22+
}
1823

1924
/// The context in which the rule is executed.
2025
public let context: Context

Sources/generate-pipeline/PipelineGenerator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ final class PipelineGenerator: FileGenerator {
9696
"""
9797
)
9898

99-
for ruleName in ruleCollector.allFormatters.sorted() {
99+
for ruleName in ruleCollector.allFormatters.map({ $0.typeName }).sorted() {
100100
handle.write(
101101
"""
102102
node = \(ruleName)(context: context).visit(node)

Sources/generate-pipeline/RuleCollector.swift

+15-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Foundation
14+
import SwiftFormatCore
1415
import SwiftSyntax
1516

1617
// These rules will not be added to the pipeline.
@@ -19,7 +20,7 @@ let suppressRules = ["UseEarlyExits", "UseWhereClausesInForLoops"]
1920
/// Collects information about rules in the formatter code base.
2021
final class RuleCollector {
2122
/// Information about a detected rule.
22-
private struct DetectedRule {
23+
struct DetectedRule: Hashable {
2324
/// The type name of the rule.
2425
let typeName: String
2526

@@ -28,13 +29,16 @@ final class RuleCollector {
2829

2930
/// Indicates whether the rule can format code (all rules can lint).
3031
let canFormat: Bool
32+
33+
/// Indicates whether the rule is disabled by default, i.e. requires opting in to use it.
34+
let isOptIn: Bool
3135
}
3236

3337
/// A list of all rules that can lint (thus also including format rules) found in the code base.
34-
var allLinters = Set<String>()
38+
var allLinters = Set<DetectedRule>()
3539

3640
/// A list of all the format-only rules found in the code base.
37-
var allFormatters = Set<String>()
41+
var allFormatters = Set<DetectedRule>()
3842

3943
/// A dictionary mapping syntax node types to the lint/format rules that visit them.
4044
var syntaxNodeLinters = [String: [String]]()
@@ -63,13 +67,13 @@ final class RuleCollector {
6367
if detectedRule.canFormat {
6468
// Format rules just get added to their own list; we run them each over the entire tree in
6569
// succession.
66-
allFormatters.insert(detectedRule.typeName)
70+
allFormatters.insert(detectedRule)
6771
}
6872

6973
// Lint rules (this includes format rules, which can also lint) get added to a mapping over
7074
// the names of the types they touch so that they can be interleaved into one pass over the
7175
// tree.
72-
allLinters.insert(detectedRule.typeName)
76+
allLinters.insert(detectedRule)
7377
for visitedNode in detectedRule.visitedNodes {
7478
syntaxNodeLinters[visitedNode, default: []].append(detectedRule.typeName)
7579
}
@@ -132,7 +136,12 @@ final class RuleCollector {
132136
/// Ignore it if it doesn't have any; there's no point in putting no-op rules in the pipeline.
133137
/// Otherwise, return it (we don't need to look at the rest of the inheritances).
134138
guard !visitedNodes.isEmpty else { return nil }
135-
return DetectedRule(typeName: typeName, visitedNodes: visitedNodes, canFormat: canFormat)
139+
guard let ruleType = _typeByName("SwiftFormatRules.\(typeName)") as? Rule.Type else {
140+
preconditionFailure("Failed to find type for rule named \(typeName)")
141+
}
142+
return DetectedRule(
143+
typeName: typeName, visitedNodes: visitedNodes, canFormat: canFormat,
144+
isOptIn: ruleType.isOptIn)
136145
}
137146

138147
return nil

Sources/generate-pipeline/RuleRegistryGenerator.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ final class RuleRegistryGenerator: FileGenerator {
4646
"""
4747
)
4848

49-
for ruleName in ruleCollector.allLinters.sorted() {
50-
handle.write(" \"\(ruleName)\": true,\n")
49+
for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) {
50+
handle.write(" \"\(detectedRule.typeName)\": \(!detectedRule.isOptIn),\n")
5151
}
5252
handle.write(" ]\n}\n")
5353
}

0 commit comments

Comments
 (0)