@@ -111,6 +111,10 @@ private final class _ContextInserter<C, M>: SyntaxRewriter where C: MacroExpansi
111
111
/// The nodes in this array are the _original_ nodes, not the rewritten nodes.
112
112
var rewrittenNodes = Set < Syntax > ( )
113
113
114
+ /// Any postflight code the caller should insert into the closure containing
115
+ /// the rewritten syntax tree.
116
+ var teardownItems = [ CodeBlockItemSyntax] ( )
117
+
114
118
init ( in context: C , for macro: M , rootedAt effectiveRootNode: Syntax , expressionContextName: TokenSyntax ) {
115
119
self . context = context
116
120
self . macro = macro
@@ -123,12 +127,13 @@ private final class _ContextInserter<C, M>: SyntaxRewriter where C: MacroExpansi
123
127
/// (or rather, its `callAsFunction(_:_:)` member).
124
128
///
125
129
/// - Parameters:
126
- /// - functionNameExpr: If not `nil`, the name of the function to call (as a
127
- /// member of the expression context.)
128
130
/// - node: The node to rewrite.
129
131
/// - originalNode: The original node in the original syntax tree, if `node`
130
132
/// has already been partially rewritten or substituted. If `node` has not
131
133
/// been rewritten, this argument should equal it.
134
+ /// - functionName: If not `nil`, the name of the function to call (as a
135
+ /// member function of the expression context.)
136
+ /// - additionalArguments: Any additional arguments to pass to the function.
132
137
///
133
138
/// - Returns: A rewritten copy of `node` that calls into the expression
134
139
/// context when it is evaluated at runtime.
@@ -379,8 +384,24 @@ private final class _ContextInserter<C, M>: SyntaxRewriter where C: MacroExpansi
379
384
}
380
385
381
386
override func visit( _ node: InOutExprSyntax ) -> ExprSyntax {
382
- // inout arguments cannot be forwarded through functions. In the future, we
383
- // could experiment with unsafe mutable pointers?
387
+ // Swift's Law of Exclusivity means that only one subexpression in the
388
+ // expectation ought to be interacting with `value` when it is passed
389
+ // `inout`, so it should be sufficient to capture it in a `defer` statement
390
+ // that runs after the expression is evaluated.
391
+
392
+ let teardownItem = CodeBlockItemSyntax (
393
+ item: . expr(
394
+ _rewrite (
395
+ node. expression,
396
+ originalWas: node,
397
+ calling: . identifier( " __inoutAfter " )
398
+ )
399
+ )
400
+ )
401
+ teardownItems. append ( teardownItem)
402
+
403
+ // The argument should not be expanded in-place as we can't return an
404
+ // argument passed `inout` and expect it to remain semantically correct.
384
405
return ExprSyntax ( node)
385
406
}
386
407
@@ -525,23 +546,42 @@ private final class _ContextInserter<C, M>: SyntaxRewriter where C: MacroExpansi
525
546
/// the purposes of generating expression ID values.
526
547
/// - context: The macro context in which the expression is being parsed.
527
548
///
528
- /// - Returns: A tuple containing the rewritten copy of `node` as well as a list
529
- /// of all the nodes within `node` (possibly including `node` itself) that
530
- /// were rewritten.
549
+ /// - Returns: A tuple containing the rewritten copy of `node`, a list of all
550
+ /// the nodes within `node` (possibly including `node` itself) that were
551
+ /// rewritten, and a code block containing code that should be inserted into
552
+ /// the lexical scope of `node` _before_ its rewritten equivalent.
531
553
func insertCalls(
532
554
toExpressionContextNamed expressionContextName: TokenSyntax ,
533
555
into node: some SyntaxProtocol ,
534
556
for macro: some FreestandingMacroExpansionSyntax ,
535
557
rootedAt effectiveRootNode: some SyntaxProtocol ,
536
558
in context: some MacroExpansionContext
537
- ) -> ( Syntax , rewrittenNodes: Set < Syntax > ) {
559
+ ) -> ( Syntax , rewrittenNodes: Set < Syntax > , prefixCodeBlockItems : CodeBlockItemListSyntax ) {
538
560
if let node = node. as ( ExprSyntax . self) {
539
561
_diagnoseTrivialBooleanValue ( from: node, for: macro, in: context)
540
562
}
541
563
542
564
let contextInserter = _ContextInserter ( in: context, for: macro, rootedAt: Syntax ( effectiveRootNode) , expressionContextName: expressionContextName)
543
565
let result = contextInserter. rewrite ( node)
544
- return ( result, contextInserter. rewrittenNodes)
566
+ let rewrittenNodes = contextInserter. rewrittenNodes
567
+
568
+ let prefixCodeBlockItems = CodeBlockItemListSyntax {
569
+ if !contextInserter. teardownItems. isEmpty {
570
+ CodeBlockItemSyntax (
571
+ item: . stmt(
572
+ StmtSyntax (
573
+ DeferStmtSyntax {
574
+ for teardownItem in contextInserter. teardownItems {
575
+ teardownItem
576
+ }
577
+ }
578
+ )
579
+ )
580
+ )
581
+ }
582
+ } . formatted ( ) . with ( \. trailingTrivia, . newline) . cast ( CodeBlockItemListSyntax . self)
583
+
584
+ return ( result, rewrittenNodes, prefixCodeBlockItems)
545
585
}
546
586
547
587
// MARK: - Finding optional chains
0 commit comments