Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -1101,6 +1101,36 @@ AttributeMacros: ['__capability', '__output', '__ununsed'] +**AutomaticBraces** (``BraceInsertionStyle``) :versionbadge:`clang-format 14` + If set to ``BIS_WrapLikely`` will insert braces if the line after the + control flow statement is likely to wrap. + If set to ``BIS_Always`` will always insert braces. + The default is the disabled value of ``BIS_Leave``. + + .. warning:: + + Setting ``AutomaticBraces`` to something other than `Never`, COULD + lead to incorrect code formatting due to incorrect decisions made due to + clang-formats lack of complete semantic information. + As such extra care should be taken to review code changes made by the use + of this option. + + Possible values: + + * ``BIS_Leave`` (in configuration: ``Leave``) + Do not insert or remove braces. + + * ``BIS_Always`` (in configuration: ``Always``) + Always insert braces. + + * ``BIS_WrapLikely`` (in configuration: ``WrapLikely``) + Insert braces if the line is likely to wrap. + + * ``BIS_Remove`` (in configuration: ``Remove``) + Always remove braces. + + + **BinPackArguments** (``Boolean``) :versionbadge:`clang-format 3.7` If ``false``, a function call's arguments will either be all on the same line or will have one line each. Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -228,6 +228,9 @@ `const` `volatile` `static` `inline` `constexpr` `restrict` to be controlled relative to the `type`. +- Option ``AutomaticBraces`` has been added to allow the insertion + and removal of {} from if/for/while scope + libclang -------- Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -982,6 +982,32 @@ /// \version 12 TrailingCommaStyle InsertTrailingCommas; + /// The style of inserting braces in control flow statements. + enum BraceInsertionStyle : unsigned char { + /// Do not insert or remove braces. + BIS_Leave, + /// Always insert braces. + BIS_Always, + /// Insert braces if the line is likely to wrap. + BIS_WrapLikely, + /// Always remove braces. + BIS_Remove, + }; + + /// If set to ``BIS_WrapLikely`` will insert braces if the line after the + /// control flow statement is likely to wrap. + /// If set to ``BIS_Always`` will always insert braces. + /// The default is the disabled value of ``BIS_Leave``. + /// \warning + /// Setting ``AutomaticBraces`` to something other than `Never`, COULD + /// lead to incorrect code formatting due to incorrect decisions made due to + /// clang-formats lack of complete semantic information. + /// As such extra care should be taken to review code changes made by the use + /// of this option. + /// \endwarning + /// \version 14 + BraceInsertionStyle AutomaticBraces; + /// If ``false``, a function declaration's or function definition's /// parameters will either all be on the same line or will have one line each. /// \code @@ -3666,7 +3692,7 @@ IndentPPDirectives == R.IndentPPDirectives && IndentExternBlock == R.IndentExternBlock && IndentRequires == R.IndentRequires && IndentWidth == R.IndentWidth && - Language == R.Language && + AutomaticBraces == R.AutomaticBraces && Language == R.Language && IndentWrappedFunctionNames == R.IndentWrappedFunctionNames && JavaImportGroups == R.JavaImportGroups && JavaScriptQuotes == R.JavaScriptQuotes && Index: clang/lib/Format/BraceInserter.h =================================================================== --- /dev/null +++ clang/lib/Format/BraceInserter.h @@ -0,0 +1,45 @@ +//===--- BraceInserter.h - Format C++ code --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the class for inserting braces +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_FORMAT_BRACEINSERTER_H +#define LLVM_CLANG_LIB_FORMAT_BRACEINSERTER_H + +#include "TokenAnalyzer.h" + +namespace clang { +namespace format { + +class BracesInserter : public TokenAnalyzer { +public: + BracesInserter(const Environment &Env, const FormatStyle &Style) + : TokenAnalyzer(Env, Style) {} + + std::pair + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override; + +private: + void insertBraces(SmallVectorImpl &Lines, + tooling::Replacements &Result); + + void removeBraces(SmallVectorImpl &Lines, + tooling::Replacements &Result); + + std::set Done; +}; + +} // namespace format +} // namespace clang + +#endif Index: clang/lib/Format/BraceInserter.cpp =================================================================== --- /dev/null +++ clang/lib/Format/BraceInserter.cpp @@ -0,0 +1,224 @@ +//===--- BraceInserter.cpp - Format C++ code ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements functions declared in Format.h. This will be +/// split into separate files as we go. +/// +//===----------------------------------------------------------------------===// + +#include "BraceInserter.h" +#include "clang/Format/Format.h" + +namespace clang { +namespace format { + +std::pair +BracesInserter::analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) { + tooling::Replacements Result; + if (AffectedRangeMgr.computeAffectedLines(AnnotatedLines)) + if (Style.AutomaticBraces == FormatStyle::BIS_Always || + Style.AutomaticBraces == FormatStyle::BIS_WrapLikely) + insertBraces(AnnotatedLines, Result); + else if (Style.AutomaticBraces == FormatStyle::BIS_Remove) + removeBraces(AnnotatedLines, Result); + + return {Result, 0}; +} + +void BracesInserter::insertBraces(SmallVectorImpl &Lines, + tooling::Replacements &Result) { + bool InsideCtrlFlowStmt = false; + for (AnnotatedLine *Line : Lines) { + insertBraces(Line->Children, Result); + + // Get first token that is not a comment. + const FormatToken *FirstTok = Line->First; + if (FirstTok->is(tok::comment)) + FirstTok = FirstTok->getNextNonComment(); + // The line is a comment. + if (!FirstTok) + continue; + // Get last token that is not a comment. + const FormatToken *LastTok = Line->Last; + if (LastTok->is(tok::comment)) + LastTok = LastTok->getPreviousNonComment(); + + // If this token starts a control flow stmt, mark it. + if (FirstTok->isOneOf(tok::kw_if, tok::kw_else, tok::kw_for, tok::kw_do, + tok::kw_while)) { + InsideCtrlFlowStmt = !LastTok->isOneOf(tok::l_brace, tok::semi); + continue; + } + + // If the previous line started a ctrl flow block and this line does not + // start with curly. + if (Line->Affected && !FirstTok->Finalized && InsideCtrlFlowStmt && + FirstTok->isNot(tok::l_brace)) { + const SourceLocation startBraceLoc = Line->First->Tok.getLocation(); + // In some cases clang-format will run the same transform twice which + // could lead to adding the curlies twice, skip if we already added one + // at this location. + if (Done.count(startBraceLoc)) + break; + + if (Style.AutomaticBraces == + FormatStyle::BraceInsertionStyle::BIS_Always || + Line->Last->OriginalColumn > Style.ColumnLimit) { + Done.insert(startBraceLoc); + cantFail(Result.add(tooling::Replacement(Env.getSourceManager(), + startBraceLoc, 0, "{"))); + + // Mostly we can ignore putting a \n before the } and let the + // Formatter handle it, unless the last token is a // comment, Doing + // so avoids issues with macro continuation. And a line comment + // cannot be part of a continuation. + std::string ending = "}"; + if (Line->Last->is(tok::comment)) + ending = "\n}"; + cantFail(Result.add( + tooling::Replacement(Env.getSourceManager(), + Line->Last->Tok.getLocation().getLocWithOffset( + Line->Last->TokenText.size()), + 0, ending))); + } + } + InsideCtrlFlowStmt = false; + } +} + +// Class that allows getting Next over multiple annotated lines +class NextTokenFetcher { + SmallVectorImpl &Lines; + +public: + NextTokenFetcher(SmallVectorImpl &Lines) : Lines(Lines) {} + + FormatToken *getNext(int &Line, FormatToken *current) { + if (Line == 0 && current == nullptr) { + return Lines[0]->First; + } + if (current && !current->Next && Line < (Lines.size() - 1)) { + Line++; + return Lines[Line]->First; + } + return current->Next; + } +}; + +// Scan thorough tokens looking for if/for/while etc... +// find matching () e.g. if(), for(), while() +// if { follows then find matching } +// count number of ; between { ... } +// if number of ; > 1 leave braces +// if number of ; <= 1 remove braces +void BracesInserter::removeBraces(SmallVectorImpl &Lines, + tooling::Replacements &Result) { + + NextTokenFetcher fetcher(Lines); + int LineNumber = 0; + + FormatToken *NextToken = fetcher.getNext(LineNumber, nullptr); + while (NextToken) { + FormatToken *StartingToken = NextToken; + NextToken = fetcher.getNext(LineNumber, NextToken); + + // Does the line start with one of the tokens we are expecting to + // remove a brace from + if (StartingToken->isOneOf(tok::kw_if, tok::kw_for, tok::kw_else, + tok::kw_while)) { + + FormatToken *Next = fetcher.getNext(LineNumber, StartingToken); + if (!Next) { + // We are are the end of the annotated lines. + continue; + } + + // if/for and while are handled a little differently + if (StartingToken->isOneOf(tok::kw_if, tok::kw_for, tok::kw_while)) { + if (!Next->is(tok::l_paren) || !Next->MatchingParen) { + continue; + } + FormatToken *RParen = Next->MatchingParen; + if (!RParen->Next) { + continue; + } + Next = RParen; + } else if (StartingToken->is(tok::kw_else)) { + // If we are an 'else' then simple move onto removing the braces. + Next = StartingToken; + } + + FormatToken *LBrace = fetcher.getNext(LineNumber, Next); + if (!LBrace) + continue; + + // We could be `while(x);` or `else if` just continue from + // this point if we are. + if (LBrace && LBrace->isOneOf(tok::semi, tok::kw_if)) { + NextToken = LBrace; + continue; + } + + if (LBrace && !LBrace->is(tok::l_brace)) + continue; + + // Shoot along unit we get to the end brace, but count + // the number of functional lines by counting semi's + // don't allow sub {} + // This will be out for nested loops or {} initializers + // but air on the side of caution. + FormatToken *InnerScopeToken = LBrace; + int CountSemi = 0; + while (InnerScopeToken && !InnerScopeToken->is(tok::r_brace)) { + if (InnerScopeToken->is(tok::semi)) + CountSemi++; + InnerScopeToken = fetcher.getNext(LineNumber, InnerScopeToken); + if (InnerScopeToken->is(tok::l_brace)) { + CountSemi = 2; + break; + } + if (InnerScopeToken->is(tok::kw_if)) { + CountSemi = 2; + break; + } + } + + // We should now be at the end of at an }. + FormatToken *RBrace = InnerScopeToken; + if (!RBrace || !RBrace->is(tok::r_brace)) { + continue; + } + + // Too many lines. + if (CountSemi != 1) { + NextToken = RBrace; + continue; + } + + // We are finally at the point where we know the positions of {} + // And we think this is for just a single line of scope. + const SourceLocation startBraceLoc = LBrace->Tok.getLocation(); + const SourceLocation endBraceLoc = RBrace->Tok.getLocation(); + + // Remove the { and } + cantFail(Result.add( + tooling::Replacement(Env.getSourceManager(), endBraceLoc, 1, ""))); + cantFail(Result.add( + tooling::Replacement(Env.getSourceManager(), startBraceLoc, 1, ""))); + + // Move on from where we go to. + NextToken = RBrace; + } + } +} + +} // namespace format +} // namespace clang Index: clang/lib/Format/CMakeLists.txt =================================================================== --- clang/lib/Format/CMakeLists.txt +++ clang/lib/Format/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangFormat AffectedRangeManager.cpp BreakableToken.cpp + BraceInserter.cpp ContinuationIndenter.cpp Format.cpp FormatToken.cpp Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -14,6 +14,7 @@ #include "clang/Format/Format.h" #include "AffectedRangeManager.h" +#include "BraceInserter.h" #include "BreakableToken.h" #include "ContinuationIndenter.h" #include "FormatInternal.h" @@ -214,6 +215,15 @@ } }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::BraceInsertionStyle &Value) { + IO.enumCase(Value, "Leave", FormatStyle::BIS_Leave); + IO.enumCase(Value, "Always", FormatStyle::BIS_Always); + IO.enumCase(Value, "WrapLikely", FormatStyle::BIS_WrapLikely); + IO.enumCase(Value, "Remove", FormatStyle::BIS_Remove); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::BinaryOperatorStyle &Value) { IO.enumCase(Value, "All", FormatStyle::BOS_All); @@ -731,6 +741,7 @@ IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("IndentWrappedFunctionNames", Style.IndentWrappedFunctionNames); + IO.mapOptional("AutomaticBraces", Style.AutomaticBraces); IO.mapOptional("InsertTrailingCommas", Style.InsertTrailingCommas); IO.mapOptional("JavaImportGroups", Style.JavaImportGroups); IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes); @@ -1137,6 +1148,7 @@ LLVMStyle.IndentRequires = false; LLVMStyle.IndentWrappedFunctionNames = false; LLVMStyle.IndentWidth = 2; + LLVMStyle.AutomaticBraces = FormatStyle::BIS_Leave; LLVMStyle.PPIndentWidth = -1; LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None; LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave; @@ -2982,6 +2994,11 @@ }); } + if (Style.AutomaticBraces != FormatStyle::BIS_Leave) + Passes.emplace_back([&](const Environment &Env) { + return BracesInserter(Env, Expanded).process(); + }); + if (Style.Language == FormatStyle::LK_JavaScript && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave) Passes.emplace_back([&](const Environment &Env) { Index: clang/unittests/Format/BraceInserterTests.cpp =================================================================== --- /dev/null +++ clang/unittests/Format/BraceInserterTests.cpp @@ -0,0 +1,432 @@ +//===- unittest/Format/BraceInserterTest.cpp - brace insertion unit tests -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Format/Format.h" + +#include "../Tooling/ReplacementTest.h" +#include "FormatTestUtils.h" + +#include "llvm/Support/Debug.h" +#include "llvm/Support/MemoryBuffer.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "brace-inserter-test" + +using clang::tooling::ReplacementTest; +using clang::tooling::toReplacements; +using testing::ScopedTrace; + +namespace clang { +namespace format { +namespace { + +class BraceInserterTest : public ::testing::Test { +protected: + enum StatusCheck { SC_ExpectComplete, SC_ExpectIncomplete, SC_DoNotCheck }; + + std::string format(llvm::StringRef Code, + const FormatStyle &Style = getLLVMStyle(), + StatusCheck CheckComplete = SC_ExpectComplete) { + LLVM_DEBUG(llvm::errs() << "---\n"); + LLVM_DEBUG(llvm::errs() << Code << "\n\n"); + std::vector Ranges(1, tooling::Range(0, Code.size())); + FormattingAttemptStatus Status; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "", &Status); + if (CheckComplete != SC_DoNotCheck) { + bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; + EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) + << Code << "\n\n"; + } + auto Result = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast(Result)); + LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); + return *Result; + } + + FormatStyle getStyleWithColumns(FormatStyle Style, unsigned ColumnLimit) { + Style.ColumnLimit = ColumnLimit; + return Style; + } + + FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) { + return getStyleWithColumns(getLLVMStyle(), ColumnLimit); + } + + void _verifyFormat(const char *File, int Line, llvm::StringRef Expected, + llvm::StringRef Code, + const FormatStyle &Style = getLLVMStyle()) { + ScopedTrace t(File, Line, ::testing::Message() << Code.str()); + EXPECT_EQ(Expected.str(), format(Expected, Style)) + << "Expected code is not stable"; + EXPECT_EQ(Expected.str(), format(Code, Style)); + // clang::format::internal::reformat does not run any of the options that + // modify code for ObjC + if (Style.Language == FormatStyle::LK_Cpp && + Style.AutomaticBraces == FormatStyle::BIS_Leave) { + // Objective-C++ is a superset of C++, so everything checked for C++ + // needs to be checked for Objective-C++ as well. + FormatStyle ObjCStyle = Style; + ObjCStyle.Language = FormatStyle::LK_ObjC; + EXPECT_EQ(Expected.str(), format(test::messUp(Code), ObjCStyle)); + } + } + + void _verifyFormat(const char *File, int Line, llvm::StringRef Code, + const FormatStyle &Style = getLLVMStyle()) { + _verifyFormat(File, Line, Code, test::messUp(Code), Style); + } +}; + +#define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__) + +TEST_F(BraceInserterTest, InsertBraces) { + FormatStyle Style = getLLVMStyle(); + + StringRef IfSourceShort = "if (condition)\n" + " call_function(arg, arg);"; + verifyFormat(IfSourceShort); + + StringRef IfSourceLong = "if (condition)\n" + " call_function(arg, arg, arg, arg, arg, arg);"; + verifyFormat(IfSourceLong); + + StringRef IfElseSourceShort = "if (condition)\n" + " a_function(arg, arg);\n" + "else\n" + " another_function(arg, arg);"; + verifyFormat(IfElseSourceShort); + + StringRef IfElseSourceLong = + "if (condition)\n" + " a_function(arg, arg, arg, arg, arg, arg);\n" + "else\n" + " another_function(arg, arg, arg, arg, arg, arg);"; + verifyFormat(IfElseSourceLong); + + StringRef ForSourceShort = "for (auto val : container)\n" + " call_function(arg, arg);"; + verifyFormat(ForSourceShort); + + StringRef ForSourceLong = "for (auto val : container)\n" + " call_function(arg, arg, arg, arg, arg, arg);"; + verifyFormat(ForSourceLong); + + StringRef WhileShort = "while (condition)\n" + " call_function(arg, arg);"; + verifyFormat(WhileShort); + + StringRef WhileLong = "while (condition)\n" + " call_function(arg, arg, arg, arg, arg, arg);"; + verifyFormat(WhileLong); + + StringRef DoShort = "do\n" + " call_function(arg, arg);\n" + "while (condition);"; + verifyFormat(DoShort); + + StringRef DoLong = "do\n" + " call_function(arg, arg, arg, arg, arg, arg);\n" + "while (condition);"; + verifyFormat(DoLong); + + StringRef ChainedConditionals = "if (A)\n" + " if (B)\n" + " callAB();\n" + " else\n" + " callA();\n" + "else if (B)\n" + " callB();\n" + "else\n" + " call();"; + verifyFormat(ChainedConditionals); + + StringRef ChainedDoWhiles = "do\n" + " while (arg++)\n" + " do\n" + " while (arg++)\n" + " call_function(arg, arg);\n" + " while (condition);\n" + "while (condition);"; + verifyFormat(ChainedDoWhiles); + + StringRef IfWithMultilineComment = + "if /*condition*/ (condition) /*condition*/\n" + " you_do_you(); /*condition*/"; + verifyFormat(IfWithMultilineComment); + + StringRef IfSinglelineCommentOnConditional = "if (condition) // my test\n" + " you_do_you();"; + verifyFormat(IfSinglelineCommentOnConditional); + + Style.AutomaticBraces = FormatStyle::BIS_Always; + + verifyFormat("while (condition) {\n" + " call_function(arg, arg);\n" + "}", + WhileShort, Style); + + verifyFormat("while (condition) {\n" + " call_function(arg, arg, arg, arg, arg, arg);\n" + "}", + WhileLong, Style); + + verifyFormat("do {\n" + " call_function(arg, arg);\n" + "} while (condition);", + DoShort, Style); + + verifyFormat("do {\n" + " call_function(arg, arg, arg, arg, arg, arg);\n" + "} while (condition);", + DoLong, Style); + + verifyFormat("if (condition) {\n" + " call_function(arg, arg);\n" + "}", + IfSourceShort, Style); + + verifyFormat("if (condition) {\n" + " call_function(arg, arg);\n" + "}", + IfSourceShort, Style); + + verifyFormat("if (condition) {\n" + " call_function(arg, arg, arg, arg, arg, arg);\n" + "}", + IfSourceLong, Style); + + verifyFormat("if (condition) {\n" + " a_function(arg, arg);\n" + "} else {\n" + " another_function(arg, arg);\n" + "}", + IfElseSourceShort, Style); + + verifyFormat("if (condition) {\n" + " a_function(arg, arg, arg, arg, arg, arg);\n" + "} else {\n" + " another_function(arg, arg, arg, arg, arg, arg);\n" + "}", + IfElseSourceLong, Style); + + verifyFormat("for (auto val : container) {\n" + " call_function(arg, arg);\n" + "}", + ForSourceShort, Style); + + verifyFormat("for (auto val : container) {\n" + " call_function(arg, arg, arg, arg, arg, arg);\n" + "}", + ForSourceLong, Style); + + verifyFormat("if (A)\n" + " if (B) {\n" + " callAB();\n" + " } else {\n" + " callA();\n" + " }\n" + "else if (B) {\n" + " callB();\n" + "} else {\n" + " call();\n" + "}", + ChainedConditionals, Style); + + verifyFormat("do\n" + " while (arg++)\n" + " do\n" + " while (arg++) {\n" + " call_function(arg, arg);\n" + " }\n" + " while (condition);\n" + "while (condition);", + ChainedDoWhiles, Style); + + verifyFormat("if /*condition*/ (condition) /*condition*/\n" + "{\n" + " you_do_you(); /*condition*/\n" + "}", + IfWithMultilineComment, Style); + + verifyFormat("if (condition) // my test\n" + "{\n" + " you_do_you();\n" + "}", + IfSinglelineCommentOnConditional, Style); + + Style.AutomaticBraces = FormatStyle::BIS_WrapLikely; + Style.ColumnLimit = 35; + + verifyFormat(IfSourceShort, IfSourceShort, Style); + + verifyFormat("if (condition) {\n" + " call_function(arg, arg, arg, arg,\n" + " arg, arg);\n" + "}", + IfSourceLong, Style); + + verifyFormat(IfElseSourceShort, IfElseSourceShort, Style); + + verifyFormat("if (condition) {\n" + " a_function(arg, arg, arg, arg,\n" + " arg, arg);\n" + "} else {\n" + " another_function(arg, arg, arg,\n" + " arg, arg, arg);\n" + "}", + IfElseSourceLong, Style); + + verifyFormat(ForSourceShort, ForSourceShort, Style); + + verifyFormat("for (auto val : container) {\n" + " call_function(arg, arg, arg, arg,\n" + " arg, arg);\n" + "}", + ForSourceLong, Style); + + format("while (foo) ", Style); +} + +TEST_F(BraceInserterTest, InsertBracesMacro) { + auto Style = getLLVMStyleWithColumns(20); + + verifyFormat("#define FOO1(x) \\\n" + " if (x) \\\n" + " return true; \\\n" + " else \\\n" + " return false;", + Style); + + Style.AutomaticBraces = FormatStyle::BIS_Always; + + verifyFormat("#define FOO2(x) \\\n" + " if (x) { \\\n" + " return true; \\\n" + " } else { \\\n" + " return false; \\\n" + " }", + "#define FOO2(x) \\\n" + " if (x) \\\n" + " return true; \\\n" + " else \\\n" + " return false;", + Style); + + verifyFormat("#define FOO3(x) \\\n" + " if (x) { \\\n" + " return true; \\\n" + " } else { \\\n" + " return false; \\\n" + " }", + Style); +} + +TEST_F(BraceInserterTest, InsertBracesComments) { + auto Style = getLLVMStyle(); + + verifyFormat("if (x)\n" + " return true;\n" + "else\n" + " return false;", + Style); + + Style.AutomaticBraces = FormatStyle::BIS_Always; + + verifyFormat("if (x) {\n" + " return true; // Comment\n" + "} else {\n" + " return false; // Comment\n" + "}", + "if (x)\n" + " return true; // Comment\n" + "else\n" + " return false; // Comment", + Style); +} + +TEST_F(BraceInserterTest, RemoveBraces) { + auto Style = getLLVMStyle(); + + Style.AutomaticBraces = FormatStyle::BIS_Remove; + verifyFormat("if (x)\n" + " return true;\n", + "if (x) {\n" + " return true;\n" + "}", + Style); + + verifyFormat("while (x)\n" + " foo();\n", + "while (x) {\n" + " foo();\n" + "}", + Style); + + verifyFormat("if (x)\n" + " return true;\n" + "else\n" + " return false;\n", + "if (x) {\n" + " return true;\n" + "} else {\n" + " return false;\n" + "}\n", + Style); + + verifyFormat("if (x)\n" + " return true;\n" + "else if (x == 1)\n" + " return false;\n", + "if (x) {\n" + " return true;\n" + "} else if (x == 1) {\n" + " return false;\n" + "}\n", + Style); + + verifyFormat("if (x) {\n" + " foo();\n" + " bar();\n" + "} else if (x == 1)\n" + " return false;\n", + "if (x) {\n" + " foo();\n" + " bar();\n" + "} else if (x == 1) {\n" + " return false;\n" + "}\n", + Style); + + verifyFormat("if (x)\n" + " foo();\n" + "else if (x == 1) {\n" + " baz();\n" + " bar();\n" + "}\n", + "if (x) {\n" + " foo();\n" + "} else if (x == 1) {\n" + " baz();\n" + " bar();\n" + "}\n", + Style); + + verifyFormat("if (x) {\n" + " if (y)\n" + " foo();\n" + "} else {\n" + " baz();\n" + " bar();\n" + "}\n", + Style); +} + +} // namespace +} // namespace format +} // namespace clang Index: clang/unittests/Format/CMakeLists.txt =================================================================== --- clang/unittests/Format/CMakeLists.txt +++ clang/unittests/Format/CMakeLists.txt @@ -3,6 +3,7 @@ ) add_clang_unittest(FormatTests + BraceInserterTests.cpp CleanupTest.cpp FormatTest.cpp FormatTestComments.cpp Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -72,7 +72,10 @@ EXPECT_EQ(Expected.str(), format(Expected, Style)) << "Expected code is not stable"; EXPECT_EQ(Expected.str(), format(Code, Style)); - if (Style.Language == FormatStyle::LK_Cpp) { + // clang::format::internal::reformat does not run any of the options that + // modify code for ObjC + if (Style.Language == FormatStyle::LK_Cpp && + Style.AutomaticBraces == FormatStyle::BIS_Leave) { // Objective-C++ is a superset of C++, so everything checked for C++ // needs to be checked for Objective-C++ as well. FormatStyle ObjCStyle = Style; @@ -18758,6 +18761,16 @@ CHECK_PARSE("IndentExternBlock: false", IndentExternBlock, FormatStyle::IEBS_NoIndent); + Style.AutomaticBraces = FormatStyle::BIS_Leave; + CHECK_PARSE("AutomaticBraces: Always", AutomaticBraces, + FormatStyle::BIS_Always); + CHECK_PARSE("AutomaticBraces: WrapLikely", AutomaticBraces, + FormatStyle::BIS_WrapLikely); + CHECK_PARSE("AutomaticBraces: Leave", AutomaticBraces, + FormatStyle::BIS_Leave); + CHECK_PARSE("AutomaticBraces: Remove", AutomaticBraces, + FormatStyle::BIS_Remove); + Style.BitFieldColonSpacing = FormatStyle::BFCS_None; CHECK_PARSE("BitFieldColonSpacing: Both", BitFieldColonSpacing, FormatStyle::BFCS_Both); @@ -22396,7 +22409,6 @@ "}"; EXPECT_EQ(Code, format(Code, Style)); } - } // namespace } // namespace format } // namespace clang