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

Reply via email to