diff --git a/tests/Lint/main.cpp b/tests/Lint/main.cpp index 69bd436..4e07d94 100644 --- a/tests/Lint/main.cpp +++ b/tests/Lint/main.cpp @@ -6,6 +6,9 @@ import Crafter.Build; namespace fs = std::filesystem; using namespace Crafter; +extern "C" int setenv(const char* name, const char* value, int overwrite); +extern "C" int unsetenv(const char* name); + namespace { std::int32_t Failures = 0; @@ -705,6 +708,31 @@ int main() { // two assertions above cover it. } + // A configuration whose std module cannot be built is in the build graph + // all the same: its files cannot be parsed, which has to fail the run. + // Its command was cleared, which read as "not a translation unit", and + // every semantic rule passed the files. A sysroot without libc++ is one + // way there. The std module's directory is made before its build fails, + // so the cache is redirected around this run. + { + Scratch s("ast-no-std-module"); + s.Write("f", "int Value = 1;\n"); + Configuration cfg = s.Config({"f"}); + cfg.sysroot = (s.dir / "empty-sysroot").string(); + fs::create_directories(cfg.sysroot); + bool ran = false; + cfg.AddAstLintRule("needs-ast", [&ran](LintContext&) { ran = true; }); + const char* realCache = std::getenv("XDG_CACHE_HOME"); + const std::string saved = realCache ? realCache : ""; + setenv("XDG_CACHE_HOME", (s.dir / "cache").c_str(), 1); + LintSummary summary = RunLint(cfg, Mode(LintMode::Report)); + if (saved.empty()) unsetenv("XDG_CACHE_HOME"); + else setenv("XDG_CACHE_HOME", saved.c_str(), 1); + Check(!ran, "ast: rule is skipped when the std module cannot be built"); + Check(summary.errors > 0, "ast: a std module that cannot be built fails the run"); + Check(!summary.Clean(), "ast: a std module that cannot be built is not Clean"); + } + // --no-ast skips those rules deliberately and exits normally. { Scratch s("ast-optout");