Skip to content

[ASTGen] Generate postfix #if expressions #77089

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 104 additions & 19 deletions lib/ASTGen/Sources/ASTGen/Exprs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ func isExprMigrated(_ node: ExprSyntax) -> Bool {
.editorPlaceholderExpr, .floatLiteralExpr, .forceUnwrapExpr, .functionCallExpr,
.genericSpecializationExpr, .ifExpr, .infixOperatorExpr, .inOutExpr,
.integerLiteralExpr, .isExpr, .memberAccessExpr, .nilLiteralExpr, .optionalChainingExpr,
.packElementExpr, .packExpansionExpr, .patternExpr,
.packElementExpr, .packExpansionExpr, .patternExpr, .postfixIfConfigExpr,
.postfixOperatorExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr,
.simpleStringLiteralExpr, .subscriptCallExpr, .stringLiteralExpr, .superExpr,
.switchExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr,
.unresolvedTernaryExpr, .ternaryExpr:
break

// Known unimplemented kinds.
case .keyPathExpr, .macroExpansionExpr, .postfixIfConfigExpr:
case .keyPathExpr, .macroExpansionExpr:
return false

// Unknown expr kinds.
Expand Down Expand Up @@ -141,15 +141,14 @@ extension ASTGenVisitor {
return self.generate(nilLiteralExpr: node).asExpr
case .optionalChainingExpr(let node):
return self.generate(optionalChainingExpr: node).asExpr
break
case .packElementExpr(let node):
return self.generate(packElementExpr: node).asExpr
case .packExpansionExpr(let node):
return self.generate(packExpansionExpr: node).asExpr
case .patternExpr(let node):
return self.generate(patternExpr: node).asExpr
case .postfixIfConfigExpr:
break
case .postfixIfConfigExpr(let node):
return self.generate(postfixIfConfigExpr: node)
case .postfixOperatorExpr(let node):
return self.generate(postfixOperatorExpr: node).asExpr
case .prefixOperatorExpr(let node):
Expand Down Expand Up @@ -188,6 +187,45 @@ extension ASTGenVisitor {
preconditionFailure("isExprMigrated() mismatch")
}

func generate(expr node: ExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedExpr {
if let postfixIfConfigBaseExpr {
// Generating tail part of a postfix #if expression.
return self.generatePostfixIfConfigExprSuffix(expr: node, baseExpr: postfixIfConfigBaseExpr)
} else {
return self.generate(expr: node)
}
}

/// Generate function for interior of postfix #if expressions. The base expression is provided by the caller.
///
/// ```
/// foo // <- baseExpr
/// #if FLAG
/// .bar(arg)?[idx]!++ // <- node
/// #endif
/// ```
func generatePostfixIfConfigExprSuffix(expr node: ExprSyntax, baseExpr: BridgedExpr) -> BridgedExpr {
switch node.as(ExprSyntaxEnum.self) {
case .memberAccessExpr(let node):
return self.generate(memberAccessExpr: node, postfixIfConfigBaseExpr: baseExpr)
case .postfixIfConfigExpr(let node):
return self.generate(postfixIfConfigExpr: node, postfixIfConfigBaseExpr: baseExpr)
case .functionCallExpr(let node):
return self.generate(functionCallExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
case .subscriptCallExpr(let node):
return self.generate(subscriptCallExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
case .postfixOperatorExpr(let node):
return self.generate(postfixOperatorExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
case .optionalChainingExpr(let node):
return self.generate(optionalChainingExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
case .forceUnwrapExpr(let node):
return self.generate(forceUnwrapExpr: node, postfixIfConfigBaseExpr: baseExpr).asExpr
default:
// FIXME: Diagnose 'invalid expression for a postfix #if expression'
preconditionFailure("expected postfix expression suffix in #if expression clause")
}
}

func generate(arrowExpr node: ArrowExprSyntax) -> BridgedArrowExpr {
let asyncLoc: BridgedSourceLoc
let throwsLoc: BridgedSourceLoc
Expand Down Expand Up @@ -292,10 +330,10 @@ extension ASTGenVisitor {
)
}

func generate(forceUnwrapExpr node: ForceUnwrapExprSyntax) -> BridgedForceValueExpr {
func generate(forceUnwrapExpr node: ForceUnwrapExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedForceValueExpr {
return .createParsed(
self.ctx,
subExpr: self.generate(expr: node.expression),
subExpr: self.generate(expr: node.expression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr),
exclaimLoc: self.generateSourceLoc(node.exclamationMark)
)
}
Expand Down Expand Up @@ -365,7 +403,7 @@ extension ASTGenVisitor {
)
}

func generate(functionCallExpr node: FunctionCallExprSyntax) -> BridgedCallExpr {
func generate(functionCallExpr node: FunctionCallExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedCallExpr {
if !node.arguments.isEmpty || node.trailingClosure == nil {
if node.leftParen == nil {
self.diagnose(
Expand All @@ -379,7 +417,7 @@ extension ASTGenVisitor {
}
}

let callee = generate(expr: node.calledExpression)
let callee = self.generate(expr: node.calledExpression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
let arguments = generateArgumentList(
leftParen: node.leftParen,
labeledExprList: node.arguments,
Expand Down Expand Up @@ -497,25 +535,40 @@ extension ASTGenVisitor {
)
}

func generate(memberAccessExpr node: MemberAccessExprSyntax) -> BridgedExpr {
func generate(memberAccessExpr node: MemberAccessExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedExpr {
let baseExpr: BridgedExpr?
if let base = node.base {
baseExpr = self.generate(expr: base, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
} else if let postfixIfConfigBaseExpr {
// Dot member syntax right after '#if' line. E.g.
// foo // <- postfixIfConfigBaseExpr
// #if FLAG
// .bar // <- Generating this.
// .baz
// #endif
baseExpr = postfixIfConfigBaseExpr
} else {
baseExpr = nil
}

let dotLoc = self.generateSourceLoc(node.period)
let nameAndLoc = generateDeclNameRef(declReferenceExpr: node.declName)

if let base = node.base {
if let baseExpr {
if node.declName.baseName.keywordKind == .`self` {
// TODO: Diagnose if there's arguments
assert(node.declName.argumentNames == nil)

return BridgedDotSelfExpr.createParsed(
self.ctx,
subExpr: self.generate(expr: base),
subExpr: baseExpr,
dotLoc: dotLoc,
selfLoc: self.generateSourceLoc(node.declName)
).asExpr
} else {
return BridgedUnresolvedDotExpr.createParsed(
self.ctx,
base: self.generate(expr: base),
base: baseExpr,
dotLoc: dotLoc,
name: nameAndLoc.name,
nameLoc: nameAndLoc.loc
Expand Down Expand Up @@ -548,10 +601,10 @@ extension ASTGenVisitor {
)
}

func generate(optionalChainingExpr node: OptionalChainingExprSyntax) -> BridgedBindOptionalExpr {
func generate(optionalChainingExpr node: OptionalChainingExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedBindOptionalExpr {
return .createParsed(
self.ctx,
subExpr: self.generate(expr: node.expression),
subExpr: self.generate(expr: node.expression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr),
questionLoc: self.generateSourceLoc(node.questionMark)
)
}
Expand Down Expand Up @@ -600,14 +653,46 @@ extension ASTGenVisitor {
)
}

func generate(postfixOperatorExpr node: PostfixOperatorExprSyntax) -> BridgedPostfixUnaryExpr {
func generate(postfixIfConfigExpr node: PostfixIfConfigExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedExpr {
let baseExpr: BridgedExpr
if let base = node.base {
baseExpr = self.generate(expr: base, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
} else if let postfixIfConfigBaseExpr {
// This is a nested postifx #if expression. E.g.
//
// foo // <- postfixIfConfigBaseExpr
// #if FLAG
// #if FLAG2 // <- This
// .bar
// #endif
// .baz
// #endif
//
baseExpr = postfixIfConfigBaseExpr
} else {
// FIXME: Diagnostics
preconditionFailure("expected PostfixIfConfigExprSyntax.base not nil")
}

guard let active = self.activeClause(in: node.config) else {
return baseExpr
}
guard case .postfixExpression(let parsedTail) = active.elements else {
// FIXME: Diagnostics
preconditionFailure("expected postfixExpression in IfConfigClauseSyntax.Elements")
}
return self.generatePostfixIfConfigExprSuffix(expr: parsedTail, baseExpr: baseExpr)
}

func generate(postfixOperatorExpr node: PostfixOperatorExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedPostfixUnaryExpr {
let operand = self.generate(expr: node.expression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
return .createParsed(
self.ctx,
operator: self.createOperatorRefExpr(
token: node.operator,
kind: .postfixOperator
).asExpr,
operand: self.generate(expr: node.expression)
operand: operand
)
}

Expand Down Expand Up @@ -691,8 +776,8 @@ extension ASTGenVisitor {
).asExpr
}

func generate(subscriptCallExpr node: SubscriptCallExprSyntax) -> BridgedSubscriptExpr {
let callee = generate(expr: node.calledExpression)
func generate(subscriptCallExpr node: SubscriptCallExprSyntax, postfixIfConfigBaseExpr: BridgedExpr? = nil) -> BridgedSubscriptExpr {
let callee = generate(expr: node.calledExpression, postfixIfConfigBaseExpr: postfixIfConfigBaseExpr)
let arguments = generateArgumentList(
leftParen: node.leftSquare,
labeledExprList: node.arguments,
Expand Down
130 changes: 130 additions & 0 deletions test/ASTGen/if_config_expr.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature ParserASTGen -D CONDITION_1

// -enable-experimental-feature requires an asserts build
// REQUIRES: asserts
// REQUIRES: swift_swift_parser

postfix operator ++
postfix func ++ (_: Int) -> Int { 0 }

struct OneResult {}
struct TwoResult {}

protocol MyProto {
func optionalMethod() -> [Int]?
}
struct MyStruct {
var optionalMember: MyProto? { nil }
func methodOne() -> OneResult { OneResult() }
func methodTwo() -> TwoResult { TwoResult() }
}

func globalFunc<T>(_ arg: T) -> T { arg }

func testBasic(baseExpr: MyStruct) {
baseExpr
#if CONDITION_1
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
#else
.methodTwo()
#endif
}

MyStruct()
#if CONDITION_1
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
#else
.methodTwo()
#endif


func testInvalidContent(baseExpr: MyStruct, otherExpr: Int) {
baseExpr // expected-warning {{expression of type 'MyStruct' is unused}}
#if CONDITION_1
{ print(1) } // expected-error {{closure expression is unused}}
#endif

baseExpr // expected-warning {{expression of type 'MyStruct' is unused}}
#if CONDITION_1
+ otherExpr // expected-error {{unexpected code '+ otherExpr' in conditional compilation block}}
#endif

baseExpr
#if CONDITION_1
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}

print("debug") // expected-error {{unexpected code 'print("debug")' in conditional compilation block}}
#endif
}

func testExprKind(baseExpr: MyStruct, idx: Int) {
baseExpr
#if CONDITION_1
.optionalMember?.optionalMethod()![idx]++ // expected-warning {{result of operator '++' is unused}}
#else
.otherMethod(arg) {
//...
}
#endif

baseExpr
#if CONDITION_1
.methodOne() + 12 // expected-error {{unexpected code '+ 12' in conditional compilation block}}
// expected-warning@-1 {{result of call to 'methodOne()' is unused}}
#endif
}

func emptyElse(baseExpr: MyStruct) {
baseExpr
#if CONDITION_1
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
#elseif CONDITION_2
// OK. Do nothing.
#endif

baseExpr
#if CONDITION_1
.methodOne() // expected-warning {{result of call to 'methodOne()' is unused}}
#elseif CONDITION_2
return // expected-error {{unexpected 'return' keyword in conditional compilation block}}
#endif
}

func consecutiveIfConfig(baseExpr: MyStruct) {
baseExpr
#if CONDITION_1
.methodOne()
#endif
#if CONDITION_2
.methodTwo()
#endif
.unknownMethod() // expected-error {{value of type 'OneResult' has no member 'unknownMethod'}}
}

func nestedIfConfig(baseExpr: MyStruct) {
baseExpr
#if CONDITION_1
#if CONDITION_2
.methodOne()
#endif
#if CONDITION_1
.methodTwo() // expected-warning {{result of call to 'methodTwo()' is unused}}
#endif
#else
.unknownMethod1()
#if CONDITION_2
.unknownMethod2()
#endif
#endif
}

func ifconfigExprInExpr(baseExpr: MyStruct) {
globalFunc( // expected-warning {{result of call to 'globalFunc' is unused}}
baseExpr
#if CONDITION_1
.methodOne()
#else
.methodTwo()
#endif
)
}