https://github.com/mygitljf created https://github.com/llvm/llvm-project/pull/199103
## Summary `clang-format` aborts on triple-bracket input like `[[[a]]` with `Assertion 'Left.isNot(tok::l_square)' failed.` This patch fixes the crash by rejecting the C++ attribute classification when the candidate `[[` is itself preceded by another `[`. ## Problem `isCppAttribute` only inspects the tokens *following* `[[`. For malformed input `[[[a]]` it returns `true` on the **middle** `[` (its `Next`/`Next->Next` look like a valid `[[ident...]]` sequence), so that token gets `TT_AttributeLSquare` while the outer `[` stays as a raw `tok::l_square`. This breaks the invariant guarded by `assert(Left.isNot(tok::l_square))` in `canBreakBefore` (`TokenAnotator.cpp:6660`) and aborts the process. ## Solution Add one defensive check to `isCppAttribute` that mirrors the existing ObjC-array-literal exclusion: bail out when `Tok.Previous` is another `[`. Legal C++ never has `[[[` in any context, so this is a strict refinement with no behavioural impact on well-formed code. ## Testing - `[[[a]]`, `[[[a]]]`, `[[ [a] ]]` no longer crash; output is preserved as-is. - `[[nodiscard]] int foo();` and other legal C++11/17 attributes format identically to before. - Regression cases added to `FormatTest.DoNotCrashOnInvalidInput`. - All 1264 `FormatTests` gtest pass. - All 33 `clang/test/Format/` lit tests pass. - `ninja check-clang-format` passes end-to-end. >From 70d1648e9d5a973acd5e710f485764be63d80038 Mon Sep 17 00:00:00 2001 From: mygitljf <[email protected]> Date: Thu, 21 May 2026 19:53:27 +0000 Subject: [PATCH] [clang-format] Fix a crash on triple-bracket input like `[[[a]]` `isCppAttribute` only checks the tokens following `[[`, so for malformed input `[[[a]]` it returns true on the *middle* `[`: that token's `Next`/`Next->Next` look like a valid `[[ident...]]` sequence. The middle `[` is then annotated as `TT_AttributeLSquare` while the outer `[` stays as a raw `tok::l_square`, which violates the invariant guarded by `assert(Left.isNot(tok::l_square))` in `canBreakBefore` and aborts the process. Reject the C++ attribute classification when the candidate `[[` is itself preceded by another `[`. Legal C++ never has `[[[` in any context, so this is a strict refinement that mirrors the existing defensive check that excludes ObjC `@[...]` array literals. Fixes #199010 --- clang/lib/Format/TokenAnnotator.cpp | 6 ++++++ clang/unittests/Format/FormatTest.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 43e4f6796b6dd..63b4f65418a93 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -90,6 +90,12 @@ static bool isCppAttribute(bool IsCpp, const FormatToken &Tok) { // The first square bracket is part of an ObjC array literal if (Tok.Previous && Tok.Previous->is(tok::at)) return false; + // A C++ attribute specifier '[[...]]' never appears directly inside another + // '['. Treating malformed input like '[[[a]]' as one would mis-annotate the + // inner '[' as TT_AttributeLSquare while leaving the outer '[' as a raw + // tok::l_square, tripping an assertion in canBreakBefore. + if (Tok.Previous && Tok.Previous->is(tok::l_square)) + return false; const FormatToken *AttrTok = Tok.Next->Next; if (!AttrTok) return false; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 83e2c5b38ceaf..6f01492ddce82 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -22508,6 +22508,9 @@ TEST_F(FormatTest, DoNotCrashOnInvalidInput) { verifyNoCrash(" tst %o5 ! are we doing the gray case?\n" "LY52: ! [internal]"); verifyNoCrash("operator foo *;"); + verifyNoCrash("[[[a]]"); + verifyNoCrash("[[[a]]]"); + verifyNoCrash("[[ [a] ]]"); } TEST_F(FormatTest, FormatsTableGenCode) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
