diff --git a/implementations/Crafter.Build-Lint.cpp b/implementations/Crafter.Build-Lint.cpp index 357fb33..39e4be0 100644 --- a/implementations/Crafter.Build-Lint.cpp +++ b/implementations/Crafter.Build-Lint.cpp @@ -1149,6 +1149,10 @@ format` applies it to disk, and `crafter-build lint` reports where it would. // command per configuration — GetCompileCommand walks the dependency tree. const bool anyRuleNeedsAst = std::ranges::any_of(rules, [](const LintRule* r) { return r->needsAst; }); std::unordered_map commands; + // Why a configuration of the build graph has no command: its files + // cannot be parsed, which fails the run. Cleared to nothing, the command + // read as "not a translation unit" and every semantic rule passed them. + std::unordered_map commandErrors; if (anyRuleNeedsAst && !opts.noAst) { for (Configuration* c : localConfigs) { auto ensure = [&](const Configuration& cfg) { @@ -1166,10 +1170,14 @@ format` applies it to disk, and `crafter-build lint` reports where it would. Progress::Task task(std::format("Building std PCM ({}-{})", cfg.target, cfg.march)); fs::create_directories(assembled.stdPcmDir); std::string error = BuildStdPcm(cfg, assembled.stdPcmDir/"std.pcm"); - if (!error.empty()) command.clear(); + if (!error.empty()) { + command.clear(); + commandErrors.emplace(&cfg, std::format("building the std module for {}: {}", cfg.target, error)); + } } - } catch (const std::exception&) { - command.clear(); // surfaced per file as an AST reason + } catch (const std::exception& e) { + command.clear(); + commandErrors.emplace(&cfg, std::format("assembling the compile command for {}: {}", cfg.target, e.what())); } commands.emplace(&cfg, std::move(command)); }; @@ -1200,6 +1208,11 @@ format` applies it to disk, and `crafter-build lint` reports where it would. // decides what is safe to touch. Skip it and make the run fail. if (rule->needsAst) { if (opts.noAst) continue; + if (auto failed = commandErrors.find(owner); failed != commandErrors.end()) { + astFailures.emplace(file, failed->second); + skippedAstRules.insert(rule->name); + continue; + } // No compile command means this file is not a translation unit // of the build graph — project.cpp, which LoadProject compiles // with its own flags, or a header. A semantic rule does not