Skip to content

Commit 533fbae

Browse files
committed
[clang-format] Add experimental option to remove LLVM braces
See the style examples at: https://llvm.org/docs/CodingStandards.html#don-t-use-braces-on-simple-single-statement-bodies-of-if-else-loop-statements Differential Revision: https://reviews.llvm.org/D116316
1 parent a4e255f commit 533fbae

File tree

9 files changed

+774
-28
lines changed

9 files changed

+774
-28
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3402,6 +3402,62 @@ the configuration (without a prefix: ``Auto``).
34023402
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
34033403
* information */
34043404
3405+
**RemoveBracesLLVM** (``Boolean``) :versionbadge:`clang-format 14`
3406+
Remove optional braces of control statements (``if``, ``else``, ``for``,
3407+
and ``while``) in C++ according to the LLVM coding style.
3408+
3409+
.. warning::
3410+
3411+
This option will be renamed and expanded to support other styles.
3412+
3413+
.. warning::
3414+
3415+
Setting this option to `true` could lead to incorrect code formatting due
3416+
to clang-format's lack of complete semantic information. As such, extra
3417+
care should be taken to review code changes made by this option.
3418+
3419+
.. code-block:: c++
3420+
3421+
false: true:
3422+
3423+
if (isa<FunctionDecl>(D)) { vs. if (isa<FunctionDecl>(D))
3424+
handleFunctionDecl(D); handleFunctionDecl(D);
3425+
} else if (isa<VarDecl>(D)) { else if (isa<VarDecl>(D))
3426+
handleVarDecl(D); handleVarDecl(D);
3427+
}
3428+
3429+
if (isa<VarDecl>(D)) { vs. if (isa<VarDecl>(D)) {
3430+
for (auto *A : D.attrs()) { for (auto *A : D.attrs())
3431+
if (shouldProcessAttr(A)) { if (shouldProcessAttr(A))
3432+
handleAttr(A); handleAttr(A);
3433+
} }
3434+
}
3435+
}
3436+
3437+
if (isa<FunctionDecl>(D)) { vs. if (isa<FunctionDecl>(D))
3438+
for (auto *A : D.attrs()) { for (auto *A : D.attrs())
3439+
handleAttr(A); handleAttr(A);
3440+
}
3441+
}
3442+
3443+
if (auto *D = (T)(D)) { vs. if (auto *D = (T)(D)) {
3444+
if (shouldProcess(D)) { if (shouldProcess(D))
3445+
handleVarDecl(D); handleVarDecl(D);
3446+
} else { else
3447+
markAsIgnored(D); markAsIgnored(D);
3448+
} }
3449+
}
3450+
3451+
if (a) { vs. if (a)
3452+
b(); b();
3453+
} else { else if (c)
3454+
if (c) { d();
3455+
d(); else
3456+
} else { e();
3457+
e();
3458+
}
3459+
}
3460+
34053461
**SeparateDefinitionBlocks** (``SeparateDefinitionStyle``) :versionbadge:`clang-format 14`
34063462
Specifies the use of empty lines to separate definition blocks, including
34073463
classes, structs, enums, and functions.

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ clang-format
310310
`const` `volatile` `static` `inline` `constexpr` `restrict`
311311
to be controlled relative to the `type`.
312312

313+
- Option ``RemoveBracesLLVM`` has been added to remove optional braces of
314+
control statements for the LLVM style.
315+
313316
- Option ``SeparateDefinitionBlocks`` has been added to insert or remove empty
314317
lines between definition blocks including functions, classes, structs, enums,
315318
and namespaces.

