|
30 | 30 | #include "swift/AST/Stmt.h"
|
31 | 31 | #include "swift/AST/TypeRepr.h"
|
32 | 32 | #include "swift/Basic/STLExtras.h"
|
| 33 | +#include "swift/Parse/Lexer.h" |
33 | 34 | #include "llvm/Support/Compiler.h"
|
34 | 35 |
|
35 | 36 | using namespace swift;
|
@@ -90,14 +91,78 @@ static SourceLoc translateLocForReplacedRange(SourceManager &sourceMgr,
|
90 | 91 | NullablePtr<ASTScopeImpl>
|
91 | 92 | ASTScopeImpl::findChildContaining(SourceLoc loc,
|
92 | 93 | SourceManager &sourceMgr) const {
|
| 94 | + auto *moduleDecl = this->getSourceFile()->getParentModule(); |
| 95 | + auto *locSourceFile = moduleDecl->getSourceFileContainingLocation(loc); |
| 96 | + |
93 | 97 | // Use binary search to find the child that contains this location.
|
94 | 98 | auto *const *child = llvm::lower_bound(
|
95 | 99 | getChildren(), loc,
|
96 |
| - [&sourceMgr](const ASTScopeImpl *scope, SourceLoc loc) { |
| 100 | + [&](const ASTScopeImpl *scope, SourceLoc loc) { |
97 | 101 | auto rangeOfScope = scope->getCharSourceRangeOfScope(sourceMgr);
|
98 | 102 | ASTScopeAssert(!sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(),
|
99 | 103 | rangeOfScope.getStart()),
|
100 | 104 | "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 | + |
101 | 166 | loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc);
|
102 | 167 | return (rangeOfScope.getEnd() == loc ||
|
103 | 168 | sourceMgr.isBeforeInBuffer(rangeOfScope.getEnd(), loc));
|
|
0 commit comments