[clang] [clang-format] Stop treating the new expression as a lambda (PR #223266)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 8 06:46:52 PDT 2026
https://github.com/sstwcw updated https://github.com/llvm/llvm-project/pull/223266
>From dac247bb35b3c365b151a3008eeabb0b5d4f9894 Mon Sep 17 00:00:00 2001
From: sstwcw <su3e8a96kzlver at posteo.net>
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 <su3e8a96kzlver at posteo.net>
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 <su3e8a96kzlver at posteo.net>
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;
More information about the cfe-commits
mailing list