clang/include/clang/Format/Format.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,6 +3049,60 @@ struct FormatStyle {
30493049
bool ReflowComments;
30503050
// clang-format on
30513051

3052+
/// Remove optional braces of control statements (``if``, ``else``, ``for``,
3053+
/// and ``while``) in C++ according to the LLVM coding style.
3054+
/// \warning
3055+
/// This option will be renamed and expanded to support other styles.
3056+
/// \endwarning
3057+
/// \warning
3058+
/// Setting this option to `true` could lead to incorrect code formatting due
3059+
/// to clang-format's lack of complete semantic information. As such, extra
3060+
/// care should be taken to review code changes made by this option.
3061+
/// \endwarning
3062+
/// \code
3063+
/// false: true:
3064+
///
3065+
/// if (isa<FunctionDecl>(D)) { vs. if (isa<FunctionDecl>(D))
3066+
/// handleFunctionDecl(D); handleFunctionDecl(D);
3067+
/// } else if (isa<VarDecl>(D)) { else if (isa<VarDecl>(D))
3068+
/// handleVarDecl(D); handleVarDecl(D);
3069+
/// }
3070+
///
3071+
/// if (isa<VarDecl>(D)) { vs. if (isa<VarDecl>(D)) {
3072+
/// for (auto *A : D.attrs()) { for (auto *A : D.attrs())
3073+
/// if (shouldProcessAttr(A)) { if (shouldProcessAttr(A))
3074+
/// handleAttr(A); handleAttr(A);
3075+
/// } }
3076+
/// }
3077+
/// }
3078+
///
3079+
/// if (isa<FunctionDecl>(D)) { vs. if (isa<FunctionDecl>(D))
3080+
/// for (auto *A : D.attrs()) { for (auto *A : D.attrs())
3081+
/// handleAttr(A); handleAttr(A);
3082+
/// }
3083+
/// }
3084+
///
3085+
/// if (auto *D = (T)(D)) { vs. if (auto *D = (T)(D)) {
3086+
/// if (shouldProcess(D)) { if (shouldProcess(D))
3087+
/// handleVarDecl(D); handleVarDecl(D);
3088+
/// } else { else
3089+
/// markAsIgnored(D); markAsIgnored(D);
3090+
/// } }
3091+
/// }
3092+
///
3093+
/// if (a) { vs. if (a)
3094+
/// b(); b();
3095+
/// } else { else if (c)
3096+
/// if (c) { d();
3097+
/// d(); else
3098+
/// } else { e();
3099+
/// e();
3100+
/// }
3101+
/// }
3102+
/// \endcode
3103+
/// \version 14
3104+
bool RemoveBracesLLVM;
3105+
30523106
/// \brief The style if definition blocks should be separated.
30533107
enum SeparateDefinitionStyle {
30543108
/// Leave definition blocks as they are.
@@ -3858,6 +3912,7 @@ struct FormatStyle {
38583912
QualifierOrder == R.QualifierOrder &&
38593913
RawStringFormats == R.RawStringFormats &&
38603914
ReferenceAlignment == R.ReferenceAlignment &&
3915+
RemoveBracesLLVM == R.RemoveBracesLLVM &&
38613916
SeparateDefinitionBlocks == R.SeparateDefinitionBlocks &&
38623917
ShortNamespaceLines == R.ShortNamespaceLines &&
38633918
SortIncludes == R.SortIncludes &&

clang/lib/Format/Format.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,7 @@ template <> struct MappingTraits<FormatStyle> {
781781
IO.mapOptional("RawStringFormats", Style.RawStringFormats);
782782
IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment);
783783
IO.mapOptional("ReflowComments", Style.ReflowComments);
784+
IO.mapOptional("RemoveBracesLLVM", Style.RemoveBracesLLVM);
784785
IO.mapOptional("SeparateDefinitionBlocks", Style.SeparateDefinitionBlocks);
785786
IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines);
786787
IO.mapOptional("SortIncludes", Style.SortIncludes);
@@ -1214,6 +1215,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
12141215
LLVMStyle.UseCRLF = false;
12151216
LLVMStyle.UseTab = FormatStyle::UT_Never;
12161217
LLVMStyle.ReflowComments = true;
1218+
LLVMStyle.RemoveBracesLLVM = false;
12171219
LLVMStyle.SpacesInParentheses = false;
12181220
LLVMStyle.SpacesInSquareBrackets = false;
12191221
LLVMStyle.SpaceInEmptyBlock = false;
@@ -1748,6 +1750,45 @@ FormatStyle::GetLanguageStyle(FormatStyle::LanguageKind Language) const {
17481750

17491751
namespace {
17501752

1753+
class BracesRemover : public TokenAnalyzer {
1754+
public:
1755+
BracesRemover(const Environment &Env, const FormatStyle &Style)
1756+
: TokenAnalyzer(Env, Style) {}
1757+
1758+
std::pair<tooling::Replacements, unsigned>
1759+
analyze(TokenAnnotator &Annotator,
1760+
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
1761+
FormatTokenLexer &Tokens) override {
1762+
AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
1763+
tooling::Replacements Result;
1764+
removeBraces(AnnotatedLines, Result);
1765+
return {Result, 0};
1766+
}
1767+
1768+
private:
1769+
// Remove optional braces.
1770+
void removeBraces(SmallVectorImpl<AnnotatedLine *> &Lines,
1771+
tooling::Replacements &Result) {
1772+
const auto &SourceMgr = Env.getSourceManager();
1773+
for (AnnotatedLine *Line : Lines) {
1774+
removeBraces(Line->Children, Result);
1775+
if (!Line->Affected)
1776+
continue;
1777+
for (FormatToken *Token = Line->First; Token; Token = Token->Next) {
1778+
if (!Token->Optional)
1779+
continue;
1780+
assert(Token->isOneOf(tok::l_brace, tok::r_brace));
1781+
const auto Start = Token == Line->Last
1782+
? Token->WhitespaceRange.getBegin()
1783+
: Token->Tok.getLocation();
1784+
const auto Range =
1785+
CharSourceRange::getCharRange(Start, Token->Tok.getEndLoc());
1786+
cantFail(Result.add(tooling::Replacement(SourceMgr, Range, "")));
1787+
}
1788+
}
1789+
}
1790+
};
1791+
17511792
class JavaScriptRequoter : public TokenAnalyzer {
17521793
public:
17531794
JavaScriptRequoter(const Environment &Env, const FormatStyle &Style)
@@ -3049,6 +3090,11 @@ reformat(const FormatStyle &Style, StringRef Code,
30493090
});
30503091
}
30513092

3093+
if (Style.isCpp() && Style.RemoveBracesLLVM)
3094+
Passes.emplace_back([&](const Environment &Env) {
3095+
return BracesRemover(Env, Expanded).process();
3096+
});
3097+
30523098
if (Style.Language == FormatStyle::LK_Cpp) {
30533099
if (Style.FixNamespaceComments)
30543100
Passes.emplace_back([&](const Environment &Env) {

clang/lib/Format/FormatToken.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,9 @@ struct FormatToken {
442442
/// This starts an array initializer.
443443
bool IsArrayInitializer = false;
444444

445+
/// Is optional and can be removed.
446+
bool Optional = false;
447+
445448
/// If this token starts a block, this contains all the unwrapped lines
446449
/// in it.
447450
SmallVector<AnnotatedLine *, 1> Children;

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ class AnnotatingParser {
780780
unsigned CommaCount = 0;
781781
while (CurrentToken) {
782782
if (CurrentToken->is(tok::r_brace)) {
783+
assert(Left->Optional == CurrentToken->Optional);
783784
Left->MatchingParen = CurrentToken;
784785
CurrentToken->MatchingParen = Left;
785786
if (Style.AlignArrayOfStructures != FormatStyle::AIAS_None) {

0 commit comments

Comments
 (0)