[clang] Fix dependency output for #embed (#178001)

When requesting FileEntryRef for embedded file, make sure to not use an
absolute path. Instead, create a proper relative path if we're looking
for a file from current file.

Fixes https://github.com/llvm/llvm-project/issues/161950
This commit is contained in:
Mariya Podchishchaeva 2026-02-02 11:00:46 +01:00 committed by GitHub
parent 23f9e42480
commit 485e69bc52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 102 additions and 30 deletions

View File

@ -222,6 +222,8 @@ Bug Fixes in This Version
- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
- Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -2604,13 +2604,11 @@ public:
/// resource. \p isAngled indicates whether the file reference is for
/// system \#include's or not (i.e. using <> instead of ""). If \p OpenFile
/// is true, the file looked up is opened for reading, otherwise it only
/// validates that the file exists. Quoted filenames are looked up relative
/// to \p LookupFromFile if it is nonnull.
/// validates that the file exists.
///
/// Returns std::nullopt on failure.
OptionalFileEntryRef
LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
const FileEntry *LookupFromFile = nullptr);
OptionalFileEntryRef LookupEmbedFile(StringRef Filename, bool isAngled,
bool OpenFile);
/// Return true if we're in the top-level file, not in a \#include.
bool isInPrimaryFile() const;
@ -2920,8 +2918,7 @@ private:
SmallVectorImpl<char> &RelativePath, SmallVectorImpl<char> &SearchPath,
ModuleMap::KnownHeader &SuggestedModule, bool isAngled);
// Binary data inclusion
void HandleEmbedDirective(SourceLocation HashLoc, Token &Tok,
const FileEntry *LookupFromFile = nullptr);
void HandleEmbedDirective(SourceLocation HashLoc, Token &Tok);
void HandleEmbedDirectiveImpl(SourceLocation HashLoc,
const LexEmbedParametersResult &Params,
StringRef BinaryContents, StringRef FileName);

View File

