@@ -1103,8 +1103,48 @@ extension Parser {
11031103 continue
11041104 }
11051105
1106- // Check for a .name or .1 suffix.
1106+ // Check for a .name, .1, .name(), .name("Kiwi"), .name(fruit:),
1107+ // .name(_:), .name(fruit: "Kiwi) suffix.
11071108 if self . at ( . period) {
1109+ // Parse as a keypath method if fully applied.
1110+ if self . experimentalFeatures. contains ( . keypathWithMethodMembers)
1111+ && self . withLookahead ( { $0. isAppliedKeyPathMethod ( ) } )
1112+ {
1113+ let ( unexpectedPeriod, period, declName, _) = parseDottedExpressionSuffix (
1114+ previousNode: components. last? . raw ?? rootType? . raw ?? backslash. raw
1115+ )
1116+ let leftParen = self . consumeAnyToken ( )
1117+ var args : [ RawLabeledExprSyntax ] = [ ]
1118+ if !self . at ( . rightParen) {
1119+ args = self . parseArgumentListElements (
1120+ pattern: pattern,
1121+ allowTrailingComma: true
1122+ )
1123+ }
1124+ let ( unexpectedBeforeRParen, rightParen) = self . expect ( . rightParen)
1125+ components. append (
1126+ RawKeyPathComponentSyntax (
1127+ unexpectedPeriod,
1128+ period: period,
1129+ component: . method(
1130+ RawKeyPathMethodComponentSyntax (
1131+ declName: declName,
1132+ leftParen: leftParen,
1133+ arguments: RawLabeledExprListSyntax (
1134+ elements: args,
1135+ arena: self . arena
1136+ ) ,
1137+ unexpectedBeforeRParen,
1138+ rightParen: rightParen,
1139+ arena: self . arena
1140+ )
1141+ ) ,
1142+ arena: self . arena
1143+ )
1144+ )
1145+ continue
1146+ }
1147+ // Else, parse as a property.
11081148 let ( unexpectedPeriod, period, declName, generics) = parseDottedExpressionSuffix (
11091149 previousNode: components. last? . raw ?? rootType? . raw ?? backslash. raw
11101150 )
@@ -1128,7 +1168,6 @@ extension Parser {
11281168 // No more postfix expressions.
11291169 break
11301170 }
1131-
11321171 return RawKeyPathExprSyntax (
11331172 unexpectedBeforeBackslash,
11341173 backslash: backslash,
@@ -2017,6 +2056,37 @@ extension Parser {
20172056}
20182057
20192058extension Parser . Lookahead {
2059+ /// Check if the keypath method is applied, and not partially applied which should be parsed as a key path property.
2060+ mutating func isAppliedKeyPathMethod( ) -> Bool {
2061+ var lookahead = self . lookahead ( )
2062+ var hasLParen = false , hasRParen = false
2063+
2064+ while true {
2065+ let token = lookahead. peek ( ) . rawTokenKind
2066+ if token == . endOfFile || lookahead. atStartOfLine {
2067+ break
2068+ }
2069+ if token == . leftParen {
2070+ hasLParen = true
2071+ }
2072+ if token == . colon {
2073+ lookahead. consumeAnyToken ( )
2074+ // If there's a colon followed by a right parenthesis, it is
2075+ // a partial application and should be parsed as a property.
2076+ if lookahead. peek ( ) . rawTokenKind == . rightParen {
2077+ return false
2078+ }
2079+ }
2080+ if token == . rightParen {
2081+ hasRParen = true
2082+ }
2083+ lookahead. consumeAnyToken ( )
2084+ }
2085+ // If parentheses exist with no partial application pattern,
2086+ // parse as a key path method.
2087+ return hasLParen && hasRParen ? true : false
2088+ }
2089+
20202090 mutating func atStartOfLabelledTrailingClosure( ) -> Bool {
20212091 // Fast path: the next two tokens must be a label and a colon.
20222092 // But 'default:' is ambiguous with switch cases and we disallow it
0 commit comments