https://github.com/johnnyb2543 created 
https://github.com/llvm/llvm-project/pull/210630

This patch resolves a clang-format regression where unspaced User-Defined 
Literals (UDLs) were being formatted incorrectly.
Root Cause
The lexer failed to correctly classify the preceding token sequence as a single 
user-defined literal operator. Since 
FormatTokenLexer::tryMergeUserDefinedLiteral did not properly merge the 
operator token and its suffix (e.g., _mag), the internal TokenAnnotator 
evaluated the subsequent < token as a comparison operator.
Changes
- Updated FormatTokenLexer::tryMergeUserDefinedLiteral to ensure proper token 
merging for UDLs
- Added unit tests in FormatTest.cpp to verify correct spacing behavior and 
prevent future regressions.

Fixes #210135 

>From 36a2d7403fc90e45e58f55983ab1074c28ace5d9 Mon Sep 17 00:00:00 2001
From: John Boncore <[email protected]>
Date: Sun, 19 Jul 2026 16:31:10 -0400
Subject: [PATCH] [clang-format] Fix template parsing regression for unspaced
 user-defined literals

---
 clang/lib/Format/FormatTokenLexer.cpp | 32 +++++++++++++++++++++++++++
 clang/unittests/Format/FormatTest.cpp |  5 ++++-
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Format/FormatTokenLexer.cpp 
b/clang/lib/Format/FormatTokenLexer.cpp
index 0cf01875af833..12815e8cb3679 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -595,6 +595,38 @@ bool FormatTokenLexer::tryMergeUserDefinedLiteral() {
   if (Tokens.size() < 2)
     return false;
 
+         // --- INTERCEPT STRING/CHARACTER UDLs ALREADY MERGED BY THE RAW 
LEXER ---
+  // --- INTERCEPT STRING/CHARACTER UDLs ALREADY MERGED BY THE RAW LEXER ---
+  // --- INTERCEPT STRING/CHARACTER UDLs ALREADY MERGED BY THE RAW LEXER ---
+  if (Tokens.back()->isOneOf(tok::string_literal, tok::char_constant, 
tok::numeric_constant) &&
+      Tokens.end()[-2]->is(tok::kw_operator)) {
+
+    FormatToken *OpToken = Tokens[Tokens.size() - 2];
+    FormatToken *LiteralToken = Tokens.back();
+
+           // Strict guard: Do not merge if this is a member access call 
(e.g., x.operator""_a())
+    if (Tokens.size() >= 3) {
+      FormatToken *PrevToken = Tokens[Tokens.size() - 3];
+      if (PrevToken->isOneOf(tok::period, tok::arrow) ||
+          PrevToken->TokenText == "." || PrevToken->TokenText == "->") {
+        return false;
+      }
+    }
+
+           // Ensure they are touching in the source text
+    if (!LiteralToken->hasWhitespaceBefore()) {
+      OpToken->TokenText = StringRef(OpToken->TokenText.data(),
+                                     OpToken->TokenText.size() + 
LiteralToken->TokenText.size());
+      OpToken->ColumnWidth += LiteralToken->ColumnWidth;
+
+      OpToken->Tok.setKind(tok::identifier);
+
+      Tokens.erase(&Tokens.back());
+      return true;
+    }
+  }
+
+         // --- ORIGINAL NUMERIC LITERAL LOGIC KICKS IN HERE ---
   auto *First = Tokens.end() - 2;
   auto &Suffix = First[1];
   if (Suffix->hasWhitespaceBefore() || Suffix->TokenText != "$")
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index ec9ad612832f5..3ee336e6c46ad 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -16,7 +16,10 @@ namespace test {
 namespace {
 
 class FormatTest : public test::FormatTestBase {};
-
+TEST_F(FormatTest, FormatsUserDefinedLiteralTemplates) {
+    verifyFormat("template <char... Cs> auto f() { return 
operator\"\"_mag<Cs...>(); }");
+    verifyFormat("template <char... Cs> auto f() { return operator 
\"\"_mag<Cs...>(); }");
+}
 TEST_F(FormatTest, MessUp) {
   EXPECT_EQ("1 2 3", messUp("1 2 3"));
   EXPECT_EQ("1 2 3", messUp("1\n2\n3"));

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to