[clang][deps] Stop lexing if hit a failure while loading a PCH/module in a submodule. (#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
This commit is contained in:
Volodymyr Sapsai 2025-07-07 12:28:03 -07:00 committed by GitHub
parent 9923565fb8
commit 3b05edfc5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 0 deletions

View File

@ -4587,6 +4587,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)) {

View File

@ -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>