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

Reply via email to