diff --git a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift index 6da75f84254..d807c71c00d 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift @@ -21,6 +21,7 @@ public enum ExperimentalFeature: String, CaseIterable { case coroutineAccessors case valueGenerics case abiAttribute + case keypathWithMethodMembers /// The name of the feature as it is written in the compiler's `Features.def` file. public var featureName: String { @@ -41,6 +42,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "ValueGenerics" case .abiAttribute: return "ABIAttribute" + case .keypathWithMethodMembers: + return "KeypathWithMethodMembers" } } @@ -63,6 +66,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "value generics" case .abiAttribute: return "@abi attribute" + case .keypathWithMethodMembers: + return "keypaths with method members" } } diff --git a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift index 18cef4ca1dd..bfe92058354 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift @@ -1208,6 +1208,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) @@ -1312,6 +1317,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 39f57d72ff5..53867ea09bb 100644 --- a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift +++ b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift @@ -182,6 +182,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 a6a7d8f03ad..ffb61aa66dd 100644 --- a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift +++ b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift @@ -570,6 +570,10 @@ class ValidateSyntaxNodes: XCTestCase { ), ValidationFailure(node: .lifetimeTypeSpecifier, message: "could conform to trait 'Parenthesized' but does not"), ValidationFailure(node: .codeBlockFile, message: "could conform to trait 'WithCodeBlock' 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 201d53f3d55..0e4c76aa1f0 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// #if compiler(>=6) -@_spi(RawSyntax) internal import SwiftSyntax +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax #else -@_spi(RawSyntax) import SwiftSyntax +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax #endif extension TokenConsumer { @@ -1115,11 +1115,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, @@ -1140,7 +1183,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 c999428f365..f80e6c9f7b4 100644 --- a/Sources/SwiftParser/generated/ExperimentalFeatures.swift +++ b/Sources/SwiftParser/generated/ExperimentalFeatures.swift @@ -48,6 +48,9 @@ extension Parser.ExperimentalFeatures { /// Whether to enable the parsing of @abi attribute. public static let abiAttribute = Self (rawValue: 1 << 7) + /// Whether to enable the parsing of keypaths with method members. + public static let keypathWithMethodMembers = Self (rawValue: 1 << 8) + /// 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) { @@ -68,6 +71,8 @@ extension Parser.ExperimentalFeatures { self = .valueGenerics case "ABIAttribute": self = .abiAttribute + 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 787846cd0e1..d1750c4b738 100644 --- a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift @@ -249,6 +249,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 c27b045ecf2..b81a97dc3f4 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -1951,6 +1951,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 6de3d50f4aa..84377725edc 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -1324,6 +1324,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 8b9cccfe59e..ca1d687edd8 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -1676,6 +1676,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 1d55a7cb1c5..33faa4840da 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -178,6 +178,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) @@ -638,6 +640,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 887cf329302..ed0f371c275 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -178,6 +178,8 @@ public enum SyntaxKind: Sendable { case keyPathComponentList case keyPathComponent case keyPathExpr + @_spi(ExperimentalLanguageFeatures) + case keyPathMethodComponent case keyPathOptionalComponent case keyPathPropertyComponent case keyPathSubscriptComponent @@ -763,6 +765,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 c297d8f31ce..44236ea93fb 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -1203,6 +1203,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 @@ -2964,6 +2972,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))) @@ -3971,6 +3984,8 @@ open class SyntaxRewriter { return self.visitKeyPathComponentSyntaxImpl(_:) case .keyPathExpr: return self.visitKeyPathExprSyntaxImpl(_:) + case .keyPathMethodComponent: + return self.visitKeyPathMethodComponentSyntaxImpl(_:) case .keyPathOptionalComponent: return self.visitKeyPathOptionalComponentSyntaxImpl(_:) case .keyPathPropertyComponent: @@ -4561,6 +4576,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 519c8a64ec9..3645b1493ea 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -1930,6 +1930,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. @@ -4811,6 +4825,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 { @@ -6217,6 +6239,8 @@ open class SyntaxVisitor { return self.visitKeyPathComponentSyntaxImpl(_:) case .keyPathExpr: return self.visitKeyPathExprSyntaxImpl(_:) + case .keyPathMethodComponent: + return self.visitKeyPathMethodComponentSyntaxImpl(_:) case .keyPathOptionalComponent: return self.visitKeyPathOptionalComponentSyntaxImpl(_:) case .keyPathPropertyComponent: @@ -6807,6 +6831,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 2e4d2cb833a..f3819344607 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 RawSyntaxArena + ) { + 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 885d3839979..868615b7d9d 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -1822,6 +1822,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)) @@ -3462,6 +3474,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 3c49ca82268..63f2a4058d2 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((RawSyntaxArena(), ( + 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), rawAllocationArena: RawSyntaxArena()).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), rawAllocationArena: RawSyntaxArena()).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), rawAllocationArena: RawSyntaxArena()).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), rawAllocationArena: RawSyntaxArena()).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), rawAllocationArena: RawSyntaxArena()).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), rawAllocationArena: RawSyntaxArena()).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 = RawSyntaxArena() + 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, + rawAllocationArena: 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), rawAllocationArena: RawSyntaxArena()).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), rawAllocationArena: RawSyntaxArena()).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), rawAllocationArena: RawSyntaxArena()).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 fbb65d34701..70b277c179d 100644 --- a/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift +++ b/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift @@ -982,6 +982,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 fbc13533209..8d09f4d9630 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]"#,