https://github.com/sstwcw updated https://github.com/llvm/llvm-project/pull/223266
>From dac247bb35b3c365b151a3008eeabb0b5d4f9894 Mon Sep 17 00:00:00 2001 From: sstwcw <[email protected]> Date: Sun, 13 Sep 2026 18:29:48 +0000 Subject: [PATCH 1/3] [clang-format] Stop treating the new expression as a lambda before ```C++ x = new a *[] { nullptr }; x = new a *[] { nullptr, // nullptr, }; ``` after ```C++ x = new a *[]{nullptr}; x = new a *[]{ nullptr, // nullptr, }; ``` Fixes #38820. The program previously treated the part starting with the square brackets as a lambda when the type ended in a star. --- clang/lib/Format/UnwrappedLineParser.cpp | 22 ++++++++++++++++++- clang/unittests/Format/FormatTest.cpp | 10 +++++++++ clang/unittests/Format/TokenAnnotatorTest.cpp | 10 +++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index ac3eb6fa5bcf5..ddef3ba5ce909 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2473,7 +2473,25 @@ bool UnwrappedLineParser::tryToParseLambdaIntroducer() { nextToken(); if (Previous) { const auto *PrevPrev = Previous->getPreviousNonComment(); - if (Previous->is(tok::star) && PrevPrev && PrevPrev->isTypeName(LangOpts)) + // The star may be part of the type in a trailing return type or new + // expression. Then the square brackets will mean array instead of capture. + auto StarIsType = [&]() { + if (!PrevPrev) + return false; + if (PrevPrev->isTypeName(LangOpts)) + return true; + if (PrevPrev->isNot(tok::identifier)) + return false; + const auto *Tok = PrevPrev->getPreviousNonComment(); + // Skip the placement part of the new expression. + if (Tok && Tok->is(tok::r_paren)) { + Tok = Tok->MatchingParen; + if (Tok) + Tok = Tok->getPreviousNonComment(); + } + return Tok && Tok->is(tok::kw_new); + }; + if (Previous->is(tok::star) && StarIsType()) return false; if (Previous->closesScope()) { // Not a potential C-style cast. @@ -2753,6 +2771,8 @@ bool UnwrappedLineParser::parseParens(TokenType StarAndAmpTokenType, FormatTok->setBlockKind(BK_BracedInit); } } + RParen->MatchingParen = LParen; + LParen->MatchingParen = RParen; return SeenEqual; } case tok::r_brace: diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 5aed37aa56d52..57d5d3ed1914d 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12303,6 +12303,16 @@ TEST_F(FormatTest, UnderstandsNewAndDelete) { " p->delete ();\n" "}"); + verifyFormat("x = new a *[]{nullptr};"); + verifyFormat("x = new (a) a *[]{\n" + " nullptr, //\n" + " nullptr,\n" + "};"); + verifyFormat("x = new a *[]{\n" + " nullptr, //\n" + " nullptr,\n" + "};"); + FormatStyle AfterPlacementOperator = getLLVMStyle(); AfterPlacementOperator.SpaceBeforeParens = FormatStyle::SBPO_Custom; EXPECT_TRUE( diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index f5ff5229f7232..a6f5af43f5799 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2459,6 +2459,16 @@ TEST_F(TokenAnnotatorTest, UnderstandsLambdas) { EXPECT_TOKEN(Tokens[3], tok::star, TT_BinaryOperator); EXPECT_TOKEN(Tokens[4], tok::l_square, TT_LambdaLSquare); EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_LambdaLBrace); + + // The new declarator is not a lambda. + Tokens = annotate("x = new a *[]{};"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[4], tok::star, TT_PointerOrReference); + EXPECT_TOKEN(Tokens[5], tok::l_square, TT_ArraySubscriptLSquare); + Tokens = annotate("x = new (a) a *[]{};"); + ASSERT_EQ(Tokens.size(), 14u) << Tokens; + EXPECT_TOKEN(Tokens[7], tok::star, TT_PointerOrReference); + EXPECT_TOKEN(Tokens[8], tok::l_square, TT_ArraySubscriptLSquare); } TEST_F(TokenAnnotatorTest, UnderstandsFunctionAnnotations) { >From 81f52304bc8950911927982f331ec4c0c1d7ebac Mon Sep 17 00:00:00 2001 From: sstwcw <[email protected]> Date: Thu, 1 Oct 2026 14:26:10 +0000 Subject: [PATCH 2/3] Use parseNew --- clang/lib/Format/UnwrappedLineParser.cpp | 101 +++++++++++++---------- clang/lib/Format/UnwrappedLineParser.h | 4 +- clang/unittests/Format/FormatTest.cpp | 16 ++++ 3 files changed, 77 insertions(+), 44 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index ddef3ba5ce909..036750059d431 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2182,9 +2182,10 @@ void UnwrappedLineParser::parseStructuralElement( parseSquare(); break; case tok::kw_new: - if (Style.isCSharp() && - (Tokens->peekNextToken()->isAccessSpecifierKeyword() || - (Previous && Previous->isAccessSpecifierKeyword()))) { + if ((Style.isCSharp() && + (Tokens->peekNextToken()->isAccessSpecifierKeyword() || + (Previous && Previous->isAccessSpecifierKeyword()))) || + FormatTok->endsSequence(tok::kw_new, tok::kw_operator)) { nextToken(); } else { parseNew(); @@ -2473,25 +2474,7 @@ bool UnwrappedLineParser::tryToParseLambdaIntroducer() { nextToken(); if (Previous) { const auto *PrevPrev = Previous->getPreviousNonComment(); - // The star may be part of the type in a trailing return type or new - // expression. Then the square brackets will mean array instead of capture. - auto StarIsType = [&]() { - if (!PrevPrev) - return false; - if (PrevPrev->isTypeName(LangOpts)) - return true; - if (PrevPrev->isNot(tok::identifier)) - return false; - const auto *Tok = PrevPrev->getPreviousNonComment(); - // Skip the placement part of the new expression. - if (Tok && Tok->is(tok::r_paren)) { - Tok = Tok->MatchingParen; - if (Tok) - Tok = Tok->getPreviousNonComment(); - } - return Tok && Tok->is(tok::kw_new); - }; - if (Previous->is(tok::star) && StarIsType()) + if (Previous->is(tok::star) && PrevPrev && PrevPrev->isTypeName(LangOpts)) return false; if (Previous->closesScope()) { // Not a potential C-style cast. @@ -2517,7 +2500,7 @@ bool UnwrappedLineParser::tryToParseLambdaIntroducer() { if (Next->is(tok::greater)) return false; } - parseSquare(/*LambdaIntroducer=*/true); + parseSquare(/*SkipLambda=*/true); return true; } @@ -2771,8 +2754,6 @@ bool UnwrappedLineParser::parseParens(TokenType StarAndAmpTokenType, FormatTok->setBlockKind(BK_BracedInit); } } - RParen->MatchingParen = LParen; - LParen->MatchingParen = RParen; return SeenEqual; } case tok::r_brace: @@ -2853,8 +2834,8 @@ bool UnwrappedLineParser::parseParens(TokenType StarAndAmpTokenType, return SeenEqual; } -void UnwrappedLineParser::parseSquare(bool LambdaIntroducer) { - if (!LambdaIntroducer) { +void UnwrappedLineParser::parseSquare(bool SkipLambda) { + if (!SkipLambda) { assert(FormatTok->is(tok::l_square) && "'[' expected."); if (tryToParseLambda()) return; @@ -3363,28 +3344,62 @@ void UnwrappedLineParser::parseNew() { nextToken(); } while (!eof()); - } + } else if (Style.isJava()) { + // In Java, we can parse everything up to the parens, which aren't optional. + do { + // There should not be a ;, { or } before the new's open paren. + if (FormatTok->isOneOf(tok::semi, tok::l_brace, tok::r_brace)) + return; - if (!Style.isJava()) - return; + // Consume the parens. + if (FormatTok->is(tok::l_paren)) { + parseParens(); - // In Java, we can parse everything up to the parens, which aren't optional. - do { - // There should not be a ;, { or } before the new's open paren. - if (FormatTok->isOneOf(tok::semi, tok::l_brace, tok::r_brace)) + // If there is a class body of an anonymous class, consume that as + // child. + if (FormatTok->is(tok::l_brace)) + parseChildBlock(); + return; + } + nextToken(); + } while (!eof()); + } else if (Style.isCpp()) { + // The brace should not appear here in a new expression. It likely means + // that the line is incomplete or that new is not a keyword such as in + // Objective-C. + if (FormatTok->is(tok::l_brace)) return; - - // Consume the parens. + if (FormatTok->is(tok::l_paren)) + parseParens(); if (FormatTok->is(tok::l_paren)) { parseParens(); - - // If there is a class body of an anonymous class, consume that as child. - if (FormatTok->is(tok::l_brace)) - parseChildBlock(); - return; + } else { + // When there are 2 pairs of parentheses, the second will be the type. + // Otherwise, the type may be outside parentheses. It is handled here. + while (!eof()) { + if (FormatTok->isOneOf(tok::kw_alignas, tok::kw_decltype)) { + nextToken(); + if (FormatTok->is(tok::l_paren)) + parseParens(); + } else if (FormatTok->is(tok::l_square)) { + nextToken(); + parseSquare(/*SkipLambda=*/true); + } else if (FormatTok->is(tok::less)) { + nextToken(); + parseBracedList(/*IsAngleBracket=*/true); + } else if (FormatTok->Tok.getIdentifierInfo() || + FormatTok->isOneOf(tok::coloncolon, tok::star)) { + nextToken(); + } else { + break; + } + } } - nextToken(); - } while (!eof()); + if (FormatTok->is(tok::l_brace)) + parseBracedList(); + else if (FormatTok->is(tok::l_paren)) + parseParens(); + } } void UnwrappedLineParser::parseLoopBody(bool KeepBraces, bool WrapRightBrace) { diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 8fa4e9f7540d5..aede5c2e47428 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -150,7 +150,9 @@ class UnwrappedLineParser { bool parseBracedList(bool IsAngleBracket = false, bool IsEnum = false); bool parseParens(TokenType StarAndAmpTokenType = TT_Unknown, bool InMacroCall = false); - void parseSquare(bool LambdaIntroducer = false); + // SkipLambda means the square does not begin a lambda expression. The opening + // square bracket is assumed to be consumed when it is true. + void parseSquare(bool SkipLambda = false); void keepAncestorBraces(); void parseUnbracedBody(bool CheckEOF = false); void handleAttributes(); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 57d5d3ed1914d..6b90f350f654a 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12312,6 +12312,22 @@ TEST_F(FormatTest, UnderstandsNewAndDelete) { " nullptr, //\n" " nullptr,\n" "};"); + verifyFormat("x = new a::a *[]{\n" + " nullptr, //\n" + " nullptr,\n" + "};"); + verifyFormat("x = new a<a> *[]{\n" + " nullptr, //\n" + " nullptr,\n" + "};"); + verifyFormat("x = new (a) a<a> *[]{\n" + " nullptr, //\n" + " nullptr,\n" + "};"); + verifyFormat("x = new (a *[]){\n" + " nullptr, //\n" + " nullptr,\n" + "};"); FormatStyle AfterPlacementOperator = getLLVMStyle(); AfterPlacementOperator.SpaceBeforeParens = FormatStyle::SBPO_Custom; >From 1bd843fe5c5a8e121423a4c337e7c0410bed0ffd Mon Sep 17 00:00:00 2001 From: sstwcw <[email protected]> Date: Thu, 8 Oct 2026 13:46:31 +0000 Subject: [PATCH 3/3] Add test --- clang/unittests/Format/FormatTest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 6b90f350f654a..36d6d1b37d188 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12328,6 +12328,10 @@ TEST_F(FormatTest, UnderstandsNewAndDelete) { " nullptr, //\n" " nullptr,\n" "};"); + verifyFormat("x = new (a)(a<a> *[]){\n" + " nullptr, //\n" + " nullptr,\n" + "};"); FormatStyle AfterPlacementOperator = getLLVMStyle(); AfterPlacementOperator.SpaceBeforeParens = FormatStyle::SBPO_Custom; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
