Skip to content

Commit d53cd0b

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

File tree

2 files changed

+230
-19
lines changed

2 files changed

+230
-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/ifconfig_expr.swift

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

0 commit comments

Comments
 (0)