@@ -2158,17 +2158,22 @@ SwiftLangSupport::findUSRRange(StringRef DocumentName, StringRef USR) {
2158
2158
namespace {
2159
2159
class RelatedIdScanner : public SourceEntityWalker {
2160
2160
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;
2162
2166
SourceManager &SourceMgr;
2163
2167
unsigned BufferID = -1 ;
2164
2168
bool Cancelled = false ;
2165
2169
2166
2170
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) {
2172
2177
if (auto *V = dyn_cast<VarDecl>(D)) {
2173
2178
// Always use the canonical var decl for comparison. This is so we
2174
2179
// pick up all occurrences of x in case statements like the below:
@@ -2190,6 +2195,45 @@ class RelatedIdScanner : public SourceEntityWalker {
2190
2195
}
2191
2196
2192
2197
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
+
2193
2237
bool walkToDeclPre (Decl *D, CharSourceRange Range) override {
2194
2238
if (Cancelled)
2195
2239
return false ;
@@ -2232,7 +2276,7 @@ class RelatedIdScanner : public SourceEntityWalker {
2232
2276
2233
2277
bool passId (CharSourceRange Range) {
2234
2278
unsigned Offset = SourceMgr.getLocOffsetInBuffer (Range.getStart (),BufferID);
2235
- Ranges.push_back ({ Offset, Range.getByteLength () });
2279
+ Ranges.insert ({ Offset, Range.getByteLength ()});
2236
2280
return !Cancelled;
2237
2281
}
2238
2282
};
@@ -2301,21 +2345,54 @@ void SwiftLangSupport::findRelatedIdentifiersInFile(
2301
2345
if (VD->isOperator ())
2302
2346
return ;
2303
2347
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
+ }
2305
2367
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 )) {
2309
2371
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);
2310
2380
}
2311
- } else if (DeclContext *LocalDC = VD->getDeclContext ()->getLocalContext ()) {
2312
- Scanner.walk (LocalDC);
2313
- } else {
2314
- Scanner.walk (SrcFile);
2315
2381
}
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
+ });
2316
2394
};
2317
2395
Action ();
2318
-
2319
2396
RelatedIdentsInfo Info;
2320
2397
Info.Ranges = Ranges;
2321
2398
Receiver (RequestResult<RelatedIdentsInfo>::fromResult (Info));
0 commit comments