Author: sstwcw Date: 2026-10-21T14:55:45Z New Revision: 4c830588b1a9a4fafb892d6221167456a0344304
URL: https://github.com/llvm/llvm-project/commit/4c830588b1a9a4fafb892d6221167456a0344304 DIFF: https://github.com/llvm/llvm-project/commit/4c830588b1a9a4fafb892d6221167456a0344304.diff LOG: [clang-format] Stop treating the new expression as a lambda (#223266) 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 and brackets. Added: Modified: clang/lib/Format/UnwrappedLineParser.cpp clang/lib/Format/UnwrappedLineParser.h clang/unittests/Format/FormatTest.cpp clang/unittests/Format/TokenAnnotatorTest.cpp Removed: ################################################################################ diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index ae20b98e577f4..72ebb8f75120f 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2208,9 +2208,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(); @@ -2525,7 +2526,7 @@ bool UnwrappedLineParser::tryToParseLambdaIntroducer() { if (Next->is(tok::greater)) return false; } - parseSquare(/*LambdaIntroducer=*/true); + parseSquare(/*SkipLambda=*/true); return true; } @@ -2859,8 +2860,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; @@ -3386,28 +3387,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 2e1755ad3b2c6..48d8eaabef1bd 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -153,7 +153,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 c5ac46f5c4e14..3a680fe9a6a72 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12323,6 +12323,36 @@ 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" + "};"); + 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" + "};"); + verifyFormat("x = new (a)(a<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 039ee5eb840fe..138486071b330 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) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
