diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -488,10 +488,17 @@ unsigned StoredPosition = Tokens->getPosition(); FormatToken *Tok = FormatTok; const FormatToken *PrevTok = Tok->Previous; + // A rough guess at whether a block is a lambda expression. + bool ProbablyLambda = false; // Keep a stack of positions of lbrace tokens. We will // update information about whether an lbrace starts a // braced init list or a different block during the loop. - SmallVector LBraceStack; + struct StackEntry { + FormatToken *Tok; + const FormatToken *PrevTok, *NextTok; + bool ProbablyLambda; + }; + SmallVector LBraceStack; assert(Tok->is(tok::l_brace)); do { // Get next non-comment token. @@ -521,12 +528,13 @@ } else { Tok->setBlockKind(BK_Unknown); } - LBraceStack.push_back(Tok); + LBraceStack.push_back({Tok, PrevTok, NextTok, ProbablyLambda}); break; case tok::r_brace: if (LBraceStack.empty()) break; - if (LBraceStack.back()->is(BK_Unknown)) { + switch (LBraceStack.back().Tok->getBlockKind()) { + case BK_Unknown: { bool ProbablyBracedList = false; if (Style.Language == FormatStyle::LK_Proto) { ProbablyBracedList = NextTok->isOneOf(tok::comma, tok::r_square); @@ -554,7 +562,7 @@ // If we already marked the opening brace as braced list, the closing // must also be part of it. - ProbablyBracedList = LBraceStack.back()->is(TT_BracedListLBrace); + ProbablyBracedList = LBraceStack.back().Tok->is(TT_BracedListLBrace); ProbablyBracedList = ProbablyBracedList || (Style.isJavaScript() && @@ -595,15 +603,43 @@ } if (ProbablyBracedList) { Tok->setBlockKind(BK_BracedInit); - LBraceStack.back()->setBlockKind(BK_BracedInit); + LBraceStack.back().Tok->setBlockKind(BK_BracedInit); } else { Tok->setBlockKind(BK_Block); - LBraceStack.back()->setBlockKind(BK_Block); + LBraceStack.back().Tok->setBlockKind(BK_Block); } + break; + } + case BK_Block: + // A block usually means the outer braces are also a block, except for + // the cases below where a block is inside an expression. + // A statement expression. + if ((NextTok && NextTok->is(tok::r_paren)) || + (LBraceStack.back().PrevTok && + LBraceStack.back().PrevTok->isOneOf(tok::l_paren, tok::caret, + TT_FatArrow))) { + break; + } + // A lambda expression. If the closing parenthesis is the condition + // part of a for loop, the block should already have been marked as a + // block when the `for` keyword is encountered. + if (LBraceStack.back().ProbablyLambda) + break; + if (LBraceStack.size() >= 2 && + LBraceStack.end()[-2].Tok->is(BK_Unknown)) { + LBraceStack.end()[-2].Tok->setBlockKind(BK_Block); + } + break; + case BK_BracedInit: + break; } LBraceStack.pop_back(); break; case tok::identifier: + if (Style.isJavaScript() && Tok->is(Keywords.kw_function)) { + ProbablyLambda = true; + break; + } if (!Tok->is(TT_StatementMacro)) break; [[fallthrough]]; @@ -615,8 +651,12 @@ case tok::kw_switch: case tok::kw_try: case tok::kw___try: - if (!LBraceStack.empty() && LBraceStack.back()->is(BK_Unknown)) - LBraceStack.back()->setBlockKind(BK_Block); + ProbablyLambda = false; + if (!LBraceStack.empty() && LBraceStack.back().Tok->is(BK_Unknown)) + LBraceStack.back().Tok->setBlockKind(BK_Block); + break; + case tok::l_square: + ProbablyLambda = true; break; default: break; @@ -626,9 +666,9 @@ } while (Tok->isNot(tok::eof) && !LBraceStack.empty()); // Assume other blocks for all unclosed opening braces. - for (FormatToken *LBrace : LBraceStack) - if (LBrace->is(BK_Unknown)) - LBrace->setBlockKind(BK_Block); + for (auto Entry : LBraceStack) + if (Entry.Tok->is(BK_Unknown)) + Entry.Tok->setBlockKind(BK_Block); FormatTok = Tokens->setPosition(StoredPosition); } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -55,6 +55,15 @@ TEST_F(FormatTest, FormatsNestedBlockStatements) { EXPECT_EQ("{\n {\n {}\n }\n}", format("{{{}}}")); + // The innermost block is on a single line because a block with only 1 + // statement is hard-coded to be squeezed into one line in + // ContinuationIndenter, not because it is recognized as an initializer list. + verifyFormat("int main() {\n" + " {\n" + " { int a = 0; }\n" + " }\n" + " {}\n" + "}"); } TEST_F(FormatTest, FormatsNestedCall) { diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -40,6 +40,8 @@ EXPECT_EQ((FormatTok)->getType(), Type) << *(FormatTok) #define EXPECT_TOKEN_PRECEDENCE(FormatTok, Prec) \ EXPECT_EQ((FormatTok)->getPrecedence(), Prec) << *(FormatTok) +#define EXPECT_BRACE_KIND(FormatTok, Kind) \ + EXPECT_EQ(FormatTok->getBlockKind(), Kind) #define EXPECT_TOKEN(FormatTok, Kind, Type) \ do { \ EXPECT_TOKEN_KIND(FormatTok, Kind); \ @@ -1783,6 +1785,22 @@ EXPECT_TOKEN(Tokens[3], tok::colon, TT_CaseLabelColon); } +TEST_F(TokenAnnotatorTest, UnderstandsNestedBlocks) { + // The closing braces are not annotated. It doesn't seem to cause a + // problem. So we only test for the opening braces. + auto Tokens = annotate("{\n" + " {\n" + " { int a = 0; }\n" + " }\n" + " {}\n" + "}"); + ASSERT_EQ(Tokens.size(), 14u) << Tokens; + EXPECT_BRACE_KIND(Tokens[0], BK_Block); + EXPECT_BRACE_KIND(Tokens[1], BK_Block); + EXPECT_BRACE_KIND(Tokens[2], BK_Block); + EXPECT_BRACE_KIND(Tokens[10], BK_Block); +} + } // namespace } // namespace format } // namespace clang