https://github.com/owenca updated https://github.com/llvm/llvm-project/pull/148345
>From 3affefb96efe2d2955be66c247276b81d5d1d3a1 Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Sat, 12 Jul 2025 00:09:49 -0700 Subject: [PATCH 1/2] [clang-format] Add FunctionLikeMacros option This allows RemoveParentheses to skip the invocations of function-like macros. Fixes #68354. Fixes #147780. --- clang/docs/ClangFormatStyleOptions.rst | 6 ++++++ clang/docs/ReleaseNotes.rst | 2 ++ clang/include/clang/Format/Format.h | 6 ++++++ clang/lib/Format/Format.cpp | 1 + clang/lib/Format/FormatToken.h | 1 + clang/lib/Format/FormatTokenLexer.cpp | 4 ++++ clang/lib/Format/TokenAnnotator.cpp | 3 ++- clang/lib/Format/UnwrappedLineParser.cpp | 14 +++++++++----- clang/lib/Format/UnwrappedLineParser.h | 3 ++- clang/unittests/Format/ConfigParseTest.cpp | 1 + clang/unittests/Format/FormatTest.cpp | 4 ++++ 11 files changed, 38 insertions(+), 7 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index ab374c1886165..b2fb14c1b3011 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4073,6 +4073,12 @@ the configuration (without a prefix: ``Auto``). For example: BOOST_FOREACH. +.. _FunctionLikeMacros: + +**FunctionLikeMacros** (``List of Strings``) :versionbadge:`clang-format 21` :ref:`¶ <FunctionLikeMacros>` + A vector of function-like macros whose invocations should be skipped by + ``RemoveParentheses``. + .. _IfMacros: **IfMacros** (``List of Strings``) :versionbadge:`clang-format 13` :ref:`¶ <IfMacros>` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e81a3d4976cf8..a26b0e4cc8d2c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1132,6 +1132,8 @@ clang-format ``enum`` enumerator lists. - Add ``OneLineFormatOffRegex`` option for turning formatting off for one line. - Add ``SpaceAfterOperatorKeyword`` option. +- Add ``FunctionLikeMacros`` option so that their invocations are skipped by + ``RemoveParentheses``. clang-refactor -------------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 74b516fe4f071..7e7eb21e2a3ba 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2786,6 +2786,11 @@ struct FormatStyle { /// \version 3.7 std::vector<std::string> ForEachMacros; + /// A vector of function-like macros whose invocations should be skipped by + /// ``RemoveParentheses``. + /// \version 21 + std::vector<std::string> FunctionLikeMacros; + tooling::IncludeStyle IncludeStyle; /// A vector of macros that should be interpreted as conditionals @@ -5382,6 +5387,7 @@ struct FormatStyle { R.ExperimentalAutoDetectBinPacking && FixNamespaceComments == R.FixNamespaceComments && ForEachMacros == R.ForEachMacros && + FunctionLikeMacros == R.FunctionLikeMacros && IncludeStyle.IncludeBlocks == R.IncludeStyle.IncludeBlocks && IncludeStyle.IncludeCategories == R.IncludeStyle.IncludeCategories && IncludeStyle.IncludeIsMainRegex == diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 4956607602be0..30eab05117b76 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1068,6 +1068,7 @@ template <> struct MappingTraits<FormatStyle> { Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments); IO.mapOptional("ForEachMacros", Style.ForEachMacros); + IO.mapOptional("FunctionLikeMacros", Style.FunctionLikeMacros); IO.mapOptional("IfMacros", Style.IfMacros); IO.mapOptional("IncludeBlocks", Style.IncludeStyle.IncludeBlocks); IO.mapOptional("IncludeCategories", Style.IncludeStyle.IncludeCategories); diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 94014aee3221f..9252a795a0b5e 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -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 */ \ diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index d8ee5cb90aaa4..3cdcbd15cd739 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -43,6 +43,10 @@ FormatTokenLexer::FormatTokenLexer( auto Identifier = &IdentTable.get(ForEachMacro); Macros.insert({Identifier, TT_ForEachMacro}); } + for (const std::string &FunctionLikeMacro : Style.FunctionLikeMacros) { + auto Identifier = &IdentTable.get(FunctionLikeMacro); + Macros.insert({Identifier, TT_FunctionLikeMacro}); + } for (const std::string &IfMacro : Style.IfMacros) { auto Identifier = &IdentTable.get(IfMacro); Macros.insert({Identifier, TT_IfMacro}); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index aab8f054655fe..739209a5681f8 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -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(); diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 7e8634aeec4e0..91b8fdc8a3c38 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -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; } diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 2d1492c1a6b8c..8e29680ff244b 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -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); diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index aedfdd151d6d3..c2058fb46cc45 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -940,6 +940,7 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE("StatementMacros: [QUNUSED, QT_REQUIRE_VERSION]", StatementMacros, std::vector<std::string>({"QUNUSED", "QT_REQUIRE_VERSION"})); + CHECK_PARSE_LIST(FunctionLikeMacros); CHECK_PARSE_LIST(JavaImportGroups); CHECK_PARSE_LIST(Macros); CHECK_PARSE_LIST(NamespaceMacros); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 944e7c3fb152a..789d822853e26 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -28479,6 +28479,10 @@ TEST_F(FormatTest, RemoveParentheses) { verifyFormat("MOCK_METHOD(void, Function, (), override);", "MOCK_METHOD(void, Function, (), (override));", Style); + Style.FunctionLikeMacros.push_back("MOCK_METHOD"); + verifyFormat("MOCK_METHOD((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); >From 31a8cccf9760ba38b4d48fd63297994b1fc13bfb Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Sun, 13 Jul 2025 12:18:34 -0700 Subject: [PATCH 2/2] Rename the option --- clang/docs/ClangFormatStyleOptions.rst | 12 ++++++------ clang/docs/ReleaseNotes.rst | 4 ++-- clang/include/clang/Format/Format.h | 13 +++++++------ clang/lib/Format/Format.cpp | 3 ++- clang/lib/Format/FormatTokenLexer.cpp | 8 ++++---- clang/lib/Format/FormatTokenLexer.h | 4 ++-- clang/unittests/Format/ConfigParseTest.cpp | 2 +- clang/unittests/Format/FormatTest.cpp | 6 +++--- 8 files changed, 27 insertions(+), 25 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index b2fb14c1b3011..0e21ef0244f78 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4073,12 +4073,6 @@ the configuration (without a prefix: ``Auto``). For example: BOOST_FOREACH. -.. _FunctionLikeMacros: - -**FunctionLikeMacros** (``List of Strings``) :versionbadge:`clang-format 21` :ref:`¶ <FunctionLikeMacros>` - A vector of function-like macros whose invocations should be skipped by - ``RemoveParentheses``. - .. _IfMacros: **IfMacros** (``List of Strings``) :versionbadge:`clang-format 13` :ref:`¶ <IfMacros>` @@ -4981,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>` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a26b0e4cc8d2c..e01751c2f03f5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1132,8 +1132,8 @@ clang-format ``enum`` enumerator lists. - Add ``OneLineFormatOffRegex`` option for turning formatting off for one line. - Add ``SpaceAfterOperatorKeyword`` option. -- Add ``FunctionLikeMacros`` option so that their invocations are skipped by - ``RemoveParentheses``. +- Add ``MacrosSkippedByRemoveParentheses`` option so that their invocations are + skipped by ``RemoveParentheses``. clang-refactor -------------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 7e7eb21e2a3ba..b4f2a87fe7e83 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2786,11 +2786,6 @@ struct FormatStyle { /// \version 3.7 std::vector<std::string> ForEachMacros; - /// A vector of function-like macros whose invocations should be skipped by - /// ``RemoveParentheses``. - /// \version 21 - std::vector<std::string> FunctionLikeMacros; - tooling::IncludeStyle IncludeStyle; /// A vector of macros that should be interpreted as conditionals @@ -3493,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 @@ -5387,7 +5387,6 @@ struct FormatStyle { R.ExperimentalAutoDetectBinPacking && FixNamespaceComments == R.FixNamespaceComments && ForEachMacros == R.ForEachMacros && - FunctionLikeMacros == R.FunctionLikeMacros && IncludeStyle.IncludeBlocks == R.IncludeStyle.IncludeBlocks && IncludeStyle.IncludeCategories == R.IncludeStyle.IncludeCategories && IncludeStyle.IncludeIsMainRegex == @@ -5416,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 && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 30eab05117b76..78c09be458f0a 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1068,7 +1068,6 @@ template <> struct MappingTraits<FormatStyle> { Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments); IO.mapOptional("ForEachMacros", Style.ForEachMacros); - IO.mapOptional("FunctionLikeMacros", Style.FunctionLikeMacros); IO.mapOptional("IfMacros", Style.IfMacros); IO.mapOptional("IncludeBlocks", Style.IncludeStyle.IncludeBlocks); IO.mapOptional("IncludeCategories", Style.IncludeStyle.IncludeCategories); @@ -1100,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); diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 3cdcbd15cd739..49da3160daf50 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -43,10 +43,6 @@ FormatTokenLexer::FormatTokenLexer( auto Identifier = &IdentTable.get(ForEachMacro); Macros.insert({Identifier, TT_ForEachMacro}); } - for (const std::string &FunctionLikeMacro : Style.FunctionLikeMacros) { - auto Identifier = &IdentTable.get(FunctionLikeMacro); - Macros.insert({Identifier, TT_FunctionLikeMacro}); - } for (const std::string &IfMacro : Style.IfMacros) { auto Identifier = &IdentTable.get(IfMacro); Macros.insert({Identifier, TT_IfMacro}); @@ -78,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) @@ -1477,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)) diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h index 026383db1fe6c..57c572af3defd 100644 --- a/clang/lib/Format/FormatTokenLexer.h +++ b/clang/lib/Format/FormatTokenLexer.h @@ -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. diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index c2058fb46cc45..d17109aebc0f8 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -940,9 +940,9 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE("StatementMacros: [QUNUSED, QT_REQUIRE_VERSION]", StatementMacros, std::vector<std::string>({"QUNUSED", "QT_REQUIRE_VERSION"})); - CHECK_PARSE_LIST(FunctionLikeMacros); CHECK_PARSE_LIST(JavaImportGroups); CHECK_PARSE_LIST(Macros); + CHECK_PARSE_LIST(MacrosSkippedByRemoveParentheses); CHECK_PARSE_LIST(NamespaceMacros); CHECK_PARSE_LIST(ObjCPropertyAttributeOrder); CHECK_PARSE_LIST(TableGenBreakingDAGArgOperators); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 789d822853e26..0bc1c6d45656e 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -28479,9 +28479,9 @@ TEST_F(FormatTest, RemoveParentheses) { verifyFormat("MOCK_METHOD(void, Function, (), override);", "MOCK_METHOD(void, Function, (), (override));", Style); - Style.FunctionLikeMacros.push_back("MOCK_METHOD"); - verifyFormat("MOCK_METHOD((int), func, ((std::map<int, int>)), (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); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits