diff --git a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift index 4b6381d1e77..9b0af3f9f37 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift @@ -22,6 +22,7 @@ public enum ExperimentalFeature: String, CaseIterable { case valueGenerics case abiAttribute case unsafeExpression + case keypathWithMethodMembers /// The name of the feature as it is written in the compiler's `Features.def` file. public var featureName: String { @@ -44,6 +45,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "ABIAttribute" case .unsafeExpression: return "WarnUnsafe" + case .keypathWithMethodMembers: + return "KeypathWithMethodMembers" } } @@ -68,6 +71,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "@abi attribute" case .unsafeExpression: return "'unsafe' expression" + case .keypathWithMethodMembers: + return "keypaths with method members" } } diff --git a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift index 7aad34d15ec..a6c255f0b68 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift @@ -1209,6 +1209,11 @@ public let EXPR_NODES: [Node] = [ name: "property", kind: .node(kind: .keyPathPropertyComponent) ), + Child( + name: "method", + kind: .node(kind: .keyPathMethodComponent), + experimentalFeature: .keypathWithMethodMembers + ), Child( name: "subscript", kind: .node(kind: .keyPathSubscriptComponent) @@ -1313,6 +1318,33 @@ public let EXPR_NODES: [Node] = [ ] ), + Node( + kind: .keyPathMethodComponent, + base: .syntax, + experimentalFeature: .keypathWithMethodMembers, + nameForDiagnostics: "key path method component", + documentation: "A key path component like `.method()`, `.method(10)`, or `.method(val: 10)`.", + children: [ + Child( + name: "declName", + kind: .node(kind: .declReferenceExpr) + ), + Child( + name: "leftParen", + kind: .token(choices: [.token(.leftParen)]) + ), + Child( + name: "arguments", + kind: .collection(kind: .labeledExprList, collectionElementName: "Argument"), + nameForDiagnostics: "arguments" + ), + Child( + name: "rightParen", + kind: .token(choices: [.token(.rightParen)]) + ), + ] + ), + Node( kind: .macroExpansionExpr, base: .expr, diff --git a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift index 99274e5f59c..23a83cd5ff1 100644 --- a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift +++ b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift @@ -179,6 +179,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case keyPathOptionalComponent case keyPathPropertyComponent case keyPathSubscriptComponent + case keyPathMethodComponent case labeledExpr case labeledExprList case labeledSpecializeArgument diff --git a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift index 5148bf09142..fc7337342a0 100644 --- a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift +++ b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift @@ -549,6 +549,10 @@ class ValidateSyntaxNodes: XCTestCase { message: "could conform to trait 'Parenthesized' but does not" ), ValidationFailure(node: .lifetimeTypeSpecifier, message: "could conform to trait 'Parenthesized' but does not"), + ValidationFailure( + node: .keyPathMethodComponent, + message: "could conform to trait 'Parenthesized' but does not" + ), ] ) } diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index bf1c9fba52d..cb90aeb87ee 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -10,11 +10,15 @@ // //===----------------------------------------------------------------------===// -#if compiler(>=6) -@_spi(RawSyntax) internal import SwiftSyntax +#if swift(>=6) +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax +#else +#if swift(>=5.8) +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax #else @_spi(RawSyntax) import SwiftSyntax #endif +#endif extension TokenConsumer { mutating func atStartOfExpression() -> Bool { @@ -1103,11 +1107,54 @@ extension Parser { continue } - // Check for a .name or .1 suffix. + // Check for a .name, .1, .name(), .name("Kiwi"), .name(fruit:), + // .name(_:), .name(fruit: "Kiwi) suffix. if self.at(.period) { let (unexpectedPeriod, period, declName, generics) = parseDottedExpressionSuffix( previousNode: components.last?.raw ?? rootType?.raw ?? backslash.raw ) + + // If fully applied method component, parse as a keypath method. + if self.experimentalFeatures.contains(.keypathWithMethodMembers) + && self.at(.leftParen) + { + var (unexpectedBeforeLParen, leftParen) = self.expect(.leftParen) + if let generics = generics { + unexpectedBeforeLParen = RawUnexpectedNodesSyntax( + (unexpectedBeforeLParen?.elements ?? []) + [RawSyntax(generics)], + arena: self.arena + ) + } + let args = self.parseArgumentListElements( + pattern: pattern, + allowTrailingComma: true + ) + let (unexpectedBeforeRParen, rightParen) = self.expect(.rightParen) + + components.append( + RawKeyPathComponentSyntax( + unexpectedPeriod, + period: period, + component: .method( + RawKeyPathMethodComponentSyntax( + declName: declName, + unexpectedBeforeLParen, + leftParen: leftParen, + arguments: RawLabeledExprListSyntax( + elements: args, + arena: self.arena + ), + unexpectedBeforeRParen, + rightParen: rightParen, + arena: self.arena + ) + ), + arena: self.arena + ) + ) + continue + } + // Else, parse as a property. components.append( RawKeyPathComponentSyntax( unexpectedPeriod, @@ -1128,7 +1175,6 @@ extension Parser { // No more postfix expressions. break } - return RawKeyPathExprSyntax( unexpectedBeforeBackslash, backslash: backslash, diff --git a/Sources/SwiftParser/generated/ExperimentalFeatures.swift b/Sources/SwiftParser/generated/ExperimentalFeatures.swift index a5a8e2c21d1..6f65c3ec0d9 100644 --- a/Sources/SwiftParser/generated/ExperimentalFeatures.swift +++ b/Sources/SwiftParser/generated/ExperimentalFeatures.swift @@ -51,6 +51,9 @@ extension Parser.ExperimentalFeatures { /// Whether to enable the parsing of 'unsafe' expression. public static let unsafeExpression = Self (rawValue: 1 << 8) + /// Whether to enable the parsing of keypaths with method members. + public static let keypathWithMethodMembers = Self (rawValue: 1 << 9) + /// Creates a new value representing the experimental feature with the /// given name, or returns nil if the name is not recognized. public init?(name: String) { @@ -73,6 +76,8 @@ extension Parser.ExperimentalFeatures { self = .abiAttribute case "WarnUnsafe": self = .unsafeExpression + case "KeypathWithMethodMembers": + self = .keypathWithMethodMembers default: return nil } diff --git a/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift index 8491eafd390..cb530567a81 100644 --- a/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift @@ -206,6 +206,8 @@ private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String? { return "generic where clause" case \KeyPathExprSyntax.root: return "root" + case \KeyPathMethodComponentSyntax.arguments: + return "arguments" case \KeyPathSubscriptComponentSyntax.arguments: return "arguments" case \LabeledExprSyntax.label: diff --git a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift index a254a205b59..613b3996451 100644 --- a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift @@ -243,6 +243,8 @@ extension SyntaxKind { return "key path component" case .keyPathExpr: return "key path" + case .keyPathMethodComponent: + return "key path method component" case .keyPathOptionalComponent: return "key path optional component" case .keyPathPropertyComponent: diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index 4829d69a06a..44ddafeb975 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -1901,6 +1901,24 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "components" case \KeyPathExprSyntax.unexpectedAfterComponents: return "unexpectedAfterComponents" + case \KeyPathMethodComponentSyntax.unexpectedBeforeDeclName: + return "unexpectedBeforeDeclName" + case \KeyPathMethodComponentSyntax.declName: + return "declName" + case \KeyPathMethodComponentSyntax.unexpectedBetweenDeclNameAndLeftParen: + return "unexpectedBetweenDeclNameAndLeftParen" + case \KeyPathMethodComponentSyntax.leftParen: + return "leftParen" + case \KeyPathMethodComponentSyntax.unexpectedBetweenLeftParenAndArguments: + return "unexpectedBetweenLeftParenAndArguments" + case \KeyPathMethodComponentSyntax.arguments: + return "arguments" + case \KeyPathMethodComponentSyntax.unexpectedBetweenArgumentsAndRightParen: + return "unexpectedBetweenArgumentsAndRightParen" + case \KeyPathMethodComponentSyntax.rightParen: + return "rightParen" + case \KeyPathMethodComponentSyntax.unexpectedAfterRightParen: + return "unexpectedAfterRightParen" case \KeyPathOptionalComponentSyntax.unexpectedBeforeQuestionOrExclamationMark: return "unexpectedBeforeQuestionOrExclamationMark" case \KeyPathOptionalComponentSyntax.questionOrExclamationMark: diff --git a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift index d19e9dfc4aa..d11f3b0f6cf 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -1294,6 +1294,16 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + @_spi(ExperimentalLanguageFeatures) + override open func visit(_ node: KeyPathMethodComponentSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + @_spi(ExperimentalLanguageFeatures) + override open func visitPost(_ node: KeyPathMethodComponentSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: KeyPathOptionalComponentSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } diff --git a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift index 989f9a4aada..785c2ed0fc9 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -1672,6 +1672,7 @@ extension Syntax { .node(KeyPathComponentListSyntax.self), .node(KeyPathComponentSyntax.self), .node(KeyPathExprSyntax.self), + .node(KeyPathMethodComponentSyntax.self), .node(KeyPathOptionalComponentSyntax.self), .node(KeyPathPropertyComponentSyntax.self), .node(KeyPathSubscriptComponentSyntax.self), diff --git a/Sources/SwiftSyntax/generated/SyntaxEnum.swift b/Sources/SwiftSyntax/generated/SyntaxEnum.swift index e9db28be985..43c47a4d8f7 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -172,6 +172,8 @@ public enum SyntaxEnum: Sendable { case keyPathComponentList(KeyPathComponentListSyntax) case keyPathComponent(KeyPathComponentSyntax) case keyPathExpr(KeyPathExprSyntax) + @_spi(ExperimentalLanguageFeatures) + case keyPathMethodComponent(KeyPathMethodComponentSyntax) case keyPathOptionalComponent(KeyPathOptionalComponentSyntax) case keyPathPropertyComponent(KeyPathPropertyComponentSyntax) case keyPathSubscriptComponent(KeyPathSubscriptComponentSyntax) @@ -625,6 +627,8 @@ extension Syntax { return .keyPathComponent(KeyPathComponentSyntax(self)!) case .keyPathExpr: return .keyPathExpr(KeyPathExprSyntax(self)!) + case .keyPathMethodComponent: + return .keyPathMethodComponent(KeyPathMethodComponentSyntax(self)!) case .keyPathOptionalComponent: return .keyPathOptionalComponent(KeyPathOptionalComponentSyntax(self)!) case .keyPathPropertyComponent: diff --git a/Sources/SwiftSyntax/generated/SyntaxKind.swift b/Sources/SwiftSyntax/generated/SyntaxKind.swift index dd7b1455ab8..caefc095ff7 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -172,6 +172,8 @@ public enum SyntaxKind: Sendable { case keyPathComponentList case keyPathComponent case keyPathExpr + @_spi(ExperimentalLanguageFeatures) + case keyPathMethodComponent case keyPathOptionalComponent case keyPathPropertyComponent case keyPathSubscriptComponent @@ -750,6 +752,8 @@ public enum SyntaxKind: Sendable { return KeyPathComponentSyntax.self case .keyPathExpr: return KeyPathExprSyntax.self + case .keyPathMethodComponent: + return KeyPathMethodComponentSyntax.self case .keyPathOptionalComponent: return KeyPathOptionalComponentSyntax.self case .keyPathPropertyComponent: diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index 7b4d0fedb81..174f9722506 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -1179,6 +1179,14 @@ open class SyntaxRewriter { return ExprSyntax(KeyPathExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } + /// Visit a `KeyPathMethodComponentSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + @_spi(ExperimentalLanguageFeatures) + open func visit(_ node: KeyPathMethodComponentSyntax) -> KeyPathMethodComponentSyntax { + return KeyPathMethodComponentSyntax(unsafeCasting: visitChildren(node._syntaxNode)) + } + /// Visit a ``KeyPathOptionalComponentSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -2918,6 +2926,11 @@ open class SyntaxRewriter { Syntax(visit(KeyPathExprSyntax(unsafeCasting: node))) } + @inline(never) + private func visitKeyPathMethodComponentSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(KeyPathMethodComponentSyntax(unsafeCasting: node))) + } + @inline(never) private func visitKeyPathOptionalComponentSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(KeyPathOptionalComponentSyntax(unsafeCasting: node))) @@ -3914,6 +3927,8 @@ open class SyntaxRewriter { return self.visitKeyPathComponentSyntaxImpl(_:) case .keyPathExpr: return self.visitKeyPathExprSyntaxImpl(_:) + case .keyPathMethodComponent: + return self.visitKeyPathMethodComponentSyntaxImpl(_:) case .keyPathOptionalComponent: return self.visitKeyPathOptionalComponentSyntaxImpl(_:) case .keyPathPropertyComponent: @@ -4496,6 +4511,8 @@ open class SyntaxRewriter { return visitKeyPathComponentSyntaxImpl(node) case .keyPathExpr: return visitKeyPathExprSyntaxImpl(node) + case .keyPathMethodComponent: + return visitKeyPathMethodComponentSyntaxImpl(node) case .keyPathOptionalComponent: return visitKeyPathOptionalComponentSyntaxImpl(node) case .keyPathPropertyComponent: diff --git a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift index fab9547f7b2..d4b6803239d 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -1888,6 +1888,20 @@ open class SyntaxVisitor { open func visitPost(_ node: KeyPathExprSyntax) { } + /// Visiting `KeyPathMethodComponentSyntax` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + @_spi(ExperimentalLanguageFeatures) + open func visit(_ node: KeyPathMethodComponentSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting `KeyPathMethodComponentSyntax` and its descendants. + /// - node: the node we just finished visiting. + @_spi(ExperimentalLanguageFeatures) + open func visitPost(_ node: KeyPathMethodComponentSyntax) { + } + /// Visiting ``KeyPathOptionalComponentSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -4733,6 +4747,14 @@ open class SyntaxVisitor { visitPost(KeyPathExprSyntax(unsafeCasting: node)) } + @inline(never) + private func visitKeyPathMethodComponentSyntaxImpl(_ node: Syntax) { + if visit(KeyPathMethodComponentSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(KeyPathMethodComponentSyntax(unsafeCasting: node)) + } + @inline(never) private func visitKeyPathOptionalComponentSyntaxImpl(_ node: Syntax) { if visit(KeyPathOptionalComponentSyntax(unsafeCasting: node)) == .visitChildren { @@ -6125,6 +6147,8 @@ open class SyntaxVisitor { return self.visitKeyPathComponentSyntaxImpl(_:) case .keyPathExpr: return self.visitKeyPathExprSyntaxImpl(_:) + case .keyPathMethodComponent: + return self.visitKeyPathMethodComponentSyntaxImpl(_:) case .keyPathOptionalComponent: return self.visitKeyPathOptionalComponentSyntaxImpl(_:) case .keyPathPropertyComponent: @@ -6707,6 +6731,8 @@ open class SyntaxVisitor { self.visitKeyPathComponentSyntaxImpl(node) case .keyPathExpr: self.visitKeyPathExprSyntaxImpl(node) + case .keyPathMethodComponent: + self.visitKeyPathMethodComponentSyntaxImpl(node) case .keyPathOptionalComponent: self.visitKeyPathOptionalComponentSyntaxImpl(node) case .keyPathPropertyComponent: diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift index b720aab2e5d..c602f005492 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift @@ -66,17 +66,22 @@ public struct RawKeyPathComponentListSyntax: RawSyntaxNodeProtocol { public struct RawKeyPathComponentSyntax: RawSyntaxNodeProtocol { public enum Component: RawSyntaxNodeProtocol { case property(RawKeyPathPropertyComponentSyntax) + /// - Note: Requires experimental feature `keypathWithMethodMembers`. + @_spi(ExperimentalLanguageFeatures) + case method(RawKeyPathMethodComponentSyntax) case `subscript`(RawKeyPathSubscriptComponentSyntax) case optional(RawKeyPathOptionalComponentSyntax) public static func isKindOf(_ raw: RawSyntax) -> Bool { - RawKeyPathPropertyComponentSyntax.isKindOf(raw) || RawKeyPathSubscriptComponentSyntax.isKindOf(raw) || RawKeyPathOptionalComponentSyntax.isKindOf(raw) + RawKeyPathPropertyComponentSyntax.isKindOf(raw) || RawKeyPathMethodComponentSyntax.isKindOf(raw) || RawKeyPathSubscriptComponentSyntax.isKindOf(raw) || RawKeyPathOptionalComponentSyntax.isKindOf(raw) } public var raw: RawSyntax { switch self { case .property(let node): return node.raw + case .method(let node): + return node.raw case .subscript(let node): return node.raw case .optional(let node): @@ -87,6 +92,8 @@ public struct RawKeyPathComponentSyntax: RawSyntaxNodeProtocol { public init?(_ node: __shared some RawSyntaxNodeProtocol) { if let node = node.as(RawKeyPathPropertyComponentSyntax.self) { self = .property(node) + } else if let node = node.as(RawKeyPathMethodComponentSyntax.self) { + self = .method(node) } else if let node = node.as(RawKeyPathSubscriptComponentSyntax.self) { self = .subscript(node) } else if let node = node.as(RawKeyPathOptionalComponentSyntax.self) { @@ -247,6 +254,101 @@ public struct RawKeyPathExprSyntax: RawExprSyntaxNodeProtocol { } } +@_spi(ExperimentalLanguageFeatures) +@_spi(RawSyntax) +public struct RawKeyPathMethodComponentSyntax: RawSyntaxNodeProtocol { + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .keyPathMethodComponent + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init( + _ unexpectedBeforeDeclName: RawUnexpectedNodesSyntax? = nil, + declName: RawDeclReferenceExprSyntax, + _ unexpectedBetweenDeclNameAndLeftParen: RawUnexpectedNodesSyntax? = nil, + leftParen: RawTokenSyntax, + _ unexpectedBetweenLeftParenAndArguments: RawUnexpectedNodesSyntax? = nil, + arguments: RawLabeledExprListSyntax, + _ unexpectedBetweenArgumentsAndRightParen: RawUnexpectedNodesSyntax? = nil, + rightParen: RawTokenSyntax, + _ unexpectedAfterRightParen: RawUnexpectedNodesSyntax? = nil, + arena: __shared SyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .keyPathMethodComponent, uninitializedCount: 9, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeDeclName?.raw + layout[1] = declName.raw + layout[2] = unexpectedBetweenDeclNameAndLeftParen?.raw + layout[3] = leftParen.raw + layout[4] = unexpectedBetweenLeftParenAndArguments?.raw + layout[5] = arguments.raw + layout[6] = unexpectedBetweenArgumentsAndRightParen?.raw + layout[7] = rightParen.raw + layout[8] = unexpectedAfterRightParen?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeDeclName: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var declName: RawDeclReferenceExprSyntax { + layoutView.children[1].map(RawDeclReferenceExprSyntax.init(raw:))! + } + + public var unexpectedBetweenDeclNameAndLeftParen: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var leftParen: RawTokenSyntax { + layoutView.children[3].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenLeftParenAndArguments: RawUnexpectedNodesSyntax? { + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var arguments: RawLabeledExprListSyntax { + layoutView.children[5].map(RawLabeledExprListSyntax.init(raw:))! + } + + public var unexpectedBetweenArgumentsAndRightParen: RawUnexpectedNodesSyntax? { + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var rightParen: RawTokenSyntax { + layoutView.children[7].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedAfterRightParen: RawUnexpectedNodesSyntax? { + layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} + @_spi(RawSyntax) public struct RawKeyPathOptionalComponentSyntax: RawSyntaxNodeProtocol { @_spi(RawSyntax) diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index 883699003c3..1d0cd0a87e4 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -1788,6 +1788,18 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 5, verify(layout[5], as: RawKeyPathComponentListSyntax.self)) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) } + func validateKeyPathMethodComponentSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { + assert(layout.count == 9) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 1, verify(layout[1], as: RawDeclReferenceExprSyntax.self)) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.leftParen)])) + assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 5, verify(layout[5], as: RawLabeledExprListSyntax.self)) + assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.rightParen)])) + assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) + } func validateKeyPathOptionalComponentSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { assert(layout.count == 3) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) @@ -3414,6 +3426,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateKeyPathComponentSyntax(kind: kind, layout: layout) case .keyPathExpr: validateKeyPathExprSyntax(kind: kind, layout: layout) + case .keyPathMethodComponent: + validateKeyPathMethodComponentSyntax(kind: kind, layout: layout) case .keyPathOptionalComponent: validateKeyPathOptionalComponentSyntax(kind: kind, layout: layout) case .keyPathPropertyComponent: diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift index 0f935a85f6c..aac7fe22374 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift @@ -19,7 +19,7 @@ /// ### Children /// /// - `period`: `.`? -/// - `component`: (``KeyPathPropertyComponentSyntax`` | ``KeyPathSubscriptComponentSyntax`` | ``KeyPathOptionalComponentSyntax``) +/// - `component`: (``KeyPathPropertyComponentSyntax`` | `KeyPathMethodComponentSyntax` | ``KeyPathSubscriptComponentSyntax`` | ``KeyPathOptionalComponentSyntax``) /// /// ### Contained in /// @@ -27,6 +27,9 @@ public struct KeyPathComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { public enum Component: SyntaxChildChoices, SyntaxHashable { case property(KeyPathPropertyComponentSyntax) + /// - Note: Requires experimental feature `keypathWithMethodMembers`. + @_spi(ExperimentalLanguageFeatures) + case method(KeyPathMethodComponentSyntax) case `subscript`(KeyPathSubscriptComponentSyntax) case optional(KeyPathOptionalComponentSyntax) @@ -34,6 +37,8 @@ public struct KeyPathComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynta switch self { case .property(let node): return node._syntaxNode + case .method(let node): + return node._syntaxNode case .subscript(let node): return node._syntaxNode case .optional(let node): @@ -45,6 +50,12 @@ public struct KeyPathComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynta self = .property(node) } + /// - Note: Requires experimental feature `keypathWithMethodMembers`. + @_spi(ExperimentalLanguageFeatures) + public init(_ node: KeyPathMethodComponentSyntax) { + self = .method(node) + } + public init(_ node: KeyPathSubscriptComponentSyntax) { self = .subscript(node) } @@ -56,6 +67,8 @@ public struct KeyPathComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynta public init?(_ node: __shared some SyntaxProtocol) { if let node = node.as(KeyPathPropertyComponentSyntax.self) { self = .property(node) + } else if let node = node.as(KeyPathMethodComponentSyntax.self) { + self = .method(node) } else if let node = node.as(KeyPathSubscriptComponentSyntax.self) { self = .subscript(node) } else if let node = node.as(KeyPathOptionalComponentSyntax.self) { @@ -66,7 +79,12 @@ public struct KeyPathComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynta } public static var structure: SyntaxNodeStructure { - return .choices([.node(KeyPathPropertyComponentSyntax.self), .node(KeyPathSubscriptComponentSyntax.self), .node(KeyPathOptionalComponentSyntax.self)]) + return .choices([ + .node(KeyPathPropertyComponentSyntax.self), + .node(KeyPathMethodComponentSyntax.self), + .node(KeyPathSubscriptComponentSyntax.self), + .node(KeyPathOptionalComponentSyntax.self) + ]) } /// Checks if the current syntax node can be cast to ``KeyPathPropertyComponentSyntax``. @@ -91,6 +109,34 @@ public struct KeyPathComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSynta return self.as(KeyPathPropertyComponentSyntax.self)! } + /// Checks if the current syntax node can be cast to `KeyPathMethodComponentSyntax`. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + /// - Note: Requires experimental feature `keypathWithMethodMembers`. + @_spi(ExperimentalLanguageFeatures) + public func `is`(_ syntaxType: KeyPathMethodComponentSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to `KeyPathMethodComponentSyntax`. + /// + /// - Returns: An instance of `KeyPathMethodComponentSyntax`, or `nil` if the cast fails. + /// - Note: Requires experimental feature `keypathWithMethodMembers`. + @_spi(ExperimentalLanguageFeatures) + public func `as`(_ syntaxType: KeyPathMethodComponentSyntax.Type) -> KeyPathMethodComponentSyntax? { + return KeyPathMethodComponentSyntax.init(self) + } + + /// Force-casts the current syntax node to `KeyPathMethodComponentSyntax`. + /// + /// - Returns: An instance of `KeyPathMethodComponentSyntax`. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + /// - Note: Requires experimental feature `keypathWithMethodMembers`. + @_spi(ExperimentalLanguageFeatures) + public func cast(_ syntaxType: KeyPathMethodComponentSyntax.Type) -> KeyPathMethodComponentSyntax { + return self.as(KeyPathMethodComponentSyntax.self)! + } + /// Checks if the current syntax node can be cast to ``KeyPathSubscriptComponentSyntax``. /// /// - Returns: `true` if the node can be cast, `false` otherwise. @@ -425,6 +471,216 @@ public struct KeyPathExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSy ]) } +// MARK: - KeyPathMethodComponentSyntax + +/// A key path component like `.method()`, `.method(10)`, or `.method(val: 10)`. +/// +/// - Note: Requires experimental feature `keypathWithMethodMembers`. +/// +/// ### Children +/// +/// - `declName`: ``DeclReferenceExprSyntax`` +/// - `leftParen`: `(` +/// - `arguments`: ``LabeledExprListSyntax`` +/// - `rightParen`: `)` +/// +/// ### Contained in +/// +/// - ``KeyPathComponentSyntax``.``KeyPathComponentSyntax/component`` +@_spi(ExperimentalLanguageFeatures) +public struct KeyPathMethodComponentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .keyPathMethodComponent else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + @_transparent + init(unsafeCasting node: Syntax) { + self._syntaxNode = node + } + + /// - Parameters: + /// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + /// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored. + public init( + leadingTrivia: Trivia? = nil, + _ unexpectedBeforeDeclName: UnexpectedNodesSyntax? = nil, + declName: DeclReferenceExprSyntax, + _ unexpectedBetweenDeclNameAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax = .leftParenToken(), + _ unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + arguments: LabeledExprListSyntax, + _ unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax = .rightParenToken(), + _ unexpectedAfterRightParen: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((SyntaxArena(), ( + unexpectedBeforeDeclName, + declName, + unexpectedBetweenDeclNameAndLeftParen, + leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments, + unexpectedBetweenArgumentsAndRightParen, + rightParen, + unexpectedAfterRightParen + ))) { (arena, _) in + let layout: [RawSyntax?] = [ + unexpectedBeforeDeclName?.raw, + declName.raw, + unexpectedBetweenDeclNameAndLeftParen?.raw, + leftParen.raw, + unexpectedBetweenLeftParenAndArguments?.raw, + arguments.raw, + unexpectedBetweenArgumentsAndRightParen?.raw, + rightParen.raw, + unexpectedAfterRightParen?.raw + ] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.keyPathMethodComponent, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeDeclName: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + public var declName: DeclReferenceExprSyntax { + get { + return Syntax(self).child(at: 1)!.cast(DeclReferenceExprSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + public var unexpectedBetweenDeclNameAndLeftParen: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `(`. + public var leftParen: TokenSyntax { + get { + return Syntax(self).child(at: 3)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + public var unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + public var arguments: LabeledExprListSyntax { + get { + return Syntax(self).child(at: 5)!.cast(LabeledExprListSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 5, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + /// Adds the provided `element` to the node's `arguments` + /// collection. + /// + /// - param element: The new `Argument` to add to the node's + /// `arguments` collection. + /// - returns: A copy of the receiver with the provided `Argument` + /// appended to its `arguments` collection. + @available(*, deprecated, message: "Use node.arguments.append(newElement) instead") + public func addArgument(_ element: LabeledExprSyntax) -> KeyPathMethodComponentSyntax { + var collection: RawSyntax + let arena = SyntaxArena() + if let col = raw.layoutView!.children[5] { + collection = col.layoutView!.appending(element.raw, arena: arena) + } else { + collection = RawSyntax.makeLayout(kind: SyntaxKind.labeledExprList, + from: [element.raw], arena: arena) + } + return Syntax(self) + .replacingChild( + at: 5, + with: collection, + rawNodeArena: arena, + allocationArena: arena + ) + .cast(KeyPathMethodComponentSyntax.self) + } + + public var unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 6, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `)`. + public var rightParen: TokenSyntax { + get { + return Syntax(self).child(at: 7)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 7, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + public var unexpectedAfterRightParen: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 8, with: Syntax(value), arena: SyntaxArena()).cast(KeyPathMethodComponentSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([ + \Self.unexpectedBeforeDeclName, + \Self.declName, + \Self.unexpectedBetweenDeclNameAndLeftParen, + \Self.leftParen, + \Self.unexpectedBetweenLeftParenAndArguments, + \Self.arguments, + \Self.unexpectedBetweenArgumentsAndRightParen, + \Self.rightParen, + \Self.unexpectedAfterRightParen + ]) +} + // MARK: - KeyPathOptionalComponentSyntax /// A key path component like `?` or `!`. diff --git a/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift b/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift index 49072c3ed42..eec7d623551 100644 --- a/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift +++ b/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift @@ -955,6 +955,37 @@ extension InitializerDeclSyntax { } } +extension KeyPathMethodComponentSyntax { + /// A convenience initializer that allows initializing syntax collections using result builders + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeDeclName: UnexpectedNodesSyntax? = nil, + declName: DeclReferenceExprSyntax, + unexpectedBetweenDeclNameAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax = .leftParenToken(), + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax = .rightParenToken(), + unexpectedAfterRightParen: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentsBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeDeclName, + declName: declName, + unexpectedBetweenDeclNameAndLeftParen, + leftParen: leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentsBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: rightParen, + unexpectedAfterRightParen, + trailingTrivia: trailingTrivia + ) + } +} + extension KeyPathSubscriptComponentSyntax { /// A convenience initializer that allows initializing syntax collections using result builders public init( diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index 1038026eb26..f157066fe8d 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// @_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftParser +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax @_spi(RawSyntax) import SwiftSyntax import XCTest @@ -256,6 +257,364 @@ final class ExpressionTests: ParserTestCase { ) } + func testKeyPathMethodAndInitializers() { + assertParse( + #"\Foo.method()"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax("Foo"), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: KeyPathComponentSyntax.Component( + KeyPathMethodComponentSyntax( + declName: DeclReferenceExprSyntax(baseName: .identifier("method")), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([]), + rightParen: .rightParenToken() + ) + ) + ) + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.method(10)"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax("Foo"), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: .init( + KeyPathMethodComponentSyntax( + declName: DeclReferenceExprSyntax(baseName: .identifier("method")), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([ + LabeledExprSyntax( + label: nil, + colon: nil, + expression: ExprSyntax("10") + ) + ]), + rightParen: .rightParenToken() + ) + ) + ) + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.method(arg: 10)"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax("Foo"), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: .init( + KeyPathMethodComponentSyntax( + declName: DeclReferenceExprSyntax(baseName: .identifier("method")), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([ + LabeledExprSyntax( + label: .identifier("arg"), + colon: .colonToken(), + expression: ExprSyntax("10") + ) + ]), + rightParen: .rightParenToken() + ) + ) + ) + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.method(_:)"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax("Foo"), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: .init( + KeyPathPropertyComponentSyntax( + declName: DeclReferenceExprSyntax( + baseName: .identifier("method"), + argumentNames: DeclNameArgumentsSyntax( + leftParen: .leftParenToken(), + arguments: [ + DeclNameArgumentSyntax(name: .wildcardToken(), colon: .colonToken()) + ], + rightParen: .rightParenToken() + ) + ) + ) + ) + ) + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.method(arg:)"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax("Foo"), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: .init( + KeyPathPropertyComponentSyntax( + declName: DeclReferenceExprSyntax( + baseName: .identifier("method"), + argumentNames: DeclNameArgumentsSyntax( + leftParen: .leftParenToken(), + arguments: [ + DeclNameArgumentSyntax(name: .identifier("arg"), colon: .colonToken()) + ], + rightParen: .rightParenToken() + ) + ) + ) + ) + ) + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.method().anotherMethod(arg: 10)"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax("Foo"), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: .init( + KeyPathMethodComponentSyntax( + declName: DeclReferenceExprSyntax(baseName: .identifier("method")), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([]), + rightParen: .rightParenToken() + ) + ) + ), + KeyPathComponentSyntax( + period: .periodToken(), + component: .init( + KeyPathMethodComponentSyntax( + declName: DeclReferenceExprSyntax(baseName: .identifier("anotherMethod")), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([ + LabeledExprSyntax( + label: .identifier("arg"), + colon: .colonToken(), + expression: ExprSyntax("10") + ) + ]), + rightParen: .rightParenToken() + ) + ) + ), + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.Type.init()"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax( + MetatypeTypeSyntax(baseType: TypeSyntax("Foo"), metatypeSpecifier: .keyword(.Type)) + ), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: KeyPathComponentSyntax.Component( + KeyPathMethodComponentSyntax( + declName: DeclReferenceExprSyntax(baseName: .keyword(.init("init")!)), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([]), + rightParen: .rightParenToken() + ) + ) + ) + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.t(a:)(2)"#, + substructure: KeyPathExprSyntax( + root: TypeSyntax("Foo"), + components: KeyPathComponentListSyntax([ + KeyPathComponentSyntax( + period: .periodToken(), + component: .init( + KeyPathMethodComponentSyntax( + declName: DeclReferenceExprSyntax( + baseName: .identifier("t"), + argumentNames: DeclNameArgumentsSyntax( + leftParen: .leftParenToken(), + arguments: [ + DeclNameArgumentSyntax(name: .identifier("a"), colon: .colonToken()) + ], + rightParen: .rightParenToken() + ) + ), + leftParen: .leftParenToken(), + arguments: LabeledExprListSyntax([ + LabeledExprSyntax( + label: nil, + colon: nil, + expression: ExprSyntax("2") + ) + ]), + rightParen: .rightParenToken() + ) + ) + ) + ]) + ), + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + \Foo.method(1️⃣ + """#, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected value and ')' to end key path method component", + fixIts: ["insert value and ')'"] + ) + ], + fixedSource: #"\Foo.method(<#expression#>)"#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.1️⃣()"#, + diagnostics: [ + DiagnosticSpec(message: "expected identifier in key path method component", fixIts: ["insert identifier"]) + ], + fixedSource: #"\Foo.<#identifier#>()"#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.method1️⃣()"#, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '' in key path method component" + ) + ], + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #"\Foo.method1️⃣(arg:2️⃣)"#, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '' in key path method component" + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "expected value in key path method component", + fixIts: ["insert value"] + ), + ], + fixedSource: #"\Foo.method(arg: <#expression#>)"#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + S()[keyPath: \.i] = 1 + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + public let keyPath2FromLibB = \AStruct.Type.property + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + public let keyPath9FromLibB = \AStruct.Type.init(val: 2025) + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + _ = ([S]()).map(\.i) + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + let some = Some(keyPath: \Demo.here) + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + _ = ([S.Type]()).map(\.init) + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + \Lens>.obj.x + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + _ = \Lens.y + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + _ = f(\String?.!.count) + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + let _ = \K.Type.init(val: 2025) + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + + assertParse( + #""" + let _ = \K.Type.init + let _ = \K.Type.init() + """#, + experimentalFeatures: .keypathWithMethodMembers + ) + } + func testKeyPathSubscript() { assertParse( #"\Foo.Type.[2]"#,