Skip to content

Commit b18e7f1

Browse files
authored
Merge pull request #66227 from xymus/recovery-notes
[Serialization] Display contextual notes on deserialization errors and misconfigurations
2 parents 008b1ca + af88a65 commit b18e7f1

File tree

6 files changed

+255
-52
lines changed

6 files changed

+255
-52
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ namespace swift {
143143
StringRef StringVal;
144144
DeclNameRef IdentifierVal;
145145
ObjCSelector ObjCSelectorVal;
146-
ValueDecl *TheValueDecl;
146+
const ValueDecl *TheValueDecl;
147147
Type TypeVal;
148148
TypeRepr *TyR;
149149
FullyQualified<Type> FullyQualifiedTypeVal;
@@ -194,7 +194,7 @@ namespace swift {
194194
: Kind(DiagnosticArgumentKind::ObjCSelector), ObjCSelectorVal(S) {
195195
}
196196

197-
DiagnosticArgument(ValueDecl *VD)
197+
DiagnosticArgument(const ValueDecl *VD)
198198
: Kind(DiagnosticArgumentKind::ValueDecl), TheValueDecl(VD) {
199199
}
200200

@@ -305,7 +305,7 @@ namespace swift {
305305
return ObjCSelectorVal;
306306
}
307307

308-
ValueDecl *getAsValueDecl() const {
308+
const ValueDecl *getAsValueDecl() const {
309309
assert(Kind == DiagnosticArgumentKind::ValueDecl);
310310
return TheValueDecl;
311311
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -876,18 +876,54 @@ ERROR(need_hermetic_seal_to_import_module,none,
876876
(Identifier))
877877

878878
ERROR(modularization_issue_decl_moved,Fatal,
879-
"reference to %select{top-level|type}0 %1 broken by a context change; "
879+
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
880880
"%1 was expected to be in %2, but now a candidate is found only in %3",
881-
(bool, DeclName, Identifier, Identifier))
881+
(bool, DeclName, const ModuleDecl*, const ModuleDecl*))
882882
ERROR(modularization_issue_decl_type_changed,Fatal,
883-
"reference to %select{top-level|type}0 %1 broken by a context change; "
883+
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
884884
"the declaration kind of %1 %select{from %2|}5 changed since building '%3'"
885-
"%select{|, it was in %2 and is now found in %4}5",
886-
(bool, DeclName, Identifier, StringRef, Identifier, bool))
885+
"%select{|, it was in %2 and is now a candidate is found only in %4}5",
886+
(bool, DeclName, const ModuleDecl*, StringRef, const ModuleDecl*, bool))
887887
ERROR(modularization_issue_decl_not_found,Fatal,
888-
"reference to %select{top-level|type}0 %1 broken by a context change; "
888+
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
889889
"%1 is not found, it was expected to be in %2",
890-
(bool, DeclName, Identifier))
890+
(bool, DeclName, const ModuleDecl*))
891+
892+
NOTE(modularization_issue_note_expected,none,
893+
"the %select{declaration|type}0 was expected to be found in module %1 at '%2'",
894+
(bool, const ModuleDecl*, StringRef))
895+
NOTE(modularization_issue_note_expected_underlying,none,
896+
"or expected to be found in the underlying module '%0' defined at '%1'",
897+
(StringRef, StringRef))
898+
NOTE(modularization_issue_note_found,none,
899+
"the %select{declaration|type}0 was actually found in module %1 at '%2'",
900+
(bool, const ModuleDecl*, StringRef))
901+
902+
NOTE(modularization_issue_stale_module,none,
903+
"the module %0 has enabled library-evolution; "
904+
"the following file may need to be deleted if the SDK was modified: '%1'",
905+
(const ModuleDecl *, StringRef))
906+
NOTE(modularization_issue_swift_version,none,
907+
"the module %0 was built with a Swift language version set to %1 "
908+
"while the current invocation uses %2; "
909+
"APINotes may change how clang declarations are imported",
910+
(const ModuleDecl *, llvm::VersionTuple, llvm::VersionTuple))
911+
NOTE(modularization_issue_audit_headers,none,
912+
"declarations in the %select{underlying|}0 clang module %1 "
913+
"may be hidden by clang preprocessor macros",
914+
(bool, const ModuleDecl*))
915+
NOTE(modularization_issue_layering_expected_local,none,
916+
"the distributed module %0 refers to the local module %1; "
917+
"this may be caused by header maps or search paths",
918+
(const ModuleDecl*, const ModuleDecl*))
919+
NOTE(modularization_issue_layering_found_local,none,
920+
"the reference may break layering; the candidate was found in "
921+
"the local module %1 for a reference from the distributed module %0",
922+
(const ModuleDecl*, const ModuleDecl*))
923+
NOTE(modularization_issue_related_modules,none,
924+
"the %select{declaration|type}0 %1 moved between related modules; "
925+
"clang preprocessor macros may affect headers shared between these modules",
926+
(bool, DeclName))
891927

892928
NOTE(modularization_issue_side_effect_extension_error,none,
893929
"could not deserialize extension",
@@ -896,9 +932,10 @@ NOTE(modularization_issue_side_effect_type_error,none,
896932
"could not deserialize type for %0",
897933
(DeclName))
898934

899-
WARNING(modularization_issue_worked_around,none,
900-
"attempting forced recovery enabled by -experimental-force-workaround-broken-modules",
901-
())
935+
NOTE(modularization_issue_worked_around,none,
936+
"attempting forced recovery enabled by "
937+
"-experimental-force-workaround-broken-modules",
938+
())
902939

903940
ERROR(reserved_member_name,none,
904941
"type member must not be named %0, since it would conflict with the"

lib/Serialization/Deserialization.cpp

Lines changed: 135 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "swift/ClangImporter/SwiftAbstractBasicReader.h"
4141
#include "swift/Serialization/SerializedModuleLoader.h"
4242
#include "clang/AST/DeclTemplate.h"
43+
#include "clang/Basic/SourceManager.h"
4344
#include "llvm/ADT/Statistic.h"
4445
#include "llvm/Support/Compiler.h"
4546
#include "llvm/Support/Debug.h"
@@ -185,10 +186,25 @@ SourceLoc ModuleFile::getSourceLoc() const {
185186
auto filename = getModuleFilename();
186187
auto bufferID = SourceMgr.getIDForBufferIdentifier(filename);
187188
if (!bufferID)
188-
bufferID = SourceMgr.addMemBufferCopy(StringRef(), filename);
189+
bufferID = SourceMgr.addMemBufferCopy("<binary format>", filename);
189190
return SourceMgr.getLocForBufferStart(*bufferID);
190191
}
191192

193+
SourceLoc ModularizationError::getSourceLoc() const {
194+
auto &SourceMgr = referenceModule->getContext().Diags.SourceMgr;
195+
auto filename = referenceModule->getModuleFilename();
196+
197+
// Synthesize some context. We don't have an actual decl here
198+
// so try to print a simple representation of the reference.
199+
std::string S;
200+
llvm::raw_string_ostream OS(S);
201+
OS << expectedModule->getName() << "." << name;
202+
203+
// If we enable these remarks by default we may want to reuse these buffers.
204+
auto bufferID = SourceMgr.addMemBufferCopy(S, filename);
205+
return SourceMgr.getLocForBufferStart(bufferID);
206+
}
207+
192208
void
193209
ModularizationError::diagnose(const ModuleFile *MF,
194210
DiagnosticBehavior limit) const {
@@ -197,18 +213,21 @@ ModularizationError::diagnose(const ModuleFile *MF,
197213
auto diagnoseError = [&](Kind errorKind) {
198214
switch (errorKind) {
199215
case Kind::DeclMoved:
200-
return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_moved,
201-
declIsType, name, expectedModuleName,
202-
foundModuleName);
216+
return ctx.Diags.diagnose(getSourceLoc(),
217+
diag::modularization_issue_decl_moved,
218+
declIsType, name, expectedModule,
219+
foundModule);
203220
case Kind::DeclKindChanged:
204221
return
205-
ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_type_changed,
206-
declIsType, name, expectedModuleName,
207-
referencedFromModuleName, foundModuleName,
208-
foundModuleName != expectedModuleName);
222+
ctx.Diags.diagnose(getSourceLoc(),
223+
diag::modularization_issue_decl_type_changed,
224+
declIsType, name, expectedModule,
225+
referenceModule->getName(), foundModule,
226+
foundModule != expectedModule);
209227
case Kind::DeclNotFound:
210-
return ctx.Diags.diagnose(MF->getSourceLoc(), diag::modularization_issue_decl_not_found,
211-
declIsType, name, expectedModuleName);
228+
return ctx.Diags.diagnose(getSourceLoc(),
229+
diag::modularization_issue_decl_not_found,
230+
declIsType, name, expectedModule);
212231
}
213232
llvm_unreachable("Unhandled ModularizationError::Kind in switch.");
214233
};
@@ -220,6 +239,103 @@ ModularizationError::diagnose(const ModuleFile *MF,
220239
// We could pass along the `path` information through notes.
221240
// However, for a top-level decl a path would just duplicate the
222241
// expected module name and the decl name from the diagnostic.
242+
243+
// Show context with relevant file paths.
244+
ctx.Diags.diagnose(SourceLoc(),
245+
diag::modularization_issue_note_expected,
246+
declIsType, expectedModule,
247+
expectedModule->getModuleSourceFilename());
248+
249+
const clang::Module *expectedUnderlying =
250+
expectedModule->findUnderlyingClangModule();
251+
if (!expectedModule->isNonSwiftModule() &&
252+
expectedUnderlying) {
253+
auto CML = ctx.getClangModuleLoader();
254+
auto &CSM = CML->getClangASTContext().getSourceManager();
255+
StringRef filename = CSM.getFilename(expectedUnderlying->DefinitionLoc);
256+
ctx.Diags.diagnose(SourceLoc(),
257+
diag::modularization_issue_note_expected_underlying,
258+
expectedUnderlying->Name,
259+
filename);
260+
}
261+
262+
if (foundModule)
263+
ctx.Diags.diagnose(SourceLoc(),
264+
diag::modularization_issue_note_found,
265+
declIsType, foundModule,
266+
foundModule->getModuleSourceFilename());
267+
268+
// A Swift language version mismatch could lead to a different set of rules
269+
// from APINotes files being applied when building the module vs when reading
270+
// from it.
271+
version::Version
272+
moduleLangVersion = referenceModule->getCompatibilityVersion(),
273+
clientLangVersion = MF->getContext().LangOpts.EffectiveLanguageVersion;
274+
ModuleDecl *referenceModuleDecl = referenceModule->getAssociatedModule();
275+
if (clientLangVersion != moduleLangVersion) {
276+
ctx.Diags.diagnose(SourceLoc(),
277+
diag::modularization_issue_swift_version,
278+
referenceModuleDecl, moduleLangVersion,
279+
clientLangVersion);
280+
}
281+
282+
// If the error is in a resilient swiftmodule adjacent to a swiftinterface,
283+
// deleting the module to rebuild from the swiftinterface may fix the issue.
284+
// Limit this suggestion to distributed Swift modules to not hint at
285+
// deleting local caches and such.
286+
bool referenceModuleIsDistributed = referenceModuleDecl &&
287+
referenceModuleDecl->isNonUserModule();
288+
if (referenceModule->getResilienceStrategy() ==
289+
ResilienceStrategy::Resilient &&
290+
referenceModuleIsDistributed) {
291+
ctx.Diags.diagnose(SourceLoc(),
292+
diag::modularization_issue_stale_module,
293+
referenceModuleDecl,
294+
referenceModule->getModuleFilename());
295+
}
296+
297+
// If the missing decl was expected to be in a clang module,
298+
// it may be hidden by some clang defined passed via `-Xcc` affecting how
299+
// headers are seen.
300+
if (expectedUnderlying) {
301+
ctx.Diags.diagnose(SourceLoc(),
302+
diag::modularization_issue_audit_headers,
303+
expectedModule->isNonSwiftModule(), expectedModule);
304+
}
305+
306+
// If the reference goes from a distributed module to a local module,
307+
// the compiler may have picked up an undesired module. We usually expect
308+
// distributed modules to only reference other distributed modules.
309+
// Local modules can reference both local modules and distributed modules.
310+
if (referenceModuleIsDistributed) {
311+
if (!expectedModule->isNonUserModule()) {
312+
ctx.Diags.diagnose(SourceLoc(),
313+
diag::modularization_issue_layering_expected_local,
314+
referenceModuleDecl, expectedModule);
315+
} else if (foundModule && !foundModule->isNonUserModule()) {
316+
ctx.Diags.diagnose(SourceLoc(),
317+
diag::modularization_issue_layering_found_local,
318+
referenceModuleDecl, foundModule);
319+
}
320+
}
321+
322+
// If a type moved between MyModule and MyModule_Private, it can be caused
323+
// by the use of `-Xcc -D` to change the API of the modules, leading to
324+
// decls moving between both modules.
325+
if (errorKind == Kind::DeclMoved ||
326+
errorKind == Kind::DeclKindChanged) {
327+
StringRef foundModuleName = foundModule->getName().str();
328+
StringRef expectedModuleName = expectedModule->getName().str();
329+
if (foundModuleName != expectedModuleName &&
330+
(foundModuleName.startswith(expectedModuleName) ||
331+
expectedModuleName.startswith(foundModuleName)) &&
332+
(expectedUnderlying ||
333+
expectedModule->findUnderlyingClangModule())) {
334+
ctx.Diags.diagnose(SourceLoc(),
335+
diag::modularization_issue_related_modules,
336+
declIsType, name);
337+
}
338+
}
223339
}
224340

225341
void TypeError::diagnose(const ModuleFile *MF) const {
@@ -1957,7 +2073,7 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
19572073
SmallVector<char, 64> strScratch;
19582074

19592075
auto errorKind = ModularizationError::Kind::DeclNotFound;
1960-
Identifier foundIn;
2076+
ModuleDecl *foundIn = nullptr;
19612077
bool isType = false;
19622078

19632079
if (recordID == XREF_TYPE_PATH_PIECE ||
@@ -2011,28 +2127,26 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
20112127
// one because otherwise it would have succeeded on the first search.
20122128
// This is usually caused by the use of poorly modularized headers.
20132129
errorKind = ModularizationError::Kind::DeclMoved;
2014-
foundIn = otherModule->getName();
2130+
foundIn = otherModule;
20152131
break;
20162132
} else if (hadAMatchBeforeFiltering) {
20172133
// Found a match that was filtered out. This may be from the same
20182134
// expected module if there's a type difference. This can be caused
20192135
// by the use of different Swift language versions between a library
20202136
// with serialized SIL and a client.
20212137
errorKind = ModularizationError::Kind::DeclKindChanged;
2022-
foundIn = otherModule->getName();
2138+
foundIn = otherModule;
20232139
break;
20242140
}
20252141
}
20262142
}
20272143

20282144
auto declName = getXRefDeclNameForError();
2029-
auto expectedIn = baseModule->getName();
2030-
auto referencedFrom = getName();
20312145
auto error = llvm::make_error<ModularizationError>(declName,
20322146
isType,
20332147
errorKind,
2034-
expectedIn,
2035-
referencedFrom,
2148+
baseModule,
2149+
this,
20362150
foundIn,
20372151
pathTrace);
20382152

@@ -2043,13 +2157,13 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
20432157
if (getContext().LangOpts.ForceWorkaroundBrokenModules &&
20442158
errorKind == ModularizationError::Kind::DeclMoved &&
20452159
!values.empty()) {
2046-
// Print the error as a remark and notify of the recovery attempt.
2047-
getContext().Diags.diagnose(getSourceLoc(),
2048-
diag::modularization_issue_worked_around);
2160+
// Print the error as a warning and notify of the recovery attempt.
20492161
llvm::handleAllErrors(std::move(error),
20502162
[&](const ModularizationError &modularError) {
2051-
modularError.diagnose(this, DiagnosticBehavior::Note);
2163+
modularError.diagnose(this, DiagnosticBehavior::Warning);
20522164
});
2165+
getContext().Diags.diagnose(SourceLoc(),
2166+
diag::modularization_issue_worked_around);
20532167
} else {
20542168
return std::move(error);
20552169
}

0 commit comments

Comments
 (0)