Skip to content

Commit

Permalink
[clang-format] Add option to insert braces after control statements
Browse files Browse the repository at this point in the history
Adds a new option InsertBraces to insert the optional braces after
if, else, for, while, and do in C++.

Differential Revision: https://reviews.llvm.org/D120217
  • Loading branch information
owenca committed Feb 22, 2022
1 parent 95fed2b commit 77e60bc
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 19 deletions.
33 changes: 33 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Expand Up @@ -2756,6 +2756,39 @@ the configuration (without a prefix: ``Auto``).
LoooooooooooooooooooooooooooooooooooooooongReturnType
LoooooooooooooooooooooooooooooooongFunctionDeclaration();

**InsertBraces** (``Boolean``) :versionbadge:`clang-format 15`
Insert braces after control statements (``if``, ``else``, ``for``, ``do``,
and ``while``) in C++ unless the control statements are inside macro
definitions or the braces would enclose preprocessor directives.

.. warning::

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<FunctionDecl>(D)) vs. if (isa<FunctionDecl>(D)) {
handleFunctionDecl(D); handleFunctionDecl(D);
else if (isa<VarDecl>(D)) } else if (isa<VarDecl>(D)) {
handleVarDecl(D); handleVarDecl(D);
else } else {
return; return;
}

while (i--) vs. while (i--) {
for (auto *A : D.attrs()) for (auto *A : D.attrs()) {
handleAttr(A); handleAttr(A);
}
}
do vs. do {
--i; --i;
while (i); } while (i);

**InsertTrailingCommas** (``TrailingCommaStyle``) :versionbadge:`clang-format 12`
If set to ``TCS_Wrapped`` will insert trailing commas in container
literals (arrays and objects) that wrap across multiple lines.
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -188,6 +188,9 @@ clang-format

- Changed ``BreakBeforeConceptDeclarations`` from ``Boolean`` to an enum.

- Option ``InsertBraces`` has been added to insert optional braces after control
statements.

libclang
--------

Expand Down
32 changes: 32 additions & 0 deletions clang/include/clang/Format/Format.h
Expand Up @@ -2571,6 +2571,38 @@ struct FormatStyle {
/// \version 3.7
bool IndentWrappedFunctionNames;

/// Insert braces after control statements (``if``, ``else``, ``for``, ``do``,
/// and ``while``) in C++ unless the control statements are inside macro
/// definitions or the braces would enclose preprocessor directives.
/// \warning
/// 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<FunctionDecl>(D)) vs. if (isa<FunctionDecl>(D)) {
/// handleFunctionDecl(D); handleFunctionDecl(D);
/// else if (isa<VarDecl>(D)) } else if (isa<VarDecl>(D)) {
/// handleVarDecl(D); handleVarDecl(D);
/// else } else {
/// return; return;
/// }
///
/// while (i--) vs. while (i--) {
/// for (auto *A : D.attrs()) for (auto *A : D.attrs()) {
/// handleAttr(A); handleAttr(A);
/// }
/// }
///
/// do vs. do {
/// --i; --i;
/// while (i); } while (i);
/// \endcode
/// \version 15
bool InsertBraces;

/// A vector of prefixes ordered by the desired groups for Java imports.
///
/// One group's prefix can be a subset of another - the longest prefix is
Expand Down
51 changes: 50 additions & 1 deletion clang/lib/Format/Format.cpp
Expand Up @@ -768,6 +768,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("IndentWidth", Style.IndentWidth);
IO.mapOptional("IndentWrappedFunctionNames",
Style.IndentWrappedFunctionNames);
IO.mapOptional("InsertBraces", Style.InsertBraces);
IO.mapOptional("InsertTrailingCommas", Style.InsertTrailingCommas);
IO.mapOptional("JavaImportGroups", Style.JavaImportGroups);
IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes);
Expand Down Expand Up @@ -1223,6 +1224,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.IndentWrappedFunctionNames = false;
LLVMStyle.IndentWidth = 2;
LLVMStyle.PPIndentWidth = -1;
LLVMStyle.InsertBraces = false;
LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None;
LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave;
LLVMStyle.JavaScriptWrapImports = true;
Expand Down Expand Up @@ -1661,7 +1663,7 @@ ParseError validateQualifierOrder(FormatStyle *Style) {
return ParseError::DuplicateQualifierSpecified;
}

// Ensure the list has 'type' in it
// Ensure the list has 'type' in it.
auto type = std::find(Style->QualifierOrder.begin(),
Style->QualifierOrder.end(), "type");
if (type == Style->QualifierOrder.end())
Expand Down Expand Up @@ -1821,6 +1823,48 @@ class BracesRemover : public TokenAnalyzer {
}
};

