Skip to content

Commit 64e27a2

Browse files
committed
[SoruceKit] Add the underlying variable to related identifiers for a captured variable
Previously, the related idents request wouldn’t look through caputred variables like `[foo]`. Change the logic to consider the captured variable as well as the variable that’s implicitly declared for use inside the closure. rdar://81628899
1 parent 2f0ae44 commit 64e27a2

File tree

2 files changed

+136
-16
lines changed

2 files changed

+136
-16
lines changed

test/SourceKit/RelatedIdents/related_idents.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,24 @@ func `escapedName`(`x`: Int) {}
7575
escapedName(`x`: 2)
7676
escapedName(`x`:)
7777

78+
func closureCapture() {
79+
let test = 0
80+
81+
let outerClosure = { [test] in
82+
let innerClosure = { [test] in
83+
print(test)
84+
}
85+
}
86+
}
87+
88+
func closureCaptureWithExplicitName() {
89+
let test = 0
90+
91+
let closure = { [test = test] in
92+
print(test)
93+
}
94+
}
95+
7896
// RUN: %sourcekitd-test -req=related-idents -pos=6:17 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK1 %s
7997
// CHECK1: START RANGES
8098
// CHECK1-NEXT: 1:7 - 2
@@ -165,3 +183,28 @@ escapedName(`x`:)
165183
// CHECK11-NEXT: 75:1 - 11
166184
// CHECK11-NEXT: 76:1 - 11
167185

