@@ -1563,6 +1563,54 @@ std::string getResourcesPath() {
15631563 return GetResourcesPath (getBinaryForResourcesPath ());
15641564}
15651565
1566+ // --- Builtin virtual include directory ---
1567+ // c2pulse injects a wrapper assert.h so that #include <assert.h> always
1568+ // results in assert() being translated to __c2pulse_c_assert(), regardless
1569+ // of whether assert.h is included before or after c2pulse.h.
1570+ static const char BUILTIN_INCLUDE_DIR [] = " /c2pulse_builtins" ;
1571+ static const char BUILTIN_ASSERT_H_PATH [] = " /c2pulse_builtins/assert.h" ;
1572+ // The wrapper uses #include_next to pull in the real system assert.h and then
1573+ // re-overrides the assert macro so c2pulse can always intercept assert() calls.
1574+ static const char BUILTIN_ASSERT_H_CONTENT [] =
1575+ " #include_next <assert.h>\n "
1576+ " #ifdef C2PULSE\n "
1577+ " #undef assert\n "
1578+ " #define assert(x) __c2pulse_c_assert(x)\n "
1579+ " #endif\n " ;
1580+
1581+ // Stable unique IDs for builtin virtual filesystem entries (device 2 to avoid
1582+ // collisions with real files which use device 0).
1583+ static const llvm::sys::fs::UniqueID BUILTIN_ASSERT_H_UID (2 , 1 );
1584+ static const llvm::sys::fs::UniqueID BUILTIN_INCLUDE_DIR_UID (2 , 2 );
1585+
1586+ // A VFS file that serves static string content (used for builtin headers).
1587+ class BuiltinVFSFile : public llvm ::vfs::File {
1588+ llvm::StringRef name;
1589+ llvm::StringRef content;
1590+
1591+ public:
1592+ BuiltinVFSFile (llvm::StringRef n, llvm::StringRef c) : name(n), content(c) {}
1593+
1594+ llvm::ErrorOr<llvm::vfs::Status> status () override {
1595+ llvm::sys::TimePoint<> time;
1596+ return llvm::vfs::Status (
1597+ name, BUILTIN_ASSERT_H_UID , time, 0 , 0 , content.size (),
1598+ llvm::sys::fs::file_type::regular_file, llvm::sys::fs::perms::all_all);
1599+ }
1600+
1601+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
1602+ getBuffer (const Twine &Name, int64_t FileSize = -1 ,
1603+ bool RequiresNullTerminator = true ,
1604+ bool IsVolatile = false ) override {
1605+ if (!RequiresNullTerminator)
1606+ return llvm::MemoryBuffer::getMemBuffer (content, name);
1607+ return llvm::MemoryBuffer::getMemBufferCopy (content, name);
1608+ }
1609+
1610+ std::error_code close () override { return {}; }
1611+ llvm::ErrorOr<std::string> getName () override { return name.str (); }
1612+ };
1613+
15661614llvm::vfs::Status mkStatus (Ref<rust::crate::vfs::VFSEntry> entry) {
15671615 auto fileName = entry.get_file_name ();
15681616 llvm::sys::fs::UniqueID unique (0 , (uint64_t )fileName.as_ptr ());
@@ -1607,6 +1655,14 @@ class CtxVFS : public llvm::vfs::FileSystem {
16071655 RefMut<Ctx> ctx;
16081656 IntrusiveRefCntPtr<llvm::vfs::FileSystem> realFS;
16091657
1658+ static bool isBuiltinFile (llvm::StringRef path) {
1659+ return path == BUILTIN_ASSERT_H_PATH ;
1660+ }
1661+
1662+ static bool isBuiltinDir (llvm::StringRef path) {
1663+ return path == BUILTIN_INCLUDE_DIR ;
1664+ }
1665+
16101666public:
16111667 CtxVFS (RefMut<Ctx> c) : ctx(c), realFS(llvm::vfs::getRealFileSystem()) {}
16121668
@@ -1615,7 +1671,22 @@ class CtxVFS : public llvm::vfs::FileSystem {
16151671 }
16161672
16171673 llvm::ErrorOr<llvm::vfs::Status> status (const Twine &Path) override {
1618- auto res = ctx.read_vfs_file (toStr (Path.str ()));
1674+ auto pathStr = Path.str ();
1675+ if (isBuiltinFile (pathStr)) {
1676+ llvm::sys::TimePoint<> time;
1677+ return llvm::vfs::Status (BUILTIN_ASSERT_H_PATH , BUILTIN_ASSERT_H_UID ,
1678+ time, 0 , 0 , sizeof (BUILTIN_ASSERT_H_CONTENT ) - 1 ,
1679+ llvm::sys::fs::file_type::regular_file,
1680+ llvm::sys::fs::perms::all_all);
1681+ }
1682+ if (isBuiltinDir (pathStr)) {
1683+ llvm::sys::TimePoint<> time;
1684+ return llvm::vfs::Status (BUILTIN_INCLUDE_DIR , BUILTIN_INCLUDE_DIR_UID ,
1685+ time, 0 , 0 , 0 ,
1686+ llvm::sys::fs::file_type::directory_file,
1687+ llvm::sys::fs::perms::all_all);
1688+ }
1689+ auto res = ctx.read_vfs_file (toStr (pathStr));
16191690 if (!res.is_ok ()) {
16201691 // TODO: fallback for directories
16211692 return realFS->status (Path);
@@ -1625,6 +1696,12 @@ class CtxVFS : public llvm::vfs::FileSystem {
16251696
16261697 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
16271698 openFileForRead (const Twine &Path) override {
1699+ if (isBuiltinFile (Path.str ())) {
1700+ return std::make_unique<BuiltinVFSFile>(
1701+ llvm::StringRef (BUILTIN_ASSERT_H_PATH ),
1702+ llvm::StringRef (BUILTIN_ASSERT_H_CONTENT ,
1703+ sizeof (BUILTIN_ASSERT_H_CONTENT ) - 1 ));
1704+ }
16281705 auto res = ctx.read_vfs_file (toStr (Path.str ()));
16291706 if (!res.is_ok ()) {
16301707 return llvm::errc::no_such_file_or_directory;
@@ -1652,7 +1729,10 @@ class CtxVFS : public llvm::vfs::FileSystem {
16521729 }
16531730
16541731 bool exists (const Twine &Path) override {
1655- auto res = ctx.read_vfs_file (toStr (Path.str ()));
1732+ auto pathStr = Path.str ();
1733+ if (isBuiltinFile (pathStr) || isBuiltinDir (pathStr))
1734+ return true ;
1735+ auto res = ctx.read_vfs_file (toStr (pathStr));
16561736 if (res.is_ok ())
16571737 return true ;
16581738
@@ -1705,6 +1785,12 @@ static void parse_file(RefMut<Ctx> ctx) {
17051785 incPath.c_str (), ArgumentInsertPosition::BEGIN ));
17061786 }
17071787
1788+ // Add the builtin include directory last so it ends up first in the
1789+ // command line (highest priority). This ensures our wrapper assert.h
1790+ // is found before any system assert.h.
1791+ Tool.appendArgumentsAdjuster (getInsertArgumentAdjuster (
1792+ {" -I" , BUILTIN_INCLUDE_DIR }, ArgumentInsertPosition::BEGIN ));
1793+
17081794 C2PulseActionFactory factory (ctx, rangeMap);
17091795 Tool.run (&factory);
17101796}
0 commit comments