Skip to content

Commit 65cf28b

Browse files
committed
[Macros] Cache parsed syntax tree in compiler plugins
The compiler may send the same syntax to the plugins multiple times. For example, 'memberAttribute' macro request contains parent nominal decl syntax, and the compiler sends a request for each members. Parsing it multiple times is a waste. rdar://129624305
1 parent e072677 commit 65cf28b

File tree

3 files changed

+57
-18
lines changed

3 files changed

+57
-18
lines changed

Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,15 @@ public class CompilerPluginMessageHandler<Provider: PluginProvider> {
112112
/// Object to provide actual plugin functions.
113113
let provider: Provider
114114

115+
/// Syntax registry shared between multiple requests.
116+
let syntaxRegistry: ParsedSyntaxRegistry
117+
115118
/// Plugin host capability
116119
var hostCapability: HostCapability
117120

118121
public init(provider: Provider) {
119122
self.provider = provider
123+
self.syntaxRegistry = ParsedSyntaxRegistry()
120124
self.hostCapability = HostCapability()
121125
}
122126

Sources/SwiftCompilerPluginMessageHandling/Macros.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ extension CompilerPluginMessageHandler {
5656
expandingSyntax: PluginMessage.Syntax,
5757
lexicalContext: [PluginMessage.Syntax]?
5858
) -> PluginToHostMessage {
59-
let sourceManager = SourceManager()
59+
let sourceManager = SourceManager(syntaxrRegistry: syntaxRegistry)
6060
let syntax = sourceManager.add(expandingSyntax, foldingWith: .standardOperators)
6161

6262
let context = PluginMacroExpansionContext(
@@ -120,7 +120,7 @@ extension CompilerPluginMessageHandler {
120120
conformanceListSyntax: PluginMessage.Syntax?,
121121
lexicalContext: [PluginMessage.Syntax]?
122122
) -> PluginToHostMessage {
123-
let sourceManager = SourceManager()
123+
let sourceManager = SourceManager(syntaxrRegistry: syntaxRegistry)
124124
let attributeNode = sourceManager.add(
125125
attributeSyntax,
126126
foldingWith: .standardOperators

Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,49 @@ import SwiftSyntax
2424
import SwiftSyntaxMacros
2525
#endif
2626

27+
/// Caching parser for PluginMessage.Syntax
28+
class ParsedSyntaxRegistry {
29+
struct Key: Hashable {
30+
let source: String
31+
let kind: PluginMessage.Syntax.Kind
32+
}
33+
34+
private var storage: [Key: Syntax] = [:]
35+
36+
private func parse(source: String, kind: PluginMessage.Syntax.Kind) -> Syntax {
37+
var parser = Parser(source)
38+
switch kind {
39+
case .declaration:
40+
return Syntax(DeclSyntax.parse(from: &parser))
41+
case .statement:
42+
return Syntax(StmtSyntax.parse(from: &parser))
43+
case .expression:
44+
return Syntax(ExprSyntax.parse(from: &parser))
45+
case .type:
46+
return Syntax(TypeSyntax.parse(from: &parser))
47+
case .pattern:
48+
return Syntax(PatternSyntax.parse(from: &parser))
49+
case .attribute:
50+
return Syntax(AttributeSyntax.parse(from: &parser))
51+
}
52+
}
53+
54+
func get(source: String, kind: PluginMessage.Syntax.Kind) -> Syntax {
55+
let key = Key(source: source, kind: kind)
56+
if let cached = storage[key] {
57+
return cached
58+
}
59+
60+
let node = parse(source: source, kind: kind)
61+
storage[key] = node
62+
return node
63+
}
64+
65+
func clear() {
66+
storage = [:]
67+
}
68+
}
69+
2770
/// Manages known source code combined with their filename/fileID. This can be
2871
/// used to get line/column from a syntax node in the managed source code.
2972
class SourceManager {
@@ -67,32 +110,24 @@ class SourceManager {
67110
var endUTF8Offset: Int
68111
}
69112

113+
/// Caching syntax parser.
114+
private let syntaxRegistry: ParsedSyntaxRegistry
115+
70116
/// Syntax added by `add(_:)` method. Keyed by the `id` of the node.
71117
private var knownSourceSyntax: [Syntax.ID: KnownSourceSyntax] = [:]
72118

119+
init(syntaxrRegistry: ParsedSyntaxRegistry) {
120+
self.syntaxRegistry = syntaxrRegistry
121+
}
122+
73123
/// Convert syntax information to a ``Syntax`` node. The location informations
74124
/// are cached in the source manager to provide `location(of:)` et al.
75125
func add(
76126
_ syntaxInfo: PluginMessage.Syntax,
77127
foldingWith operatorTable: OperatorTable? = nil
78128
) -> Syntax {
79129

80-
var node: Syntax
81-
var parser = Parser(syntaxInfo.source)
82-
switch syntaxInfo.kind {
83-
case .declaration:
84-
node = Syntax(DeclSyntax.parse(from: &parser))
85-
case .statement:
86-
node = Syntax(StmtSyntax.parse(from: &parser))
87-
case .expression:
88-
node = Syntax(ExprSyntax.parse(from: &parser))
89-
case .type:
90-
node = Syntax(TypeSyntax.parse(from: &parser))
91-
case .pattern:
92-
node = Syntax(PatternSyntax.parse(from: &parser))
93-
case .attribute:
94-
node = Syntax(AttributeSyntax.parse(from: &parser))
95-
}
130+
var node = syntaxRegistry.get(source: syntaxInfo.source, kind: syntaxInfo.kind)
96131
if let operatorTable {
97132
node = operatorTable.foldAll(node, errorHandler: { _ in /*ignore*/ })
98133
}

0 commit comments

Comments
 (0)