[clang] [clang-format] Fix annotation of alternative operator and (PR #199112)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 5 06:22:52 PDT 2026
https://github.com/mygitljf updated https://github.com/llvm/llvm-project/pull/199112
>From f708f5ea68b74e859fb2fc963281082869fbd5c6 Mon Sep 17 00:00:00 2001
From: mygitljf <2410316423 at qq.com>
Date: Thu, 21 May 2026 20:59:39 +0000
Subject: [PATCH 1/2] [clang-format] Fix assertion in alignChainedConditionals
The pointer-alignment compensation block in AlignTokenSequence
unconditionally inserts -Shift spaces in front of any token whose
predecessor is annotated TT_PointerOrReference. It assumed that
the +Shift had just been added to that token's Spaces by the
preceding increment, but the +Shift is gated on the token being
the matched anchor (or a continuation in a nested scope). On a
non-anchor token that gate is skipped, and the unconditional
-Shift drops Spaces below zero, tripping the assertion in
IncrementChangeSpaces.
Make the compensation share the same gate as the +Shift it is
meant to balance, so a -Shift only ever runs when the matching
+Shift did.
Fixes #199027
---
clang/lib/Format/WhitespaceManager.cpp | 10 +++++++---
clang/unittests/Format/AlignmentTest.cpp | 5 +++++
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index 4a72abbfa9ec3..da990c03134a3 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -415,9 +415,11 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
// This is for lines that are split across multiple lines, as mentioned in
// the ScopeStack comment. The stack size being 1 means that the token is
// not in a scope that should not move.
- if ((!Matches.empty() && Matches[0] == i) ||
+ const bool ShiftAppliedToCurrent =
+ (!Matches.empty() && Matches[0] == i) ||
(ScopeStack.size() == 1u && CurrentChange.NewlinesBefore > 0 &&
- InsideNestedScope)) {
+ InsideNestedScope);
+ if (ShiftAppliedToCurrent) {
CurrentChange.IndentedFromColumn += Shift;
IncrementChangeSpaces(i, Shift, Changes);
}
@@ -430,7 +432,9 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
// If PointerAlignment is PAS_Right, keep *s or &s next to the token,
// except if the token is equal, then a space is needed.
- if ((Style.PointerAlignment == FormatStyle::PAS_Right ||
+ // Skip unless the +Shift increment above ran on CurrentChange.
+ if (ShiftAppliedToCurrent &&
+ (Style.PointerAlignment == FormatStyle::PAS_Right ||
Style.ReferenceAlignment == FormatStyle::RAS_Right) &&
CurrentChange.Spaces != 0 &&
CurrentChange.Tok->isNoneOf(tok::equal, tok::r_paren,
diff --git a/clang/unittests/Format/AlignmentTest.cpp b/clang/unittests/Format/AlignmentTest.cpp
index fbc0cb4d825ea..55e53d80284cc 100644
--- a/clang/unittests/Format/AlignmentTest.cpp
+++ b/clang/unittests/Format/AlignmentTest.cpp
@@ -3630,6 +3630,11 @@ TEST_F(AlignmentTest, ContinuedAligned) {
Style);
}
+TEST_F(AlignmentTest, AlignChainedConditionalsNoCrashOnPointerLikeOperator) {
+ verifyNoCrash(
+ " #xxxx??x<xxxxxxx||??x<xxxxxxx and xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+}
+
} // namespace
} // namespace test
} // namespace format
>From 505b24905fde9a6ceb84737203ae62384cae7e1e Mon Sep 17 00:00:00 2001
From: mygitljf <2410316423 at qq.com>
Date: Fri, 5 Jun 2026 13:12:49 +0000
Subject: [PATCH 2/2] [clang-format] Annotate alternative operator and as
binary
The alternative spelling `and` is lexed as tok::ampamp just like `&&`, so I
saw determineStarAmpUsage() annotate it as TT_PointerOrReference on malformed
input, which later tripped an assertion in WhitespaceManager. So I now annotate
`and` as TT_BinaryOperator before the pointer/reference heuristic. I left
`bitand` alone since, like `&`, it can still be a reference.
Fixes #199027
---
clang/lib/Format/TokenAnnotator.cpp | 8 ++++++++
clang/lib/Format/WhitespaceManager.cpp | 10 +++-------
clang/unittests/Format/TokenAnnotatorTest.cpp | 5 +++++
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 43e4f6796b6dd..bda5a1487999e 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -3037,6 +3037,14 @@ class AnnotatingParser {
if (Style.isCSharp() && Tok.is(tok::ampamp))
return TT_BinaryOperator;
+ // The keyword `and` (tok::ampamp) is always binary, never a declarator.
+ // Not extended to `bitand` (tok::amp), which can be a reference.
+ if (Tok.is(tok::ampamp)) {
+ const auto *Info = Tok.Tok.getIdentifierInfo();
+ if (Info && Info->isCPlusPlusOperatorKeyword())
+ return TT_BinaryOperator;
+ }
+
if (Style.isVerilog()) {
// In Verilog, `*` can only be a binary operator. `&` can be either unary
// or binary. `*` also includes `*>` in module path declarations in
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index da990c03134a3..4a72abbfa9ec3 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -415,11 +415,9 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
// This is for lines that are split across multiple lines, as mentioned in
// the ScopeStack comment. The stack size being 1 means that the token is
// not in a scope that should not move.
- const bool ShiftAppliedToCurrent =
- (!Matches.empty() && Matches[0] == i) ||
+ if ((!Matches.empty() && Matches[0] == i) ||
(ScopeStack.size() == 1u && CurrentChange.NewlinesBefore > 0 &&
- InsideNestedScope);
- if (ShiftAppliedToCurrent) {
+ InsideNestedScope)) {
CurrentChange.IndentedFromColumn += Shift;
IncrementChangeSpaces(i, Shift, Changes);
}
@@ -432,9 +430,7 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
// If PointerAlignment is PAS_Right, keep *s or &s next to the token,
// except if the token is equal, then a space is needed.
- // Skip unless the +Shift increment above ran on CurrentChange.
- if (ShiftAppliedToCurrent &&
- (Style.PointerAlignment == FormatStyle::PAS_Right ||
+ if ((Style.PointerAlignment == FormatStyle::PAS_Right ||
Style.ReferenceAlignment == FormatStyle::RAS_Right) &&
CurrentChange.Spaces != 0 &&
CurrentChange.Tok->isNoneOf(tok::equal, tok::r_paren,
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 48ae9e144cc2a..694c8d5e1aabe 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -4194,6 +4194,11 @@ TEST_F(TokenAnnotatorTest, CppAltOperatorKeywords) {
ASSERT_EQ(Tokens.size(), 7u) << Tokens;
EXPECT_TOKEN(Tokens[3], tok::caretequal, TT_BinaryOperator);
+ Tokens = annotate(" #xxxx??x<xxxxxxx||??x<xxxxxxx and "
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ ASSERT_EQ(Tokens.size(), 16u) << Tokens;
+ EXPECT_TOKEN(Tokens[13], tok::ampamp, TT_BinaryOperator);
+
const auto StyleC = getLLVMStyle(FormatStyle::LK_C);
Tokens = annotate("xor = foo;", StyleC);
More information about the cfe-commits
mailing list