Skip to content

Commit ce0bbb1

Browse files
committed
[ASTGen] Generate postfix #if expressions
1 parent e362264 commit ce0bbb1

File tree

2 files changed

+234
-19
lines changed

2 files changed

+234
-19
lines changed

lib/ASTGen/Sources/ASTGen/Exprs.swift

Lines changed: 104 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ func isExprMigrated(_ node: ExprSyntax) -> Bool {
4747
.editorPlaceholderExpr, .floatLiteralExpr, .forceUnwrapExpr, .functionCallExpr,
4848
.genericSpecializationExpr, .ifExpr, .infixOperatorExpr, .inOutExpr,
4949
.integerLiteralExpr, .isExpr, .memberAccessExpr, .nilLiteralExpr, .optionalChainingExpr,
50-
.packElementExpr, .packExpansionExpr, .patternExpr,
50+
.packElementExpr, .packExpansionExpr, .patternExpr, .postfixIfConfigExpr,
5151
.postfixOperatorExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr,
5252
.simpleStringLiteralExpr, .subscriptCallExpr, .stringLiteralExpr, .superExpr,
5353
.switchExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr,
5454
.unresolvedTernaryExpr, .ternaryExpr:
5555
break
5656

5757
// Known unimplemented kinds.
58-
case .keyPathExpr, .macroExpansionExpr, .postfixIfConfigExpr:
58+
case .keyPathExpr, .macroExpansionExpr:
5959
return false
6060

6161
// Unknown expr kinds.
@@ -141,15 +141,14 @@ extension ASTGenVisitor {
141141
return self.generate(nilLiteralExpr: node).asExpr
142142
case .optionalChainingExpr(let node):
143143
return self.generate(optionalChainingExpr: node).asExpr
144-
break
145144
case .packElementExpr(let node):
146145
return self.generate(packElementExpr: node).asExpr
147146
case .packExpansionExpr(let node):
148147
return self.generate(packExpansionExpr: node).asExpr
149148
case .patternExpr(let node):
150149
return self.generate(patternExpr: node).asExpr
151-
case .postfixIfConfigExpr:
152-
break
150+
case .postfixIfConfigExpr(let node):
151+
return self.generate(postfixIfConfigExpr: node)
153152
case .postfixOperatorExpr(let node):
154153
return self.generate(postfixOperatorExpr: node).asExpr
155154
case .prefixOperatorExpr(let node):
@@ -188,6 +187,45 @@ extension ASTGenVisitor {
188187
preconditionFailure("isExprMigrated() mismatch")
189188
}
190189

190+
func generate(expr node: ExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedExpr {
191+
if let postfixIfConfigBaseExpr {
192+
// Generating tail part of a postfix #if expression.
193+
return self.generatePostfixIfConfigExprSuffix(expr: node, baseExpr: postfixIfConfigBaseExpr)
194+
} else {
195+
return self.generate(expr: node)
196+
}
197+
}
198+
199+
/// Generate function for interior of postfix #if expressions. The base expression is provided by the caller.
200+
///
201+
/// ```
202+
/// foo // <- baseExpr
203+
/// #if FLAG
204+
/// .bar(arg)?[idx]!++ // <- node
205+
/// #endif
206+
/// ```
207+
func generatePostfixIfConfigExprSuffix(expr node: ExprSyntax, baseExpr: BridgedExpr) -> BridgedExpr {
208+
switch node.as(ExprSyntaxEnum.self) {
209+
case .memberAccessExpr(let node):
210+
return self.generate(memberAccessExpr: node, postfixIfConfigBaseExpr: baseExpr)
211+
case .postfixIfConfigExpr(let node):
212+
return self.generate(postfixIfConfigExpr: node, postfixIfConfigBaseExpr: baseExpr)
213+
case .functionCallExpr(let node):
214+
return self.generate(functionCallExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
215+
case .subscriptCallExpr(let node):
216+
return self.generate(subscriptCallExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
217+
case .postfixOperatorExpr(let node):
218+
return self.generate(postfixOperatorExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
219+
case .optionalChainingExpr(let node):
220+
return self.generate(optionalChainingExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
221+
case .forceUnwrapExpr(let node):
222+
return self.generate(forceUnwrapExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
223+
default:
224+
// FIXME: Diagnose 'invalid expression for a postfix #if expression'
225+
preconditionFailure("expected postfix expression suffix in #if expression clause")
226+
}
227+
}
228+
191229
func generate(arrowExpr node: ArrowExprSyntax) -> BridgedArrowExpr {
192230
let asyncLoc: BridgedSourceLoc
193231
let throwsLoc: BridgedSourceLoc
@@ -292,10 +330,10 @@ extension ASTGenVisitor {
292330
)
293331
}
294332

295-
func generate(forceUnwrapExpr node: ForceUnwrapExprSyntax) -> BridgedForceValueExpr {
333+
func generate(forceUnwrapExpr node: ForceUnwrapExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedForceValueExpr {
296334
return .createParsed(
297335
self.ctx,
298-
subExpr: self.generate(expr: node.expression),
336+
subExpr: self.generate(expr: node.expression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr),
299337
exclaimLoc: self.generateSourceLoc(node.exclamationMark)
300338
)
301339
}
@@ -365,7 +403,7 @@ extension ASTGenVisitor {
365403
)
366404
}
367405

368-
func generate(functionCallExpr node: FunctionCallExprSyntax) -> BridgedCallExpr {
406+
func generate(functionCallExpr node: FunctionCallExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedCallExpr {
369407
if !node.arguments.isEmpty || node.trailingClosure == nil {
370408
if node.leftParen == nil {
371409
self.diagnose(
@@ -379,7 +417,7 @@ extension ASTGenVisitor {
379417
}
380418
}
381419

382-
let callee = generate(expr: node.calledExpression)
420+
let callee = self.generate(expr: node.calledExpression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
383421
let arguments = generateArgumentList(
384422
leftParen: node.leftParen,
385423
labeledExprList: node.arguments,
@@ -497,25 +535,40 @@ extension ASTGenVisitor {
497535
)
498536
}
499537

500-
func generate(memberAccessExpr node: MemberAccessExprSyntax) -> BridgedExpr {
538+
func generate(memberAccessExpr node: MemberAccessExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedExpr {
539+
let baseExpr: BridgedExpr?
540+
if let base = node.base {
541+
baseExpr = self.generate(expr: base, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
542+
} else if let postfixIfConfigBaseExpr {
543+
// Dot member syntax right after '#if' line. E.g.
544+
// foo // <- postfixIfConfigBaseExpr
545+
// #if FLAG
546+
// .bar // <- Generating this.
547+
// .baz
548+
// #endif
549+
baseExpr = postfixIfConfigBaseExpr
550+
} else {
551+
baseExpr = nil
552+
}
553+
501554
let dotLoc = self.generateSourceLoc(node.period)
502555
let nameAndLoc = generateDeclNameRef(declReferenceExpr: node.declName)
503556

504-
if let base = node.base {
557+
if let baseExpr {
505558
if node.declName.baseName.keywordKind == .`self` {
506559
// TODO: Diagnose if there's arguments
507560
assert(node.declName.argumentNames == nil)
508561

509562
return BridgedDotSelfExpr.createParsed(
510563
self.ctx,
511-
subExpr: self.generate(expr: base),
564+
subExpr: baseExpr,
512565
dotLoc: dotLoc,
513566
selfLoc: self.generateSourceLoc(node.declName)
514567
).asExpr
515568
} else {
516569
return BridgedUnresolvedDotExpr.createParsed(
517570
self.ctx,
518-
base: self.generate(expr: base),
571+
base: baseExpr,
519572
dotLoc: dotLoc,
520573
name: nameAndLoc.name,
521574
nameLoc: nameAndLoc.loc
@@ -548,10 +601,10 @@ extension ASTGenVisitor {
548601
)
549602
}
550603

551-
func generate(optionalChainingExpr node: OptionalChainingExprSyntax) -> BridgedBindOptionalExpr {
604+
func generate(optionalChainingExpr node: OptionalChainingExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedBindOptionalExpr {
552605
return .createParsed(
553606
self.ctx,
554-
subExpr: self.generate(expr: node.expression),
607+
subExpr: self.generate(expr: node.expression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr),
555608
questionLoc: self.generateSourceLoc(node.questionMark)
556609
)
557610
}
@@ -600,14 +653,46 @@ extension ASTGenVisitor {
600653
)
601654
}
602655

603-
func generate(postfixOperatorExpr node: PostfixOperatorExprSyntax) -> BridgedPostfixUnaryExpr {
656+
func generate(postfixIfConfigExpr node: PostfixIfConfigExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedExpr {
657+
let baseExpr: BridgedExpr
658+
if let base = node.base {
659+
baseExpr = self.generate(expr: base, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
660+
} else if let postfixIfConfigBaseExpr {
661+
// This is a nested postifx #if expression. E.g.
662+
//
663+
// foo // <- postfixIfConfigBaseExpr
664+
// #if FLAG
665+
// #if FLAG2 // <- This
666+
// .bar
667+
// #endif
668+
// .baz
669+
// #endif
670+
//
671+
baseExpr = postfixIfConfigBaseExpr
672+
} else {
673+
// FIXME: Diagnostics
674+
preconditionFailure("expected PostfixIfConfigExprSyntax.base not nil")
675+
}
676+
677+
guard let active = self.activeClause(in: node.config) else {
678+
return baseExpr
679+
}
680+
guard case .postfixExpression(let parsedTail) = active.elements else {
681+
// FIXME: Diagnostics
682+
preconditionFailure("expected postfixExpression in IfConfigClauseSyntax.Elements")
683+
}
684+
return self.generatePostfixIfConfigExprSuffix(expr: parsedTail, baseExpr: baseExpr)
685+
}
686+
687+
func generate(postfixOperatorExpr node: PostfixOperatorExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedPostfixUnaryExpr {
688+
let operand = self.generate(expr: node.expression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
604689
return .createParsed(
605690
self.ctx,
606691
operator: self.createOperatorRefExpr(
607692
token: node.operator,
608693
kind: .postfixOperator
609694
).asExpr,
610-
operand: self.generate(expr: node.expression)
695+
operand: operand
611696
)
612697
}
613698

@@ -691,8 +776,8 @@ extension ASTGenVisitor {
691776
).asExpr
692777
}
693778

694-
func generate(subscriptCallExpr node: SubscriptCallExprSyntax) -> BridgedSubscriptExpr {
695-
let callee = generate(expr: node.calledExpression)
779+
func generate(subscriptCallExpr node: SubscriptCallExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedSubscriptExpr {
780+
let callee = generate(expr: node.calledExpression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
696781
let arguments = generateArgumentList(
697782
leftParen: node.leftSquare,
698783
labeledExprList: node.arguments,

test/ASTGen/if_config_expr.swift

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature ParserASTGen -D CONDITION_1
2+
3+
// -enable-experimental-feature requires an asserts build
4+
// REQUIRES: asserts
5+
// REQUIRES: swift_swift_parser
6+
7+
postfix operator ++
8+
postfix func ++ (_: Int) -> Int { 0 }
9+
10+
struct OneResult {}
11+
struct TwoResult {}
12+
13+
protocol MyProto {
14+
func optionalMethod() -> [Int]?
15+
}
16+
struct MyStruct {
17+
var optionalMember: MyProto? { nil }
18+
func methodOne() -> OneResult { OneResult() }
19+
func methodTwo() -> TwoResult { TwoResult() }
20+
}
21+
22+
func globalFunc<T>(_ arg: T) -> T { arg }
23+
24+
func testBasic(baseExpr: MyStruct) {
25+
baseExpr
26+
#if CONDITION_1
27+
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
28+
#else
29+
.methodTwo()
30+
#endif
31+
}
32+
33+
MyStruct()
34+
#if CONDITION_1
35+
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
36+
#else
37+
.methodTwo()
38+
#endif
39+
40+
41+
func testInvalidContent(baseExpr: MyStruct, otherExpr: Int) {
42+
baseExpr // expected-warning {{expression of type 'MyStruct' is unused}}
43+
#if CONDITION_1
44+
{ print(1) } // expected-error {{closure expression is unused}}
45+
#endif
46+
47+
baseExpr // expected-warning {{expression of type 'MyStruct' is unused}}
48+
#if CONDITION_1
49+
+ otherExpr // expected-error {{unexpected code '+ otherExpr' in conditional compilation block}}
50+
#endif
51+
52+
baseExpr
53+
#if CONDITION_1
54+
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
55+
56+
print("debug") // expected-error {{unexpected code 'print("debug")' in conditional compilation block}}
57+
#endif
58+
}
59+
60+
func testExprKind(baseExpr: MyStruct, idx: Int) {
61+
baseExpr
62+
#if CONDITION_1
63+
.optionalMember?.optionalMethod()![idx]++ // expected-warning {{result of operator '++' is unused}}
64+
#else
65+
.otherMethod(arg) {
66+
//...
67+
}
68+
#endif
69+
70+
baseExpr
71+
#if CONDITION_1
72+
.methodOne() + 12 // expected-error {{unexpected code '+ 12' in conditional compilation block}}
73+
// expected-warning@-1 {{result of call to 'methodOne()' is unused}}
74+
#endif
75+
}
76+
77+
func emptyElse(baseExpr: MyStruct) {
78+
baseExpr
79+
#if CONDITION_1
80+
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
81+
#elseif CONDITION_2
82+
// OK. Do nothing.
83+
#endif
84+
85+
baseExpr
86+
#if CONDITION_1
87+
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
88+
#elseif CONDITION_2
89+
return // expected-error {{unexpected 'return' keyword in conditional compilation block}}
90+
#endif
91+
}
92+
93+
func consecutiveIfConfig(baseExpr: MyStruct) {
94+
baseExpr
95+
#if CONDITION_1
96+
.methodOne()
97+
#endif
98+
#if CONDITION_2
99+
.methodTwo()
100+
#endif
101+
.unknownMethod() // expected-error {{value of type 'OneResult' has no member 'unknownMethod'}}
102+
}
103+
104+
func nestedIfConfig(baseExpr: MyStruct) {
105+
baseExpr
106+
#if CONDITION_1
107+
#if CONDITION_2
108+
.methodOne()
109+
#endif
110+
#if CONDITION_1
111+
.methodTwo() // expected-warning {{result of call to 'methodTwo()' is unused}}
112+
#endif
113+
#else
114+
.unknownMethod1()
115+
#if CONDITION_2
116+
.unknownMethod2()
117+
#endif
118+
#endif
119+
}
120+
121+
func ifconfigExprInExpr(baseExpr: MyStruct) {
122+
globalFunc( // expected-warning {{result of call to 'globalFunc' is unused}}
123+
baseExpr
124+
#if CONDITION_1
125+
.methodOne()
126+
#else
127+
.methodTwo()
128+
#endif
129+
)
130+
}

0 commit comments

Comments
 (0)