@ -1187,9 +1187,9 @@ OptionalFileEntryRef Preprocessor::LookupFile(
return std::nullopt;
}
OptionalFileEntryRef
Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
const FileEntry *LookupFromFile) {
OptionalFileEntryRef Preprocessor::LookupEmbedFile(StringRef Filename,
bool isAngled,
bool OpenFile) {
FileManager &FM = this->getFileManager();
if (llvm::sys::path::is_absolute(Filename)) {
// lookup path or immediately fail
@ -1215,13 +1215,15 @@ Preprocessor::LookupEmbedFile(StringRef Filename, bool isAngled, bool OpenFile,
SmallString<512> LookupPath;
// Non-angled lookup
if (!isAngled) {
OptionalFileEntryRef LookupFromFile = getCurrentFileLexer()->getFileEntry();
if (LookupFromFile) {
// Use file-based lookup.
StringRef FullFileDir = LookupFromFile->tryGetRealPathName();
if (!FullFileDir.empty()) {
SeparateComponents(LookupPath, FullFileDir, Filename, true);
SmallString<1024> TmpDir;
TmpDir = LookupFromFile->getDir().getName();
llvm::sys::path::append(TmpDir, Filename);
if (!TmpDir.empty()) {
llvm::Expected<FileEntryRef> ShouldBeEntry = FM.getFileRef(
LookupPath, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
TmpDir, OpenFile, /*CacheFailure=*/true, /*IsText=*/false);
if (ShouldBeEntry)
return llvm::expectedToOptional(std::move(ShouldBeEntry));
llvm::consumeError(ShouldBeEntry.takeError());
@ -1487,12 +1489,8 @@ void Preprocessor::HandleDirective(Token &Result) {
return HandleIdentSCCSDirective(Result);
case tok::pp_sccs:
return HandleIdentSCCSDirective(Result);
case tok::pp_embed: {
if (PreprocessorLexer *CurrentFileLexer = getCurrentFileLexer())
if (OptionalFileEntryRef FERef = CurrentFileLexer->getFileEntry())
return HandleEmbedDirective(Introducer.getLocation(), Result, *FERef);
return HandleEmbedDirective(Introducer.getLocation(), Result, nullptr);
}
case tok::pp_embed:
return HandleEmbedDirective(Introducer.getLocation(), Result);
case tok::pp_assert:
//isExtension = true; // FIXME: implement #assert
break;
@ -4076,8 +4074,8 @@ void Preprocessor::HandleEmbedDirectiveImpl(
EnterTokenStream(std::move(Toks), TotalNumToks, true, true);
}
void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok,
const FileEntry *LookupFromFile) {
void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc,
Token &EmbedTok) {
// Give the usual extension/compatibility warnings.
if (LangOpts.C23)
Diag(EmbedTok, diag::warn_compat_pp_embed_directive);
@ -4126,7 +4124,7 @@ void Preprocessor::HandleEmbedDirective(SourceLocation HashLoc, Token &EmbedTok,
return;
OptionalFileEntryRef MaybeFileRef =
this->LookupEmbedFile(Filename, isAngled, true, LookupFromFile);
this->LookupEmbedFile(Filename, isAngled, /*OpenFile=*/true);
if (!MaybeFileRef) {
// could not find file
if (Callbacks && Callbacks->EmbedFileNotFound(Filename)) {

View File

@ -1287,11 +1287,8 @@ EmbedResult Preprocessor::EvaluateHasEmbed(Token &Tok, IdentifierInfo *II) {
this->GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename);
// If GetIncludeFilenameSpelling set the start ptr to null, there was an
// error.
const FileEntry *LookupFromFile =
this->getCurrentFileLexer() ? *this->getCurrentFileLexer()->getFileEntry()
: static_cast<FileEntry *>(nullptr);
OptionalFileEntryRef MaybeFileEntry =
this->LookupEmbedFile(Filename, isAngled, false, LookupFromFile);
this->LookupEmbedFile(Filename, isAngled, false);
if (Callbacks) {
Callbacks->HasEmbed(LParenLoc, Filename, isAngled, MaybeFileEntry);
}

View File

@ -1,4 +1,4 @@
// RUN: %clang %s -fsyntax-only -std=c23 -M --embed-dir=%S/Inputs -Xclang -verify | FileCheck %s
// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c23 -MT %s.o --embed-dir=%S/Inputs -dependency-file - | FileCheck %s
// Yes this looks very strange indeed, but the goal is to test that we add
// files referenced by both __has_embed and #embed when we generate
@ -14,7 +14,8 @@ _Static_assert('b' == data);
#endif
// expected-no-diagnostics
// CHECK: embed_dependencies.c \
// CHECK-NEXT: jk.txt \
// CHECK: embed_dependencies.c.o
// CHECK-NEXT: embed_dependencies.c
// CHECK-NEXT: jk.txt
// CHECK-NEXT: Inputs{{[/\\]}}single_byte.txt

View File

@ -69,6 +69,33 @@ public:
}
};
class EmbedCollector : public PPCallbacks {
public:
Preprocessor &PP;
SmallVectorImpl<StringRef> &EmbeddedFiles;
EmbedCollector(Preprocessor &PP, SmallVectorImpl<StringRef> &EmbeddedFiles)
: PP(PP), EmbeddedFiles(EmbeddedFiles) {}
void EmbedDirective(SourceLocation, StringRef, bool,
OptionalFileEntryRef File,
const LexEmbedParametersResult &) override {
assert(File && "expected to only be called when the file is found");
StringRef Filename =
llvm::sys::path::remove_leading_dotslash(File->getName());
EmbeddedFiles.push_back(Filename);
}
void HasEmbed(SourceLocation, StringRef, bool,
OptionalFileEntryRef File) override {
if (!File)
return;
StringRef Filename =
llvm::sys::path::remove_leading_dotslash(File->getName());
EmbeddedFiles.push_back(Filename);
}
};
TEST_F(PPDependencyDirectivesTest, MacroGuard) {
// "head1.h" has a macro guard and should only be included once.
// "head2.h" and "head3.h" have tokens following the macro check, they should
@ -155,4 +182,54 @@ TEST_F(PPDependencyDirectivesTest, MacroGuard) {
EXPECT_EQ(IncludedFilesSlash, ExpectedIncludes);
}
TEST_F(PPDependencyDirectivesTest, Embed) {
auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
VFS->setCurrentWorkingDirectory("/source");
VFS->addFile("/source/inputs/jk.txt", 0,
llvm::MemoryBuffer::getMemBuffer("jk"));
VFS->addFile("/source/inputs/single_byte.txt", 0,
llvm::MemoryBuffer::getMemBuffer("a"));
VFS->addFile("/source/inputs/single_byte1.txt", 0,
llvm::MemoryBuffer::getMemBuffer("b"));
VFS->addFile(
"/source/inc/head.h", 0,
llvm::MemoryBuffer::getMemBuffer("#embed \"inputs/single_byte.txt\"\n"
"extern int foo;\n"));
VFS->addFile(
"main.c", 0,
llvm::MemoryBuffer::getMemBuffer("#include \"inc/head.h\"\n"
"#if __has_embed(\"inputs/jk.txt\")\n"
"const char arr[] =\n"
"#embed \"inputs/single_byte1.txt\"\n"
";\n"
"#endif\n"));
FileMgr.setVirtualFileSystem(VFS);
OptionalFileEntryRef FE;
ASSERT_THAT_ERROR(FileMgr.getFileRef("main.c").moveInto(FE),
llvm::Succeeded());
SourceMgr.setMainFileID(
SourceMgr.createFileID(*FE, SourceLocation(), SrcMgr::C_User));
PreprocessorOptions PPOpts;
HeaderSearchOptions HSOpts;
TrivialModuleLoader ModLoader;
HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());
Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,
/*IILookup =*/nullptr,
/*OwnsHeaderSearch =*/false);
PP.Initialize(*Target);
SmallVector<StringRef> EmbeddedFiles;
PP.addPPCallbacks(std::make_unique<EmbedCollector>(PP, EmbeddedFiles));
PP.EnterMainSourceFile();
PP.LexTokensUntilEOF();
SmallVector<StringRef> ExpectedEmbeds{
"inputs/single_byte.txt",
"inputs/jk.txt",
"inputs/single_byte1.txt",
};
EXPECT_EQ(EmbeddedFiles, ExpectedEmbeds);
}
} // anonymous namespace