https://github.com/LeGusto updated 
https://github.com/llvm/llvm-project/pull/212328

From a42fc20b9252a4f6e70ef497d46d172b513fa346 Mon Sep 17 00:00:00 2001
From: LeGusto <[email protected]>
Date: Mon, 27 Jul 2026 22:00:43 +0300
Subject: [PATCH 1/2] [clang-format] Don't case-fix numeric literals in macro
 invocations

NumericLiteralCaseFixer transformed macro invocations such as FOO(0xa) to 
FOO(0xA), which is incorrect when the argument is stringized.

Fix recognizes possible macro invocations in NumericLiteralCaseFixer::process 
by replicating the logic of FormatToken::isPossibleMacro and tracking the 
nesting of the invocation parentheses.

Addresses #200731
---
 clang/lib/Format/NumericLiteralCaseFixer.cpp  | 26 +++++++++++++++++--
 .../Format/NumericLiteralCaseTest.cpp         | 26 +++++++++++++++++++
 2 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Format/NumericLiteralCaseFixer.cpp 
b/clang/lib/Format/NumericLiteralCaseFixer.cpp
index b58b3c7ee0189..ba55ebdcb8f41 100644
--- a/clang/lib/Format/NumericLiteralCaseFixer.cpp
+++ b/clang/lib/Format/NumericLiteralCaseFixer.cpp
@@ -43,6 +43,11 @@ static bool isNumericLiteralCaseFixerNeeded(const 
FormatStyle &Style) {
          Option.ExponentLetter != Leave || Option.Suffix != Leave;
 }
 
