Skip to content

[clang-format] Add MacrosSkippedByRemoveParentheses option #148345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4975,6 +4975,12 @@ the configuration (without a prefix: ``Auto``).
A(z); -> z;
A(a, b); // will not be expanded.

.. _MacrosSkippedByRemoveParentheses:

**MacrosSkippedByRemoveParentheses** (``List of Strings``) :versionbadge:`clang-format 21` :ref:`<MacrosSkippedByRemoveParentheses>`
A vector of function-like macros whose invocations should be skipped by
``RemoveParentheses``.

.. _MainIncludeChar:

**MainIncludeChar** (``MainIncludeCharDiscriminator``) :versionbadge:`clang-format 19` :ref:`<MainIncludeChar>`
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,8 @@ clang-format
``enum`` enumerator lists.
- Add ``OneLineFormatOffRegex`` option for turning formatting off for one line.
- Add ``SpaceAfterOperatorKeyword`` option.
- Add ``MacrosSkippedByRemoveParentheses`` option so that their invocations are
skipped by ``RemoveParentheses``.

clang-refactor
--------------
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3488,6 +3488,11 @@ struct FormatStyle {
/// \version 17
std::vector<std::string> Macros;

/// A vector of function-like macros whose invocations should be skipped by
/// ``RemoveParentheses``.
/// \version 21
std::vector<std::string> MacrosSkippedByRemoveParentheses;

/// The maximum number of consecutive empty lines to keep.
/// \code
/// MaxEmptyLinesToKeep: 1 vs. MaxEmptyLinesToKeep: 0
Expand Down Expand Up @@ -5410,6 +5415,8 @@ struct FormatStyle {
LambdaBodyIndentation == R.LambdaBodyIndentation &&
LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin &&
MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros &&
MacrosSkippedByRemoveParentheses ==
R.MacrosSkippedByRemoveParentheses &&
MaxEmptyLinesToKeep == R.MaxEmptyLinesToKeep &&
NamespaceIndentation == R.NamespaceIndentation &&
NamespaceMacros == R.NamespaceMacros &&
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,8 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd);
IO.mapOptional("Macros", Style.Macros);
IO.mapOptional("MacrosSkippedByRemoveParentheses",
Style.MacrosSkippedByRemoveParentheses);
IO.mapOptional("MainIncludeChar", Style.IncludeStyle.MainIncludeChar);
IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep);
IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ namespace format {
TYPE(FunctionDeclarationName) \
TYPE(FunctionDeclarationLParen) \
TYPE(FunctionLBrace) \
TYPE(FunctionLikeMacro) \
TYPE(FunctionLikeOrFreestandingMacro) \
TYPE(FunctionTypeLParen) \
/* The colons as part of a C11 _Generic selection */ \
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Format/FormatTokenLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ FormatTokenLexer::FormatTokenLexer(
Macros.insert({Identifier, TT_StatementAttributeLikeMacro});
}

for (const auto &Macro : Style.MacrosSkippedByRemoveParentheses)
MacrosSkippedByRemoveParentheses.insert(&IdentTable.get(Macro));
for (const auto &TemplateName : Style.TemplateNames)
TemplateNames.insert(&IdentTable.get(TemplateName));
for (const auto &TypeName : Style.TypeNames)
Expand Down Expand Up @@ -1473,6 +1475,8 @@ FormatToken *FormatTokenLexer::getNextToken() {
FormatTok->setType(TT_MacroBlockBegin);
else if (MacroBlockEndRegex.match(Text))
FormatTok->setType(TT_MacroBlockEnd);
else if (MacrosSkippedByRemoveParentheses.contains(Identifier))
FormatTok->setFinalizedType(TT_FunctionLikeMacro);
else if (TemplateNames.contains(Identifier))
FormatTok->setFinalizedType(TT_TemplateName);
else if (TypeNames.contains(Identifier))
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Format/FormatTokenLexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ class FormatTokenLexer {

llvm::SmallMapVector<IdentifierInfo *, TokenType, 8> Macros;

llvm::SmallPtrSet<IdentifierInfo *, 8> TemplateNames, TypeNames,
VariableTemplates;
llvm::SmallPtrSet<IdentifierInfo *, 8> MacrosSkippedByRemoveParentheses,
TemplateNames, TypeNames, VariableTemplates;

bool FormattingDisabled;
llvm::Regex FormatOffRegex; // For one line.
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2090,7 +2090,8 @@ class AnnotatingParser {
TT_RecordLBrace, TT_StructLBrace, TT_UnionLBrace, TT_RequiresClause,
TT_RequiresClauseInARequiresExpression, TT_RequiresExpression,
TT_RequiresExpressionLParen, TT_RequiresExpressionLBrace,
TT_CompoundRequirementLBrace, TT_BracedListLBrace)) {
TT_CompoundRequirementLBrace, TT_BracedListLBrace,
TT_FunctionLikeMacro)) {
CurrentToken->setType(TT_Unknown);
}
CurrentToken->Role.reset();
Expand Down
14 changes: 9 additions & 5 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2579,30 +2579,34 @@ bool UnwrappedLineParser::parseBracedList(bool IsAngleBracket, bool IsEnum) {
/// double ampersands. This applies for all nested scopes as well.
///
/// Returns whether there is a `=` token between the parentheses.
bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) {
bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType,
bool InMacroCall) {
assert(FormatTok->is(tok::l_paren) && "'(' expected.");
auto *LParen = FormatTok;
auto *Prev = FormatTok->Previous;
bool SeenComma = false;
bool SeenEqual = false;
bool MightBeFoldExpr = false;
nextToken();
const bool MightBeStmtExpr = FormatTok->is(tok::l_brace);
if (!InMacroCall && Prev && Prev->is(TT_FunctionLikeMacro))
InMacroCall = true;
do {
switch (FormatTok->Tok.getKind()) {
case tok::l_paren:
if (parseParens(AmpAmpTokenType))
if (parseParens(AmpAmpTokenType, InMacroCall))
SeenEqual = true;
if (Style.isJava() && FormatTok->is(tok::l_brace))
parseChildBlock();
break;
case tok::r_paren: {
auto *Prev = LParen->Previous;
auto *RParen = FormatTok;
nextToken();
if (Prev) {
auto OptionalParens = [&] {
if (MightBeStmtExpr || MightBeFoldExpr || Line->InMacroBody ||
SeenComma || Style.RemoveParentheses == FormatStyle::RPS_Leave ||
if (MightBeStmtExpr || MightBeFoldExpr || SeenComma || InMacroCall ||
Line->InMacroBody ||
Style.RemoveParentheses == FormatStyle::RPS_Leave ||
RParen->getPreviousNonComment() == LParen) {
return false;
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Format/UnwrappedLineParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ class UnwrappedLineParser {
bool *HasLabel = nullptr);
bool tryToParseBracedList();
bool parseBracedList(bool IsAngleBracket = false, bool IsEnum = false);
bool parseParens(TokenType AmpAmpTokenType = TT_Unknown);
bool parseParens(TokenType AmpAmpTokenType = TT_Unknown,
bool InMacroCall = false);
void parseSquare(bool LambdaIntroducer = false);
void keepAncestorBraces();
void parseUnbracedBody(bool CheckEOF = false);
Expand Down
1 change: 1 addition & 0 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,7 @@ TEST(ConfigParseTest, ParsesConfiguration) {

CHECK_PARSE_LIST(JavaImportGroups);
CHECK_PARSE_LIST(Macros);
CHECK_PARSE_LIST(MacrosSkippedByRemoveParentheses);
CHECK_PARSE_LIST(NamespaceMacros);
CHECK_PARSE_LIST(ObjCPropertyAttributeOrder);
CHECK_PARSE_LIST(TableGenBreakingDAGArgOperators);
Expand Down
4 changes: 4 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28479,6 +28479,10 @@ TEST_F(FormatTest, RemoveParentheses) {
verifyFormat("MOCK_METHOD(void, Function, (), override);",
"MOCK_METHOD(void, Function, (), (override));", Style);

Style.MacrosSkippedByRemoveParentheses.push_back("FOO");
verifyFormat("FOO((a && b));", Style);
verifyFormat("FOO((int), func, ((std::map<int, int>)), (override));", Style);

Style.RemoveParentheses = FormatStyle::RPS_ReturnStatement;
verifyFormat("#define Return0 return (0);", Style);
verifyFormat("return 0;", "return (0);", Style);
Expand Down
Loading