[clang] [clang][deps] Stop lexing if hit a failure while loading a PCH/module in a submodule. (PR #146976)

Volodymyr Sapsai via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 3 17:04:39 PDT 2025


https://github.com/vsapsai created https://github.com/llvm/llvm-project/pull/146976

Otherwise we are continuing in an invalid state and can easily crash.

It is a follow-up to cde90e68f8123e7abef3f9e18d79980aa19f460a but an important difference is when a failure happens in a submodule. In this case in `Preprocessor::HandleEndOfFile` `tok::eof` is replaced by `tok::annot_module_end`. And after exiting a file with bad `#include/#import` we work with a new buffer, so `BufferPtr < BufferEnd`. As there are no signs to stop lexing we just keep doing it.

The fix is the same as in dc9fdaf2171cc480300d5572606a8ede1678d18b in `Lexer::LexTokenInternal` but this time in `Lexer::LexDependencyDirectiveToken` as well.

rdar://152499276

>From 6731f2079e69b7565875aa97f11829c1d758bf82 Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <vsapsai at apple.com>
Date: Thu, 3 Jul 2025 16:40:26 -0700
Subject: [PATCH] [clang][deps] Stop lexing if hit a failure while loading a
 PCH/module in a submodule.

Otherwise we are continuing in an invalid state and can easily crash.

It is a follow-up to cde90e68f8123e7abef3f9e18d79980aa19f460a but an
important difference is when a failure happens in a submodule. In this
case in `Preprocessor::HandleEndOfFile` `tok::eof` is replaced by
`tok::annot_module_end`. And after exiting a file with bad `#include/#import`
we work with a new buffer, so `BufferPtr < BufferEnd`. As there are no signs
to stop lexing we just keep doing it.

The fix is the same as in `dc9fdaf2171cc480300d5572606a8ede1678d18b` in
`Lexer::LexTokenInternal` but this time in `Lexer::LexDependencyDirectiveToken`
as well.

rdar://152499276
---
 clang/lib/Lex/Lexer.cpp                       |  3 ++
 .../ClangScanDeps/fatal-module-loader-error.m | 42 +++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 clang/test/ClangScanDeps/fatal-module-loader-error.m

diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 30ac48b8010b8..8a0560615bf97 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -4589,6 +4589,9 @@ bool Lexer::LexDependencyDirectiveToken(Token &Result) {
 
   if (Result.is(tok::hash) && Result.isAtStartOfLine()) {
     PP->HandleDirective(Result);
+    if (PP->hadModuleLoaderFatalFailure())
+      // With a fatal failure in the module loader, we abort parsing.
+      return true;
     return false;
   }
   if (Result.is(tok::raw_identifier)) {
diff --git a/clang/test/ClangScanDeps/fatal-module-loader-error.m b/clang/test/ClangScanDeps/fatal-module-loader-error.m
new file mode 100644
index 0000000000000..ed4d8628a6471
--- /dev/null
+++ b/clang/test/ClangScanDeps/fatal-module-loader-error.m
@@ -0,0 +1,42 @@
+// This tests that after a fatal module loader error, we do not continue parsing.
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: mkdir %t/ModulesCache
+// RUN: touch %t/ModulesCache/Unusable.pcm
+
+// RUN: not clang-scan-deps -format experimental-full -mode preprocess-dependency-directives -- \
+// RUN:   %clang -fmodules -fimplicit-modules -Xclang -fdisable-module-hash -fmodules-cache-path=%t/ModulesCache \
+// RUN:   -F %t/Frameworks -c %t/test.m -o %t/test.o
+// RUN: ls %t/ModulesCache | not grep AfterUnusable
+
+//--- Frameworks/Unusable.framework/Headers/Unusable.h
+// empty
+//--- Frameworks/Unusable.framework/Modules/module.modulemap
+framework module Unusable { header "Unusable.h" }
+
+//--- Frameworks/AfterUnusable.framework/Headers/AfterUnusable.h
+// empty
+//--- Frameworks/AfterUnusable.framework/Modules/module.modulemap
+framework module AfterUnusable { header "AfterUnusable.h" }
+
+//--- Frameworks/Importer.framework/Headers/Importer.h
+#import <Importer/ImportUnusable.h>
+// Parsing should have stopped and we should not handle AfterUnusable.
+#import <AfterUnusable/AfterUnusable.h>
+
+//--- Frameworks/Importer.framework/Headers/ImportUnusable.h
+// It is important that this header is a submodule.
+#import <Unusable/Unusable.h>
+// Parsing should have stopped and we should not handle AfterUnusable.
+#import <AfterUnusable/AfterUnusable.h>
+
+//--- Frameworks/Importer.framework/Modules/module.modulemap
+framework module Importer {
+  umbrella header "Importer.h"
+  module * { export * }
+  export *
+}
+
+//--- test.m
+#import <Importer/Importer.h>



More information about the cfe-commits mailing list