+// An all-caps identifier is likely a macro; see FormatToken::isPossibleMacro.
+static bool isPossibleMacroName(StringRef Name) {
+  return Name.size() > 1 && Name == Name.upper();
+}
+
 static std::string
 transformComponent(StringRef Component,
                    FormatStyle::NumericLiteralComponentStyle ConfigValue) {
@@ -137,8 +142,25 @@ NumericLiteralCaseFixer::process(const Environment &Env,
 
   Token Tok;
   tooling::Replacements Result;
+  int MacroArgDepth = 0; // Paren depth inside a possible macro invocation.
+
+  for (bool Skip = false, AfterMacroName = false, AfterScope = false;
+       !Lex.LexFromRawLexer(Tok);) {
+    // Leave arguments of a likely macro invocation (e.g. FOO(0xa)) untouched.
+    if (Tok.is(tok::raw_identifier)) {
+      AfterMacroName =
+          !AfterScope && isPossibleMacroName(Tok.getRawIdentifier());
+    } else if (Tok.is(tok::l_paren)) {
+      if (MacroArgDepth > 0 || AfterMacroName)
+        ++MacroArgDepth;
+      AfterMacroName = false;
+    } else {
+      if (Tok.is(tok::r_paren) && MacroArgDepth > 0)
+        --MacroArgDepth;
+      AfterMacroName = false;
+    }
+    AfterScope = Tok.is(tok::coloncolon);
 
-  for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) {
     // Skip tokens that are too small to contain a formattable literal.
     // Size=2 is the smallest possible literal that could contain formattable
     // components, for example "1u".
@@ -157,7 +179,7 @@ NumericLiteralCaseFixer::process(const Environment &Env,
       continue;
     }
 
-    if (Skip || Tok.isNot(tok::numeric_constant) ||
+    if (Skip || MacroArgDepth > 0 || Tok.isNot(tok::numeric_constant) ||
         !AffectedRangeMgr.affectsCharSourceRange(
             CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) {
       continue;
diff --git a/clang/unittests/Format/NumericLiteralCaseTest.cpp 
b/clang/unittests/Format/NumericLiteralCaseTest.cpp
index ecd230d73f692..f59fbed2c5ab0 100644
--- a/clang/unittests/Format/NumericLiteralCaseTest.cpp
+++ b/clang/unittests/Format/NumericLiteralCaseTest.cpp
@@ -340,6 +340,32 @@ TEST_F(NumericLiteralCaseTest, 
UnderScoreSeparatorLanguages) {
   verifyFormat("o = 0o0_10_010;", "o = 0O0_10_010;", Style);
 }
 
+TEST_F(NumericLiteralCaseTest, IgnoresMacroInvocations) {
+  // Arguments of a likely macro invocation aren't literals.
+  constexpr StringRef A("FOO(0xabc);");
+  constexpr StringRef B("FOO(bar(1), 0xabc);");
+  // Non-macro contexts, where literals are still formatted.
+  constexpr StringRef C("foo(0xabc);");
+  constexpr StringRef D("FOO(1) + 0xabc;");
+  constexpr StringRef E("T(0xabc);");
+  constexpr StringRef F("NS::FOO(0xabc);");
+  verifyFormat(A);
+  verifyFormat(B);
+  verifyFormat(C);
+  verifyFormat(D);
+  verifyFormat(E);
+  verifyFormat(F);
+
+  auto Style = getLLVMStyle();
+  Style.NumericLiteralCase.HexDigit = FormatStyle::NLCS_Upper;
+  verifyFormat(A, Style);
+  verifyFormat(B, Style);
+  verifyFormat("foo(0xABC);", C, Style);
+  verifyFormat("FOO(1) + 0xABC;", D, Style);
+  verifyFormat("T(0xABC);", E, Style);
+  verifyFormat("NS::FOO(0xABC);", F, Style);
+}
+
 } // namespace
 } // namespace test
 } // namespace format

From 8c051f3ba1809cf2c559252c6f5caaa53d72e659 Mon Sep 17 00:00:00 2001
From: LeGusto <[email protected]>
Date: Sun, 6 Sep 2026 02:38:41 +0300
Subject: [PATCH 2/2] Change custom macro detection logic to instead check
 WhitespaceSensitiveMacros

---
 clang/lib/Format/NumericLiteralCaseFixer.cpp  | 23 ++++++------
 .../Format/NumericLiteralCaseTest.cpp         | 36 ++++++++++++-------
 2 files changed, 34 insertions(+), 25 deletions(-)

diff --git a/clang/lib/Format/NumericLiteralCaseFixer.cpp 
b/clang/lib/Format/NumericLiteralCaseFixer.cpp
index ba55ebdcb8f41..a28853c36f65b 100644
--- a/clang/lib/Format/NumericLiteralCaseFixer.cpp
+++ b/clang/lib/Format/NumericLiteralCaseFixer.cpp
@@ -16,6 +16,7 @@
 #include "NumericLiteralInfo.h"
 
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
 
 #include <algorithm>
 
@@ -43,11 +44,6 @@ static bool isNumericLiteralCaseFixerNeeded(const 
FormatStyle &Style) {
          Option.ExponentLetter != Leave || Option.Suffix != Leave;
 }
 
-// An all-caps identifier is likely a macro; see FormatToken::isPossibleMacro.
-static bool isPossibleMacroName(StringRef Name) {
-  return Name.size() > 1 && Name == Name.upper();
-}
-
 static std::string
 transformComponent(StringRef Component,
                    FormatStyle::NumericLiteralComponentStyle ConfigValue) {
@@ -135,6 +131,12 @@ NumericLiteralCaseFixer::process(const Environment &Env,
   const auto &SourceMgr = Env.getSourceManager();
   AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());
 
+  // Don't recase pp-numbers inside whitespace-sensitive macros (e.g.
+  // STRINGIZE(0xa)); recasing a stringized argument would change the result.
+  llvm::StringSet<> UntouchableMacros;
+  for (StringRef Macro : Style.WhitespaceSensitiveMacros)
+    UntouchableMacros.insert(Macro);
+
   const auto ID = Env.getFileID();
   const auto LangOpts = getFormattingLangOpts(Style);
   Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts);
@@ -142,14 +144,12 @@ NumericLiteralCaseFixer::process(const Environment &Env,
 
   Token Tok;
   tooling::Replacements Result;
-  int MacroArgDepth = 0; // Paren depth inside a possible macro invocation.
+  int MacroArgDepth = 0; // Paren depth inside an untouchable macro invocation.
 
-  for (bool Skip = false, AfterMacroName = false, AfterScope = false;
-       !Lex.LexFromRawLexer(Tok);) {
-    // Leave arguments of a likely macro invocation (e.g. FOO(0xa)) untouched.
+  for (bool Skip = false, AfterMacroName = false; !Lex.LexFromRawLexer(Tok);) {
+    // Track whether we are inside the argument list of an untouchable macro.
     if (Tok.is(tok::raw_identifier)) {
-      AfterMacroName =
-          !AfterScope && isPossibleMacroName(Tok.getRawIdentifier());
+      AfterMacroName = UntouchableMacros.contains(Tok.getRawIdentifier());
     } else if (Tok.is(tok::l_paren)) {
       if (MacroArgDepth > 0 || AfterMacroName)
         ++MacroArgDepth;
@@ -159,7 +159,6 @@ NumericLiteralCaseFixer::process(const Environment &Env,
         --MacroArgDepth;
       AfterMacroName = false;
     }
-    AfterScope = Tok.is(tok::coloncolon);
 
     // Skip tokens that are too small to contain a formattable literal.
     // Size=2 is the smallest possible literal that could contain formattable
diff --git a/clang/unittests/Format/NumericLiteralCaseTest.cpp 
b/clang/unittests/Format/NumericLiteralCaseTest.cpp
index f59fbed2c5ab0..f1b9f1e6b3e08 100644
--- a/clang/unittests/Format/NumericLiteralCaseTest.cpp
+++ b/clang/unittests/Format/NumericLiteralCaseTest.cpp
@@ -340,15 +340,19 @@ TEST_F(NumericLiteralCaseTest, 
UnderScoreSeparatorLanguages) {
   verifyFormat("o = 0o0_10_010;", "o = 0O0_10_010;", Style);
 }
 
-TEST_F(NumericLiteralCaseTest, IgnoresMacroInvocations) {
-  // Arguments of a likely macro invocation aren't literals.
-  constexpr StringRef A("FOO(0xabc);");
-  constexpr StringRef B("FOO(bar(1), 0xabc);");
-  // Non-macro contexts, where literals are still formatted.
-  constexpr StringRef C("foo(0xabc);");
-  constexpr StringRef D("FOO(1) + 0xabc;");
-  constexpr StringRef E("T(0xabc);");
-  constexpr StringRef F("NS::FOO(0xabc);");
+TEST_F(NumericLiteralCaseTest, IgnoresWhitespaceSensitiveMacros) {
+  // Arguments of a whitespace-sensitive macro (e.g. a stringizing macro) must
+  // not be recased; everything else still is.
+  constexpr StringRef A("a = STRINGIZE(0xabc);");
+  constexpr StringRef B("b = PP_STRINGIZE(0xa, 0xb);");
+  // Nested parens and multiple literals inside the macro are all left alone.
+  constexpr StringRef C("c = STRINGIZE(f(0xa) + 0xb);");
+  // A literal outside the macro is still formatted.
+  constexpr StringRef D("d = STRINGIZE(1) + 0xabc;");
+  // A macro that is not whitespace-sensitive, and a plain literal, are still
+  // formatted.
+  constexpr StringRef E("e = FOO(0xabc);");
+  constexpr StringRef F("f = 0xabc;");
   verifyFormat(A);
   verifyFormat(B);
   verifyFormat(C);
@@ -358,12 +362,18 @@ TEST_F(NumericLiteralCaseTest, IgnoresMacroInvocations) {
 
   auto Style = getLLVMStyle();
   Style.NumericLiteralCase.HexDigit = FormatStyle::NLCS_Upper;
+  // STRINGIZE and PP_STRINGIZE are whitespace-sensitive by default.
   verifyFormat(A, Style);
   verifyFormat(B, Style);
-  verifyFormat("foo(0xABC);", C, Style);
-  verifyFormat("FOO(1) + 0xABC;", D, Style);
-  verifyFormat("T(0xABC);", E, Style);
-  verifyFormat("NS::FOO(0xABC);", F, Style);
+  verifyFormat(C, Style);
+  verifyFormat("d = STRINGIZE(1) + 0xABC;", D, Style);
+  verifyFormat("e = FOO(0xABC);", E, Style);
+  verifyFormat("f = 0xABC;", F, Style);
+
+  // A user-declared macro is honored too, including its nested arguments.
+  Style.WhitespaceSensitiveMacros.push_back("FOO");
+  verifyFormat(E, Style);
+  verifyFormat("g = FOO(h(0xa), 0xbc);", Style);
 }
 
 } // namespace

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

Reply via email to