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 ==<T>(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 *<let X: Int, let Y: Int>(l: A<X>, r: A<Y>) -> Int { l.int * r.int }
+      """
+    )
+  }
 }