https://github.com/harrishancock updated https://github.com/llvm/llvm-project/pull/183352
>From 0c2d90a0ce7a905b724a6266fa36700eb7936db0 Mon Sep 17 00:00:00 2001 From: Harris Hancock <[email protected]> Date: Wed, 25 Feb 2026 12:05:56 +0000 Subject: [PATCH 1/5] [clang-format] Add TryMacros and CatchMacros options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TryMacros and CatchMacros configuration options that allow macros to be formatted like native try/catch blocks. This enables proper handling of custom exception macros such as KJ_TRY/KJ_CATCH and JSG_TRY/JSG_CATCH used in the Cloudflare Workers runtime (workerd). The implementation: - Adds TT_TryMacro and TT_CatchMacro token types - Remaps macro identifiers to tok::kw_try / tok::kw_catch during lexing so they flow through the existing parseTryCatch() structural parsing - Extends parseTryCatch() to skip parenthesized macro arguments - Ensures tryTransformTryUsageForC() preserves TryMacro tokens, which would otherwise be reset to tok::identifier when followed by '(' - Suppresses space before '(' for TryMacro/CatchMacro tokens even when SpaceBeforeParens includes AfterControlStatements, because these are macros that happen to act as keywords, not keywords that happen to look like macros — KJ_CATCH(e) should look like a macro invocation, not like 'catch (e)' Supported formatting behaviors (mirroring native try/catch): - Catch-cuddling (} CATCH_MACRO(...) {) - BraceWrapping.BeforeCatch (catch on its own line) - All BreakBeforeBraces styles (Stroustrup, Allman, Whitesmiths, GNU) - Multiple catch blocks - Nested try-catch macros Assisted-by: Claude (anthropic.com) --- clang/docs/ClangFormatStyleOptions.rst | 54 ++++++ clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/Format/Format.h | 56 ++++++- clang/lib/Format/Format.cpp | 2 + clang/lib/Format/FormatToken.h | 2 + clang/lib/Format/FormatTokenLexer.cpp | 18 ++ clang/lib/Format/TokenAnnotator.cpp | 20 ++- clang/lib/Format/UnwrappedLineParser.cpp | 4 + clang/unittests/Format/ConfigParseTest.cpp | 12 ++ clang/unittests/Format/FormatTest.cpp | 182 +++++++++++++++++++++ 10 files changed, 342 insertions(+), 10 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index ed4e2aaa26052..ad51339c8673a 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3933,6 +3933,33 @@ the configuration (without a prefix: ``Auto``). +.. _CatchMacros: + +**CatchMacros** (``List of Strings``) :versionbadge:`clang-format 23` :ref:`¶ <CatchMacros>` + A vector of macros that should be interpreted as catch blocks + instead of as function calls. + + These are expected to be macros of the form: + + .. code-block:: c++ + + TRY_MACRO(...) { + ... + } CATCH_MACRO(...) { + ... + } + + The parenthesized arguments are optional for both try and catch macros. + + In the .clang-format configuration file, this can be configured like: + + .. code-block:: yaml + + CatchMacros: [CATCH_MACRO] + + For example: `KJ_CATCH + <https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#throwing-and-catching-exceptions>`_ + .. _ColumnLimit: **ColumnLimit** (``Unsigned``) :versionbadge:`clang-format 3.7` :ref:`¶ <ColumnLimit>` @@ -7365,6 +7392,33 @@ the configuration (without a prefix: ``Auto``). A ``<`` after a template name is annotated as a template opener instead of a binary operator. +.. _TryMacros: + +**TryMacros** (``List of Strings``) :versionbadge:`clang-format 23` :ref:`¶ <TryMacros>` + A vector of macros that should be interpreted as try blocks + instead of as function calls. + + These are expected to be macros of the form: + + .. code-block:: c++ + + TRY_MACRO(...) { + ... + } CATCH_MACRO(...) { + ... + } + + The parenthesized arguments are optional for both try and catch macros. + + In the .clang-format configuration file, this can be configured like: + + .. code-block:: yaml + + TryMacros: [TRY_MACRO] + + For example: `KJ_TRY + <https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#throwing-and-catching-exceptions>`_ + .. _TypeNames: **TypeNames** (``List of Strings``) :versionbadge:`clang-format 17` :ref:`¶ <TypeNames>` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cb1010aee1edd..56599e18ea893 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -418,6 +418,8 @@ AST Matchers clang-format ------------ +- Add ``TryMacros`` and ``CatchMacros`` options to allow macros to be formatted + as ``try`` and ``catch`` blocks, including catch-cuddling. - Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the '-'/'+' and the return type in Objective-C method declarations - Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 2028c963ac306..d7a4ec79cc2a3 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2633,6 +2633,30 @@ struct FormatStyle { /// \version 3.9 bool BreakStringLiterals; + /// A vector of macros that should be interpreted as catch blocks + /// instead of as function calls. + /// + /// These are expected to be macros of the form: + /// \code + /// TRY_MACRO(...) { + /// ... + /// } CATCH_MACRO(...) { + /// ... + /// } + /// \endcode + /// + /// The parenthesized arguments are optional for both try and catch macros. + /// + /// In the .clang-format configuration file, this can be configured like: + /// \code{.yaml} + /// CatchMacros: [CATCH_MACRO] + /// \endcode + /// + /// For example: `KJ_CATCH + /// <https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#throwing-and-catching-exceptions>`_ + /// \version 23 + std::vector<std::string> CatchMacros; + /// The column limit. /// /// A column limit of ``0`` means that there is no column limit. In this case, @@ -5632,6 +5656,30 @@ struct FormatStyle { /// \version 9 std::vector<std::string> TypenameMacros; + /// A vector of macros that should be interpreted as try blocks + /// instead of as function calls. + /// + /// These are expected to be macros of the form: + /// \code + /// TRY_MACRO(...) { + /// ... + /// } CATCH_MACRO(...) { + /// ... + /// } + /// \endcode + /// + /// The parenthesized arguments are optional for both try and catch macros. + /// + /// In the .clang-format configuration file, this can be configured like: + /// \code{.yaml} + /// TryMacros: [TRY_MACRO] + /// \endcode + /// + /// For example: `KJ_TRY + /// <https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#throwing-and-catching-exceptions>`_ + /// \version 23 + std::vector<std::string> TryMacros; + /// This option is **deprecated**. See ``LF`` and ``CRLF`` of ``LineEnding``. /// \version 10 // bool UseCRLF; @@ -5810,7 +5858,8 @@ struct FormatStyle { BreakInheritanceList == R.BreakInheritanceList && BreakStringLiterals == R.BreakStringLiterals && BreakTemplateDeclarations == R.BreakTemplateDeclarations && - ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas && + CatchMacros == R.CatchMacros && ColumnLimit == R.ColumnLimit && + CommentPragmas == R.CommentPragmas && CompactNamespaces == R.CompactNamespaces && ConstructorInitializerIndentWidth == R.ConstructorInitializerIndentWidth && @@ -5935,8 +5984,9 @@ struct FormatStyle { R.TableGenBreakingDAGArgOperators && TableGenBreakInsideDAGArg == R.TableGenBreakInsideDAGArg && TabWidth == R.TabWidth && TemplateNames == R.TemplateNames && - TypeNames == R.TypeNames && TypenameMacros == R.TypenameMacros && - UseTab == R.UseTab && VariableTemplates == R.VariableTemplates && + TryMacros == R.TryMacros && TypeNames == R.TypeNames && + TypenameMacros == R.TypenameMacros && UseTab == R.UseTab && + VariableTemplates == R.VariableTemplates && VerilogBreakBetweenInstancePorts == R.VerilogBreakBetweenInstancePorts && WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 2f67ec86b101a..2969d938ccbd7 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1242,6 +1242,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals); IO.mapOptional("BreakTemplateDeclarations", Style.BreakTemplateDeclarations); + IO.mapOptional("CatchMacros", Style.CatchMacros); IO.mapOptional("ColumnLimit", Style.ColumnLimit); IO.mapOptional("CommentPragmas", Style.CommentPragmas); IO.mapOptional("CompactNamespaces", Style.CompactNamespaces); @@ -1405,6 +1406,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("TemplateNames", Style.TemplateNames); IO.mapOptional("TypeNames", Style.TypeNames); + IO.mapOptional("TryMacros", Style.TryMacros); IO.mapOptional("TypenameMacros", Style.TypenameMacros); IO.mapOptional("UseTab", Style.UseTab); IO.mapOptional("VariableTemplates", Style.VariableTemplates); diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 56abd702aaafe..454dbb747dffc 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -44,6 +44,7 @@ namespace format { /* The colon at the end of a case label. */ \ TYPE(CaseLabelColon) \ TYPE(CastRParen) \ + TYPE(CatchMacro) \ TYPE(ClassLBrace) \ /* Name of class/struct/union/interface definition. */ \ TYPE(ClassHeadName) \ @@ -196,6 +197,7 @@ namespace format { TYPE(TrailingAnnotation) \ TYPE(TrailingReturnArrow) \ TYPE(TrailingUnaryOperator) \ + TYPE(TryMacro) \ TYPE(TypeDeclarationParen) \ TYPE(TemplateName) \ TYPE(TypeName) \ diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 4a087d9e6dc2b..83d1a2008a4a0 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -63,6 +63,14 @@ FormatTokenLexer::FormatTokenLexer( auto Identifier = &IdentTable.get(NamespaceMacro); Macros.insert({Identifier, TT_NamespaceMacro}); } + for (const std::string &TryMacro : Style.TryMacros) { + auto Identifier = &IdentTable.get(TryMacro); + Macros.insert({Identifier, TT_TryMacro}); + } + for (const std::string &CatchMacro : Style.CatchMacros) { + auto Identifier = &IdentTable.get(CatchMacro); + Macros.insert({Identifier, TT_CatchMacro}); + } for (const std::string &WhitespaceSensitiveMacro : Style.WhitespaceSensitiveMacros) { auto Identifier = &IdentTable.get(WhitespaceSensitiveMacro); @@ -539,6 +547,12 @@ bool FormatTokenLexer::tryTransformTryUsageForC() { auto &Try = *(Tokens.end() - 2); if (Try->isNot(tok::kw_try)) return false; + // Don't reset TryMacros back to identifier — they legitimately use kw_try + // even when followed by '(' (for macro arguments like JSG_TRY(js)). If a + // configured TryMacro name is also used as a plain identifier somewhere, that + // is a user configuration error, consistent with IfMacros/ForEachMacros. + if (Try->is(TT_TryMacro)) + return false; auto &Next = *(Tokens.end() - 1); if (Next->isOneOf(tok::l_brace, tok::colon, tok::hash, tok::comment)) return false; @@ -1471,6 +1485,10 @@ FormatToken *FormatTokenLexer::getNextToken() { // the tok value seems to be needed. Not sure if there's a more elegant // way. FormatTok->Tok.setKind(tok::kw_if); + } else if (it->second == TT_TryMacro) { + FormatTok->Tok.setKind(tok::kw_try); + } else if (it->second == TT_CatchMacro) { + FormatTok->Tok.setKind(tok::kw_catch); } } else if (FormatTok->is(tok::identifier)) { if (MacroBlockBeginRegex.match(Text)) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 34e81bbc97578..0e31c300db058 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2105,13 +2105,14 @@ class AnnotatingParser { if (!CurrentToken->isTypeFinalized() && CurrentToken->isNoneOf( TT_LambdaLSquare, TT_LambdaLBrace, TT_AttributeMacro, TT_IfMacro, - TT_ForEachMacro, TT_TypenameMacro, TT_FunctionLBrace, - TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_FatArrow, - TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator, - TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral, - TT_UntouchableMacroFunc, TT_StatementAttributeLikeMacro, - TT_FunctionLikeOrFreestandingMacro, TT_ClassLBrace, TT_EnumLBrace, - TT_RecordLBrace, TT_StructLBrace, TT_UnionLBrace, TT_RequiresClause, + TT_ForEachMacro, TT_TypenameMacro, TT_TryMacro, TT_CatchMacro, + TT_FunctionLBrace, TT_ImplicitStringLiteral, TT_InlineASMBrace, + TT_FatArrow, TT_LambdaArrow, TT_NamespaceMacro, + TT_OverloadedOperator, TT_RegexLiteral, TT_TemplateString, + TT_ObjCStringLiteral, TT_UntouchableMacroFunc, + TT_StatementAttributeLikeMacro, TT_FunctionLikeOrFreestandingMacro, + TT_ClassLBrace, TT_EnumLBrace, TT_RecordLBrace, TT_StructLBrace, + TT_UnionLBrace, TT_RequiresClause, TT_RequiresClauseInARequiresExpression, TT_RequiresExpression, TT_RequiresExpressionLParen, TT_RequiresExpressionLBrace, TT_CompoundRequirementLBrace, TT_BracedListLBrace, @@ -5065,6 +5066,11 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, const bool IsVerilog = Style.isVerilog(); assert(!IsVerilog || !IsCpp); + // TryMacros/CatchMacros should look like macro invocations, not keywords. + // Don't add a space before their argument list. + if (Left.isOneOf(TT_TryMacro, TT_CatchMacro) && Right.is(tok::l_paren)) + return false; + // Never ever merge two words. if (Keywords.isWordLike(Right, IsVerilog) && Keywords.isWordLike(Left, IsVerilog)) { diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index f57ef1328eac7..7a5ef2bee5f34 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -3059,7 +3059,11 @@ FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind, void UnwrappedLineParser::parseTryCatch() { assert(FormatTok->isOneOf(tok::kw_try, tok::kw___try) && "'try' expected"); + bool IsTryMacro = FormatTok->is(TT_TryMacro); nextToken(); + // If the try keyword was a macro like JSG_TRY(js), skip its arguments. + if (IsTryMacro && FormatTok->is(tok::l_paren)) + parseParens(); bool NeedsUnwrappedLine = false; bool HasCtorInitializer = false; if (FormatTok->is(tok::colon)) { diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index f270602f32604..e0bbf082eb88c 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -1031,6 +1031,18 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE_LIST(TypenameMacros); CHECK_PARSE_LIST(VariableTemplates); + Style.TryMacros.clear(); + CHECK_PARSE("TryMacros: [KJ_TRY]", TryMacros, + std::vector<std::string>{"KJ_TRY"}); + CHECK_PARSE("TryMacros: [KJ_TRY, JSG_TRY]", TryMacros, + std::vector<std::string>({"KJ_TRY", "JSG_TRY"})); + + Style.CatchMacros.clear(); + CHECK_PARSE("CatchMacros: [KJ_CATCH]", CatchMacros, + std::vector<std::string>{"KJ_CATCH"}); + CHECK_PARSE("CatchMacros: [KJ_CATCH, JSG_CATCH]", CatchMacros, + std::vector<std::string>({"KJ_CATCH", "JSG_CATCH"})); + Style.WhitespaceSensitiveMacros.clear(); CHECK_PARSE("WhitespaceSensitiveMacros: [STRINGIZE]", WhitespaceSensitiveMacros, std::vector<std::string>{"STRINGIZE"}); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 43633b582a8ab..cc23c4c78916c 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -4895,6 +4895,188 @@ TEST_F(FormatTest, FormatTryCatch) { verifyIncompleteFormat("try {} catch ("); } +TEST_F(FormatTest, TryMacros) { + FormatStyle Style = getLLVMStyle(); + Style.TryMacros.push_back("KJ_TRY"); + Style.TryMacros.push_back("JSG_TRY"); + Style.CatchMacros.push_back("KJ_CATCH"); + Style.CatchMacros.push_back("JSG_CATCH"); + + // Basic try-catch macro formatting (catch attaches to closing brace). + // No space before '(' — macros look like macro invocations. + verifyFormat("KJ_TRY {\n" + " doStuff();\n" + "} KJ_CATCH(e) {\n" + " handleError();\n" + "}", + Style); + + // Try macro with arguments (like JSG_TRY(js)). + verifyFormat("JSG_TRY(js) {\n" + " doStuff();\n" + "} JSG_CATCH(e) {\n" + " handleError();\n" + "}", + Style); + + // Variadic catch macro (like JSG_CATCH(exception, ...)). + verifyFormat("JSG_TRY(js) {\n" + " doStuff();\n" + "} JSG_CATCH(e, js) {\n" + " handleError();\n" + "}", + Style); + + // Catch macro without arguments. + verifyFormat("KJ_TRY {\n" + " doStuff();\n" + "} KJ_CATCH {\n" + " handleError();\n" + "}", + Style); + + // Multiple catch blocks. + verifyFormat("KJ_TRY {\n" + " doStuff();\n" + "} KJ_CATCH(e1) {\n" + " handleError1();\n" + "} KJ_CATCH(e2) {\n" + " handleError2();\n" + "}", + Style); + + // With BeforeCatch = true, catch goes on its own line. + FormatStyle WrappedStyle = Style; + WrappedStyle.BraceWrapping.BeforeCatch = true; + WrappedStyle.BreakBeforeBraces = FormatStyle::BS_Custom; + verifyFormat("KJ_TRY {\n" + " doStuff();\n" + "}\n" + "KJ_CATCH(e) {\n" + " handleError();\n" + "}", + WrappedStyle); + + // Native try/catch is unaffected by TryMacros/CatchMacros. + verifyFormat("try {\n" + " doStuff();\n" + "} catch (const std::exception &e) {\n" + " handleError();\n" + "}", + Style); + + // Try macro with arguments and BeforeCatch = true. + verifyFormat("JSG_TRY(js) {\n" + " doStuff();\n" + "}\n" + "JSG_CATCH(e, js) {\n" + " handleError();\n" + "}", + WrappedStyle); + + // Empty try-catch bodies. + verifyFormat("KJ_TRY {\n" + "} KJ_CATCH(e) {\n" + "}", + Style); + + // Nested try-catch macros. + verifyFormat("KJ_TRY {\n" + " KJ_TRY {\n" + " doStuff();\n" + " } KJ_CATCH(inner) {\n" + " handleInner();\n" + " }\n" + "} KJ_CATCH(outer) {\n" + " handleOuter();\n" + "}", + Style); + + // Comment between try macro and brace. + verifyFormat("KJ_TRY /* comment */ {\n" + "} KJ_CATCH(e) {\n" + "}", + Style); + + // Incomplete try-catch macro. + verifyIncompleteFormat("KJ_TRY {} KJ_CATCH(", Style); + + // Brace style: Stroustrup (break before catch). + FormatStyle Stroustrup = Style; + Stroustrup.BreakBeforeBraces = FormatStyle::BS_Stroustrup; + verifyFormat("KJ_TRY {\n" + " // something\n" + "}\n" + "KJ_CATCH(e) {\n" + " // something\n" + "}", + Stroustrup); + + // Brace style: Allman (braces on own lines). + FormatStyle Allman = Style; + Allman.BreakBeforeBraces = FormatStyle::BS_Allman; + verifyFormat("KJ_TRY\n" + "{\n" + " // something\n" + "}\n" + "KJ_CATCH(e)\n" + "{\n" + " // something\n" + "}", + Allman); + + // Brace style: Whitesmiths (indented braces). + FormatStyle Whitesmiths = Style; + Whitesmiths.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; + verifyFormat("KJ_TRY\n" + " {\n" + " // something\n" + " }\n" + "KJ_CATCH(e)\n" + " {\n" + " // something\n" + " }", + Whitesmiths); + + // Brace style: GNU (indented control statement braces). + FormatStyle GNU = Style; + GNU.BreakBeforeBraces = FormatStyle::BS_GNU; + verifyFormat("KJ_TRY\n" + " {\n" + " // something\n" + " }\n" + "KJ_CATCH(e)\n" + " {\n" + " // something\n" + " }", + GNU); + + // SpaceBeforeParens: ControlStatements — macros still get no space. + FormatStyle SpaceCtrl = Style; + SpaceCtrl.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; + verifyFormat("KJ_TRY {\n" + "} KJ_CATCH(e) {\n" + "}", + SpaceCtrl); + verifyFormat("JSG_TRY(js) {\n" + "} JSG_CATCH(e) {\n" + "}", + SpaceCtrl); + // Native catch still gets the space with ControlStatements. + verifyFormat("try {\n" + "} catch (...) {\n" + "}", + SpaceCtrl); + + // Input reformatting: poorly formatted input gets fixed. + verifyFormat("KJ_TRY {\n" + " foo();\n" + "} KJ_CATCH(e) {\n" + " bar();\n" + "}", + "KJ_TRY{foo();}KJ_CATCH(e){bar();}", Style); +} + TEST_F(FormatTest, FormatTryAsAVariable) { verifyFormat("int try;"); verifyFormat("int try, size;"); >From c7402cd181ff7d6ee93805d38e1aaa9d0a0f4a92 Mon Sep 17 00:00:00 2001 From: Harris Hancock <[email protected]> Date: Thu, 26 Feb 2026 15:09:30 +0000 Subject: [PATCH 2/5] fixup: address simple review feedback --- clang/docs/ReleaseNotes.rst | 4 +- clang/include/clang/Format/Format.h | 48 ++++++++++++------------ clang/lib/Format/Format.cpp | 2 +- clang/lib/Format/FormatTokenLexer.cpp | 8 +--- clang/lib/Format/UnwrappedLineParser.cpp | 2 +- 5 files changed, 29 insertions(+), 35 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 56599e18ea893..e76e9dedf7fc4 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -418,14 +418,14 @@ AST Matchers clang-format ------------ -- Add ``TryMacros`` and ``CatchMacros`` options to allow macros to be formatted - as ``try`` and ``catch`` blocks, including catch-cuddling. - Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the '-'/'+' and the return type in Objective-C method declarations - Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking constructor initializers after commas, keeping the colon on the same line. - Extend ``BreakBinaryOperations`` to accept a structured configuration with per-operator break rules and minimum chain length gating via ``PerOperator``. +- Add ``TryMacros`` and ``CatchMacros`` options to allow macros to be formatted + as ``try`` and ``catch`` blocks, including catch-cuddling. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d7a4ec79cc2a3..a3cd791099eca 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -5629,6 +5629,30 @@ struct FormatStyle { /// \version 20 std::vector<std::string> TemplateNames; + /// A vector of macros that should be interpreted as try blocks + /// instead of as function calls. + /// + /// These are expected to be macros of the form: + /// \code + /// TRY_MACRO(...) { + /// ... + /// } CATCH_MACRO(...) { + /// ... + /// } + /// \endcode + /// + /// The parenthesized arguments are optional for both try and catch macros. + /// + /// In the .clang-format configuration file, this can be configured like: + /// \code{.yaml} + /// TryMacros: [TRY_MACRO] + /// \endcode + /// + /// For example: `KJ_TRY + /// <https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#throwing-and-catching-exceptions>`_ + /// \version 23 + std::vector<std::string> TryMacros; + /// A vector of non-keyword identifiers that should be interpreted as type /// names. /// @@ -5656,30 +5680,6 @@ struct FormatStyle { /// \version 9 std::vector<std::string> TypenameMacros; - /// A vector of macros that should be interpreted as try blocks - /// instead of as function calls. - /// - /// These are expected to be macros of the form: - /// \code - /// TRY_MACRO(...) { - /// ... - /// } CATCH_MACRO(...) { - /// ... - /// } - /// \endcode - /// - /// The parenthesized arguments are optional for both try and catch macros. - /// - /// In the .clang-format configuration file, this can be configured like: - /// \code{.yaml} - /// TryMacros: [TRY_MACRO] - /// \endcode - /// - /// For example: `KJ_TRY - /// <https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#throwing-and-catching-exceptions>`_ - /// \version 23 - std::vector<std::string> TryMacros; - /// This option is **deprecated**. See ``LF`` and ``CRLF`` of ``LineEnding``. /// \version 10 // bool UseCRLF; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 2969d938ccbd7..2b0b76accacac 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1405,8 +1405,8 @@ template <> struct MappingTraits<FormatStyle> { Style.TableGenBreakInsideDAGArg); IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("TemplateNames", Style.TemplateNames); - IO.mapOptional("TypeNames", Style.TypeNames); IO.mapOptional("TryMacros", Style.TryMacros); + IO.mapOptional("TypeNames", Style.TypeNames); IO.mapOptional("TypenameMacros", Style.TypenameMacros); IO.mapOptional("UseTab", Style.UseTab); IO.mapOptional("VariableTemplates", Style.VariableTemplates); diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 83d1a2008a4a0..19c74a29803e4 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -545,13 +545,7 @@ bool FormatTokenLexer::tryTransformTryUsageForC() { if (Tokens.size() < 2) return false; auto &Try = *(Tokens.end() - 2); - if (Try->isNot(tok::kw_try)) - return false; - // Don't reset TryMacros back to identifier — they legitimately use kw_try - // even when followed by '(' (for macro arguments like JSG_TRY(js)). If a - // configured TryMacro name is also used as a plain identifier somewhere, that - // is a user configuration error, consistent with IfMacros/ForEachMacros. - if (Try->is(TT_TryMacro)) + if (Try->isNot(tok::kw_try) || Try->is(TT_TryMacro)) return false; auto &Next = *(Tokens.end() - 1); if (Next->isOneOf(tok::l_brace, tok::colon, tok::hash, tok::comment)) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 7a5ef2bee5f34..016ef0ceecaa0 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -3059,7 +3059,7 @@ FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind, void UnwrappedLineParser::parseTryCatch() { assert(FormatTok->isOneOf(tok::kw_try, tok::kw___try) && "'try' expected"); - bool IsTryMacro = FormatTok->is(TT_TryMacro); + const bool IsTryMacro = FormatTok->is(TT_TryMacro); nextToken(); // If the try keyword was a macro like JSG_TRY(js), skip its arguments. if (IsTryMacro && FormatTok->is(tok::l_paren)) >From d18634f88f673e027d4872a1c238b865fedd6e73 Mon Sep 17 00:00:00 2001 From: Harris Hancock <[email protected]> Date: Thu, 26 Feb 2026 15:10:39 +0000 Subject: [PATCH 3/5] fixup: support SpaceBeforeParens --- clang/docs/ClangFormatStyleOptions.rst | 16 ++++++++++++++ clang/include/clang/Format/Format.h | 25 ++++++++++++++++++---- clang/lib/Format/Format.cpp | 6 ++++++ clang/lib/Format/TokenAnnotator.cpp | 13 ++++++----- clang/unittests/Format/ConfigParseTest.cpp | 2 ++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index ad51339c8673a..0d6b011646a0b 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -6819,6 +6819,14 @@ the configuration (without a prefix: ``Auto``). AfterControlStatements: true AfterFunctionDefinitionName: true + * ``bool AfterCatchMacros`` If ``true``, put space between catch macros and opening parentheses. + + .. code-block:: c++ + + true: false: + CATCH (...) vs. CATCH(...) + <catch-body> <catch-body> + * ``bool AfterControlStatements`` If ``true``, put space between control statement keywords (for/if/while...) and opening parentheses. @@ -6906,6 +6914,14 @@ the configuration (without a prefix: ``Auto``). ... ... } } + * ``bool AfterTryMacros`` If ``true``, put space between try macros and opening parentheses. + + .. code-block:: c++ + + true: false: + TRY (...) vs. TRY(...) + <try-body> <try-body> + * ``bool BeforeNonEmptyParentheses`` If ``true``, put a space before opening parentheses only if the parentheses are not empty. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index a3cd791099eca..bf28b1e6dfdcd 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -5068,6 +5068,13 @@ struct FormatStyle { /// AfterFunctionDefinitionName: true /// \endcode struct SpaceBeforeParensCustom { + /// If ``true``, put space between catch macros and opening parentheses. + /// \code + /// true: false: + /// CATCH (...) vs. CATCH(...) + /// <catch-body> <catch-body> + /// \endcode + bool AfterCatchMacros; /// If ``true``, put space between control statement keywords /// (for/if/while...) and opening parentheses. /// \code @@ -5145,6 +5152,13 @@ struct FormatStyle { /// } } /// \endcode bool AfterRequiresInExpression; + /// If ``true``, put space between try macros and opening parentheses. + /// \code + /// true: false: + /// TRY (...) vs. TRY(...) + /// <try-body> <try-body> + /// \endcode + bool AfterTryMacros; /// If ``true``, put a space before opening parentheses only if the /// parentheses are not empty. /// \code @@ -5155,15 +5169,17 @@ struct FormatStyle { bool BeforeNonEmptyParentheses; SpaceBeforeParensCustom() - : AfterControlStatements(false), AfterForeachMacros(false), - AfterFunctionDeclarationName(false), + : AfterCatchMacros(false), AfterControlStatements(false), + AfterForeachMacros(false), AfterFunctionDeclarationName(false), AfterFunctionDefinitionName(false), AfterIfMacros(false), AfterNot(false), AfterOverloadedOperator(false), AfterPlacementOperator(true), AfterRequiresInClause(false), - AfterRequiresInExpression(false), BeforeNonEmptyParentheses(false) {} + AfterRequiresInExpression(false), AfterTryMacros(false), + BeforeNonEmptyParentheses(false) {} bool operator==(const SpaceBeforeParensCustom &Other) const { - return AfterControlStatements == Other.AfterControlStatements && + return AfterCatchMacros == Other.AfterCatchMacros && + AfterControlStatements == Other.AfterControlStatements && AfterForeachMacros == Other.AfterForeachMacros && AfterFunctionDeclarationName == Other.AfterFunctionDeclarationName && @@ -5174,6 +5190,7 @@ struct FormatStyle { AfterPlacementOperator == Other.AfterPlacementOperator && AfterRequiresInClause == Other.AfterRequiresInClause && AfterRequiresInExpression == Other.AfterRequiresInExpression && + AfterTryMacros == Other.AfterTryMacros && BeforeNonEmptyParentheses == Other.BeforeNonEmptyParentheses; } }; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 2b0b76accacac..d49d3202506da 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -831,6 +831,7 @@ struct ScalarEnumerationTraits<FormatStyle::SpaceAroundPointerQualifiersStyle> { template <> struct MappingTraits<FormatStyle::SpaceBeforeParensCustom> { static void mapping(IO &IO, FormatStyle::SpaceBeforeParensCustom &Spacing) { + IO.mapOptional("AfterCatchMacros", Spacing.AfterCatchMacros); IO.mapOptional("AfterControlStatements", Spacing.AfterControlStatements); IO.mapOptional("AfterForeachMacros", Spacing.AfterForeachMacros); IO.mapOptional("AfterFunctionDefinitionName", @@ -844,6 +845,7 @@ template <> struct MappingTraits<FormatStyle::SpaceBeforeParensCustom> { IO.mapOptional("AfterRequiresInClause", Spacing.AfterRequiresInClause); IO.mapOptional("AfterRequiresInExpression", Spacing.AfterRequiresInExpression); + IO.mapOptional("AfterTryMacros", Spacing.AfterTryMacros); IO.mapOptional("BeforeNonEmptyParentheses", Spacing.BeforeNonEmptyParentheses); } @@ -1680,9 +1682,11 @@ static void expandPresetsSpaceBeforeParens(FormatStyle &Expanded) { switch (Expanded.SpaceBeforeParens) { case FormatStyle::SBPO_ControlStatements: + Expanded.SpaceBeforeParensOptions.AfterCatchMacros = true; Expanded.SpaceBeforeParensOptions.AfterControlStatements = true; Expanded.SpaceBeforeParensOptions.AfterForeachMacros = true; Expanded.SpaceBeforeParensOptions.AfterIfMacros = true; + Expanded.SpaceBeforeParensOptions.AfterTryMacros = true; break; case FormatStyle::SBPO_ControlStatementsExceptControlMacros: Expanded.SpaceBeforeParensOptions.AfterControlStatements = true; @@ -1885,9 +1889,11 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.SpaceBeforeJsonColon = false; LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; LLVMStyle.SpaceBeforeParensOptions = {}; + LLVMStyle.SpaceBeforeParensOptions.AfterCatchMacros = true; LLVMStyle.SpaceBeforeParensOptions.AfterControlStatements = true; LLVMStyle.SpaceBeforeParensOptions.AfterForeachMacros = true; LLVMStyle.SpaceBeforeParensOptions.AfterIfMacros = true; + LLVMStyle.SpaceBeforeParensOptions.AfterTryMacros = true; LLVMStyle.SpaceBeforeRangeBasedForLoopColon = true; LLVMStyle.SpaceBeforeSquareBrackets = false; LLVMStyle.SpaceInEmptyBraces = FormatStyle::SIEB_Never; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 0e31c300db058..10e84d9a193ad 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4935,6 +4935,14 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, return Style.SpaceBeforeParensOptions.AfterIfMacros || spaceRequiredBeforeParens(Right); } + if (Left.is(TT_TryMacro)) { + return Style.SpaceBeforeParensOptions.AfterTryMacros || + spaceRequiredBeforeParens(Right); + } + if (Left.is(TT_CatchMacro)) { + return Style.SpaceBeforeParensOptions.AfterCatchMacros || + spaceRequiredBeforeParens(Right); + } if (Style.SpaceBeforeParens == FormatStyle::SBPO_Custom && Left.isPlacementOperator() && Right.isNot(TT_OverloadedOperatorLParen) && @@ -5066,11 +5074,6 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, const bool IsVerilog = Style.isVerilog(); assert(!IsVerilog || !IsCpp); - // TryMacros/CatchMacros should look like macro invocations, not keywords. - // Don't add a space before their argument list. - if (Left.isOneOf(TT_TryMacro, TT_CatchMacro) && Right.is(tok::l_paren)) - return false; - // Never ever merge two words. if (Keywords.isWordLike(Right, IsVerilog) && Keywords.isWordLike(Left, IsVerilog)) { diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index e0bbf082eb88c..d24b08f493911 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -252,6 +252,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtEndOfFile); CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtStartOfBlock); CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtStartOfFile); + CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterCatchMacros); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterControlStatements); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterForeachMacros); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, @@ -262,6 +263,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterNot); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterOverloadedOperator); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterPlacementOperator); + CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterTryMacros); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, BeforeNonEmptyParentheses); CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, ExceptDoubleParentheses); CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InCStyleCasts); >From 1c7ecc65fc6d0701f06a76a15eab956ee3cc7f3e Mon Sep 17 00:00:00 2001 From: Harris Hancock <[email protected]> Date: Thu, 26 Feb 2026 17:05:29 +0000 Subject: [PATCH 4/5] fixup: revise tests and add SpaceBeforeParens tests --- clang/unittests/Format/FormatTest.cpp | 145 ++++++++------------------ 1 file changed, 43 insertions(+), 102 deletions(-) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index cc23c4c78916c..afbc2f3ba7a2b 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -4902,32 +4902,27 @@ TEST_F(FormatTest, TryMacros) { Style.CatchMacros.push_back("KJ_CATCH"); Style.CatchMacros.push_back("JSG_CATCH"); - // Basic try-catch macro formatting (catch attaches to closing brace). - // No space before '(' — macros look like macro invocations. verifyFormat("KJ_TRY {\n" " doStuff();\n" - "} KJ_CATCH(e) {\n" + "} KJ_CATCH (e) {\n" " handleError();\n" "}", Style); - // Try macro with arguments (like JSG_TRY(js)). - verifyFormat("JSG_TRY(js) {\n" + verifyFormat("JSG_TRY (js) {\n" " doStuff();\n" - "} JSG_CATCH(e) {\n" + "} JSG_CATCH (e) {\n" " handleError();\n" "}", Style); - // Variadic catch macro (like JSG_CATCH(exception, ...)). - verifyFormat("JSG_TRY(js) {\n" + verifyFormat("JSG_TRY (js) {\n" " doStuff();\n" - "} JSG_CATCH(e, js) {\n" + "} JSG_CATCH (e, js) {\n" " handleError();\n" "}", Style); - // Catch macro without arguments. verifyFormat("KJ_TRY {\n" " doStuff();\n" "} KJ_CATCH {\n" @@ -4935,29 +4930,15 @@ TEST_F(FormatTest, TryMacros) { "}", Style); - // Multiple catch blocks. verifyFormat("KJ_TRY {\n" " doStuff();\n" - "} KJ_CATCH(e1) {\n" + "} KJ_CATCH (e1) {\n" " handleError1();\n" - "} KJ_CATCH(e2) {\n" + "} KJ_CATCH (e2) {\n" " handleError2();\n" "}", Style); - // With BeforeCatch = true, catch goes on its own line. - FormatStyle WrappedStyle = Style; - WrappedStyle.BraceWrapping.BeforeCatch = true; - WrappedStyle.BreakBeforeBraces = FormatStyle::BS_Custom; - verifyFormat("KJ_TRY {\n" - " doStuff();\n" - "}\n" - "KJ_CATCH(e) {\n" - " handleError();\n" - "}", - WrappedStyle); - - // Native try/catch is unaffected by TryMacros/CatchMacros. verifyFormat("try {\n" " doStuff();\n" "} catch (const std::exception &e) {\n" @@ -4965,116 +4946,76 @@ TEST_F(FormatTest, TryMacros) { "}", Style); - // Try macro with arguments and BeforeCatch = true. - verifyFormat("JSG_TRY(js) {\n" - " doStuff();\n" - "}\n" - "JSG_CATCH(e, js) {\n" - " handleError();\n" - "}", - WrappedStyle); - - // Empty try-catch bodies. verifyFormat("KJ_TRY {\n" - "} KJ_CATCH(e) {\n" + "} KJ_CATCH (e) {\n" "}", Style); - // Nested try-catch macros. verifyFormat("KJ_TRY {\n" " KJ_TRY {\n" " doStuff();\n" - " } KJ_CATCH(inner) {\n" + " } KJ_CATCH (inner) {\n" " handleInner();\n" " }\n" - "} KJ_CATCH(outer) {\n" + "} KJ_CATCH (outer) {\n" " handleOuter();\n" "}", Style); - // Comment between try macro and brace. verifyFormat("KJ_TRY /* comment */ {\n" - "} KJ_CATCH(e) {\n" + "} KJ_CATCH (e) {\n" "}", Style); - // Incomplete try-catch macro. verifyIncompleteFormat("KJ_TRY {} KJ_CATCH(", Style); - // Brace style: Stroustrup (break before catch). - FormatStyle Stroustrup = Style; - Stroustrup.BreakBeforeBraces = FormatStyle::BS_Stroustrup; + FormatStyle WrappedStyle = Style; + WrappedStyle.BraceWrapping.BeforeCatch = true; + WrappedStyle.BreakBeforeBraces = FormatStyle::BS_Custom; verifyFormat("KJ_TRY {\n" - " // something\n" + " doStuff();\n" "}\n" - "KJ_CATCH(e) {\n" - " // something\n" + "KJ_CATCH (e) {\n" + " handleError();\n" "}", - Stroustrup); + WrappedStyle); - // Brace style: Allman (braces on own lines). - FormatStyle Allman = Style; - Allman.BreakBeforeBraces = FormatStyle::BS_Allman; - verifyFormat("KJ_TRY\n" - "{\n" - " // something\n" + verifyFormat("JSG_TRY (js) {\n" + " doStuff();\n" "}\n" - "KJ_CATCH(e)\n" - "{\n" - " // something\n" + "JSG_CATCH (e, js) {\n" + " handleError();\n" "}", - Allman); - - // Brace style: Whitesmiths (indented braces). - FormatStyle Whitesmiths = Style; - Whitesmiths.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; - verifyFormat("KJ_TRY\n" - " {\n" - " // something\n" - " }\n" - "KJ_CATCH(e)\n" - " {\n" - " // something\n" - " }", - Whitesmiths); - - // Brace style: GNU (indented control statement braces). - FormatStyle GNU = Style; - GNU.BreakBeforeBraces = FormatStyle::BS_GNU; - verifyFormat("KJ_TRY\n" - " {\n" - " // something\n" - " }\n" - "KJ_CATCH(e)\n" - " {\n" - " // something\n" - " }", - GNU); + WrappedStyle); - // SpaceBeforeParens: ControlStatements — macros still get no space. - FormatStyle SpaceCtrl = Style; - SpaceCtrl.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; + FormatStyle ExceptMacros = Style; + ExceptMacros.SpaceBeforeParens = + FormatStyle::SBPO_ControlStatementsExceptControlMacros; verifyFormat("KJ_TRY {\n" "} KJ_CATCH(e) {\n" "}", - SpaceCtrl); - verifyFormat("JSG_TRY(js) {\n" - "} JSG_CATCH(e) {\n" - "}", - SpaceCtrl); - // Native catch still gets the space with ControlStatements. + ExceptMacros); verifyFormat("try {\n" "} catch (...) {\n" "}", - SpaceCtrl); + ExceptMacros); - // Input reformatting: poorly formatted input gets fixed. - verifyFormat("KJ_TRY {\n" - " foo();\n" - "} KJ_CATCH(e) {\n" - " bar();\n" + FormatStyle CustomSpace = Style; + CustomSpace.SpaceBeforeParens = FormatStyle::SBPO_Custom; + CustomSpace.SpaceBeforeParensOptions = {}; + CustomSpace.SpaceBeforeParensOptions.AfterTryMacros = true; + CustomSpace.SpaceBeforeParensOptions.AfterCatchMacros = false; + verifyFormat("JSG_TRY (js) {\n" + "} JSG_CATCH(e) {\n" + "}", + CustomSpace); + + CustomSpace.SpaceBeforeParensOptions.AfterTryMacros = false; + CustomSpace.SpaceBeforeParensOptions.AfterCatchMacros = true; + verifyFormat("JSG_TRY(js) {\n" + "} JSG_CATCH (e) {\n" "}", - "KJ_TRY{foo();}KJ_CATCH(e){bar();}", Style); + CustomSpace); } TEST_F(FormatTest, FormatTryAsAVariable) { >From 855e17def16b712dd5c51205bc94bf2a52eb709b Mon Sep 17 00:00:00 2001 From: Harris Hancock <[email protected]> Date: Thu, 26 Feb 2026 17:08:31 +0000 Subject: [PATCH 5/5] fixup: add TokenAnnotator test for TryMacros / CatchMacros --- clang/unittests/Format/TokenAnnotatorTest.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index defd78aedfd70..c3f93a11e8078 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -4133,6 +4133,23 @@ TEST_F(TokenAnnotatorTest, TypenameMacro) { EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_Unknown); } +TEST_F(TokenAnnotatorTest, UnderstandsTryCatchMacros) { + auto Style = getLLVMStyle(); + Style.TryMacros.push_back("KJ_TRY"); + Style.CatchMacros.push_back("KJ_CATCH"); + + auto Tokens = annotate("KJ_TRY {} KJ_CATCH(e) {}", Style); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::kw_try, TT_TryMacro); + EXPECT_TOKEN(Tokens[3], tok::kw_catch, TT_CatchMacro); + + Tokens = annotate("KJ_TRY(js) {} KJ_CATCH(e) {}", Style); + ASSERT_EQ(Tokens.size(), 13u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::kw_try, TT_TryMacro); + EXPECT_TOKEN(Tokens[6], tok::kw_catch, TT_CatchMacro); + EXPECT_TOKEN(Tokens[10], tok::l_brace, TT_ControlStatementLBrace); +} + TEST_F(TokenAnnotatorTest, GNULanguageStandard) { auto Style = getGNUStyle(); EXPECT_EQ(Style.Standard, FormatStyle::LS_Latest); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
