feat: collect #embed and __has_embed directives in PPCallbacks (#309)

## Summary
- Add `Embed` and `HasEmbed` structs to `directive.h` for storing
`#embed` / `__has_embed` directive info
- Implement `EmbedDirective` and `HasEmbed` PPCallbacks in
`DirectiveCollector`, using the current `CompilationUnitRef` API
- Add unit tests for both directives (including non-existent file
handling)

Rebased onto current main, resolving conflicts from the `Compiler/` →
`compile/` restructuring and the `CompilationUnitRef` API migration.

Original PR by @Guo-Shiyu.

## Test plan
- [x] `pixi run unit-test` — 465 tests passed, 0 failures

Co-authored-by: ykiko <ykikoykikoykiko@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Shiyu
2026-04-06 15:51:26 +08:00
committed by GitHub
parent 0c107fc2c5
commit d253c1f099
3 changed files with 142 additions and 0 deletions

View File

@@ -13,6 +13,8 @@ std::vector<HasInclude> has_includes;
std::vector<Condition> conditions;
std::vector<MacroRef> macros;
std::vector<Pragma> pragmas;
std::vector<Embed> embeds;
std::vector<HasEmbed> has_embeds;
using u32 = std::uint32_t;
@@ -25,6 +27,8 @@ void run(llvm::StringRef code) {
conditions = unit->directives()[fid].conditions;
macros = unit->directives()[fid].macros;
pragmas = unit->directives()[fid].pragmas;
embeds = unit->directives()[fid].embeds;
has_embeds = unit->directives()[fid].has_embeds;
}
void EXPECT_INCLUDE(u32 index, llvm::StringRef position, llvm::StringRef path) {
@@ -66,6 +70,25 @@ void EXPECT_MACRO(u32 index, MacroRef::Kind kind, llvm::StringRef position) {
ASSERT_EQ(offset, point(position));
}
void EXPECT_EMBED(u32 index, llvm::StringRef position, llvm::StringRef filename) {
auto& embed = embeds[index];
auto [_, offset] = unit->decompose_location(embed.loc);
ASSERT_EQ(offset, point(position));
ASSERT_TRUE(embed.file.has_value());
ASSERT_EQ(embed.file_name, filename);
}
void EXPECT_HAS_EMBED(u32 index,
llvm::StringRef position,
llvm::StringRef filename,
bool exists = true) {
auto& has_embed = has_embeds[index];
auto [_, offset] = unit->decompose_location(has_embed.loc);
ASSERT_EQ(offset, point(position));
ASSERT_EQ(has_embed.file.has_value(), exists);
ASSERT_EQ(has_embed.file_name, filename);
}
void EXPECT_PRAGMA(u32 index, Pragma::Kind kind, llvm::StringRef pos, llvm::StringRef text) {
auto& pragma = pragmas[index];
auto [_, offset] = unit->decompose_location(pragma.loc);
@@ -196,6 +219,65 @@ $(2)#pragma endregion
EXPECT_PRAGMA(2, Pragma::Kind::EndRegion, "2", "#pragma endregion");
};
TEST_CASE(Embed) {
run(R"cpp(
#[bytes10.bin]
0123456789
#[bytes5.bin]
ABCDE
#[main.cpp]
const char e0 = {
$(0)#embed "bytes10.bin"
};
const char e1 = {
$(1)#embed "bytes10.bin"
};
const char e2 = {
$(2)#embed "bytes5.bin"
};
const char e3 = {
$(3)#embed "bytes5.bin"
};
const char e4 = {
$(4)#embed "non-existed.bin"
};
)cpp");
// e4 will not be processed by clang::PPCallbacks::EmbedDirective(), so there are only 4
// embeds.
ASSERT_EQ(embeds.size(), 4U);
EXPECT_EMBED(0, "0", "bytes10.bin");
EXPECT_EMBED(1, "1", "bytes10.bin");
EXPECT_EMBED(2, "2", "bytes5.bin");
EXPECT_EMBED(3, "3", "bytes5.bin");
};
TEST_CASE(HasEmbed) {
run(R"cpp(
#[test.bin]
#[main.cpp]
#embed "test.bin"
#if __has_embed$(0)("test.bin")
#endif
#if __has_embed$(1)("non-existed.bin")
#endif
)cpp");
ASSERT_EQ(has_embeds.size(), 2U);
EXPECT_HAS_EMBED(0, "0", "test.bin");
EXPECT_HAS_EMBED(1, "1", "non-existed.bin", /*exists=*/false);
};
}; // TEST_SUITE(Directive)
} // namespace