[Dependency Scanning] Emit a detailed error diagnostic on Clang module variant discovery

In expectation, this should never happen. Such a situation means that within the same scanning action, Clang Dependency Scanner has produced two different variants of the same module. This is not supposed to happen, but we are currently hunting down the rare cases where it does, seemingly due to differences in Clang Scanner direct by-name queries and transitive header lookup queries.
This commit is contained in:
Artem Chikin
2025-05-05 16:17:34 -07:00
parent 696ba3ed78
commit 7f950ff180
6 changed files with 74 additions and 19 deletions

View File

@@ -886,11 +886,61 @@ void ModuleDependenciesCache::recordDependency(
}
void ModuleDependenciesCache::recordDependencies(
ModuleDependencyVector dependencies) {
ModuleDependencyVector dependencies, DiagnosticEngine &diags) {
for (const auto &dep : dependencies) {
if (!hasDependency(dep.first))
if (hasDependency(dep.first)) {
if (dep.first.Kind == ModuleDependencyKind::Clang) {
auto priorClangModuleDetails =
findKnownDependency(dep.first).getAsClangModule();
auto newClangModuleDetails = dep.second.getAsClangModule();
auto priorContextHash = priorClangModuleDetails->contextHash;
auto newContextHash = newClangModuleDetails->contextHash;
if (priorContextHash != newContextHash) {
// This situation means that within the same scanning action, Clang
// Dependency Scanner has produced two different variants of the same
// module. This is not supposed to happen, but we are currently
// hunting down the rare cases where it does, seemingly due to
// differences in Clang Scanner direct by-name queries and transitive
// header lookup queries.
//
// Emit a failure diagnostic here that is hopefully more actionable
// for the time being.
diags.diagnose(SourceLoc(), diag::dependency_scan_unexpected_variant,
dep.first.ModuleName);
diags.diagnose(
SourceLoc(),
diag::dependency_scan_unexpected_variant_context_hash_note,
priorContextHash, newContextHash);
diags.diagnose(
SourceLoc(),
diag::dependency_scan_unexpected_variant_module_map_note,
priorClangModuleDetails->moduleMapFile,
newClangModuleDetails->moduleMapFile);
auto diagnoseExtraCommandLineFlags =
[&diags](const ClangModuleDependencyStorage *checkModuleDetails,
const ClangModuleDependencyStorage *baseModuleDetails,
bool isNewlyDiscovered) -> void {
std::unordered_set<std::string> baseCommandLineSet(
baseModuleDetails->buildCommandLine.begin(),
baseModuleDetails->buildCommandLine.end());
for (const auto &checkArg : checkModuleDetails->buildCommandLine)
if (baseCommandLineSet.find(checkArg) == baseCommandLineSet.end())
diags.diagnose(
SourceLoc(),
diag::dependency_scan_unexpected_variant_extra_arg_note,
isNewlyDiscovered, checkArg);
};
diagnoseExtraCommandLineFlags(priorClangModuleDetails,
newClangModuleDetails, true);
diagnoseExtraCommandLineFlags(newClangModuleDetails,
priorClangModuleDetails, false);
}
}
} else
recordDependency(dep.first.ModuleName, dep.second);
if (dep.second.getKind() == ModuleDependencyKind::Clang) {
if (dep.first.Kind == ModuleDependencyKind::Clang) {
auto clangModuleDetails = dep.second.getAsClangModule();
addSeenClangModule(clang::tooling::dependencies::ModuleID{
dep.first.ModuleName, clangModuleDetails->contextHash});