@@ -146,6 +146,122 @@ class ActiveSyntaxRewriter<Configuration: BuildConfiguration> : SyntaxRewriter {
146
146
147
147
return super. visit ( rewrittenNode)
148
148
}
149
+
150
+ /// Apply the given base to the postfix expression.
151
+ private func applyBaseToPostfixExpression(
152
+ base: ExprSyntax , postfix: ExprSyntax
153
+ ) -> ExprSyntax {
154
+ /// Try to apply the base to the postfix expression using the given
155
+ /// keypath into a specific node type.
156
+ ///
157
+ /// Returns the new expression node on success, `nil` when the node kind
158
+ /// didn't match.
159
+ func tryApply< Node: ExprSyntaxProtocol > (
160
+ _ keyPath: WritableKeyPath < Node , ExprSyntax >
161
+ ) -> ExprSyntax ? {
162
+ guard let node = postfix. as ( Node . self) else {
163
+ return nil
164
+ }
165
+
166
+ let newExpr = applyBaseToPostfixExpression ( base: base, postfix: node [ keyPath: keyPath] )
167
+ return ExprSyntax ( node. with ( keyPath, newExpr) )
168
+ }
169
+
170
+ // Member access
171
+ if let memberAccess = postfix. as ( MemberAccessExprSyntax . self) {
172
+ guard let memberBase = memberAccess. base else {
173
+ // If this member access has no base, this is the base we are
174
+ // replacing, terminating the recursion. Do so now.
175
+ return ExprSyntax ( memberAccess. with ( \. base, base) )
176
+ }
177
+
178
+ let newBase = applyBaseToPostfixExpression ( base: base, postfix: memberBase)
179
+ return ExprSyntax ( memberAccess. with ( \. base, newBase) )
180
+ }
181
+
182
+ // Generic arguments <...>
183
+ if let result = tryApply ( \SpecializeExprSyntax . expression) {
184
+ return result
185
+ }
186
+
187
+ // Call (...)
188
+ if let result = tryApply ( \FunctionCallExprSyntax . calledExpression) {
189
+ return result
190
+ }
191
+
192
+ // Subscript [...]
193
+ if let result = tryApply ( \SubscriptExprSyntax . calledExpression) {
194
+ return result
195
+ }
196
+
197
+ // Optional chaining ?
198
+ if let result = tryApply ( \OptionalChainingExprSyntax . expression) {
199
+ return result
200
+ }
201
+
202
+ // Forced optional value !
203
+ if let result = tryApply ( \ForcedValueExprSyntax . expression) {
204
+ return result
205
+ }
206
+
207
+ // Postfix unary operator.
208
+ if let result = tryApply ( \PostfixUnaryExprSyntax . expression) {
209
+ return result
210
+ }
211
+
212
+ // #if
213
+ if let postfixIfConfig = postfix. as ( PostfixIfConfigExprSyntax . self) {
214
+ return dropInactive ( outerBase: base, postfixIfConfig: postfixIfConfig)
215
+ }
216
+
217
+ assert ( false , " Unhandled postfix expression in #if elimination " )
218
+ return base
219
+ }
220
+
221
+ /// Drop inactive regions from a postfix `#if` configuration, applying the
222
+ /// outer "base" expression to the rewritten node.
223
+ private func dropInactive(
224
+ outerBase: ExprSyntax ? ,
225
+ postfixIfConfig: PostfixIfConfigExprSyntax
226
+ ) -> ExprSyntax {
227
+ // Determine the active clause within this syntax node.
228
+ // TODO: Swallows errors
229
+ guard let activeClause = try ? postfixIfConfig. config. activeClause ( in: configuration) ,
230
+ case . `postfixExpression`( let postfixExpr) = activeClause. elements else {
231
+ // If there is no active clause, return the base.
232
+
233
+ // Prefer the base we have and, if not, use the outer base.
234
+ // TODO: Can we have both? If so, then what?
235
+ if let base = postfixIfConfig. base ?? outerBase {
236
+ return base
237
+ }
238
+
239
+ // If there was no base, we're in an erroneous syntax tree that would
240
+ // never be produced by the parser. Synthesize a missing expression
241
+ // syntax node so clients can recover more gracefully.
242
+ return ExprSyntax (
243
+ MissingExprSyntax (
244
+ placeholder: . init( . identifier( " _ " ) , presence: . missing)
245
+ )
246
+ )
247
+ }
248
+
249
+ // If there is no base, return the postfix expression.
250
+ guard let base = postfixIfConfig. base ?? outerBase else {
251
+ return postfixExpr
252
+ }
253
+
254
+ // Apply the base to the postfix expression.
255
+ return applyBaseToPostfixExpression ( base: base, postfix: postfixExpr)
256
+ }
257
+
258
+ // TODO: PostfixIfConfigExprSyntax has a different form that doesn't work
259
+ // well with the way dropInactive is written. We essentially need to
260
+ // thread a the "base" into the active clause.
261
+ override func visit( _ node: PostfixIfConfigExprSyntax ) -> ExprSyntax {
262
+ let rewrittenNode = dropInactive ( outerBase: nil , postfixIfConfig: node)
263
+ return visit ( rewrittenNode)
264
+ }
149
265
}
150
266
151
267
0 commit comments