@@ -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,80 @@ 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
+
2237
+ bool walkToStmtPre (Stmt *S) override {
2238
+ if (Cancelled)
2239
+ return false ;
2240
+
2241
+ if (auto CondStmt = dyn_cast<LabeledConditionalStmt>(S)) {
2242
+ for (const StmtConditionElement &Cond : CondStmt->getCond ()) {
2243
+ if (Cond.getKind () != StmtConditionElement::CK_PatternBinding) {
2244
+ continue ;
2245
+ }
2246
+ auto Init = dyn_cast<DeclRefExpr>(Cond.getInitializer ());
2247
+ if (!Init) {
2248
+ continue ;
2249
+ }
2250
+ auto ReferencedVar = dyn_cast_or_null<VarDecl>(Init->getDecl ());
2251
+ if (!ReferencedVar) {
2252
+ continue ;
2253
+ }
2254
+
2255
+ Cond.getPattern ()->forEachVariable ([&](VarDecl *DeclaredVar) {
2256
+ if (DeclaredVar->getLoc () != Init->getLoc ()) {
2257
+ return ;
2258
+ }
2259
+ assert (DeclaredVar->getName () == ReferencedVar->getName ());
2260
+ if (DeclaredVar == Dcl) {
2261
+ RelatedDecls.push_back (ReferencedVar);
2262
+ }
2263
+ if (ReferencedVar == Dcl) {
2264
+ RelatedDecls.push_back (DeclaredVar);
2265
+ }
2266
+ });
2267
+ }
2268
+ }
2269
+ return true ;
2270
+ }
2271
+
2193
2272
bool walkToDeclPre (Decl *D, CharSourceRange Range) override {
2194
2273
if (Cancelled)
2195
2274
return false ;
@@ -2232,7 +2311,7 @@ class RelatedIdScanner : public SourceEntityWalker {
2232
2311
2233
2312
bool passId (CharSourceRange Range) {
2234
2313
unsigned Offset = SourceMgr.getLocOffsetInBuffer (Range.getStart (),BufferID);
2235
- Ranges.push_back ({ Offset, Range.getByteLength () });
2314
+ Ranges.insert ({ Offset, Range.getByteLength ()});
2236
2315
return !Cancelled;
2237
2316
}
2238
2317
};
@@ -2301,21 +2380,54 @@ void SwiftLangSupport::findRelatedIdentifiersInFile(
2301
2380
if (VD->isOperator ())
2302
2381
return ;
2303
2382
2304
- RelatedIdScanner Scanner (SrcFile, BufferID, VD, Ranges);
2383
+ // Record ranges in a set first so we don't record some ranges twice.
2384
+ // This could happen in capture lists where e.g. `[foo]` is both the
2385
+ // reference of the captured variable and the declaration of the
2386
+ // variable usable in the closure.
2387
+ llvm::SmallDenseSet<std::pair<unsigned , unsigned >, 8 > RangesSet;
2388
+
2389
+ // List of decls whose ranges should be reported as related identifiers.
2390
+ SmallVector<ValueDecl *, 2 > Worklist;
2391
+ Worklist.push_back (VD);
2392
+
2393
+ // Decls that we have already visited, so we don't walk circles.
2394
+ SmallPtrSet<ValueDecl *, 2 > VisitedDecls;
2395
+ while (!Worklist.empty ()) {
2396
+ ValueDecl *Dcl = Worklist.back ();
2397
+ Worklist.pop_back ();
2398
+ if (!VisitedDecls.insert (Dcl).second ) {
2399
+ // We have already visited this decl. Don't visit it again.
2400
+ continue ;
2401
+ }
2402
+
2403
+ RelatedIdScanner Scanner (SrcFile, BufferID, Dcl, RangesSet, Worklist);
2305
2404
2306
- if (auto *Case = getCaseStmtOfCanonicalVar (VD)) {
2307
- Scanner.walk (Case);
2308
- while ((Case = Case->getFallthroughDest ().getPtrOrNull ())) {
2405
+ if (auto *Case = getCaseStmtOfCanonicalVar (Dcl)) {
2309
2406
Scanner.walk (Case);
2407
+ while ((Case = Case->getFallthroughDest ().getPtrOrNull ())) {
2408
+ Scanner.walk (Case);
2409
+ }
2410
+ } else if (DeclContext *LocalDC =
2411
+ Dcl->getDeclContext ()->getLocalContext ()) {
2412
+ Scanner.walk (LocalDC);
2413
+ } else {
2414
+ Scanner.walk (SrcFile);
2310
2415
}
2311
- } else if (DeclContext *LocalDC = VD->getDeclContext ()->getLocalContext ()) {
2312
- Scanner.walk (LocalDC);
2313
- } else {
2314
- Scanner.walk (SrcFile);
2315
2416
}
2417
+
2418
+ // Sort ranges so we get deterministic output.
2419
+ Ranges.insert (Ranges.end (), RangesSet.begin (), RangesSet.end ());
2420
+ llvm::sort (Ranges,
2421
+ [](const std::pair<unsigned , unsigned > &LHS,
2422
+ const std::pair<unsigned , unsigned > &RHS) -> bool {
2423
+ if (LHS.first == RHS.first ) {
2424
+ return LHS.second < RHS.second ;
2425
+ } else {
2426
+ return LHS.first < RHS.first ;
2427
+ }
2428
+ });
2316
2429
};
2317
2430
Action ();
2318
-
2319
2431
RelatedIdentsInfo Info;
2320
2432
Info.Ranges = Ranges;
2321
2433
Receiver (RequestResult<RelatedIdentsInfo>::fromResult (Info));
0 commit comments