Skip to content

Commit 07319d0

Browse files
committed
[ASTScope] Teach ASTScope lookup to find source locations inside macro-expanded
scopes. ASTScope lookup can no longer assume that all scopes are within the same source file. Scope trees can now contain scopes that are in macro expansion buffers, which live in different source files with independent source ranges from the root of a given scope tree. To handle this when searching for a scope containing a given source location, ASTScope lookup needs to walk up the chain of macro expansion buffers to find the lowest common buffer in which to compare source locations. This fixes a number of issues with unqualified lookup into and from within macro-expanded code.
1 parent 14f1172 commit 07319d0

File tree

6 files changed

+93
-7
lines changed

6 files changed

+93
-7
lines changed

lib/AST/ASTScopeLookup.cpp

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/AST/Stmt.h"
3131
#include "swift/AST/TypeRepr.h"
3232
#include "swift/Basic/STLExtras.h"
33+
#include "swift/Parse/Lexer.h"
3334
#include "llvm/Support/Compiler.h"
3435

3536
using namespace swift;
@@ -90,14 +91,78 @@ static SourceLoc translateLocForReplacedRange(SourceManager &sourceMgr,
9091
NullablePtr<ASTScopeImpl>
9192
ASTScopeImpl::findChildContaining(SourceLoc loc,
9293
SourceManager &sourceMgr) const {
94+
auto *moduleDecl = this->getSourceFile()->getParentModule();
95+
auto *locSourceFile = moduleDecl->getSourceFileContainingLocation(loc);
96+
9397
// Use binary search to find the child that contains this location.
9498
auto *const *child = llvm::lower_bound(
9599
getChildren(), loc,
96-
[&sourceMgr](const ASTScopeImpl *scope, SourceLoc loc) {
100+
[&](const ASTScopeImpl *scope, SourceLoc loc) {
97101
auto rangeOfScope = scope->getCharSourceRangeOfScope(sourceMgr);
98102
ASTScopeAssert(!sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(),
99103
rangeOfScope.getStart()),
100104
"Source range is backwards");
105+
106+
// If the scope source range and the loc are in two different source
107+
// files, one or both of them are in a macro expansion buffer.
108+
109+
// Note that `scope->getSourceFile()` returns the root of the source tree,
110+
// not the source file containing the location of the ASTScope.
111+
auto scopeStart = scope->getSourceRangeOfThisASTNode().Start;
112+
auto *scopeSourceFile = moduleDecl->getSourceFileContainingLocation(scopeStart);
113+
114+
if (scopeSourceFile != locSourceFile) {
115+
// To compare a source location that is possibly inside a macro expansion
116+
// with a source range that is also possibly in a macro expansion (not
117+
// necessarily the same one as before) we need to find the LCA in the
118+
// source file tree of macro expansions, and compare the original source
119+
// ranges within that common ancestor. We can't walk all the way up to the
120+
// source file containing the parent scope we're searching the children of,
121+
// because two independent (possibly nested) macro expansions can have the
122+
// same original source range in that file; freestanding and peer macros
123+
// mean that we can have arbitrarily nested macro expansions that all add
124+
// declarations to the same scope, that all originate from a single macro
125+
// invocation in the original source file.
126+
127+
// A map from enclosing source files to original source ranges of the macro
128+
// expansions within that file, recording the chain of macro expansions for
129+
// the given scope.
130+
llvm::SmallDenseMap<const SourceFile *, SourceRange> scopeExpansions;
131+
132+
// Walk up the chain of macro expansion buffers for the scope, recording the
133+
// original source range of the macro expansion along the way using generated
134+
// source info.
135+
auto *scopeExpansion = scopeSourceFile;
136+
scopeExpansions[scopeExpansion] = scope->getSourceRangeOfThisASTNode();
137+
while (auto *ancestor = scopeExpansion->getEnclosingSourceFile()) {
138+
auto generatedInfo =
139+
sourceMgr.getGeneratedSourceInfo(*scopeExpansion->getBufferID());
140+
scopeExpansions[ancestor] = generatedInfo->originalSourceRange;
141+
scopeExpansion = ancestor;
142+
}
143+
144+
// Walk up the chain of macro expansion buffers for the source loc we're
145+
// searching for to find the LCA using `scopeExpansions`.
146+
auto *potentialLCA = locSourceFile;
147+
auto expansionLoc = loc;
148+
while (potentialLCA) {
149+
auto scopeExpansion = scopeExpansions.find(potentialLCA);
150+
if (scopeExpansion != scopeExpansions.end()) {
151+
// Take the original expansion range within the LCA of the loc and
152+
// the scope to compare.
153+
rangeOfScope =
154+
Lexer::getCharSourceRangeFromSourceRange(sourceMgr, scopeExpansion->second);
155+
loc = expansionLoc;
156+
break;
157+
}
158+
159+
auto generatedInfo =
160+
sourceMgr.getGeneratedSourceInfo(*potentialLCA->getBufferID());
161+
expansionLoc = generatedInfo->originalSourceRange.Start;
162+
potentialLCA = potentialLCA->getEnclosingSourceFile();
163+
}
164+
}
165+
101166
loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc);
102167
return (rangeOfScope.getEnd() == loc ||
103168
sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(), loc));

lib/AST/ASTVerifier.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3692,6 +3692,18 @@ class Verifier : public ASTWalker {
36923692
return;
36933693
} else if (Decl *D = Parent.getAsDecl()) {
36943694
Enclosing = D->getSourceRange();
3695+
3696+
// If the current source range is in a macro expansion buffer, its enclosing
3697+
// context can be in the source file where the macro expansion originated. In
3698+
// this case, grab the source range of the original ASTNode that was expanded.
3699+
if (!Ctx.SourceMgr.rangeContains(Enclosing, Current)) {
3700+
auto *expansionBuffer =
3701+
D->getModuleContext()->getSourceFileContainingLocation(Current.Start);
3702+
if (auto expansion = expansionBuffer->getMacroExpansion()) {
3703+
Current = expansion.getSourceRange();
3704+
}
3705+
}
3706+
36953707
if (D->isImplicit())
36963708
return;
36973709
// FIXME: This is not working well for decl parents.

lib/AST/DeclContext.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,11 @@ void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint,
984984
if (getASTContext().SourceMgr.isBeforeInBuffer(prevEnd, nextStart))
985985
return;
986986

987+
// Synthesized member macros can add new members in a macro expansion buffer.
988+
auto *memberSourceFile = member->getInnermostDeclContext()->getParentSourceFile();
989+
if (memberSourceFile->getFulfilledMacroRole() == MacroRole::SynthesizedMembers)
990+
return;
991+
987992
llvm::errs() << "Source ranges out of order in addMember():\n";
988993
prev->dump(llvm::errs());
989994
next->dump(llvm::errs());

lib/Sema/TypeCheckMacros.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1242,8 +1242,10 @@ bool swift::expandSynthesizedMembers(CustomAttr *attr, MacroDecl *macro,
12421242
bool synthesizedMembers = false;
12431243
auto topLevelDecls = macroSourceFile->getTopLevelDecls();
12441244
for (auto member : topLevelDecls) {
1245+
// Note that synthesized members are not considered implicit. They have
1246+
// proper source ranges that should be validated, and ASTScope does not
1247+
// expand implicit scopes to the parent scope tree.
12451248
member->setDeclContext(decl->getInnermostDeclContext());
1246-
member->setImplicit();
12471249

12481250
if (auto *nominal = dyn_cast<NominalTypeDecl>(decl)) {
12491251
nominal->addMember(member);

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,15 @@ public struct AddMembers: MemberDeclarationMacro {
360360

361361
let storageVariable: VariableDeclSyntax =
362362
"""
363-
var storage = Storage()
363+
private var storage = Storage()
364364
"""
365365

366366
let instanceMethod: FunctionDeclSyntax =
367367
"""
368-
func method() { print("synthesized method") }
368+
func getStorage() -> Storage {
369+
print("synthesized method")
370+
return storage
371+
}
369372
"""
370373

371374
return [

test/Macros/macro_expand_synthesized_members.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@
1414
@addMembers
1515
struct S {
1616
func useSynthesized() {
17-
print(type(of: storage))
18-
method()
17+
print(type(of: getStorage()))
1918
}
2019
}
2120

2221
let s = S()
2322

24-
// CHECK: Storage
2523
// CHECK: synthesized method
24+
// CHECK: Storage
2625
s.useSynthesized()

0 commit comments

Comments
 (0)