Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -3395,6 +3395,59 @@ /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of * information */ +**RemoveBracesLLVM** (``Boolean``) :versionbadge:`clang-format 14` + Remove optional braces of control statements (``if``, ``else``, ``for``, + and ``while``) in C++ according to the LLVM coding style. + + .. warning:: + + This option will be renamed and expanded to support other styles! + Setting this option to `true` could lead to incorrect code formatting due + to clang-format's lack of complete semantic information. As such, extra + care should be taken to review code changes made by this option. + + .. code-block:: c++ + + false: true: + + if (isa(D)) { vs. if (isa(D)) + handleFunctionDecl(D); handleFunctionDecl(D); + } else if (isa(D)) { else if (isa(D)) + handleVarDecl(D); handleVarDecl(D); + } + + if (isa(D)) { vs. if (isa(D)) { + for (auto *A : D.attrs()) { for (auto *A : D.attrs()) + if (shouldProcessAttr(A)) { if (shouldProcessAttr(A)) + handleAttr(A); handleAttr(A); + } } + } + } + + if (isa(D)) { vs. if (isa(D)) + for (auto *A : D.attrs()) { for (auto *A : D.attrs()) + handleAttr(A); handleAttr(A); + } + } + + if (auto *D = (T)(D)) { vs. if (auto *D = (T)(D)) { + if (shouldProcess(D)) { if (shouldProcess(D)) + handleVarDecl(D); handleVarDecl(D); + } else { else + markAsIgnored(D); markAsIgnored(D); + } } + } + + if (a) { vs. if (a) + b(); b(); + } else { else if (c) + if (c) { d(); + d(); else + } else { e(); + e(); + } + } + **ShortNamespaceLines** (``Unsigned``) :versionbadge:`clang-format 14` The maximal number of unwrapped lines that a short namespace spans. Defaults to 1. Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -3050,6 +3050,58 @@ bool ReflowComments; // clang-format on + /// Remove optional braces of control statements (``if``, ``else``, ``for``, + /// and ``while``) in C++ according to the LLVM coding style. + /// \warning + /// This option will be renamed and expanded to support other styles! + /// Setting this option to `true` could lead to incorrect code formatting due + /// to clang-format's lack of complete semantic information. As such, extra + /// care should be taken to review code changes made by this option. + /// \endwarning + /// \code + /// false: true: + /// + /// if (isa(D)) { vs. if (isa(D)) + /// handleFunctionDecl(D); handleFunctionDecl(D); + /// } else if (isa(D)) { else if (isa(D)) + /// handleVarDecl(D); handleVarDecl(D); + /// } + /// + /// if (isa(D)) { vs. if (isa(D)) { + /// for (auto *A : D.attrs()) { for (auto *A : D.attrs()) + /// if (shouldProcessAttr(A)) { if (shouldProcessAttr(A)) + /// handleAttr(A); handleAttr(A); + /// } } + /// } + /// } + /// + /// if (isa(D)) { vs. if (isa(D)) + /// for (auto *A : D.attrs()) { for (auto *A : D.attrs()) + /// handleAttr(A); handleAttr(A); + /// } + /// } + /// + /// if (auto *D = (T)(D)) { vs. if (auto *D = (T)(D)) { + /// if (shouldProcess(D)) { if (shouldProcess(D)) + /// handleVarDecl(D); handleVarDecl(D); + /// } else { else + /// markAsIgnored(D); markAsIgnored(D); + /// } } + /// } + /// + /// if (a) { vs. if (a) + /// b(); b(); + /// } else { else if (c) + /// if (c) { d(); + /// d(); else + /// } else { e(); + /// e(); + /// } + /// } + /// \endcode + /// \version 14 + bool RemoveBracesLLVM; + /// The maximal number of unwrapped lines that a short namespace spans. /// Defaults to 1. /// @@ -3791,6 +3843,7 @@ QualifierOrder == R.QualifierOrder && RawStringFormats == R.RawStringFormats && ReferenceAlignment == R.ReferenceAlignment && + RemoveBracesLLVM == R.RemoveBracesLLVM && ShortNamespaceLines == R.ShortNamespaceLines && SortIncludes == R.SortIncludes && SortJavaStaticImport == R.SortJavaStaticImport && Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -769,6 +769,7 @@ IO.mapOptional("RawStringFormats", Style.RawStringFormats); IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment); IO.mapOptional("ReflowComments", Style.ReflowComments); + IO.mapOptional("RemoveBracesLLVM", Style.RemoveBracesLLVM); IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines); IO.mapOptional("SortIncludes", Style.SortIncludes); IO.mapOptional("SortJavaStaticImport", Style.SortJavaStaticImport); @@ -1199,6 +1200,7 @@ LLVMStyle.UseCRLF = false; LLVMStyle.UseTab = FormatStyle::UT_Never; LLVMStyle.ReflowComments = true; + LLVMStyle.RemoveBracesLLVM = false; LLVMStyle.SpacesInParentheses = false; LLVMStyle.SpacesInSquareBrackets = false; LLVMStyle.SpaceInEmptyBlock = false; @@ -1732,6 +1734,45 @@ namespace { +class BracesRemover : public TokenAnalyzer { +public: + BracesRemover(const Environment &Env, const FormatStyle &Style) + : TokenAnalyzer(Env, Style) {} + + std::pair + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override { + AffectedRangeMgr.computeAffectedLines(AnnotatedLines); + tooling::Replacements Result; + removeBraces(AnnotatedLines, Result); + return {Result, 0}; + } + +private: + // Remove optional braces. + void removeBraces(SmallVectorImpl &Lines, + tooling::Replacements &Result) { + const auto &SourceMgr = Env.getSourceManager(); + for (AnnotatedLine *Line : Lines) { + if (!Line->Affected) + continue; + for (FormatToken *Token = Line->First; Token; Token = Token->Next) { + if (!Token->Optional) + continue; + if (Token->NewlinesBefore > 0) { + const auto Range = + CharSourceRange::getCharRange(Token->WhitespaceRange.getBegin(), + Token->WhitespaceRange.getEnd()); + cantFail(Result.add(tooling::Replacement(SourceMgr, Range, ""))); + } + const auto Start = Token->Tok.getLocation(); + cantFail(Result.add(tooling::Replacement(SourceMgr, Start, 1, ""))); + } + } + } +}; + class JavaScriptRequoter : public TokenAnalyzer { public: JavaScriptRequoter(const Environment &Env, const FormatStyle &Style) @@ -3039,6 +3080,11 @@ } if (Style.Language == FormatStyle::LK_Cpp) { + if (Style.RemoveBracesLLVM) + Passes.emplace_back([&](const Environment &Env) { + return BracesRemover(Env, Expanded).process(); + }); + if (Style.FixNamespaceComments) Passes.emplace_back([&](const Environment &Env) { return NamespaceEndCommentsFixer(Env, Expanded).process(); Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -442,6 +442,9 @@ /// This starts an array initializer. bool IsArrayInitializer = false; + /// Is optional and can be removed. + bool Optional = false; + /// If this token starts a block, this contains all the unwrapped lines /// in it. SmallVector Children; Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -780,6 +780,7 @@ unsigned CommaCount = 0; while (CurrentToken) { if (CurrentToken->is(tok::r_brace)) { + assert(Left->Optional == CurrentToken->Optional); Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; if (Style.AlignArrayOfStructures != FormatStyle::AIAS_None) { Index: clang/lib/Format/UnwrappedLineParser.h =================================================================== --- clang/lib/Format/UnwrappedLineParser.h +++ clang/lib/Format/UnwrappedLineParser.h @@ -81,12 +81,20 @@ void parse(); private: + enum class IfStmtKind { + NotIf, // Not an if statement. + IfOnly, // An if statement without the else clause. + IfElse, // An if statement followed by else but not else if. + IfElseIf // An if statement followed by else if. + }; + void reset(); void parseFile(); - void parseLevel(bool HasOpeningBrace); - void parseBlock(bool MustBeDeclaration = false, unsigned AddLevels = 1u, - bool MunchSemi = true, - bool UnindentWhitesmithsBraces = false); + bool precededByCommentOrPPDirective(); + bool parseLevel(bool HasOpeningBrace, IfStmtKind *IfKind = nullptr); + IfStmtKind parseBlock(bool MustBeDeclaration = false, unsigned AddLevels = 1u, + bool MunchSemi = true, + bool UnindentWhitesmithsBraces = false); void parseChildBlock(); void parsePPDirective(); void parsePPDefine(); @@ -96,13 +104,15 @@ void parsePPEndIf(); void parsePPUnknown(); void readTokenWithJavaScriptASI(); - void parseStructuralElement(bool IsTopLevel = false); + void parseStructuralElement(IfStmtKind *IfKind = nullptr, + bool IsTopLevel = false); bool tryToParseBracedList(); bool parseBracedList(bool ContinueOnSemicolons = false, bool IsEnum = false, tok::TokenKind ClosingBraceKind = tok::r_brace); void parseParens(); void parseSquare(bool LambdaIntroducer = false); - void parseIfThenElse(); + void keepAncestorBraces(); + FormatToken *parseIfThenElse(IfStmtKind *IfKind, bool KeepBraces = false); void parseTryCatch(); void parseForOrWhileLoop(); void parseDoWhile(); @@ -235,6 +245,10 @@ // owned outside of and handed into the UnwrappedLineParser. ArrayRef AllTokens; + // Keeps a stack of the states of nested control statements (true if the + // statement contains more than some predefined number of nested statements). + SmallVector NestedTooDeep; + // Represents preprocessor branch type, so we can find matching // #if/#else/#endif directives. enum PPBranchKind { Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -316,6 +316,7 @@ PreprocessorDirectives.clear(); CurrentLines = &Lines; DeclarationScopeStack.clear(); + NestedTooDeep.clear(); PPStack.clear(); Line->FirstStartColumn = FirstStartColumn; } @@ -431,7 +432,29 @@ } while (!eof()); } -void UnwrappedLineParser::parseLevel(bool HasOpeningBrace) { +bool UnwrappedLineParser::precededByCommentOrPPDirective() { + const size_t size = Lines.size(); + if (size > 0 && Lines[size - 1].InPPDirective) + return true; +#if 1 + const unsigned Position = Tokens->getPosition(); + if (Position == 0) + return false; + const FormatToken *Previous = AllTokens[Position - 1]; + assert(Previous); + return Previous->is(tok::comment) && +#else // Use the #else part after D116318 lands. + const FormatToken *Previous = Tokens->getPreviousToken(); + return Previous && Previous->is(tok::comment) && +#endif + (Previous->IsMultiline || Previous->NewlinesBefore > 0); +} + +bool UnwrappedLineParser::parseLevel(bool HasOpeningBrace, IfStmtKind *IfKind) { + // const bool StartsWithBrace = FormatTok->is(tok::l_brace); + const bool IsPrecededByCommentOrPPDirective = + precededByCommentOrPPDirective(); + unsigned StatementCount = 0; bool SwitchLabelEncountered = false; do { tok::TokenKind kind = FormatTok->Tok.getKind(); @@ -452,11 +475,15 @@ if (!FormatTok->is(TT_MacroBlockBegin) && tryToParseBracedList()) continue; parseBlock(); + ++StatementCount; + assert(StatementCount > 0 && "StatementCount overflow!"); addUnwrappedLine(); break; case tok::r_brace: - if (HasOpeningBrace) - return; + if (HasOpeningBrace) { + return StatementCount == 1 && !IsPrecededByCommentOrPPDirective && + /* !StartsWithBrace && */ !precededByCommentOrPPDirective(); + } nextToken(); addUnwrappedLine(); break; @@ -496,10 +523,13 @@ } LLVM_FALLTHROUGH; default: - parseStructuralElement(!HasOpeningBrace); + parseStructuralElement(IfKind, !HasOpeningBrace); + ++StatementCount; + assert(StatementCount > 0 && "StatementCount overflow!"); break; } } while (!eof()); + return false; } void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { @@ -655,11 +685,13 @@ return h; } -void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, unsigned AddLevels, - bool MunchSemi, - bool UnindentWhitesmithsBraces) { +UnwrappedLineParser::IfStmtKind +UnwrappedLineParser::parseBlock(bool MustBeDeclaration, unsigned AddLevels, + bool MunchSemi, + bool UnindentWhitesmithsBraces) { assert(FormatTok->isOneOf(tok::l_brace, TT_MacroBlockBegin) && "'{' or macro block token expected"); + FormatToken *Tok = FormatTok; const bool MacroBlock = FormatTok->is(TT_MacroBlockBegin); FormatTok->setBlockKind(BK_Block); @@ -694,16 +726,28 @@ MustBeDeclaration); if (AddLevels > 0u && Style.BreakBeforeBraces != FormatStyle::BS_Whitesmiths) Line->Level += AddLevels; - parseLevel(/*HasOpeningBrace=*/true); + + IfStmtKind IfKind = IfStmtKind::NotIf; + const bool SimpleBlock = parseLevel(/*HasOpeningBrace=*/true, &IfKind); if (eof()) - return; + return IfKind; if (MacroBlock ? !FormatTok->is(TT_MacroBlockEnd) : !FormatTok->is(tok::r_brace)) { Line->Level = InitialLevel; FormatTok->setBlockKind(BK_Block); - return; + return IfKind; + } + + if (SimpleBlock && Tok->is(tok::l_brace)) { + assert(FormatTok->is(tok::r_brace)); + const FormatToken *Previous = Tokens->getPreviousToken(); + assert(Previous); + if (Previous->isNot(tok::r_brace) || Previous->Optional) { + Tok->MatchingParen = FormatTok; + FormatTok->MatchingParen = Tok; + } } size_t PPEndHash = computePPHash(); @@ -734,6 +778,8 @@ CurrentLines->size() - 1; } } + + return IfKind; } static bool isGoogScope(const UnwrappedLine &Line) { @@ -1186,7 +1232,8 @@ return addUnwrappedLine(); } -void UnwrappedLineParser::parseStructuralElement(bool IsTopLevel) { +void UnwrappedLineParser::parseStructuralElement(IfStmtKind *IfKind, + bool IsTopLevel) { if (Style.Language == FormatStyle::LK_TableGen && FormatTok->is(tok::pp_include)) { nextToken(); @@ -1229,7 +1276,7 @@ if (Style.isJavaScript() && Line->MustBeDeclaration) // field/method declaration. break; - parseIfThenElse(); + parseIfThenElse(IfKind); return; case tok::kw_for: case tok::kw_while: @@ -2128,7 +2175,32 @@ } while (!eof()); } -void UnwrappedLineParser::parseIfThenElse() { +void UnwrappedLineParser::keepAncestorBraces() { + const int MaxNestingLevels = 2; + const int Size = NestedTooDeep.size(); + if (Size >= MaxNestingLevels) + NestedTooDeep[Size - MaxNestingLevels] = true; + NestedTooDeep.push_back(false); +} + +static void markOptionalBraces(FormatToken *LeftBrace) { + if (!LeftBrace) + return; + + FormatToken *RightBrace = LeftBrace->MatchingParen; + if (!RightBrace) + return; + + assert(LeftBrace->is(tok::l_brace)); + assert(RightBrace->is(tok::r_brace)); + assert(RightBrace->MatchingParen == LeftBrace); + + LeftBrace->Optional = true; + RightBrace->Optional = true; +} + +FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind, + bool KeepBraces) { auto HandleAttributes = [this]() { // Handle AttributeMacro, e.g. `if (x) UNLIKELY`. if (FormatTok->is(TT_AttributeMacro)) @@ -2145,10 +2217,17 @@ if (FormatTok->Tok.is(tok::l_paren)) parseParens(); HandleAttributes(); + + keepAncestorBraces(); + + FormatToken *IfLeftBrace = nullptr; + IfStmtKind IfBlockKind = IfStmtKind::NotIf; bool NeedsUnwrappedLine = false; if (FormatTok->Tok.is(tok::l_brace)) { + assert(!FormatTok->MatchingParen); + IfLeftBrace = FormatTok; CompoundStatementIndenter Indenter(this, Style, Line->Level); - parseBlock(); + IfBlockKind = parseBlock(); if (Style.BraceWrapping.BeforeElse) addUnwrappedLine(); else @@ -2159,12 +2238,26 @@ parseStructuralElement(); --Line->Level; } + + assert(!NestedTooDeep.empty()); + IfStmtKind Kind = IfStmtKind::IfOnly; + bool KeepIfBraces = (IfLeftBrace && !IfLeftBrace->MatchingParen) || + NestedTooDeep.back() || + IfBlockKind == IfStmtKind::IfOnly || // LLVM + IfBlockKind == IfStmtKind::IfElseIf; + FormatToken *ElseLeftBrace = nullptr; if (FormatTok->Tok.is(tok::kw_else)) { + NestedTooDeep.back() = false; + Kind = IfStmtKind::IfElse; + // KeepIfBraces = KeepIfBraces || IfBlockKind == IfStmtKind::IfOnly; nextToken(); HandleAttributes(); if (FormatTok->Tok.is(tok::l_brace)) { + assert(!FormatTok->MatchingParen); + ElseLeftBrace = FormatTok; CompoundStatementIndenter Indenter(this, Style, Line->Level); - parseBlock(); + if (parseBlock() == IfStmtKind::IfOnly) + Kind = IfStmtKind::IfElseIf; addUnwrappedLine(); } else if (FormatTok->Tok.is(tok::kw_if)) { FormatToken *Previous = Tokens->getPreviousToken(); @@ -2173,7 +2266,11 @@ addUnwrappedLine(); ++Line->Level; } - parseIfThenElse(); + Kind = IfStmtKind::IfElseIf; + IfStmtKind ElseIfKind; + const bool TooDeep = NestedTooDeep.pop_back_val(); + ElseLeftBrace = parseIfThenElse(&ElseIfKind, KeepBraces || KeepIfBraces); + NestedTooDeep.push_back(TooDeep); if (PrecededByComment) --Line->Level; } else { @@ -2184,9 +2281,27 @@ addUnwrappedLine(); --Line->Level; } - } else if (NeedsUnwrappedLine) { - addUnwrappedLine(); + } else { + KeepIfBraces = KeepIfBraces || IfBlockKind == IfStmtKind::IfElse; + if (NeedsUnwrappedLine) + addUnwrappedLine(); } + + assert(!NestedTooDeep.empty()); + bool KeepElseBraces = + (ElseLeftBrace && !ElseLeftBrace->MatchingParen) || NestedTooDeep.back(); + + NestedTooDeep.pop_back(); + + if (!KeepBraces && !KeepIfBraces && !KeepElseBraces) { + markOptionalBraces(IfLeftBrace); + markOptionalBraces(ElseLeftBrace); + } + + if (IfKind) + *IfKind = Kind; + + return IfLeftBrace; } void UnwrappedLineParser::parseTryCatch() { @@ -2224,6 +2339,8 @@ if (Style.Language == FormatStyle::LK_Java && FormatTok->is(tok::l_paren)) { parseParens(); } + + keepAncestorBraces(); if (FormatTok->is(tok::l_brace)) { CompoundStatementIndenter Indenter(this, Style, Line->Level); parseBlock(); @@ -2257,8 +2374,10 @@ parseParens(); continue; } - if (FormatTok->isOneOf(tok::semi, tok::r_brace, tok::eof)) + if (FormatTok->isOneOf(tok::semi, tok::r_brace, tok::eof)) { + NestedTooDeep.pop_back(); return; + } nextToken(); } NeedsUnwrappedLine = false; @@ -2269,6 +2388,8 @@ else NeedsUnwrappedLine = true; } + NestedTooDeep.pop_back(); + if (NeedsUnwrappedLine) addUnwrappedLine(); } @@ -2375,9 +2496,16 @@ nextToken(); if (FormatTok->Tok.is(tok::l_paren)) parseParens(); + + keepAncestorBraces(); if (FormatTok->Tok.is(tok::l_brace)) { + FormatToken *LeftBrace = FormatTok; + assert(!LeftBrace->MatchingParen); CompoundStatementIndenter Indenter(this, Style, Line->Level); parseBlock(); + assert(!NestedTooDeep.empty()); + if (!NestedTooDeep.back()) + markOptionalBraces(LeftBrace); addUnwrappedLine(); } else { addUnwrappedLine(); @@ -2385,11 +2513,14 @@ parseStructuralElement(); --Line->Level; } + NestedTooDeep.pop_back(); } void UnwrappedLineParser::parseDoWhile() { assert(FormatTok->Tok.is(tok::kw_do) && "'do' expected"); nextToken(); + + keepAncestorBraces(); if (FormatTok->Tok.is(tok::l_brace)) { CompoundStatementIndenter Indenter(this, Style, Line->Level); parseBlock(); @@ -2401,6 +2532,7 @@ parseStructuralElement(); --Line->Level; } + NestedTooDeep.pop_back(); // FIXME: Add error handling. if (!FormatTok->Tok.is(tok::kw_while)) { @@ -2471,6 +2603,8 @@ nextToken(); if (FormatTok->Tok.is(tok::l_paren)) parseParens(); + + keepAncestorBraces(); if (FormatTok->Tok.is(tok::l_brace)) { CompoundStatementIndenter Indenter(this, Style, Line->Level); parseBlock(); @@ -2481,6 +2615,7 @@ parseStructuralElement(); --Line->Level; } + NestedTooDeep.pop_back(); } void UnwrappedLineParser::parseAccessSpecifier() { Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -18682,6 +18682,7 @@ CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList); CHECK_PARSE_BOOL(Cpp11BracedListStyle); CHECK_PARSE_BOOL(ReflowComments); + CHECK_PARSE_BOOL(RemoveBracesLLVM); CHECK_PARSE_BOOL(SortUsingDeclarations); CHECK_PARSE_BOOL(SpacesInParentheses); CHECK_PARSE_BOOL(SpacesInSquareBrackets); @@ -23020,6 +23021,297 @@ Style); } +TEST_F(FormatTest, RemoveBraces) { + FormatStyle Style = getLLVMStyle(); + Style.RemoveBracesLLVM = true; + + // The following eight test cases are fully-braced versions of the examples at + // "llvm.org/docs/CodingStandards.html#don-t-use-braces-on-simple-single- + // statement-bodies-of-if-else-loop-statements". + + // 1. Omit the braces, since the body is simple and clearly associated with + // the if. + EXPECT_EQ("if (isa(D))\n" + " handleFunctionDecl(D);\n" + "else if (isa(D))\n" + " handleVarDecl(D);", + format("if (isa(D)) {\n" + " handleFunctionDecl(D);\n" + "} else if (isa(D)) {\n" + " handleVarDecl(D);\n" + "}", + Style)); + + // 2. Here we document the condition itself and not the body. + verifyFormat("if (isa(D)) {\n" + " // It is necessary that we explain the situation with this\n" + " // surprisingly long comment, so it would be unclear\n" + " // without the braces whether the following statement is in\n" + " // the scope of the `if`.\n" + " // Because the condition is documented, we can't really\n" + " // hoist this comment that applies to the body above the\n" + " // if.\n" + " handleOtherDecl(D);\n" + "}", + Style); + + // 3. Use braces on the outer `if` to avoid a potential dangling else + // situation. + EXPECT_EQ("if (isa(D)) {\n" + " for (auto *A : D.attrs())\n" + " if (shouldProcessAttr(A))\n" + " handleAttr(A);\n" + "}", + format("if (isa(D)) {\n" + " for (auto *A : D.attrs()) {\n" + " if (shouldProcessAttr(A)) {\n" + " handleAttr(A);\n" + " }\n" + " }\n" + "}", + Style)); + + // 4. Use braces for the `if` block to keep it uniform with the else block. + verifyFormat("if (isa(D)) {\n" + " handleFunctionDecl(D);\n" + "} else {\n" + " // In this else case, it is necessary that we explain the\n" + " // situation with this surprisingly long comment, so it\n" + " // would be unclear without the braces whether the\n" + " // following statement is in the scope of the `if`.\n" + " handleOtherDecl(D);\n" + "}", + Style); + + // 5. This should also omit braces. The `for` loop contains only a single + // statement, so it shouldn't have braces. The `if` also only contains a + // single simple statement (the for loop), so it also should omit braces. + EXPECT_EQ("if (isa(D))\n" + " for (auto *A : D.attrs())\n" + " handleAttr(A);", + format("if (isa(D)) {\n" + " for (auto *A : D.attrs()) {\n" + " handleAttr(A);\n" + " }\n" + "}", + Style)); + + // 6. Use braces for the outer `if` since the nested `for` is braced. + verifyFormat("if (isa(D)) {\n" + " for (auto *A : D.attrs()) {\n" + " // In this for loop body, it is necessary that we explain\n" + " // the situation with this surprisingly long comment,\n" + " // forcing braces on the `for` block.\n" + " handleAttr(A);\n" + " }\n" + "}", + Style); + + // 7. Use braces on the outer block because there are more than two levels of + // nesting. + EXPECT_EQ("if (isa(D)) {\n" + " for (auto *A : D.attrs())\n" + " for (ssize_t i : llvm::seq(count))\n" + " handleAttrOnDecl(D, A, i);\n" + "}", + format("if (isa(D)) {\n" + " for (auto *A : D.attrs()) {\n" + " for (ssize_t i : llvm::seq(count)) {\n" + " handleAttrOnDecl(D, A, i);\n" + " }\n" + " }\n" + "}", + Style)); + + // 8. Use braces on the outer block because of a nested `if`, otherwise the + // compiler would warn: `add explicit braces to avoid dangling else` + EXPECT_EQ("if (auto *D = dyn_cast(D)) {\n" + " if (shouldProcess(D))\n" + " handleVarDecl(D);\n" + " else\n" + " markAsIgnored(D);\n" + "}", + format("if (auto *D = dyn_cast(D)) {\n" + " if (shouldProcess(D)) {\n" + " handleVarDecl(D);\n" + " } else {\n" + " markAsIgnored(D);\n" + " }\n" + "}", + Style)); + + EXPECT_EQ("if (a)\n" + " b;\n" + "else if (c)\n" + " d;\n" + "else\n" + " e;", + format("if (a) {\n" + " b;\n" + "} else if (c) {\n" + " d;\n" + "} else {\n" + " e;\n" + "}", + Style)); + + verifyFormat("if (a) {\n" + " b;\n" + " c;\n" + "} else if (d) {\n" + " e;\n" + "}", + Style); + + verifyFormat("if (a) {\n" + "#undef NDEBUG\n" + " b;\n" + "} else {\n" + " c;\n" + "}", + Style); + + verifyFormat("if (a) {\n" + " // comment\n" + "} else if (b) {\n" + " c;\n" + "}", + Style); + + verifyFormat("if (a) {\n" + " b;\n" + "} else {\n" + " { c; }\n" + "}", + Style); + + EXPECT_EQ("if (a) {\n" + " if (b) // comment\n" + " c;\n" + "} else if (d) {\n" + " e;\n" + "}", + format("if (a) {\n" + " if (b) { // comment\n" + " c;\n" + " }\n" + "} else if (d) {\n" + " e;\n" + "}", + Style)); + + verifyFormat("if (a) {\n" + " if (b) {\n" + " c;\n" + " // comment\n" + " } else if (d) {\n" + " e;\n" + " }\n" + "}", + Style); + + EXPECT_EQ("if (a) {\n" + " if (b)\n" + " c;\n" + "}", + format("if (a) {\n" + " if (b) {\n" + " c;\n" + " }\n" + "}", + Style)); + + EXPECT_EQ("if (a)\n" + " if (b)\n" + " c;\n" + " else\n" + " d;\n" + "else\n" + " e;", + format("if (a) {\n" + " if (b) {\n" + " c;\n" + " } else {\n" + " d;\n" + " }\n" + "} else {\n" + " e;\n" + "}", + Style)); + + EXPECT_EQ("if (a) {\n" + " // comment\n" + " if (b)\n" + " c;\n" + " else if (d)\n" + " e;\n" + "} else {\n" + " g;\n" + "}", + format("if (a) {\n" + " // comment\n" + " if (b) {\n" + " c;\n" + " } else if (d) {\n" + " e;\n" + " }\n" + "} else {\n" + " g;\n" + "}", + Style)); + + EXPECT_EQ("if (a)\n" + " b;\n" + "else if (c)\n" + " d;\n" + "else\n" + " e;", + format("if (a) {\n" + " b;\n" + "} else {\n" + " if (c) {\n" + " d;\n" + " } else {\n" + " e;\n" + " }\n" + "}", + Style)); + + EXPECT_EQ("if (a) {\n" + " if (b)\n" + " c;\n" + " else if (d)\n" + " e;\n" + "} else {\n" + " g;\n" + "}", + format("if (a) {\n" + " if (b)\n" + " c;\n" + " else {\n" + " if (d)\n" + " e;\n" + " }\n" + "} else {\n" + " g;\n" + "}", + Style)); + + EXPECT_EQ("if (a)\n" + " b;\n" + "else if (c)\n" + " while (d)\n" + " e;", + format("if (a) {\n" + " b;\n" + "} else if (c) {\n" + " while (d) {\n" + " e;\n" + " }\n" + "}", + Style)); +} + } // namespace } // namespace format } // namespace clang