[clang] [clang-format] Don't unconditionally rotate qualifiers past `typeof_unqual` (PR #200970)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 1 16:55:32 PDT 2026
https://github.com/Cons-Cat updated https://github.com/llvm/llvm-project/pull/200970
>From 4e83a18ff09fe3b491a2222f66c0adeb48ec8f75 Mon Sep 17 00:00:00 2001
From: Lilian Gooch <omg.lily2000 at gmail.com>
Date: Mon, 1 Jun 2026 16:29:25 -0700
Subject: [PATCH] [clang-format] Don't rotate const past __typeof_unqual
QualifierAlignmentFixer skipped const/volatile rotation for
decltype(...), typeof(...), and _Atomic(...) but not for
typeof_unqual(...). In C23 mode this produced invalid code:
const typeof_unqual(foo) -> typeof_unqual const(foo)
Add tok::kw_typeof_unqual to the skip case in QualifierAlignmentFixer
and to TT_TypeDeclarationParen detection in TokenAnnotator, mirroring
typeof. Also add it to Token::isSimpleTypeSpecifier so it is recognised
as a type by FormatToken::isTypeName.
The reachable spelling depends on language mode: typeof_unqual is a C23
keyword, while __typeof_unqual / __typeof_unqual__ are KEYALL alias
spellings. Cover all three in the C-mode test.
Assisted-by: Cursor/Claude Opus
---
clang/lib/Format/QualifierAlignmentFixer.cpp | 5 ++++-
clang/lib/Format/TokenAnnotator.cpp | 6 ++++--
clang/lib/Lex/Lexer.cpp | 1 +
clang/unittests/Format/QualifierFixerTest.cpp | 18 ++++++++++++++++++
4 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Format/QualifierAlignmentFixer.cpp b/clang/lib/Format/QualifierAlignmentFixer.cpp
index 3d1a9dc868208..b03f4098ee140 100644
--- a/clang/lib/Format/QualifierAlignmentFixer.cpp
+++ b/clang/lib/Format/QualifierAlignmentFixer.cpp
@@ -289,9 +289,12 @@ const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
if (TypeToken->isTypeName(LangOpts)) {
// The case `const decltype(foo)` -> `const decltype(foo)`
// The case `const typeof(foo)` -> `const typeof(foo)`
+ // The case `const typeof_unqual(foo)` -> `const typeof_unqual(foo)`
// The case `const _Atomic(foo)` -> `const _Atomic(foo)`
- if (TypeToken->isOneOf(tok::kw_decltype, tok::kw_typeof, tok::kw__Atomic))
+ if (TypeToken->isOneOf(tok::kw_decltype, tok::kw_typeof,
+ tok::kw_typeof_unqual, tok::kw__Atomic)) {
return Tok;
+ }
const FormatToken *LastSimpleTypeSpecifier = TypeToken;
while (isQualifierOrType(LastSimpleTypeSpecifier->getNextNonComment(),
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 43e4f6796b6dd..970aed0ddb941 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -447,14 +447,16 @@ class AnnotatingParser {
if (PrevNonComment->isAttribute()) {
OpeningParen.setType(TT_AttributeLParen);
} else if (PrevNonComment->isOneOf(TT_TypenameMacro, tok::kw_decltype,
- tok::kw_typeof,
+ tok::kw_typeof, tok::kw_typeof_unqual,
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) tok::kw___##Trait,
#include "clang/Basic/TransformTypeTraits.def"
tok::kw__Atomic)) {
OpeningParen.setType(TT_TypeDeclarationParen);
// decltype() and typeof() usually contain expressions.
- if (PrevNonComment->isOneOf(tok::kw_decltype, tok::kw_typeof))
+ if (PrevNonComment->isOneOf(tok::kw_decltype, tok::kw_typeof,
+ tok::kw_typeof_unqual)) {
Contexts.back().IsExpression = true;
+ }
}
}
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 2797212c229f5..4bfaa4a0831ad 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -120,6 +120,7 @@ bool Token::isSimpleTypeSpecifier(const LangOptions &LangOpts) const {
case tok::kw_char16_t:
case tok::kw_char32_t:
case tok::kw_typeof:
+ case tok::kw_typeof_unqual:
case tok::kw_decltype:
case tok::kw_char8_t:
return getIdentifierInfo()->isKeyword(LangOpts);
diff --git a/clang/unittests/Format/QualifierFixerTest.cpp b/clang/unittests/Format/QualifierFixerTest.cpp
index edde7bb3b5c97..584bd15d04e22 100644
--- a/clang/unittests/Format/QualifierFixerTest.cpp
+++ b/clang/unittests/Format/QualifierFixerTest.cpp
@@ -847,6 +847,24 @@ TEST_F(QualifierFixerTest, LeftQualifier) {
verifyFormat("const float (C::*p)(int);", "float const (C::*p)(int);", Style);
}
+TEST_F(QualifierFixerTest, TypeofUnqualC) {
+ FormatStyle Style = getLLVMStyle(FormatStyle::LK_C);
+ Style.QualifierAlignment = FormatStyle::QAS_Right;
+ Style.QualifierOrder = {"type", "const", "volatile"};
+
+ // Don't move past typeof_unqual or its alternate spellings.
+ verifyFormat("const typeof_unqual(foo)", Style);
+ verifyFormat("const __typeof_unqual(foo)", Style);
+ verifyFormat("const __typeof_unqual__(foo)", Style);
+
+ Style.QualifierAlignment = FormatStyle::QAS_Left;
+ Style.QualifierOrder = {"const", "volatile", "type"};
+
+ verifyFormat("typeof_unqual(foo) const", Style);
+ verifyFormat("__typeof_unqual(foo) const", Style);
+ verifyFormat("__typeof_unqual__(foo) const", Style);
+}
+
TEST_F(QualifierFixerTest, ConstVolatileQualifiersOrder) {
FormatStyle Style = getLLVMStyle();
Style.QualifierAlignment = FormatStyle::QAS_Left;
More information about the cfe-commits
mailing list