From a700b4d3f73feb6ba1d939f9880b709b2c5d5c8f Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 7 Nov 2024 14:09:13 -0800 Subject: [PATCH 1/2] Add support for @lifetime(borrow ...) --- .../Sources/SyntaxSupport/ExprNodes.swift | 2 +- .../Sources/SyntaxSupport/KeywordSpec.swift | 3 ++ Sources/SwiftParser/Expressions.swift | 5 ++ Sources/SwiftParser/TokenPrecedence.swift | 2 +- Sources/SwiftParser/TokenSpecSet.swift | 3 ++ .../generated/Parser+TokenSpecSet.swift | 52 +++++++++++++++++++ Sources/SwiftSyntax/generated/Keyword.swift | 4 ++ .../generated/raw/RawSyntaxValidation.swift | 2 +- .../generated/syntaxNodes/SyntaxNodesAB.swift | 8 +-- Tests/SwiftParserTest/AttributeTests.swift | 26 ++++++++++ 10 files changed, 101 insertions(+), 6 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift index e3446547120..fdad15af88d 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift @@ -228,7 +228,7 @@ public let EXPR_NODES: [Node] = [ children: [ Child( name: "borrowKeyword", - kind: .token(choices: [.keyword(._borrow)]) + kind: .token(choices: [.keyword(._borrow), .keyword(.borrow)]) ), Child( name: "expression", diff --git a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift index b1f35ef6433..dacec331673 100644 --- a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift +++ b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift @@ -150,6 +150,7 @@ public enum Keyword: CaseIterable { case backDeployed case before case block + case borrow case borrowing case `break` case canImport @@ -442,6 +443,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("before") case .block: return KeywordSpec("block") + case .borrow: + return KeywordSpec("borrow") case .borrowing: return KeywordSpec("borrowing") case .break: diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 21a1bc44009..6ff4327accf 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -458,6 +458,11 @@ extension Parser { ) ) case (._borrow, let handle)?: + fallthrough + case (.borrow, let handle)?: + if !atContextualExpressionModifier() { + break EXPR_PREFIX + } let borrowTok = self.eat(handle) let sub = self.parseSequenceExpressionElement( flavor: flavor, diff --git a/Sources/SwiftParser/TokenPrecedence.swift b/Sources/SwiftParser/TokenPrecedence.swift index 1cc318aedfa..efe8f63d67c 100644 --- a/Sources/SwiftParser/TokenPrecedence.swift +++ b/Sources/SwiftParser/TokenPrecedence.swift @@ -235,7 +235,7 @@ enum TokenPrecedence: Comparable { .__consuming, .final, .required, .optional, .lazy, .dynamic, .infix, .postfix, .prefix, .mutating, .nonmutating, .convenience, .override, .package, .open, .__setter_access, .indirect, .isolated, .nonisolated, .distributed, ._local, - .inout, ._mutating, ._borrow, ._borrowing, .borrowing, ._consuming, .consuming, .consume, + .inout, ._mutating, ._borrow, ._borrowing, .borrow, .borrowing, ._consuming, .consuming, .consume, .dependsOn, .scoped, .sending, // Accessors .get, .set, .didSet, .willSet, .unsafeAddress, .addressWithOwner, .addressWithNativeOwner, .unsafeMutableAddress, diff --git a/Sources/SwiftParser/TokenSpecSet.swift b/Sources/SwiftParser/TokenSpecSet.swift index e2b46112829..2748477c1c9 100644 --- a/Sources/SwiftParser/TokenSpecSet.swift +++ b/Sources/SwiftParser/TokenSpecSet.swift @@ -694,6 +694,7 @@ enum ExpressionModifierKeyword: TokenSpecSet { case _move case _borrow case `try` + case borrow case consume case copy case `repeat` @@ -706,6 +707,7 @@ enum ExpressionModifierKeyword: TokenSpecSet { case TokenSpec(._move): self = ._move case TokenSpec(._borrow): self = ._borrow case TokenSpec(.try): self = .try + case TokenSpec(.borrow): self = .borrow case TokenSpec(.consume): self = .consume case TokenSpec(.copy): self = .copy case TokenSpec(.repeat): self = .repeat @@ -720,6 +722,7 @@ enum ExpressionModifierKeyword: TokenSpecSet { case .await: return .keyword(.await) case ._move: return .keyword(._move) case ._borrow: return .keyword(._borrow) + case .borrow: return .keyword(.borrow) case .consume: return .keyword(.consume) case .copy: return .keyword(.copy) case .try: return .keyword(.try) diff --git a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift index 0ddbdbd54c0..b448c81c9c0 100644 --- a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift +++ b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift @@ -428,6 +428,58 @@ extension BooleanLiteralExprSyntax { } } +extension BorrowExprSyntax { + @_spi(Diagnostics) + public enum BorrowKeywordOptions: TokenSpecSet { + case _borrow + case borrow + + init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) { + switch PrepareForKeywordMatch(lexeme) { + case TokenSpec(._borrow): + self = ._borrow + case TokenSpec(.borrow): + self = .borrow + default: + return nil + } + } + + public init?(token: TokenSyntax) { + switch token { + case TokenSpec(._borrow): + self = ._borrow + case TokenSpec(.borrow): + self = .borrow + default: + return nil + } + } + + var spec: TokenSpec { + switch self { + case ._borrow: + return .keyword(._borrow) + case .borrow: + return .keyword(.borrow) + } + } + + /// Returns a token that satisfies the `TokenSpec` of this case. + /// + /// If the token kind of this spec has variable text, e.g. for an identifier, this returns a token with empty text. + @_spi(Diagnostics) + public var tokenSyntax: TokenSyntax { + switch self { + case ._borrow: + return .keyword(._borrow) + case .borrow: + return .keyword(.borrow) + } + } + } +} + extension _CanImportVersionInfoSyntax { @_spi(Diagnostics) public enum LabelOptions: TokenSpecSet { diff --git a/Sources/SwiftSyntax/generated/Keyword.swift b/Sources/SwiftSyntax/generated/Keyword.swift index 2ce15cdf90c..15f2bbd87e9 100644 --- a/Sources/SwiftSyntax/generated/Keyword.swift +++ b/Sources/SwiftSyntax/generated/Keyword.swift @@ -90,6 +90,7 @@ public enum Keyword: UInt8, Hashable, Sendable { case backDeployed case before case block + case borrow case borrowing case `break` case canImport @@ -416,6 +417,8 @@ public enum Keyword: UInt8, Hashable, Sendable { self = ._local case "before": self = .before + case "borrow": + self = .borrow case "deinit": self = .deinit case "didSet": @@ -890,6 +893,7 @@ public enum Keyword: UInt8, Hashable, Sendable { "backDeployed", "before", "block", + "borrow", "borrowing", "break", "canImport", diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index 55706f39f4c..5e8dc7597b2 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -455,7 +455,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { case .borrowExpr: assert(layout.count == 5) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("_borrow")])) + assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("_borrow"), .keyword("borrow")])) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 3, verify(layout[3], as: RawExprSyntax.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift index f6519df7524..bc2b9c74921 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift @@ -4497,7 +4497,7 @@ public struct BooleanLiteralExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _Lea /// ### Children /// -/// - `borrowKeyword`: `_borrow` +/// - `borrowKeyword`: (`_borrow` | `borrow`) /// - `expression`: ``ExprSyntax`` public struct BorrowExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -4515,7 +4515,7 @@ public struct BorrowExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyn public init( leadingTrivia: Trivia? = nil, _ unexpectedBeforeBorrowKeyword: UnexpectedNodesSyntax? = nil, - borrowKeyword: TokenSyntax = .keyword(._borrow), + borrowKeyword: TokenSyntax, _ unexpectedBetweenBorrowKeywordAndExpression: UnexpectedNodesSyntax? = nil, expression: some ExprSyntaxProtocol, _ unexpectedAfterExpression: UnexpectedNodesSyntax? = nil, @@ -4559,7 +4559,9 @@ public struct BorrowExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyn /// ### Tokens /// - /// For syntax trees generated by the parser, this is guaranteed to be `_borrow`. + /// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds: + /// - `_borrow` + /// - `borrow` public var borrowKeyword: TokenSyntax { get { return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) diff --git a/Tests/SwiftParserTest/AttributeTests.swift b/Tests/SwiftParserTest/AttributeTests.swift index 882839b06ce..31304050ab7 100644 --- a/Tests/SwiftParserTest/AttributeTests.swift +++ b/Tests/SwiftParserTest/AttributeTests.swift @@ -1053,4 +1053,30 @@ final class AttributeTests: ParserTestCase { """ ) } + + func testLifetimeAttribute() { + assertParse( + """ + struct NE: ~Escapable {} + + @lifetime(ne) + func derive1(ne: NE) -> NE { ne } + + @lifetime(borrow ne) + func derive2(ne: borrowing NE) -> NE { ne } + + @lifetime(ne1, n2) + func derive3(ne1: NE, ne2: NE) -> NE { ne1 } + + @lifetime(borrow ne1, n2) + func derive4(ne1: NE, ne2: NE) -> NE { ne1 } + + @lifetime(neOut: ne) + func derive5(ne: NE, neOut: inout NE) -> NE { neOut = ne } + + @lifetime(neOut: borrow ne) + func derive6(ne: borrowing NE, neOut: inout NE) -> NE { neOut = ne } + """ + ) + } } From bbf002366d5574d2d7cceec65a3286b9758aa5df Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 7 Nov 2024 15:15:05 -0800 Subject: [PATCH 2/2] Add test to verify borrow (msg) is a function call --- .../ValidateSyntaxNodes.swift | 12 +++---- Tests/SwiftParserTest/ExpressionTests.swift | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift index c9f81c34a9b..5148bf09142 100644 --- a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift +++ b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift @@ -317,12 +317,6 @@ class ValidateSyntaxNodes: XCTestCase { ), // MARK: Tokens that contain underscores - ValidationFailure( - node: .borrowExpr, - message: - "child 'borrowKeyword' has a single keyword as its only token choice and should thus be named '_borrowKeyword'" - // _borrow is underscored and thus BorrowKeyword is the correct spelling - ), ValidationFailure( node: .conventionWitnessMethodAttributeArguments, message: @@ -410,7 +404,11 @@ class ValidateSyntaxNodes: XCTestCase { message: "child 'consumeKeyword' only has keywords as its token choices and should thus end with 'Specifier'" // ConsumeKeyword can be 'consume' or '_move' and '_move' is deprecated ), - + ValidationFailure( + node: .borrowExpr, + message: "child 'borrowKeyword' only has keywords as its token choices and should thus end with 'Specifier'" + // BorrowKeyword can be 'borrow' or '_borrow', eventually '_borrow' will be deprecated + ), // MARK: Conceptually a value, not a specifier ValidationFailure( node: .booleanLiteralExpr, diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index 81be9611dcb..90498d7179a 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -1210,6 +1210,38 @@ final class ExpressionTests: ParserTestCase { assertParse("use(_borrow msg)") assertParse("_borrow msg") assertParse("let b = (_borrow self).buffer") + assertParse("borrow msg") + assertParse("use(borrow msg)") + assertParse("borrow(msg)") + assertParse("borrow (msg)") + } + + func testBorrowNameFunctionCallStructure1() { + assertParse( + """ + borrow(msg) + """, + substructure: FunctionCallExprSyntax( + calledExpression: DeclReferenceExprSyntax(baseName: .identifier("borrow")), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([LabeledExprSyntax(expression: ExprSyntax("msg"))]), + rightParen: .rightParenToken() + ) + ) + } + + func testBorrowNameFunctionCallStructure2() { + assertParse( + """ + borrow (msg) + """, + substructure: FunctionCallExprSyntax( + calledExpression: DeclReferenceExprSyntax(baseName: .identifier("borrow")), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([LabeledExprSyntax(expression: ExprSyntax("msg"))]), + rightParen: .rightParenToken() + ) + ) } func testCodeCompletionExpressions() {