@@ -2158,17 +2158,22 @@ SwiftLangSupport::findUSRRange(StringRef DocumentName, StringRef USR) {
21582158namespace {
21592159class 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
21662170public:
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
21922197private:
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