https://github.com/Lane0218 created https://github.com/llvm/llvm-project/pull/189024
This narrows clang-format's spacing heuristic for `identifier ::`. Previously, clang-format preserved existing whitespace before `::` after any identifier, which caused inputs like: ```c++ template <typename T> auto mem = &T :: member; ``` to format as: ```c++ template <typename T> auto mem = &T ::member; ``` This patch preserves that whitespace only for identifiers that look like object-like macros, such as the existing `ALWAYS_INLINE ::std::string` case. Ordinary identifiers now format as expected: ```c++ &T :: member ``` becomes ```c++ &T::member ``` Test: - `./build-cir/tools/clang/unittests/Format/FormatTests --gtest_filter=FormatTest.NestedNameSpecifiers` Fixes #188754 >From 8180665daa19001bb154e39a4ec7510aa254364f Mon Sep 17 00:00:00 2001 From: Lane0218 <[email protected]> Date: Fri, 27 Mar 2026 23:15:04 +0800 Subject: [PATCH] [clang-format] Fix spacing before :: after non-macro identifiers Preserve spaces before :: only for identifiers that likely name object-like macros, and otherwise format identifier :: as identifier::. This fixes clang-format leaving dependent nested name specifiers such as &T :: member half-formatted as &T ::member. Add a regression test for the reported case while keeping the existing ALWAYS_INLINE ::std::string coverage. Fixes #188754 --- clang/lib/Format/TokenAnnotator.cpp | 21 +++++++++++++++++---- clang/unittests/Format/FormatTest.cpp | 2 ++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d2cdc28a7da7b..6a229283681cb 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -59,6 +59,21 @@ static bool canBeObjCSelectorComponent(const FormatToken &Tok) { return Tok.Tok.getIdentifierInfo(); } +/// Returns \c true if the token likely names an object-like macro. +static bool isPossibleMacro(const FormatToken &Tok) { + if (Tok.isNot(tok::identifier)) + return false; + + StringRef Text = Tok.TokenText; + assert(!Text.empty()); + + // T, K, U, V likely could be template arguments. + if (Text.size() == 1) + return false; + + return Text == Text.upper(); +} + /// With `Left` being '(', check if we're at either `[...](` or /// `[...]<...>(`, where the [ opens a lambda capture list. // FIXME: this doesn't cover attributes/constraints before the l_paren. @@ -5646,10 +5661,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return false; } if (Right.is(tok::coloncolon) && Left.is(tok::identifier)) { - // Generally don't remove existing spaces between an identifier and "::". - // The identifier might actually be a macro name such as ALWAYS_INLINE. If - // this turns out to be too lenient, add analysis of the identifier itself. - return Right.hasWhitespaceBefore(); + // Preserve the space in constructs such as ALWAYS_INLINE ::std::string. + return isPossibleMacro(Left) && Right.hasWhitespaceBefore(); } if (Right.is(tok::coloncolon) && Left.isNoneOf(tok::l_brace, tok::comment, tok::l_paren)) { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 2701a7fca7346..6cb28f2c98c2a 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -80,6 +80,8 @@ TEST_F(FormatTest, NestedNameSpecifiers) { verifyFormat("static constexpr bool Bar = _Atomic(bar())::value;"); verifyFormat("bool a = 2 < ::SomeFunction();"); verifyFormat("ALWAYS_INLINE ::std::string getName();"); + verifyFormat("template <typename T> auto mem = &T::member;", + "template <typename T> auto mem = &T :: member;"); verifyFormat("some::string getName();"); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
