https://github.com/r4nt created https://github.com/llvm/llvm-project/pull/234527
In UnwrappedLineParser::parseFile(), TextProto and Json inputs are parsed by calling parseBracedList() directly instead of parseLevel(). Previously, parseBracedList() returned early before eof() in two cases: 1. Encountering a semicolon (even though TextFormat protobufs allow ';' as an optional field separator), which returned false without consuming the token and unwound all enclosing parseBracedList() calls. 2. Encountering an unmatched closing '}' (e.g. in partial R"pb(...)pb" raw string fragments concatenated via StrCat), which returned after consuming '}' and left any remaining tokens unconsumed. Both cases triggered the assert(eof()) in UnwrappedLineParser::parse(). >From 348466213df29c0862cc7001984854ee3447ba10 Mon Sep 17 00:00:00 2001 From: Manuel Klimek <[email protected]> Date: Wed, 21 Oct 2026 21:47:55 +0200 Subject: [PATCH] [clang-format] Avoid crash on semicolons and unmatched braces in TextProto In UnwrappedLineParser::parseFile(), TextProto and Json inputs are parsed by calling parseBracedList() directly instead of parseLevel(). Previously, parseBracedList() returned early before eof() in two cases: 1. Encountering a semicolon (even though TextFormat protobufs allow ';' as an optional field separator), which returned false without consuming the token and unwound all enclosing parseBracedList() calls. 2. Encountering an unmatched closing '}' (e.g. in partial R"pb(...)pb" raw string fragments concatenated via StrCat), which returned after consuming '}' and left any remaining tokens unconsumed. Both cases triggered the assert(eof()) in UnwrappedLineParser::parse(). --- clang/lib/Format/UnwrappedLineParser.cpp | 8 +++++--- .../unittests/Format/FormatTestTextProto.cpp | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 1b00e9015ea6e0..e5603feb379772 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -277,7 +277,8 @@ void UnwrappedLineParser::parseFile() { ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack, MustBeDeclaration); if (Style.isTextProto() || (Style.isJson() && FormatTok->IsFirst)) - parseBracedList(); + while (!eof()) + parseBracedList(); else parseLevel(); // Make sure to format the remaining tokens. @@ -2670,10 +2671,11 @@ bool UnwrappedLineParser::parseBracedList(bool IsAngleBracket, bool IsEnum) { break; case tok::semi: // JavaScript (or more precisely TypeScript) can have semicolons in braced - // lists (in so-called TypeMemberLists). Thus, the semicolon cannot be + // lists (in so-called TypeMemberLists). TextProto and Proto options also + // allow semicolons as field separators. Thus, the semicolon cannot be // used for error recovery if we have otherwise determined that this is // a braced list. - if (Style.isJavaScript()) { + if (Style.isJavaScript() || Style.isProto() || Style.isJson()) { nextToken(); break; } diff --git a/clang/unittests/Format/FormatTestTextProto.cpp b/clang/unittests/Format/FormatTestTextProto.cpp index 6cddb838a0abe3..f919af5b78dd8f 100644 --- a/clang/unittests/Format/FormatTestTextProto.cpp +++ b/clang/unittests/Format/FormatTestTextProto.cpp @@ -721,6 +721,25 @@ TEST_F(FormatTestTextProto, KeepsAmpersandsNextToKeys) { "}"); } +TEST_F(FormatTestTextProto, SupportsSemicolonSeparators) { + verifyFormat("field_a: OK; field_b: OK"); + verifyFormat("field_a: OK;\n" + "field_b { field_c: OK };\n" + "field_d: OKOKOK;\n" + "field_e: OK"); + verifyFormat("msg_field <\n" + " field_a: OK;\n" + " field_b: <>;\n" + " field_c: OK\n" + ">"); +} + +TEST_F(FormatTestTextProto, DoesNotCrashOnUnmatchedClosingBrace) { + verifyNoCrash("field_a: 1 } field_b: 2"); + verifyNoCrash("} field_a: 1"); + verifyNoCrash("msg { field_a: 1 } } field_b: 2"); +} + } // namespace } // namespace test } // namespace format _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
