Skip to content

Commit da2f1ec

Browse files
authored
Merge pull request #265 from allevato/swift-5.5-cherrypicks
Cherry-pick recent changes for Swift 5.5.
2 parents aaed43c + d8658f3 commit da2f1ec

19 files changed

+179
-52
lines changed

Package.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ let package = Package(
4747
),
4848
.target(
4949
name: "SwiftFormatTestSupport",
50-
dependencies: ["SwiftFormatCore", "SwiftFormatConfiguration"]
50+
dependencies: [
51+
"SwiftFormatCore",
52+
"SwiftFormatRules",
53+
"SwiftFormatConfiguration",
54+
]
5155
),
5256
.target(
5357
name: "SwiftFormatWhitespaceLinter",

Sources/SwiftFormat/LintPipeline.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extension LintPipeline {
2929
func visitIfEnabled<Rule: SyntaxLintRule, Node: SyntaxProtocol>(
3030
_ visitor: (Rule) -> (Node) -> SyntaxVisitorContinueKind, for node: Node
3131
) {
32-
guard context.isRuleEnabled(Rule.self.ruleName, node: Syntax(node)) else { return }
32+
guard context.isRuleEnabled(Rule.self, node: Syntax(node)) else { return }
3333
let rule = self.rule(Rule.self)
3434
_ = visitor(rule)(node)
3535
}
@@ -50,7 +50,7 @@ extension LintPipeline {
5050
// more importantly because the `visit` methods return protocol refinements of `Syntax` that
5151
// cannot currently be expressed as constraints without duplicating this function for each of
5252
// them individually.
53-
guard context.isRuleEnabled(Rule.self.ruleName, node: Syntax(node)) else { return }
53+
guard context.isRuleEnabled(Rule.self, node: Syntax(node)) else { return }
5454
let rule = self.rule(Rule.self)
5555
_ = visitor(rule)(node)
5656
}

Sources/SwiftFormat/Pipelines+Generated.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class LintPipeline: SyntaxVisitor {
6363
override func visit(_ node: CodeBlockItemListSyntax) -> SyntaxVisitorContinueKind {
6464
visitIfEnabled(DoNotUseSemicolons.visit, for: node)
6565
visitIfEnabled(OneVariableDeclarationPerLine.visit, for: node)
66+
visitIfEnabled(UseEarlyExits.visit, for: node)
6667
return .visitChildren
6768
}
6869

@@ -106,6 +107,11 @@ class LintPipeline: SyntaxVisitor {
106107
return .visitChildren
107108
}
108109

110+
override func visit(_ node: ForInStmtSyntax) -> SyntaxVisitorContinueKind {
111+
visitIfEnabled(UseWhereClausesInForLoops.visit, for: node)
112+
return .visitChildren
113+
}
114+
109115
override func visit(_ node: ForcedValueExprSyntax) -> SyntaxVisitorContinueKind {
110116
visitIfEnabled(NeverForceUnwrap.visit, for: node)
111117
return .visitChildren
@@ -314,9 +320,11 @@ extension FormatPipeline {
314320
node = OneVariableDeclarationPerLine(context: context).visit(node)
315321
node = OrderedImports(context: context).visit(node)
316322
node = ReturnVoidInsteadOfEmptyTuple(context: context).visit(node)
323+
node = UseEarlyExits(context: context).visit(node)
317324
node = UseShorthandTypeNames(context: context).visit(node)
318325
node = UseSingleLinePropertyGetter(context: context).visit(node)
319326
node = UseTripleSlashForDocumentationComments(context: context).visit(node)
327+
node = UseWhereClausesInForLoops(context: context).visit(node)
320328
return node
321329
}
322330
}

Sources/SwiftFormat/SwiftFormatter.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Foundation
1414
import SwiftFormatConfiguration
1515
import SwiftFormatCore
1616
import SwiftFormatPrettyPrint
17+
import SwiftFormatRules
1718
import SwiftSyntax
1819

1920
/// Formats Swift source code or syntax trees according to the Swift style guidelines.
@@ -108,7 +109,7 @@ public final class SwiftFormatter {
108109
let assumedURL = url ?? URL(fileURLWithPath: "source")
109110
let context = Context(
110111
configuration: configuration, diagnosticEngine: diagnosticEngine, fileURL: assumedURL,
111-
sourceFileSyntax: syntax, source: source)
112+
sourceFileSyntax: syntax, source: source, ruleNameCache: ruleNameCache)
112113
let pipeline = FormatPipeline(context: context)
113114
let transformedSyntax = pipeline.visit(Syntax(syntax))
114115

Sources/SwiftFormat/SwiftLinter.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Foundation
1414
import SwiftFormatConfiguration
1515
import SwiftFormatCore
1616
import SwiftFormatPrettyPrint
17+
import SwiftFormatRules
1718
import SwiftFormatWhitespaceLinter
1819
import SwiftSyntax
1920

@@ -88,7 +89,7 @@ public final class SwiftLinter {
8889

8990
let context = Context(
9091
configuration: configuration, diagnosticEngine: diagnosticEngine, fileURL: url,
91-
sourceFileSyntax: syntax, source: source)
92+
sourceFileSyntax: syntax, source: source, ruleNameCache: ruleNameCache)
9293
let pipeline = LintPipeline(context: context)
9394
pipeline.walk(Syntax(syntax))
9495

Sources/SwiftFormatConfiguration/RuleRegistry+Generated.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ enum RuleRegistry {
4040
"OnlyOneTrailingClosureArgument": true,
4141
"OrderedImports": true,
4242
"ReturnVoidInsteadOfEmptyTuple": true,
43+
"UseEarlyExits": false,
4344
"UseLetInEveryBoundCaseVariable": true,
4445
"UseShorthandTypeNames": true,
4546
"UseSingleLinePropertyGetter": true,
4647
"UseSynthesizedInitializer": true,
4748
"UseTripleSlashForDocumentationComments": true,
49+
"UseWhereClausesInForLoops": false,
4850
"ValidateDocumentationComments": false,
4951
]
5052
}

Sources/SwiftFormatCore/Context.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,17 @@ public class Context {
5252
/// Contains the rules have been disabled by comments for certain line numbers.
5353
public let ruleMask: RuleMask
5454

55+
/// Contains all the available rules' names associated to their types' object identifiers.
56+
public let ruleNameCache: [ObjectIdentifier: String]
57+
5558
/// Creates a new Context with the provided configuration, diagnostic engine, and file URL.
5659
public init(
5760
configuration: Configuration,
5861
diagnosticEngine: DiagnosticEngine?,
5962
fileURL: URL,
6063
sourceFileSyntax: SourceFileSyntax,
61-
source: String? = nil
64+
source: String? = nil,
65+
ruleNameCache: [ObjectIdentifier: String]
6266
) {
6367
self.configuration = configuration
6468
self.diagnosticEngine = diagnosticEngine
@@ -71,12 +75,22 @@ public class Context {
7175
syntaxNode: Syntax(sourceFileSyntax),
7276
sourceLocationConverter: sourceLocationConverter
7377
)
78+
self.ruleNameCache = ruleNameCache
7479
}
7580

7681
/// Given a rule's name and the node it is examining, determine if the rule is disabled at this
7782
/// location or not.
78-
public func isRuleEnabled(_ ruleName: String, node: Syntax) -> Bool {
83+
public func isRuleEnabled<R: Rule>(_ rule: R.Type, node: Syntax) -> Bool {
7984
let loc = node.startLocation(converter: self.sourceLocationConverter)
85+
86+
assert(
87+
ruleNameCache[ObjectIdentifier(rule)] != nil,
88+
"""
89+
Missing cached rule name for '\(rule)'! \
90+
Ensure `generate-pipelines` has been run and `ruleNameCache` was injected.
91+
""")
92+
93+
let ruleName = ruleNameCache[ObjectIdentifier(rule)] ?? R.ruleName
8094
switch ruleMask.ruleState(ruleName, at: loc) {
8195
case .default:
8296
return configuration.rules[ruleName] ?? false

Sources/SwiftFormatCore/Rule.swift

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public protocol Rule {
1717
/// The context in which the rule is executed.
1818
var context: Context { get }
1919

20-
/// The human-readable name of the rule. This defaults to the class name.
20+
/// The human-readable name of the rule. This defaults to the type name.
2121
static var ruleName: String { get }
2222

2323
/// Whether this rule is opt-in, meaning it is disabled by default.
@@ -27,27 +27,7 @@ public protocol Rule {
2727
init(context: Context)
2828
}
2929

30-
fileprivate var nameCache = [ObjectIdentifier: String]()
31-
fileprivate var nameCacheQueue = DispatchQueue(
32-
label: "com.apple.SwiftFormat.NameCache", attributes: .concurrent)
33-
3430
extension Rule {
3531
/// By default, the `ruleName` is just the name of the implementing rule class.
36-
public static var ruleName: String {
37-
let identifier = ObjectIdentifier(self)
38-
let cachedName = nameCacheQueue.sync {
39-
nameCache[identifier]
40-
}
41-
42-
if let cachedName = cachedName {
43-
return cachedName
44-
}
45-
46-
let name = String("\(self)".split(separator: ".").last!)
47-
nameCacheQueue.async(flags: .barrier) {
48-
nameCache[identifier] = name
49-
}
50-
51-
return name
52-
}
32+
public static var ruleName: String { String("\(self)".split(separator: ".").last!) }
5333
}

Sources/SwiftFormatCore/SyntaxFormatRule.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ open class SyntaxFormatRule: SyntaxRewriter, Rule {
3131
open override func visitAny(_ node: Syntax) -> Syntax? {
3232
// If the rule is not enabled, then return the node unmodified; otherwise, returning nil tells
3333
// SwiftSyntax to continue with the standard dispatch.
34-
guard context.isRuleEnabled(Self.ruleName, node: node) else { return node }
34+
guard context.isRuleEnabled(type(of: self), node: node) else { return node }
3535
return nil
3636
}
3737
}

Sources/SwiftFormatRules/OrderedImports.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ fileprivate func generateLines(codeBlockItemList: CodeBlockItemListSyntax, conte
324324
lines.append(currentLine)
325325
currentLine = Line()
326326
}
327-
let sortable = context.isRuleEnabled(OrderedImports.ruleName, node: Syntax(block))
327+
let sortable = context.isRuleEnabled(OrderedImports.self, node: Syntax(block))
328328
currentLine.syntaxNode = .importCodeBlock(block, sortable: sortable)
329329
} else {
330330
guard let syntaxNode = currentLine.syntaxNode else {

0 commit comments

Comments
 (0)