[clang] 4c83058 - [clang-format] Stop treating the new expression as a lambda (#223266)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 21 08:01:12 PDT 2026
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) {
More information about the cfe-commits
mailing list