Author: Harris Hancock Date: 2026-03-17T20:46:17+01:00 New Revision: 2ef41cca149cf133683cd70f6002b3d885842356
URL: https://github.com/llvm/llvm-project/commit/2ef41cca149cf133683cd70f6002b3d885842356 DIFF: https://github.com/llvm/llvm-project/commit/2ef41cca149cf133683cd70f6002b3d885842356.diff LOG: [clang-format] Fix Macros configuration not working with try/catch expansions (#184891) This is a superseding followup to my previous PR, https://github.com/llvm/llvm-project/pull/183352. In my previous PR, I proposed adding TryMacros and CatchMacros configuration options, similar in spirit to IfMacros and ForEachMacros. I did so because I noticed that configuration like `Macros=["TRY_MACRO=try", "CATCH_MACRO(e)=catch(e)]` did not format configured macro(s) as try/catch blocks. @owenca confirmed in my previous PR that this observed behavior is undesired, and we should prefer to fix it rather than introduce new features. This PR proposes a fix, described in detail in the commit message below the break. In general terms, it deletes a heuristic from the lexing phase, where it interacted poorly with the Macros option, and moves its functionality to the parsing phase instead. I describe a possibly cleaner fix in [a comment here](https://github.com/llvm/llvm-project/pull/183352#issuecomment-3992773126), but it has the disadvantage of unintended behavior changes for Objective-C code using `try` as an identifier. The fix in this PR avoids that unintended behavior change; the only behavior change is the bugfix itself. cc @HazardyKnusperkeks as previous reviewer, and @mydeveloperday as the heuristic's original author. --- The lexer heuristic `tryTransformTryUsageForC()` was intended to allow C code to use `try` as an identifier by demoting `tok::kw_try` to `tok::identifier` when the following token did not look like the start of a try body. However, when `MacroExpander::parseDefinition()` lexed a macro like `"TRY_MACRO=try"`, the `try` token in the expansion body was followed by `eof`, triggering the heuristic. This caused `try` to be demoted to an identifier in the macro definition, so expanded code was never parsed as a try/catch statement. Delete `tryTransformTryUsageForC()` and instead guard the two `case tok::kw_try:` dispatch sites in `UnwrappedLineParser::parseStructuralElement()`. The guard is restricted to `Style.isCpp()` (which covers C, C++, and Objective-C) and checks whether the next non-comment token is `{`, `:`, or `#` -- the tokens that can legitimately begin a try body or precede one. The old heuristic also had to check for a preceding `@` token to avoid demoting `try` in Objective-C `@try` constructs. The parser-level guard does not need this check because `@try` is routed through `case tok::at` and dispatched via `getObjCKeywordID()` to `tok::objc_try`, which calls `parseTryCatch()` directly. The bare `kw_try` token never reaches `case tok::kw_try` when parsing `@try`. Assisted-by: Claude (anthropic.com) Added: Modified: clang/lib/Format/FormatTokenLexer.cpp clang/lib/Format/FormatTokenLexer.h clang/unittests/Format/FormatTest.cpp Removed: ################################################################################ diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 4a087d9e6dc2b..0dc6f776aeca0 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -163,8 +163,6 @@ void FormatTokenLexer::tryMergePreviousTokens() { return; if (tryMergeForEach()) return; - if (Style.isCpp() && tryTransformTryUsageForC()) - return; if ((Style.Language == FormatStyle::LK_Cpp || Style.Language == FormatStyle::LK_ObjC) && @@ -533,26 +531,6 @@ bool FormatTokenLexer::tryMergeForEach() { return true; } -bool FormatTokenLexer::tryTransformTryUsageForC() { - if (Tokens.size() < 2) - return false; - auto &Try = *(Tokens.end() - 2); - if (Try->isNot(tok::kw_try)) - return false; - auto &Next = *(Tokens.end() - 1); - if (Next->isOneOf(tok::l_brace, tok::colon, tok::hash, tok::comment)) - return false; - - if (Tokens.size() > 2) { - auto &At = *(Tokens.end() - 3); - if (At->is(tok::at)) - return false; - } - - Try->Tok.setKind(tok::identifier); - return true; -} - bool FormatTokenLexer::tryMergeLessLess() { // Merge X,less,less,Y into X,lessless,Y unless X or Y is less. if (Tokens.size() < 3) diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h index 57c572af3defd..4141e1434f72f 100644 --- a/clang/lib/Format/FormatTokenLexer.h +++ b/clang/lib/Format/FormatTokenLexer.h @@ -56,7 +56,6 @@ class FormatTokenLexer { bool tryMergeNullishCoalescingEqual(); bool tryTransformCSharpForEach(); bool tryMergeForEach(); - bool tryTransformTryUsageForC(); // Merge the most recently lexed tokens into a single token if their kinds are // correct. diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 4be9b3ea42930..db01af9593b1a 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -5008,9 +5008,11 @@ TEST_F(FormatTest, FormatTryCatch) { } TEST_F(FormatTest, FormatTryAsAVariable) { - verifyFormat("int try;"); - verifyFormat("int try, size;"); - verifyFormat("try = foo();"); + auto Style = getLLVMStyle(FormatStyle::LK_C); + verifyFormat("int try;", Style); + verifyFormat("int try, size;", Style); + verifyFormat("try = foo();", Style); + verifyFormat("if (try < size) {\n return true;\n}"); verifyFormat("int catch;"); @@ -5018,7 +5020,7 @@ TEST_F(FormatTest, FormatTryAsAVariable) { verifyFormat("catch = foo();"); verifyFormat("if (catch < size) {\n return true;\n}"); - FormatStyle Style = getLLVMStyle(); + Style.Language = FormatStyle::LK_Cpp; Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterFunction = true; Style.BraceWrapping.BeforeCatch = true; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
