Skip to content

Commit 5a41fc2

Browse files
authored
[clang][modules] Name the module map files on PCM file conflict (#134475)
With implicitly-built modules, seeing something like: ``` fatal error: module 'X' is defined in both '<cache>/HASH1/X-HASH2.pcm' and '<cache>/HASH1/X-HASH3.pcm' ``` is super confusing and not actionable, because the module cache tends to be hidden from the developer. This PR adds a note to that diagnostic that names the module map files the PCM files were compiled from, hopefully giving a good enough hint for further investigation: ``` note: compiled from '<build>/X.framework/Modules/module.modulemap' and '<SDK>/X.framework/Modules/module.modulemap' ``` (I had to replace the mechanism used to convert `DiagnosticError` into something `DiagnosticsEngine` can understand, because it seemingly did not support notes.)
1 parent 76b85d3 commit 5a41fc2

File tree

3 files changed

+40
-38
lines changed

3 files changed

+40
-38
lines changed

clang/include/clang/Basic/DiagnosticSerializationKinds.td

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ def err_module_file_not_module : Error<
7575
def err_module_file_missing_top_level_submodule : Error<
7676
"module file '%0' is missing its top-level submodule">, DefaultFatal;
7777
def note_module_file_conflict : Note<
78-
"this is generally caused by modules with the same name found in multiple "
79-
"paths">;
78+
"compiled from '%0' and '%1'">;
8079

8180
def remark_module_import : Remark<
8281
"importing module '%0'%select{| into '%3'}2 from '%1'">,

clang/lib/Serialization/ASTReader.cpp

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
#include "clang/Basic/ASTSourceDescriptor.h"
4545
#include "clang/Basic/CommentOptions.h"
4646
#include "clang/Basic/Diagnostic.h"
47-
#include "clang/Basic/DiagnosticError.h"
4847
#include "clang/Basic/DiagnosticIDs.h"
4948
#include "clang/Basic/DiagnosticOptions.h"
5049
#include "clang/Basic/DiagnosticSema.h"
@@ -1552,30 +1551,27 @@ void ASTReader::Error(unsigned DiagID, StringRef Arg1, StringRef Arg2,
15521551
Diag(DiagID) << Arg1 << Arg2 << Arg3;
15531552
}
15541553

1554+
namespace {
1555+
struct AlreadyReportedDiagnosticError
1556+
: llvm::ErrorInfo<AlreadyReportedDiagnosticError> {
1557+
static char ID;
1558+
1559+
void log(raw_ostream &OS) const override {
1560+
llvm_unreachable("reporting an already-reported diagnostic error");
1561+
}
1562+
1563+
std::error_code convertToErrorCode() const override {
1564+
return llvm::inconvertibleErrorCode();
1565+
}
1566+
};
1567+
1568+
char AlreadyReportedDiagnosticError::ID = 0;
1569+
} // namespace
1570+
15551571
void ASTReader::Error(llvm::Error &&Err) const {
1556-
llvm::Error RemainingErr =
1557-
handleErrors(std::move(Err), [this](const DiagnosticError &E) {
1558-
auto Diag = E.getDiagnostic().second;
1559-
1560-
// Ideally we'd just emit it, but have to handle a possible in-flight
1561-
// diagnostic. Note that the location is currently ignored as well.
1562-
auto NumArgs = Diag.getStorage()->NumDiagArgs;
1563-
assert(NumArgs <= 3 && "Can only have up to 3 arguments");
1564-
StringRef Arg1, Arg2, Arg3;
1565-
switch (NumArgs) {
1566-
case 3:
1567-
Arg3 = Diag.getStringArg(2);
1568-
[[fallthrough]];
1569-
case 2:
1570-
Arg2 = Diag.getStringArg(1);
1571-
[[fallthrough]];
1572-
case 1:
1573-
Arg1 = Diag.getStringArg(0);
1574-
}
1575-
Error(Diag.getDiagID(), Arg1, Arg2, Arg3);
1576-
});
1577-
if (RemainingErr)
1578-
Error(toString(std::move(RemainingErr)));
1572+
handleAllErrors(
1573+
std::move(Err), [](AlreadyReportedDiagnosticError &) {},
1574+
[&](llvm::ErrorInfoBase &E) { return Error(E.message()); });
15791575
}
15801576

15811577
//===----------------------------------------------------------------------===//
@@ -3349,8 +3345,6 @@ ASTReader::ReadControlBlock(ModuleFile &F,
33493345
if (isDiagnosedResult(Result, Capabilities) || recompilingFinalized)
33503346
Diag(diag::note_module_file_imported_by)
33513347
<< F.FileName << !F.ModuleName.empty() << F.ModuleName;
3352-
if (recompilingFinalized)
3353-
Diag(diag::note_module_file_conflict);
33543348

33553349
switch (Result) {
33563350
case Failure: return Failure;
@@ -4440,6 +4434,7 @@ ASTReader::ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F,
44404434
// This module was defined by an imported (explicit) module.
44414435
Diag(diag::err_module_file_conflict) << F.ModuleName << F.FileName
44424436
<< ASTFE->getName();
4437+
// TODO: Add a note with the module map paths if they differ.
44434438
} else {
44444439
// This module was built with a different module map.
44454440
Diag(diag::err_imported_module_not_found)
@@ -6105,14 +6100,21 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F,
61056100
if (OptionalFileEntryRef CurFile = CurrentModule->getASTFile()) {
61066101
// Don't emit module relocation error if we have -fno-validate-pch
61076102
if (!bool(PP.getPreprocessorOpts().DisablePCHOrModuleValidation &
6108-
DisableValidationForModuleKind::Module) &&
6109-
CurFile != F.File) {
6110-
auto ConflictError =
6111-
PartialDiagnostic(diag::err_module_file_conflict,
6112-
ContextObj->DiagAllocator)
6103+
DisableValidationForModuleKind::Module)) {
6104+
assert(CurFile != F.File && "ModuleManager did not de-duplicate");
6105+
6106+
Diag(diag::err_module_file_conflict)
61136107
<< CurrentModule->getTopLevelModuleName() << CurFile->getName()
61146108
<< F.File.getName();
6115-
return DiagnosticError::create(CurrentImportLoc, ConflictError);
6109+
6110+
auto CurModMapFile =
6111+
ModMap.getContainingModuleMapFile(CurrentModule);
6112+
auto ModMapFile = FileMgr.getOptionalFileRef(F.ModuleMapPath);
6113+
if (CurModMapFile && ModMapFile && CurModMapFile != ModMapFile)
6114+
Diag(diag::note_module_file_conflict)
6115+
<< CurModMapFile->getName() << ModMapFile->getName();
6116+
6117+
return llvm::make_error<AlreadyReportedDiagnosticError>();
61166118
}
61176119
}
61186120

clang/test/ClangScanDeps/modules-relocated-mm-macro.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313

1414
// RUN: cp -r %t/frameworks2/A.framework %t/frameworks1
1515

16-
// RUN: not clang-scan-deps -format experimental-full -o %t/deps2.json -- \
16+
// RUN: not clang-scan-deps -format experimental-full -o %t/deps2.json 2>%t/errs -- \
1717
// RUN: %clang -fmodules -fmodules-cache-path=%t/cache \
1818
// RUN: -F %t/frameworks1 -F %t/frameworks2 \
19-
// RUN: -c %t/tu2.m -o %t/tu2.o \
20-
// RUN: 2>&1 | FileCheck %s
19+
// RUN: -c %t/tu2.m -o %t/tu2.o
20+
// RUN: FileCheck --input-file=%t/errs %s
2121

22-
// CHECK: fatal error: module 'A' is defined in both '{{.*}}.pcm' and '{{.*}}.pcm'
22+
// CHECK: fatal error: module 'A' is defined in both '{{.*}}.pcm' and '{{.*}}.pcm'
23+
// CHECK-NEXT: note: compiled from '{{.*}}frameworks1{{.*}}' and '{{.*}}frameworks2{{.*}}'
2324

2425
//--- frameworks2/A.framework/Modules/module.modulemap
2526
framework module A { header "A.h" }

0 commit comments

Comments
 (0)