186+
187+
// RUN: %sourcekitd-test -req=related-idents -pos=79:7 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK12 %s
188+
// RUN: %sourcekitd-test -req=related-idents -pos=81:25 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK12 %s
189+
// RUN: %sourcekitd-test -req=related-idents -pos=82:27 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK12 %s
190+
// RUN: %sourcekitd-test -req=related-idents -pos=83:13 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK12 %s
191+
// CHECK12: START RANGES
192+
// CHECK12-NEXT: 79:7 - 4
193+
// CHECK12-NEXT: 81:25 - 4
194+
// CHECK12-NEXT: 82:27 - 4
195+
// CHECK12-NEXT: 83:13 - 4
196+
// CHECK12-NEXT: END RANGES
197+
198+
// RUN: %sourcekitd-test -req=related-idents -pos=89:7 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK13 %s
199+
// RUN: %sourcekitd-test -req=related-idents -pos=91:27 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK13 %s
200+
// CHECK13: START RANGES
201+
// CHECK13-NEXT: 89:7 - 4
202+
// CHECK13-NEXT: 91:27 - 4
203+
// CHECK13-NEXT: END RANGES
204+
205+
// RUN: %sourcekitd-test -req=related-idents -pos=91:20 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK14 %s
206+
// RUN: %sourcekitd-test -req=related-idents -pos=92:11 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK14 %s
207+
// CHECK14: START RANGES
208+
// CHECK14-NEXT: 91:20 - 4
209+
// CHECK14-NEXT: 92:11 - 4
210+
// CHECK14-NEXT: END RANGES

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,17 +2158,22 @@ SwiftLangSupport::findUSRRange(StringRef DocumentName, StringRef USR) {
21582158
namespace {
21592159
class RelatedIdScanner : public SourceEntityWalker {
21602160
ValueDecl *Dcl;
2161-
llvm::SmallVectorImpl<std::pair<unsigned, unsigned>> &Ranges;
2161+
llvm::SmallDenseSet<std::pair<unsigned, unsigned>, 8> &Ranges;
2162+
/// Declarations that are tied to the same name as \c Dcl and should thus also
2163+
/// be renamed if \c Dcl is renamed. Most notabliy this contains closure
2164+
/// captures like `[foo]`.
2165+
llvm::SmallVectorImpl<ValueDecl *> &RelatedDecls;
21622166
SourceManager &SourceMgr;
21632167
unsigned BufferID = -1;
21642168
bool Cancelled = false;
21652169

21662170
public:
2167-
explicit RelatedIdScanner(SourceFile &SrcFile, unsigned BufferID,
2168-
ValueDecl *D,
2169-
llvm::SmallVectorImpl<std::pair<unsigned, unsigned>> &Ranges)
2170-
: Ranges(Ranges), SourceMgr(SrcFile.getASTContext().SourceMgr),
2171-
BufferID(BufferID) {
2171+
explicit RelatedIdScanner(
2172+
SourceFile &SrcFile, unsigned BufferID, ValueDecl *D,
2173+
llvm::SmallDenseSet<std::pair<unsigned, unsigned>, 8> &Ranges,
2174+
llvm::SmallVectorImpl<ValueDecl *> &RelatedDecls)
2175+
: Ranges(Ranges), RelatedDecls(RelatedDecls),
2176+
SourceMgr(SrcFile.getASTContext().SourceMgr), BufferID(BufferID) {
21722177
if (auto *V = dyn_cast<VarDecl>(D)) {
21732178
// Always use the canonical var decl for comparison. This is so we
21742179
// pick up all occurrences of x in case statements like the below:
@@ -2190,6 +2195,45 @@ class RelatedIdScanner : public SourceEntityWalker {
21902195
}
21912196

21922197
private:
2198+
bool walkToExprPre(Expr *E) override {
2199+
if (Cancelled)
2200+
return false;
2201+
2202+
// Check if there are closure captures like `[foo]` where the caputred
2203+
// variable should also be renamed
2204+
if (auto CaptureList = dyn_cast<CaptureListExpr>(E)) {
2205+
for (auto Capture : CaptureList->getCaptureList()) {
2206+
if (Capture.PBD->getPatternList().size() != 1) {
2207+
continue;
2208+
}
2209+
auto *DRE = dyn_cast_or_null<DeclRefExpr>(Capture.PBD->getInit(0));
2210+
if (!DRE) {
2211+
continue;
2212+
}
2213+
2214+
auto DeclaredVar = Capture.getVar();
2215+
if (DeclaredVar->getLoc() != DRE->getLoc()) {
2216+
// We have a capture like `[foo]` if the declared var and the
2217+
// reference share the same location.
2218+
continue;
2219+
}
2220+
2221+
auto *ReferencedVar = dyn_cast_or_null<VarDecl>(DRE->getDecl());
2222+
if (!ReferencedVar) {
2223+
continue;
2224+
}
2225+
2226+
assert(DeclaredVar->getName() == ReferencedVar->getName());
2227+
if (DeclaredVar == Dcl) {
2228+
RelatedDecls.push_back(ReferencedVar);
2229+
} else if (ReferencedVar == Dcl) {
2230+
RelatedDecls.push_back(DeclaredVar);
2231+
}
2232+
}
2233+
}
2234+
return true;
2235+
}
2236+
21932237
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
21942238
if (Cancelled)
21952239
return false;
@@ -2232,7 +2276,7 @@ class RelatedIdScanner : public SourceEntityWalker {
22322276

22332277
bool passId(CharSourceRange Range) {
22342278
unsigned Offset = SourceMgr.getLocOffsetInBuffer(Range.getStart(),BufferID);
2235-
Ranges.push_back({ Offset, Range.getByteLength() });
2279+
Ranges.insert({Offset, Range.getByteLength()});
22362280
return !Cancelled;
22372281
}
22382282
};
@@ -2301,21 +2345,54 @@ void SwiftLangSupport::findRelatedIdentifiersInFile(
23012345
if (VD->isOperator())
23022346
return;
23032347

2304-
RelatedIdScanner Scanner(SrcFile, BufferID, VD, Ranges);
2348+
// Record ranges in a set first so we don't record some ranges twice.
2349+
// This could happen in capture lists where e.g. `[foo]` is both the
2350+
// reference of the captured variable and the declaration of the
2351+
// variable usable in the closure.
2352+
llvm::SmallDenseSet<std::pair<unsigned, unsigned>, 8> RangesSet;
2353+
2354+
// List of decls whose ranges should be reported as related identifiers.
2355+
SmallVector<ValueDecl *, 2> Worklist;
2356+
Worklist.push_back(VD);
2357+
2358+
// Decls that we have already visited, so we don't walk circles.
2359+
SmallPtrSet<ValueDecl *, 2> VisitedDecls;
2360+
while (!Worklist.empty()) {
2361+
ValueDecl *Dcl = Worklist.back();
2362+
Worklist.pop_back();
2363+
if (!VisitedDecls.insert(Dcl).second) {
2364+
// We have already visited this decl. Don't visit it again.
2365+
continue;
2366+
}
23052367

2306-
if (auto *Case = getCaseStmtOfCanonicalVar(VD)) {
2307-
Scanner.walk(Case);
2308-
while ((Case = Case->getFallthroughDest().getPtrOrNull())) {
2368+
RelatedIdScanner Scanner(SrcFile, BufferID, Dcl, RangesSet, Worklist);
2369+
2370+
if (auto *Case = getCaseStmtOfCanonicalVar(Dcl)) {
23092371
Scanner.walk(Case);
2372+
while ((Case = Case->getFallthroughDest().getPtrOrNull())) {
2373+
Scanner.walk(Case);
2374+
}
2375+
} else if (DeclContext *LocalDC =
2376+
Dcl->getDeclContext()->getLocalContext()) {
2377+
Scanner.walk(LocalDC);
2378+
} else {
2379+
Scanner.walk(SrcFile);
23102380
}
2311-
} else if (DeclContext *LocalDC = VD->getDeclContext()->getLocalContext()) {
2312-
Scanner.walk(LocalDC);
2313-
} else {
2314-
Scanner.walk(SrcFile);
23152381
}
2382+
2383+
// Sort ranges so we get deterministic output.
2384+
Ranges.insert(Ranges.end(), RangesSet.begin(), RangesSet.end());
2385+
llvm::sort(Ranges,
2386+
[](const std::pair<unsigned, unsigned> &LHS,
2387+
const std::pair<unsigned, unsigned> &RHS) -> bool {
2388+
if (LHS.first == RHS.first) {
2389+
return LHS.second < RHS.second;
2390+
} else {
2391+
return LHS.first < RHS.first;
2392+
}
2393+
});
23162394
};
23172395
Action();
2318-
23192396
RelatedIdentsInfo Info;
23202397
Info.Ranges = Ranges;
23212398
Receiver(RequestResult<RelatedIdentsInfo>::fromResult(Info));

0 commit comments

Comments
 (0)