https://github.com/mygitljf updated https://github.com/llvm/llvm-project/pull/199112
>From f708f5ea68b74e859fb2fc963281082869fbd5c6 Mon Sep 17 00:00:00 2001 From: mygitljf <[email protected]> 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 <[email protected]> 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); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
