Skip to content

Commit 22c3101

Browse files
committed
Use pretty-printed decls in cycle diagnostics
This commit makes the pretty-printing behavior of `DiagnosticEngine` easily accessible via a new `Decl::getLocByPrintingIfNeeded()` call and uses that call in various places related to `extractNearestSourceLoc()`, ultimately improving diagnostics for cycles involving serialized declarations. Note that the test case added here should probably not cause a circularity error; SR-14324 tracks this bug.
1 parent bff82ab commit 22c3101

File tree

9 files changed

+221
-136
lines changed

9 files changed

+221
-136
lines changed

include/swift/AST/AnyFunctionRef.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ class AnyFunctionRef {
212212

213213
SourceLoc getLoc() const {
214214
if (auto afd = TheFunction.dyn_cast<AbstractFunctionDecl *>()) {
215-
return afd->getLoc();
215+
return afd->getLocByPrintingIfNeeded();
216216
}
217217
if (auto ce = TheFunction.dyn_cast<AbstractClosureExpr *>()) {
218218
return ce->getLoc();

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,11 @@ class alignas(1 << DeclAlignInBits) Decl {
804804
/// in diagnostics.
805805
SourceLoc getLoc(bool SerializedOK = true) const;
806806

807+
/// Returns a source location for use in diagnostics. If necessary, this
808+
/// method will ask the \c DiagnosticEngine to print the declaration into a
809+
/// source buffer and use that rather than returning an invalid \c SourceLoc.
810+
SourceLoc getLocByPrintingIfNeeded() const;
811+
807812
/// Returns the source range of the entire declaration.
808813
SourceRange getSourceRange() const;
809814

include/swift/AST/DiagnosticEngine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,10 @@ namespace swift {
10171017
//// \c finishProcessing.
10181018
bool finishProcessing();
10191019

1020+
/// Gets a source location for the indicated declaration, if necessary by
1021+
/// pretty-printing it using ASTPrinter.
1022+
SourceLoc getDiagnosticLocForDecl(const Decl *decl);
1023+
10201024
/// Format the given diagnostic text and place the result in the given
10211025
/// buffer.
10221026
static void formatDiagnosticText(

lib/AST/Decl.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,10 @@ Result->X = SM.getLocFromExternalSource(Locs->SourceFilePath, Locs->X.Line, \
630630
return Result;
631631
}
632632

633+
SourceLoc Decl::getLocByPrintingIfNeeded() const {
634+
return getASTContext().Diags.getDiagnosticLocForDecl(this);
635+
}
636+
633637
StringRef Decl::getAlternateModuleName() const {
634638
for (auto *Att: Attrs) {
635639
if (auto *OD = dyn_cast<OriginallyDefinedInAttr>(Att)) {
@@ -8185,7 +8189,7 @@ void swift::simple_display(llvm::raw_ostream &out, AccessorKind kind) {
81858189
}
81868190

81878191
SourceLoc swift::extractNearestSourceLoc(const Decl *decl) {
8188-
auto loc = decl->getLoc();
8192+
auto loc = decl->getLocByPrintingIfNeeded();
81898193
if (loc.isValid())
81908194
return loc;
81918195

lib/AST/DiagnosticEngine.cpp

Lines changed: 140 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,143 @@ static AccessLevel getBufferAccessLevel(const Decl *decl) {
920920
return level;
921921
}
922922

923+
SourceLoc DiagnosticEngine::getDiagnosticLocForDecl(const Decl *decl) {
924+
if (!decl)
925+
return SourceLoc();
926+
927+
if (decl->getLoc().isValid())
928+
return decl->getLoc();
929+
930+
// There is no location we can point to. Pretty-print the declaration
931+
// so we can point to it.
932+
SourceLoc ppLoc = PrettyPrintedDeclarations[decl];
933+
if (ppLoc.isInvalid()) {
934+
class TrackingPrinter : public StreamPrinter {
935+
SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries;
936+
AccessLevel bufferAccessLevel;
937+
938+
public:
939+
TrackingPrinter(
940+
SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries,
941+
raw_ostream &OS, AccessLevel bufferAccessLevel) :
942+
StreamPrinter(OS), Entries(Entries),
943+
bufferAccessLevel(bufferAccessLevel) {}
944+
945+
void printDeclLoc(const Decl *D) override {
946+
if (getBufferAccessLevel(D) == bufferAccessLevel)
947+
Entries.push_back({ D, OS.tell() });
948+
}
949+
};
950+
SmallVector<std::pair<const Decl *, uint64_t>, 8> entries;
951+
llvm::SmallString<128> buffer;
952+
llvm::SmallString<128> bufferName;
953+
{
954+
// The access level of the buffer we want to print. Declarations below
955+
// this access level will be omitted from the buffer; declarations
956+
// above it will be printed, but (except for Open declarations in the
957+
// Public buffer) will not be recorded in PrettyPrintedDeclarations as
958+
// the "true" SourceLoc for the declaration.
959+
AccessLevel bufferAccessLevel = getBufferAccessLevel(decl);
960+
961+
// Figure out which declaration to print. It's the top-most
962+
// declaration (not a module).
963+
const Decl *ppDecl = decl;
964+
auto dc = decl->getDeclContext();
965+
966+
// FIXME: Horrible, horrible hackaround. We're not getting a
967+
// DeclContext everywhere we should.
968+
if (!dc) {
969+
return SourceLoc();
970+
}
971+
972+
while (!dc->isModuleContext()) {
973+
switch (dc->getContextKind()) {
974+
case DeclContextKind::Module:
975+
llvm_unreachable("Not in a module context!");
976+
break;
977+
978+
case DeclContextKind::FileUnit:
979+
case DeclContextKind::TopLevelCodeDecl:
980+
break;
981+
982+
case DeclContextKind::ExtensionDecl:
983+
ppDecl = cast<ExtensionDecl>(dc);
984+
break;
985+
986+
case DeclContextKind::GenericTypeDecl:
987+
ppDecl = cast<GenericTypeDecl>(dc);
988+
break;
989+
990+
case DeclContextKind::SerializedLocal:
991+
case DeclContextKind::Initializer:
992+
case DeclContextKind::AbstractClosureExpr:
993+
case DeclContextKind::AbstractFunctionDecl:
994+
case DeclContextKind::SubscriptDecl:
995+
case DeclContextKind::EnumElementDecl:
996+
break;
997+
}
998+
999+
dc = dc->getParent();
1000+
}
1001+
1002+
// Build the module name path (in reverse), which we use to
1003+
// build the name of the buffer.
1004+
SmallVector<StringRef, 4> nameComponents;
1005+
while (dc) {
1006+
nameComponents.push_back(cast<ModuleDecl>(dc)->getName().str());
1007+
dc = dc->getParent();
1008+
}
1009+
1010+
for (unsigned i = nameComponents.size(); i; --i) {
1011+
bufferName += nameComponents[i-1];
1012+
bufferName += '.';
1013+
}
1014+
1015+
if (auto value = dyn_cast<ValueDecl>(ppDecl)) {
1016+
bufferName += value->getBaseName().userFacingName();
1017+
} else if (auto ext = dyn_cast<ExtensionDecl>(ppDecl)) {
1018+
bufferName += ext->getExtendedType().getString();
1019+
}
1020+
1021+
// If we're using a lowered access level, give the buffer a distinct
1022+
// name.
1023+
if (bufferAccessLevel != AccessLevel::Public) {
1024+
assert(bufferAccessLevel < AccessLevel::Public
1025+
&& "Above-public access levels should use public buffer");
1026+
bufferName += " (";
1027+
bufferName += getAccessLevelSpelling(bufferAccessLevel);
1028+
bufferName += ")";
1029+
}
1030+
1031+
// Pretty-print the declaration we've picked.
1032+
llvm::raw_svector_ostream out(buffer);
1033+
TrackingPrinter printer(entries, out, bufferAccessLevel);
1034+
llvm::SaveAndRestore<bool> isPrettyPrinting(IsPrettyPrintingDecl, true);
1035+
ppDecl->print(
1036+
printer,
1037+
PrintOptions::printForDiagnostics(
1038+
bufferAccessLevel,
1039+
decl->getASTContext().TypeCheckerOpts.PrintFullConvention));
1040+
}
1041+
1042+
// Build a buffer with the pretty-printed declaration.
1043+
auto bufferID = SourceMgr.addMemBufferCopy(buffer, bufferName);
1044+
auto memBufferStartLoc = SourceMgr.getLocForBufferStart(bufferID);
1045+
1046+
// Go through all of the pretty-printed entries and record their
1047+
// locations.
1048+
for (auto entry : entries) {
1049+
PrettyPrintedDeclarations[entry.first] =
1050+
memBufferStartLoc.getAdvancedLoc(entry.second);
1051+
}
1052+
1053+
// Grab the pretty-printed location.
1054+
ppLoc = PrettyPrintedDeclarations[decl];
1055+
}
1056+
1057+
return ppLoc;
1058+
}
1059+
9231060
Optional<DiagnosticInfo>
9241061
DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
9251062
auto behavior = state.determineBehavior(diagnostic);
@@ -931,140 +1068,9 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
9311068
if (loc.isInvalid() && diagnostic.getDecl()) {
9321069
const Decl *decl = diagnostic.getDecl();
9331070
// If a declaration was provided instead of a location, and that declaration
934-
// has a location we can point to, use that location.
935-
loc = decl->getLoc();
936-
937-
if (loc.isInvalid()) {
938-
// There is no location we can point to. Pretty-print the declaration
939-
// so we can point to it.
940-
SourceLoc ppLoc = PrettyPrintedDeclarations[decl];
941-
if (ppLoc.isInvalid()) {
942-
class TrackingPrinter : public StreamPrinter {
943-
SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries;
944-
AccessLevel bufferAccessLevel;
945-
946-
public:
947-
TrackingPrinter(
948-
SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries,
949-
raw_ostream &OS, AccessLevel bufferAccessLevel) :
950-
StreamPrinter(OS), Entries(Entries),
951-
bufferAccessLevel(bufferAccessLevel) {}
952-
953-
void printDeclLoc(const Decl *D) override {
954-
if (getBufferAccessLevel(D) == bufferAccessLevel)
955-
Entries.push_back({ D, OS.tell() });
956-
}
957-
};
958-
SmallVector<std::pair<const Decl *, uint64_t>, 8> entries;
959-
llvm::SmallString<128> buffer;
960-
llvm::SmallString<128> bufferName;
961-
{
962-
// The access level of the buffer we want to print. Declarations below
963-
// this access level will be omitted from the buffer; declarations
964-
// above it will be printed, but (except for Open declarations in the
965-
// Public buffer) will not be recorded in PrettyPrintedDeclarations as
966-
// the "true" SourceLoc for the declaration.
967-
AccessLevel bufferAccessLevel = getBufferAccessLevel(decl);
968-
969-
// Figure out which declaration to print. It's the top-most
970-
// declaration (not a module).
971-
const Decl *ppDecl = decl;
972-
auto dc = decl->getDeclContext();
973-
974-
// FIXME: Horrible, horrible hackaround. We're not getting a
975-
// DeclContext everywhere we should.
976-
if (!dc) {
977-
return None;
978-
}
979-
980-
while (!dc->isModuleContext()) {
981-
switch (dc->getContextKind()) {
982-
case DeclContextKind::Module:
983-
llvm_unreachable("Not in a module context!");
984-
break;
985-
986-
case DeclContextKind::FileUnit:
987-
case DeclContextKind::TopLevelCodeDecl:
988-
break;
989-
990-
case DeclContextKind::ExtensionDecl:
991-
ppDecl = cast<ExtensionDecl>(dc);
992-
break;
993-
994-
case DeclContextKind::GenericTypeDecl:
995-
ppDecl = cast<GenericTypeDecl>(dc);
996-
break;
997-
998-
case DeclContextKind::SerializedLocal:
999-
case DeclContextKind::Initializer:
1000-
case DeclContextKind::AbstractClosureExpr:
1001-
case DeclContextKind::AbstractFunctionDecl:
1002-
case DeclContextKind::SubscriptDecl:
1003-
case DeclContextKind::EnumElementDecl:
1004-
break;
1005-
}
1006-
1007-
dc = dc->getParent();
1008-
}
1009-
1010-
// Build the module name path (in reverse), which we use to
1011-
// build the name of the buffer.
1012-
SmallVector<StringRef, 4> nameComponents;
1013-
while (dc) {
1014-
nameComponents.push_back(cast<ModuleDecl>(dc)->getName().str());
1015-
dc = dc->getParent();
1016-
}
1017-
1018-
for (unsigned i = nameComponents.size(); i; --i) {
1019-
bufferName += nameComponents[i-1];
1020-
bufferName += '.';
1021-
}
1022-
1023-
if (auto value = dyn_cast<ValueDecl>(ppDecl)) {
1024-
bufferName += value->getBaseName().userFacingName();
1025-
} else if (auto ext = dyn_cast<ExtensionDecl>(ppDecl)) {
1026-
bufferName += ext->getExtendedType().getString();
1027-
}
1028-
1029-
// If we're using a lowered access level, give the buffer a distinct
1030-
// name.
1031-
if (bufferAccessLevel != AccessLevel::Public) {
1032-
assert(bufferAccessLevel < AccessLevel::Public
1033-
&& "Above-public access levels should use public buffer");
1034-
bufferName += " (";
1035-
bufferName += getAccessLevelSpelling(bufferAccessLevel);
1036-
bufferName += ")";
1037-
}
1038-
1039-
// Pretty-print the declaration we've picked.
1040-
llvm::raw_svector_ostream out(buffer);
1041-
TrackingPrinter printer(entries, out, bufferAccessLevel);
1042-
llvm::SaveAndRestore<bool> isPrettyPrinting(
1043-
IsPrettyPrintingDecl, true);
1044-
ppDecl->print(
1045-
printer,
1046-
PrintOptions::printForDiagnostics(
1047-
bufferAccessLevel,
1048-
decl->getASTContext().TypeCheckerOpts.PrintFullConvention));
1049-
}
1050-
1051-
// Build a buffer with the pretty-printed declaration.
1052-
auto bufferID = SourceMgr.addMemBufferCopy(buffer, bufferName);
1053-
auto memBufferStartLoc = SourceMgr.getLocForBufferStart(bufferID);
1054-
1055-
// Go through all of the pretty-printed entries and record their
1056-
// locations.
1057-
for (auto entry : entries) {
1058-
PrettyPrintedDeclarations[entry.first] =
1059-
memBufferStartLoc.getAdvancedLoc(entry.second);
1060-
}
1061-
1062-
// Grab the pretty-printed location.
1063-
ppLoc = PrettyPrintedDeclarations[decl];
1064-
}
1065-
1066-
loc = ppLoc;
1067-
}
1071+
// has a location we can point to (or we can generate one), use that
1072+
// location.
1073+
loc = getDiagnosticLocForDecl(decl);
10681074
}
10691075

10701076
return DiagnosticInfo(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#define MY_ENUM(_name) \
2+
enum _name _name; \
3+
enum __attribute__((enum_extensibility(open))) _name
4+
5+
typedef MY_ENUM(MyWholeNumber) {
6+
MyWholeNumberZero = 0,
7+
MyWholeNumberOne,
8+
MyWholeNumberTwo,
9+
MyWholeNumberThree,
10+
MyWholeNumberFour,
11+
MyWholeNumberFive,
12+
MyWholeNumberSix,
13+
MyWholeNumberSeven,
14+
MyWholeNumberEight
15+
};
16+
17+
@interface MyNumberwang
18+
- (_Nonnull instancetype)init;
19+
@property (readonly) MyWholeNumber number;
20+
@end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@_exported import CircularLibrary
2+
3+
extension MyWholeNumber: Strideable {
4+
public typealias Stride = RawValue.Stride
5+
6+
public func advanced(by n: Stride) -> MyWholeNumber {
7+
return MyWholeNumber(rawValue: rawValue.advanced(by: n))!
8+
}
9+
10+
public func distance(to other: MyWholeNumber) -> Stride {
11+
return rawValue.distance(to: other.rawValue)
12+
}
13+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module CircularLibrary {
2+
umbrella header "CircularLibrary.h"
3+
export *
4+
}

0 commit comments

Comments
 (0)