Skip to content

Commit caad212

Browse files
committed
[Serialization] Add notes to XRef errors for common failures
Try to give more information to the user for likely project issues causing XRef errors.
1 parent 0f7f060 commit caad212

File tree

2 files changed

+95
-3
lines changed

2 files changed

+95
-3
lines changed

lib/Serialization/Deserialization.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,8 +1469,91 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
14691469
};
14701470

14711471
if (values.empty()) {
1472+
// Couldn't resolve the reference. Try to explain the problem and leave it
1473+
// up to the caller to recover if possible.
1474+
1475+
// Look for types and value decls in other modules. This extra information
1476+
// is mostly for compiler engineers to understand a likely solution at a
1477+
// quick glance.
1478+
SmallVector<char> strScratch;
1479+
SmallVector<std::string, 2> notes;
1480+
auto declName = getXRefDeclNameForError();
1481+
if (recordID == XREF_TYPE_PATH_PIECE ||
1482+
recordID == XREF_VALUE_PATH_PIECE) {
1483+
auto &ctx = getContext();
1484+
for (auto nameAndModule : ctx.getLoadedModules()) {
1485+
auto baseModule = nameAndModule.second;
1486+
1487+
IdentifierID IID;
1488+
IdentifierID privateDiscriminator = 0;
1489+
TypeID TID = 0;
1490+
bool isType = (recordID == XREF_TYPE_PATH_PIECE);
1491+
bool inProtocolExt = false;
1492+
bool importedFromClang = false;
1493+
bool isStatic = false;
1494+
if (isType) {
1495+
XRefTypePathPieceLayout::readRecord(scratch, IID, privateDiscriminator,
1496+
inProtocolExt, importedFromClang);
1497+
} else {
1498+
XRefValuePathPieceLayout::readRecord(scratch, TID, IID, inProtocolExt,
1499+
importedFromClang, isStatic);
1500+
}
1501+
1502+
DeclBaseName name = getDeclBaseName(IID);
1503+
Type filterTy;
1504+
if (!isType) {
1505+
auto maybeType = getTypeChecked(TID);
1506+
// Any error here would have been handled previously.
1507+
if (maybeType) {
1508+
filterTy = maybeType.get();
1509+
}
1510+
}
1511+
1512+
values.clear();
1513+
if (privateDiscriminator) {
1514+
baseModule->lookupMember(values, baseModule, name,
1515+
getIdentifier(privateDiscriminator));
1516+
} else {
1517+
baseModule->lookupQualified(baseModule, DeclNameRef(name),
1518+
NL_QualifiedDefault,
1519+
values);
1520+
}
1521+
1522+
bool hadAMatchBeforeFiltering = !values.empty();
1523+
filterValues(filterTy, nullptr, nullptr, isType, inProtocolExt,
1524+
importedFromClang, isStatic, None, values);
1525+
1526+
strScratch.clear();
1527+
if (!values.empty()) {
1528+
// Found a full match in a different module. It should be a different
1529+
// one because otherwise it would have succeeded on the first search.
1530+
// This is usually caused by the use of poorly modularized headers.
1531+
auto line = "'" +
1532+
declName.getString(strScratch).str() +
1533+
"' was not found in module '" +
1534+
std::string(baseModule->getName().str()) +
1535+
"', but there is one in module '" +
1536+
std::string(nameAndModule.first.str()) +
1537+
"'. If this is imported from clang, please make sure " +
1538+
"the header is part of a single clang module.";
1539+
notes.emplace_back(line);
1540+
} else if (hadAMatchBeforeFiltering) {
1541+
// Found a match that was filtered out. This may be from the same
1542+
// expected module if there's a type difference. This can be caused
1543+
// by the use of different Swift language versions between a library
1544+
// with serialized SIL and a client.
1545+
auto line = "'" +
1546+
declName.getString(strScratch).str() +
1547+
"' in module '" +
1548+
std::string(baseModule->getName().str()) +
1549+
"' was filtered out.";
1550+
notes.emplace_back(line);
1551+
}
1552+
}
1553+
}
1554+
14721555
return llvm::make_error<XRefError>("top-level value not found", pathTrace,
1473-
getXRefDeclNameForError());
1556+
declName, notes);
14741557
}
14751558

14761559
// Filters for values discovered in the remaining path pieces.

lib/Serialization/DeserializationErrors.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,16 +276,25 @@ class XRefError : public llvm::ErrorInfo<XRefError, DeclDeserializationError> {
276276

277277
XRefTracePath path;
278278
const char *message;
279+
SmallVector<std::string, 2> notes;
279280
public:
280281
template <size_t N>
281-
XRefError(const char (&message)[N], XRefTracePath path, DeclName name)
282-
: path(path), message(message) {
282+
XRefError(const char (&message)[N], XRefTracePath path, DeclName name,
283+
SmallVector<std::string, 2> notes = {})
284+
: path(path), message(message), notes(notes) {
283285
this->name = name;
284286
}
285287

286288
void log(raw_ostream &OS) const override {
287289
OS << message << "\n";
288290
path.print(OS);
291+
292+
if (!notes.empty()) {
293+
OS << "Notes:\n";
294+
for (auto &line : notes) {
295+
OS << "* " << line << "\n";
296+
}
297+
}
289298
}
290299

291300
std::error_code convertToErrorCode() const override {

0 commit comments

Comments
 (0)