https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/203716
In code-completion mode, `Parser::trySkippingFunctionBody()` uses a TentativeParsingAction to decide whether a function body can be skipped. We use `SkipUntil(tok::r_brace, StopAtCodeCompletion)`, which consumes the matching closing brace while the tentative parse is still active. Consuming that brace lexes one token past the skipped function body. In clangd, when the completion point is reached while unwinding a self-include, this can hit the code-completion `EOF` path before the tentative parse has committed. That `EOF` path restores the lexer callback to `CLK_CachingLexer`. When control returns to CachingLex(), it tries to enter caching lex mode again and encounters the assertion (We tried to re-enter CachineLexMode while the Preprocessor was already in CachineLexMode.). We fix this by stopping before the function body's closing brace while the tentative parse is active. Once we know the body does not contain the code-completion token, commit the tentative parse first, then consume the closing brace outside backtracking/caching mode. Function try blocks conservatively fall back to normal parsing in code-completion mode, because determining the end of the full try/catch sequence requires lexing across body/catch boundaries while the tentative parse is active. Add a clangd regression test covering a self-include file where signature help is requested at the main-file #endif after the included function body. >From 9e239bd8973ae41f1264b858137f7ee94881acf8 Mon Sep 17 00:00:00 2001 From: yronglin <[email protected]> Date: Sat, 13 Jun 2026 09:56:56 -0700 Subject: [PATCH] [clangd] Fix clangd crash when code completion EOF is reached while skipping a function body Signed-off-by: yronglin <[email protected]> --- .../clangd/unittests/CodeCompleteTests.cpp | 17 +++++++++++++++++ clang/lib/Parse/ParseStmt.cpp | 14 +++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 5808b2145965f..dc94af4dddf8c 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -1918,6 +1918,23 @@ TEST(SignatureHelpTest, StalePreamble) { EXPECT_EQ(0, Results.activeParameter); } +TEST(SignatureHelpTest, EOFInSkippedFunctionBody) { + Annotations Test(R"cpp( +#ifdef IS_HEADER +void frameSizeBlocksWarning() { + auto fn = []() { + }; + fn(); +} +#else +#define IS_HEADER +#include __FILE__ +#^endif +)cpp"); + auto Results = signatures(Test.code(), Test.point()); + EXPECT_THAT(Results.signatures, IsEmpty()); +} + class IndexRequestCollector : public SymbolIndex { public: IndexRequestCollector(std::vector<Symbol> Syms = {}) : Symbols(Syms) {} diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 37f142e059930..e77d6066847b2 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -2496,8 +2496,10 @@ bool Parser::trySkippingFunctionBody() { // We're in code-completion mode. Skip parsing for all function bodies unless // the body contains the code-completion point. + if (Tok.is(tok::kw_try)) + return false; + TentativeParsingAction PA(*this); - bool IsTryCatch = Tok.is(tok::kw_try); CachedTokens Toks; bool ErrorInPrologue = ConsumeAndStoreFunctionPrologue(Toks); if (llvm::any_of(Toks, [](const Token &Tok) { @@ -2511,18 +2513,12 @@ bool Parser::trySkippingFunctionBody() { SkipMalformedDecl(); return true; } - if (!SkipUntil(tok::r_brace, StopAtCodeCompletion)) { + if (!SkipUntil(tok::r_brace, StopAtCodeCompletion | StopBeforeMatch)) { PA.Revert(); return false; } - while (IsTryCatch && Tok.is(tok::kw_catch)) { - if (!SkipUntil(tok::l_brace, StopAtCodeCompletion) || - !SkipUntil(tok::r_brace, StopAtCodeCompletion)) { - PA.Revert(); - return false; - } - } PA.Commit(); + ConsumeBrace(); return true; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
