From 64b745a162e7c8e4b01df4f8eb8e70cc76019954 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 2 May 2025 17:04:19 -0700 Subject: [PATCH] [SwiftParser] Parse operator function with value generics Operator function parsing has a heuristics to determine if '<' a part of the operator name or the generic parameter clause. Handle `let` there because value generics uses it. rdar://149556573 (cherry picked from commit e7eb0b9d7fcc68b0bd6a64278434ac4c6b3cb8f9) --- Sources/SwiftParser/Declarations.swift | 5 ++++- Tests/SwiftParserTest/ValueGenericsTests.swift | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index d186a4b0a7d..a9e4a01f449 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -1215,8 +1215,11 @@ extension Parser { let unexpectedAfterIdentifier: RawUnexpectedNodesSyntax? let identifier: RawTokenSyntax if self.at(anyIn: Operator.self) != nil || self.at(.exclamationMark, .prefixAmpersand) { + // If the name is an operator token that ends in '<' followed by an identifier or 'let', + // leave the '<' so it's parsed as a generic parameter clause. This allows things like + // 'func ==(x:T, y:T) {}'. var name = self.currentToken.tokenText - if !currentToken.isEditorPlaceholder && name.hasSuffix("<") && self.peek(isAt: .identifier) { + if !currentToken.isEditorPlaceholder && name.hasSuffix("<") && self.peek(isAt: .identifier, .keyword(.let)) { name = SyntaxText(rebasing: name.dropLast()) } unexpectedBeforeIdentifier = nil diff --git a/Tests/SwiftParserTest/ValueGenericsTests.swift b/Tests/SwiftParserTest/ValueGenericsTests.swift index 7ff15a96dc9..7350ca0b749 100644 --- a/Tests/SwiftParserTest/ValueGenericsTests.swift +++ b/Tests/SwiftParserTest/ValueGenericsTests.swift @@ -287,4 +287,12 @@ final class ValueGenericsTests: ParserTestCase { fixedSource: "func foo() -> (<#type#>-1) X" ) } + + func testOperatorFunc() { + assertParse( + """ + func *(l: A, r: A) -> Int { l.int * r.int } + """ + ) + } }