class BracesInserter : public TokenAnalyzer {
public:
BracesInserter(const Environment &Env, const FormatStyle &Style)
: TokenAnalyzer(Env, Style) {}

std::pair<tooling::Replacements, unsigned>
analyze(TokenAnnotator &Annotator,
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
FormatTokenLexer &Tokens) override {
AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
tooling::Replacements Result;
insertBraces(AnnotatedLines, Result);
return {Result, 0};
}

private:
void insertBraces(SmallVectorImpl<AnnotatedLine *> &Lines,
tooling::Replacements &Result) {
const auto &SourceMgr = Env.getSourceManager();
for (AnnotatedLine *Line : Lines) {
insertBraces(Line->Children, Result);
if (!Line->Affected)
continue;
for (FormatToken *Token = Line->First; Token && !Token->Finalized;
Token = Token->Next) {
if (Token->BraceCount == 0)
continue;
std::string Brace;
if (Token->BraceCount < 0) {
assert(Token->BraceCount == -1);
Brace = '{';
} else {
Brace = std::string(Token->BraceCount, '}');
}
Token->BraceCount = 0;
const auto Start = Token->Tok.getEndLoc();
cantFail(Result.add(tooling::Replacement(SourceMgr, Start, 0, Brace)));
}
}
}
};

class JavaScriptRequoter : public TokenAnalyzer {
public:
JavaScriptRequoter(const Environment &Env, const FormatStyle &Style)
Expand Down Expand Up @@ -3133,6 +3177,11 @@ reformat(const FormatStyle &Style, StringRef Code,
});
}

if (Style.isCpp() && Style.InsertBraces)
Passes.emplace_back([&](const Environment &Env) {
return BracesInserter(Env, Expanded).process();
});

if (Style.isCpp() && Style.RemoveBracesLLVM)
Passes.emplace_back([&](const Environment &Env) {
return BracesRemover(Env, Expanded).process();
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Format/FormatToken.h
Expand Up @@ -489,6 +489,12 @@ struct FormatToken {
/// Is optional and can be removed.
bool Optional = false;

/// Number of optional braces to be inserted after this token:
/// -1: a single left brace
/// 0: no braces
/// >0: number of right braces
int8_t BraceCount = 0;

/// If this token starts a block, this contains all the unwrapped lines
/// in it.
SmallVector<AnnotatedLine *, 1> Children;
Expand Down
69 changes: 51 additions & 18 deletions clang/lib/Format/UnwrappedLineParser.cpp
Expand Up @@ -2300,6 +2300,53 @@ void UnwrappedLineParser::keepAncestorBraces() {
NestedTooDeep.push_back(false);
}

static FormatToken *getLastNonComment(const UnwrappedLine &Line) {
for (const auto &Token : llvm::reverse(Line.Tokens))
if (Token.Tok->isNot(tok::comment))
return Token.Tok;

return nullptr;
}

void UnwrappedLineParser::parseUnbracedBody(bool CheckEOF) {
FormatToken *Tok = nullptr;

if (Style.InsertBraces && !Line->InPPDirective && !Line->Tokens.empty() &&
PreprocessorDirectives.empty()) {
Tok = getLastNonComment(*Line);
assert(Tok);
if (Tok->BraceCount < 0) {
assert(Tok->BraceCount == -1);
Tok = nullptr;
} else {
Tok->BraceCount = -1;
}
}

addUnwrappedLine();
++Line->Level;
parseStructuralElement();

if (Tok) {
assert(!Line->InPPDirective);
Tok = nullptr;
for (const auto &L : llvm::reverse(*CurrentLines)) {
if (!L.InPPDirective) {
Tok = getLastNonComment(L);
if (Tok)
break;
}
}
assert(Tok);
++Tok->BraceCount;
}

if (CheckEOF && FormatTok->is(tok::eof))
addUnwrappedLine();

--Line->Level;
}

static void markOptionalBraces(FormatToken *LeftBrace) {
if (!LeftBrace)
return;
Expand Down Expand Up @@ -2354,10 +2401,7 @@ FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind,
else
NeedsUnwrappedLine = true;
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
--Line->Level;
parseUnbracedBody();
}

bool KeepIfBraces = false;
Expand Down Expand Up @@ -2403,12 +2447,7 @@ FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind,
if (IsPrecededByComment)
--Line->Level;
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
if (FormatTok->is(tok::eof))
addUnwrappedLine();
--Line->Level;
parseUnbracedBody(/*CheckEOF=*/true);
}
} else {
if (Style.RemoveBracesLLVM)
Expand Down Expand Up @@ -2654,10 +2693,7 @@ void UnwrappedLineParser::parseForOrWhileLoop() {
}
addUnwrappedLine();
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
--Line->Level;
parseUnbracedBody();
}

if (Style.RemoveBracesLLVM)
Expand All @@ -2676,10 +2712,7 @@ void UnwrappedLineParser::parseDoWhile() {
if (Style.BraceWrapping.BeforeWhile)
addUnwrappedLine();
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
--Line->Level;
parseUnbracedBody();
}

if (Style.RemoveBracesLLVM)
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/UnwrappedLineParser.h
Expand Up @@ -119,6 +119,7 @@ class UnwrappedLineParser {
void parseParens(TokenType AmpAmpTokenType = TT_Unknown);
void parseSquare(bool LambdaIntroducer = false);
void keepAncestorBraces();
void parseUnbracedBody(bool CheckEOF = false);
FormatToken *parseIfThenElse(IfStmtKind *IfKind, bool KeepBraces = false);
void parseTryCatch();
void parseForOrWhileLoop();
Expand Down

0 comments on commit 77e60bc

Please sign in to comment.