Skip to content

Commit e51176b

Browse files
authored
Merge pull request #62622 from DougGregor/macro-expansion-backtrace-notes
2 parents f553832 + c8dea12 commit e51176b

14 files changed

+244
-41
lines changed

include/swift/AST/ASTNode.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@ namespace swift {
7272
FUNC(Decl)
7373
FUNC(Pattern)
7474
#undef FUNC
75-
75+
76+
static inline ASTNode getFromOpaqueValue(void *ptr) {
77+
ASTNode result;
78+
result.Val = decltype(result.Val)::getFromOpaqueValue(ptr);
79+
return result;
80+
}
81+
7682
SWIFT_DEBUG_DUMP;
7783
void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const;
7884

include/swift/AST/DiagnosticEngine.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ namespace swift {
502502
}
503503

504504
void addChildNote(Diagnostic &&D);
505+
void insertChildNote(unsigned beforeIndex, Diagnostic &&D);
505506
};
506507

507508
/// Describes an in-flight diagnostic, which is currently active
@@ -1164,6 +1165,11 @@ namespace swift {
11641165
/// Send \c diag to all diagnostic consumers.
11651166
void emitDiagnostic(const Diagnostic &diag);
11661167

1168+
/// Retrieve the set of child notes that describe how the generated
1169+
/// source buffer was derived, e.g., a macro expansion backtrace.
1170+
std::vector<Diagnostic> getGeneratedSourceBufferNotes(
1171+
SourceLoc loc, Optional<unsigned> &lastBufferID);
1172+
11671173
/// Handle a new diagnostic, which will either be emitted, or added to an
11681174
/// active transaction.
11691175
void handleDiagnostic(Diagnostic &&diag);

include/swift/AST/DiagnosticsCommon.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,12 @@ REMARK(remark_save_cache, none,
214214
ERROR(unknown_attribute,none,
215215
"unknown attribute '%0'", (StringRef))
216216

217+
//------------------------------------------------------------------------------
218+
// MARK: macro diagnostics
219+
//------------------------------------------------------------------------------
220+
NOTE(in_macro_expansion,none,
221+
"in expansion of macro %0 here", (DeclName))
222+
217223
//------------------------------------------------------------------------------
218224
// MARK: bridged diagnostics
219225
//------------------------------------------------------------------------------

include/swift/AST/SourceFile.h

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,6 @@ class SourceFile final : public FileUnit {
194194
friend ASTContext;
195195

196196
public:
197-
/// For source files created to hold the source code created by expanding
198-
/// a macro, this is the AST node that describes the macro expansion.
199-
///
200-
/// The source location of this AST node is the place in the source that
201-
/// triggered the creation of the macro expansion whose resulting source
202-
/// code is in this source file. This field is only valid when
203-
/// the \c SourceFileKind is \c MacroExpansion.
204-
const ASTNode macroExpansion;
205-
206197
/// Appends the given declaration to the end of the top-level decls list. Do
207198
/// not add any additional uses of this function.
208199
void addTopLevelDecl(Decl *d);
@@ -327,8 +318,7 @@ class SourceFile final : public FileUnit {
327318
llvm::StringMap<SourceFilePathInfo> getInfoForUsedFilePaths() const;
328319

329320
SourceFile(ModuleDecl &M, SourceFileKind K, Optional<unsigned> bufferID,
330-
ParsingOptions parsingOpts = {}, bool isPrimary = false,
331-
ASTNode macroExpansion = ASTNode());
321+
ParsingOptions parsingOpts = {}, bool isPrimary = false);
332322

333323
~SourceFile();
334324

@@ -489,6 +479,15 @@ class SourceFile final : public FileUnit {
489479
return BufferID;
490480
}
491481

482+
/// For source files created to hold the source code created by expanding
483+
/// a macro, this is the AST node that describes the macro expansion.
484+
///
485+
/// The source location of this AST node is the place in the source that
486+
/// triggered the creation of the macro expansion whose resulting source
487+
/// code is in this source file. This will only produce a non-null value when
488+
/// the \c SourceFileKind is \c MacroExpansion.
489+
ASTNode getMacroExpansion() const;
490+
492491
/// When this source file is enclosed within another source file, for example
493492
/// because it describes a macro expansion, return the source file it was
494493
/// enclosed in.

include/swift/Basic/SourceManager.h

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,40 @@
2323

2424
namespace swift {
2525

26+
/// Augments a buffer that was created specifically to hold generated source
27+
/// code with the reasons for it being generated.
28+
class GeneratedSourceInfo {
29+
public:
30+
/// The kind of generated source code.
31+
enum Kind {
32+
/// The expansion of a macro.
33+
MacroExpansion,
34+
35+
/// A new function body that is replacing an existing function body.
36+
ReplacedFunctionBody,
37+
} kind;
38+
39+
/// The buffer ID for the enclosing buffer, in which originalSourceRange
40+
/// resides.
41+
unsigned originalBufferID;
42+
43+
/// The source range in the enclosing buffer where this source was generated.
44+
///
45+
/// This could point at a macro expansion or at the implicit location at
46+
/// which source code was generated. Conceptually, one can think of the
47+
/// buffer described by a \c GeneratedSource instance as replacing the
48+
/// code in the \c originalSourceRange.
49+
SourceRange originalSourceRange;
50+
51+
/// The source range in the generated-source buffer where the generated
52+
/// code exists. This might be a subrange of the buffer containing the
53+
/// generated source, but it will never be from a different buffer.
54+
SourceRange generatedSourceRange;
55+
56+
/// The opaque pointer for an ASTNode.
57+
void *astNode;
58+
};
59+
2660
/// This class manages and owns source buffers.
2761
class SourceManager {
2862
public:
@@ -51,7 +85,10 @@ class SourceManager {
5185
/// to speed up stats.
5286
mutable llvm::DenseMap<StringRef, llvm::vfs::Status> StatusCache;
5387

54-
/// Holds replaced ranges. Keys are orignal ranges, and values are new ranges
88+
/// Holds generated source information, indexed by the buffer ID.
89+
llvm::DenseMap<unsigned, GeneratedSourceInfo> GeneratedSourceInfos;
90+
91+
/// Holds replaced ranges. Keys are original ranges, and values are new ranges
5592
/// in different buffers. This is used for code completion and ASTContext
5693
/// reusing compilation.
5794
llvm::DenseMap<SourceRange, SourceRange> ReplacedRanges;
@@ -108,9 +145,12 @@ class SourceManager {
108145
const llvm::DenseMap<SourceRange, SourceRange> &getReplacedRanges() const {
109146
return ReplacedRanges;
110147
}
111-
void setReplacedRange(SourceRange Orig, SourceRange New) {
112-
ReplacedRanges[Orig] = New;
113-
}
148+
149+
/// Set the generated source information associated with a given buffer.
150+
void setGeneratedSourceInfo(unsigned bufferID, GeneratedSourceInfo);
151+
152+
/// Retrieve the generated source information for the given buffer.
153+
Optional<GeneratedSourceInfo> getGeneratedSourceInfo(unsigned bufferID) const;
114154

115155
/// Record the starting source location of a regex literal.
116156
void recordRegexLiteralStartLoc(SourceLoc loc) {

lib/AST/ASTScopeCreation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
248248
ScopeCreator *scopeCreator)
249249
: SF(SF), scopeCreator(scopeCreator) {
250250
if (auto enclosingSF = SF->getEnclosingSourceFile()) {
251-
SourceLoc parentLoc = SF->macroExpansion.getStartLoc();
251+
SourceLoc parentLoc = SF->getMacroExpansion().getStartLoc();
252252
if (auto parentScope = findStartingScopeForLookup(enclosingSF, parentLoc)) {
253253
parentAndWasExpanded.setPointer(const_cast<ASTScopeImpl *>(parentScope));
254254
}

lib/AST/DiagnosticEngine.cpp

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/Decl.h"
2222
#include "swift/AST/DiagnosticSuppression.h"
2323
#include "swift/AST/DiagnosticsCommon.h"
24+
#include "swift/AST/Expr.h"
2425
#include "swift/AST/Module.h"
2526
#include "swift/AST/Pattern.h"
2627
#include "swift/AST/PrintOptions.h"
@@ -1267,10 +1268,83 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
12671268
diagnostic.isChildNote());
12681269
}
12691270

1271+
std::vector<Diagnostic> DiagnosticEngine::getGeneratedSourceBufferNotes(
1272+
SourceLoc loc, Optional<unsigned> &lastBufferID
1273+
) {
1274+
// The set of child notes we're building up.
1275+
std::vector<Diagnostic> childNotes;
1276+
1277+
// If the location is invalid, there's nothing to do.
1278+
if (loc.isInvalid())
1279+
return childNotes;
1280+
1281+
// If we already emitted these notes for a prior part of the diagnostic,
1282+
// don't do so again.
1283+
auto currentBufferID = SourceMgr.findBufferContainingLoc(loc);
1284+
if (currentBufferID == lastBufferID)
1285+
return childNotes;
1286+
1287+
// Keep track of the last buffer ID we considered.
1288+
lastBufferID = currentBufferID;
1289+
1290+
SourceLoc currentLoc = loc;
1291+
do {
1292+
auto generatedInfo = SourceMgr.getGeneratedSourceInfo(currentBufferID);
1293+
if (!generatedInfo)
1294+
return childNotes;
1295+
1296+
ASTNode expansionNode =
1297+
ASTNode::getFromOpaqueValue(generatedInfo->astNode);
1298+
1299+
switch (generatedInfo->kind) {
1300+
case GeneratedSourceInfo::MacroExpansion: {
1301+
SourceRange origRange = expansionNode.getSourceRange();
1302+
DeclName macroName;
1303+
if (auto expansionExpr = dyn_cast_or_null<MacroExpansionExpr>(
1304+
expansionNode.dyn_cast<Expr *>())) {
1305+
macroName = expansionExpr->getMacroName().getFullName();
1306+
} else {
1307+
auto expansionDecl =
1308+
cast<MacroExpansionDecl>(expansionNode.get<Decl *>());
1309+
macroName = expansionDecl->getMacro().getFullName();
1310+
}
1311+
1312+
Diagnostic expansionNote(diag::in_macro_expansion, macroName);
1313+
expansionNote.setLoc(origRange.Start);
1314+
expansionNote.addRange(
1315+
Lexer::getCharSourceRangeFromSourceRange(SourceMgr, origRange));
1316+
expansionNote.setIsChildNote(true);
1317+
childNotes.push_back(std::move(expansionNote));
1318+
break;
1319+
}
1320+
1321+
case GeneratedSourceInfo::ReplacedFunctionBody:
1322+
return childNotes;
1323+
}
1324+
1325+
// Walk up the stack.
1326+
currentLoc = expansionNode.getStartLoc();
1327+
currentBufferID = SourceMgr.findBufferContainingLoc(currentLoc);
1328+
} while (true);
1329+
}
1330+
12701331
void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
1332+
Optional<unsigned> lastBufferID;
1333+
1334+
ArrayRef<Diagnostic> childNotes = diagnostic.getChildNotes();
1335+
std::vector<Diagnostic> extendedChildNotes;
1336+
12711337
if (auto info = diagnosticInfoForDiagnostic(diagnostic)) {
1338+
// If the diagnostic location is within a buffer containing generated
1339+
// source code, add child notes showing where the generation occurred.
1340+
extendedChildNotes = getGeneratedSourceBufferNotes(info->Loc, lastBufferID);
1341+
if (!extendedChildNotes.empty()) {
1342+
extendedChildNotes.insert(extendedChildNotes.end(),
1343+
childNotes.begin(), childNotes.end());
1344+
childNotes = extendedChildNotes;
1345+
}
1346+
12721347
SmallVector<DiagnosticInfo, 1> childInfo;
1273-
auto childNotes = diagnostic.getChildNotes();
12741348
for (unsigned i : indices(childNotes)) {
12751349
auto child = diagnosticInfoForDiagnostic(childNotes[i]);
12761350
assert(child);
@@ -1302,7 +1376,7 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
13021376
// For compatibility with DiagnosticConsumers which don't know about child
13031377
// notes. These can be ignored by consumers which do take advantage of the
13041378
// grouping.
1305-
for (auto &childNote : diagnostic.getChildNotes())
1379+
for (auto &childNote : childNotes)
13061380
emitDiagnostic(childNote);
13071381
}
13081382

lib/AST/Module.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -877,10 +877,20 @@ void SourceFile::lookupClassMembers(ImportPath::Access accessPath,
877877
cache.lookupClassMembers(accessPath, consumer);
878878
}
879879

880-
SourceFile *SourceFile::getEnclosingSourceFile() const {
880+
ASTNode SourceFile::getMacroExpansion() const {
881881
if (Kind != SourceFileKind::MacroExpansion)
882882
return nullptr;
883883

884+
auto genInfo =
885+
*getASTContext().SourceMgr.getGeneratedSourceInfo(*getBufferID());
886+
return ASTNode::getFromOpaqueValue(genInfo.astNode);
887+
}
888+
889+
SourceFile *SourceFile::getEnclosingSourceFile() const {
890+
auto macroExpansion = getMacroExpansion();
891+
if (!macroExpansion)
892+
return nullptr;
893+
884894
auto sourceLoc = macroExpansion.getStartLoc();
885895
return getParentModule()->getSourceFileContainingLocation(sourceLoc);
886896
}
@@ -3263,17 +3273,13 @@ ModuleDecl::computeFileIDMap(bool shouldDiagnose) const {
32633273

32643274
SourceFile::SourceFile(ModuleDecl &M, SourceFileKind K,
32653275
Optional<unsigned> bufferID,
3266-
ParsingOptions parsingOpts, bool isPrimary,
3267-
ASTNode macroExpansion)
3276+
ParsingOptions parsingOpts, bool isPrimary)
32683277
: FileUnit(FileUnitKind::Source, M), BufferID(bufferID ? *bufferID : -1),
3269-
ParsingOpts(parsingOpts), IsPrimary(isPrimary),
3270-
macroExpansion(macroExpansion), Kind(K) {
3278+
ParsingOpts(parsingOpts), IsPrimary(isPrimary), Kind(K) {
32713279
M.getASTContext().addDestructorCleanup(*this);
32723280

32733281
assert(!IsPrimary || M.isMainModule() &&
32743282
"A primary cannot appear outside the main module");
3275-
assert(macroExpansion.isNull() == (K != SourceFileKind::MacroExpansion) &&
3276-
"Macro expansions always need an expansion node");
32773283

32783284
if (isScriptMode()) {
32793285
bool problem = M.registerEntryPointFile(this, SourceLoc(), None);

lib/Basic/SourceLoc.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,32 @@ StringRef SourceManager::extractText(CharSourceRange Range,
252252
Range.getByteLength());
253253
}
254254

255+
void SourceManager::setGeneratedSourceInfo(
256+
unsigned bufferID, GeneratedSourceInfo info
257+
) {
258+
assert(GeneratedSourceInfos.count(bufferID) == 0);
259+
GeneratedSourceInfos[bufferID] = info;
260+
261+
switch (info.kind) {
262+
case GeneratedSourceInfo::MacroExpansion:
263+
break;
264+
265+
case GeneratedSourceInfo::ReplacedFunctionBody:
266+
// Keep track of the replaced range.
267+
ReplacedRanges[info.originalSourceRange] = info.generatedSourceRange;
268+
break;
269+
}
270+
}
271+
272+
Optional<GeneratedSourceInfo> SourceManager::getGeneratedSourceInfo(
273+
unsigned bufferID
274+
) const {
275+
auto known = GeneratedSourceInfos.find(bufferID);
276+
if (known == GeneratedSourceInfos.end())
277+
return None;
278+
return known->second;
279+
}
280+
255281
Optional<unsigned>
256282
SourceManager::findBufferContainingLocInternal(SourceLoc Loc) const {
257283
assert(Loc.isValid());

lib/IDETool/CompileInstance.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,16 @@ void retypeCheckFunctionBody(AbstractFunctionDecl *func,
189189
SourceRange newRange{rangeStartLoc, rangeEndLoc};
190190

191191
// Reset the body range of the function decl, and re-typecheck it.
192-
origSM.setReplacedRange(func->getOriginalBodySourceRange(), newRange);
192+
origSM.setGeneratedSourceInfo(
193+
sliceBufferID,
194+
GeneratedSourceInfo{
195+
GeneratedSourceInfo::ReplacedFunctionBody,
196+
*func->getParentSourceFile()->getBufferID(),
197+
func->getOriginalBodySourceRange(),
198+
newRange,
199+
func
200+
}
201+
);
193202
func->setBodyToBeReparsed(newRange);
194203
(void)func->getTypecheckedBody();
195204
}

0 commit comments

Comments
 (0)