Skip to content

Commit 4cc7167

Browse files
committed
[Index] Handle shorthand if let/closure captures in local rename
Update rename to pull the outermost-declaration so that references are correctly found. Rather than keeping suppressed locations in the current parent, keep them for the whole index. Local rename starts the lookup from the innermost context it can, which could be a closure. In that case there is no parent decl on the stack and thus no where to store the locations to suppress. We could have a specific store for this case, but there shouldn't be that many of these and they're relatively cheap to store anyway. Resolves rdar://104568539.
1 parent 1a597c5 commit 4cc7167

File tree

7 files changed

+60
-36
lines changed

7 files changed

+60
-36
lines changed

include/swift/IDE/Utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ struct ResolvedCursorInfo {
161161
/// that names both the newly declared variable and the referenced variable
162162
/// by the same identifier in the source text. This includes shorthand
163163
/// closure captures (`[foo]`) and shorthand if captures
164-
/// (`if let foo {`).
164+
/// (`if let foo {`). Ordered from innermost to outermost shadows.
165+
///
165166
/// Decls that are shadowed using shorthand syntax should be reported as
166167
/// additional cursor info results.
167168
SmallVector<ValueDecl *> ShorthandShadowedDecls;

lib/IDE/CursorInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class NodeFinder : ASTWalker {
112112
std::unique_ptr<NodeFinderResult> takeResult() { return std::move(Result); }
113113

114114
/// Get the declarations that \p ShadowingDecl shadows using shorthand shadow
115-
/// syntax.
115+
/// syntax. Ordered from innermost to outermost shadows.
116116
SmallVector<ValueDecl *, 2>
117117
getShorthandShadowedDecls(ValueDecl *ShadowingDecl) {
118118
SmallVector<ValueDecl *, 2> Result;

lib/IDE/IDERequests.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ class CursorInfoResolver : public SourceEntityWalker {
6464
llvm::SmallVector<Expr*, 8> ExprStack;
6565
/// If a decl shadows another decl using shorthand syntax (`[foo]` or
6666
/// `if let foo {`), this maps the re-declared variable to the one that is
67-
/// being shadowed.
67+
/// being shadowed. Ordered from innermost to outermost shadows.
68+
///
6869
/// The transitive closure of shorthand shadowed decls should be reported as
6970
/// additional results in cursor info.
7071
llvm::DenseMap<ValueDecl *, ValueDecl *> ShorthandShadowedDecls;
@@ -180,17 +181,14 @@ ResolvedCursorInfo CursorInfoResolver::resolve(SourceLoc Loc) {
180181
walk(SrcFile);
181182

182183
if (auto ValueRefInfo = dyn_cast<ResolvedValueRefCursorInfo>(&CursorInfo)) {
183-
if (!ValueRefInfo->isRef()) {
184-
SmallVector<ValueDecl *> ShadowedDecls;
185-
// If we have a definition, add any decls that it potentially shadows
186-
auto ShorthandShadowedDecl =
187-
ShorthandShadowedDecls[ValueRefInfo->getValueD()];
188-
while (ShorthandShadowedDecl) {
189-
ShadowedDecls.push_back(ShorthandShadowedDecl);
190-
ShorthandShadowedDecl = ShorthandShadowedDecls[ShorthandShadowedDecl];
191-
}
192-
ValueRefInfo->setShorthandShadowedDecls(ShadowedDecls);
184+
SmallVector<ValueDecl *> ShadowedDecls;
185+
auto ShorthandShadowedDecl =
186+
ShorthandShadowedDecls[ValueRefInfo->getValueD()];
187+
while (ShorthandShadowedDecl) {
188+
ShadowedDecls.push_back(ShorthandShadowedDecl);
189+
ShorthandShadowedDecl = ShorthandShadowedDecls[ShorthandShadowedDecl];
193190
}
191+
ValueRefInfo->setShorthandShadowedDecls(ShadowedDecls);
194192
}
195193

196194
return CursorInfo;

lib/Index/Index.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,6 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
432432
SymbolInfo SymInfo;
433433
SymbolRoleSet Roles;
434434
SmallVector<IndexedWitness, 6> ExplicitWitnesses;
435-
SmallVector<SourceLoc, 6> RefsToSuppress;
436435
};
437436
SmallVector<Entity, 6> EntitiesStack;
438437
SmallVector<Expr *, 8> ExprStack;
@@ -449,6 +448,9 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
449448
StringScratchSpace stringStorage;
450449
ContainerTracker Containers;
451450

451+
// Already handled references that should be suppressed if found later.
452+
llvm::DenseSet<SourceLoc> RefsToSuppress;
453+
452454
// Contains a mapping for captures of the form [x], from the declared "x"
453455
// to the captured "x" in the enclosing scope. Also includes shorthand if
454456
// let bindings.
@@ -832,7 +834,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
832834
ReferenceMetaData Data) override {
833835
SourceLoc Loc = Range.getStart();
834836

835-
if (isRepressed(Loc) || Loc.isInvalid())
837+
if (Loc.isInvalid() || isSuppressed(Loc))
836838
return true;
837839

838840
// Dig back to the original captured variable
@@ -912,17 +914,13 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
912914
});
913915
}
914916

915-
void repressRefAtLoc(SourceLoc Loc) {
917+
void suppressRefAtLoc(SourceLoc Loc) {
916918
if (Loc.isInvalid()) return;
917-
assert(!EntitiesStack.empty());
918-
EntitiesStack.back().RefsToSuppress.push_back(Loc);
919+
RefsToSuppress.insert(Loc);
919920
}
920921

921-
bool isRepressed(SourceLoc Loc) const {
922-
if (EntitiesStack.empty() || Loc.isInvalid())
923-
return false;
924-
auto &Suppressed = EntitiesStack.back().RefsToSuppress;
925-
return std::find(Suppressed.begin(), Suppressed.end(), Loc) != Suppressed.end();
922+
bool isSuppressed(SourceLoc Loc) const {
923+
return Loc.isValid() && RefsToSuppress.contains(Loc);
926924
}
927925

928926
Expr *getContainingExpr(size_t index) const {
@@ -1253,7 +1251,7 @@ bool IndexSwiftASTWalker::startEntity(Decl *D, IndexSymbol &Info, bool IsRef) {
12531251
if (!handleWitnesses(D, explicitWitnesses))
12541252
return false;
12551253
}
1256-
EntitiesStack.push_back({D, Info.symInfo, Info.roles, std::move(explicitWitnesses), {}});
1254+
EntitiesStack.push_back({D, Info.symInfo, Info.roles, std::move(explicitWitnesses)});
12571255
return true;
12581256
}
12591257
}
@@ -1325,7 +1323,7 @@ bool IndexSwiftASTWalker::reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isI
13251323
Info.roles |= (unsigned)SymbolRole::Implicit;
13261324

13271325
// don't report this ref again when visitDeclReference reports it
1328-
repressRefAtLoc(Loc);
1326+
suppressRefAtLoc(Loc);
13291327

13301328
if (!reportRef(D, Loc, Info, None)) {
13311329
Cancelled = true;
@@ -1491,7 +1489,7 @@ bool IndexSwiftASTWalker::report(ValueDecl *D) {
14911489
// Suppress the reference if there is any (it is implicit and hence
14921490
// already skipped in the shorthand if let case, but explicit in the
14931491
// captured case).
1494-
repressRefAtLoc(loc);
1492+
suppressRefAtLoc(loc);
14951493

14961494
// Skip the definition of a shadowed decl
14971495
return true;

lib/Refactoring/Refactoring.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,11 @@ bool RefactoringActionLocalRename::performChange() {
912912
auto ValueRefCursorInfo = dyn_cast<ResolvedValueRefCursorInfo>(&CursorInfo);
913913
if (ValueRefCursorInfo && ValueRefCursorInfo->getValueD()) {
914914
ValueDecl *VD = ValueRefCursorInfo->typeOrValue();
915+
// The index always uses the outermost shadow for references
916+
if (!ValueRefCursorInfo->getShorthandShadowedDecls().empty()) {
917+
VD = ValueRefCursorInfo->getShorthandShadowedDecls().back();
918+
}
919+
915920
SmallVector<DeclContext *, 8> Scopes;
916921

917922
Optional<RenameRefInfo> RefInfo;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Local rename starts from the `DeclContext` of the renamed `Decl`. For
2+
// closures that means we have no parents, so check that case specifically.
3+
4+
func renameInClosure() {
5+
// RUN: %refactor -rename -dump-text -source-filename %s -pos=%(line+2):10 -new-name=renamed | %FileCheck %s
6+
// CHECK: shorthand_shadow.swift [[# @LINE+1]]:10 -> [[# @LINE+1]]:18
7+
_ = { (toRename: Int?) in
8+
// RUN: %refactor -rename -dump-text -source-filename %s -pos=%(line+2):12 -new-name=renamed | %FileCheck %s
9+
// CHECK: shorthand_shadow.swift [[# @LINE+1]]:12 -> [[# @LINE+1]]:20
10+
if let toRename {
11+
// RUN: %refactor -rename -dump-text -source-filename %s -pos=%(line+2):11 -new-name=renamed | %FileCheck %s
12+
// CHECK: shorthand_shadow.swift [[# @LINE+1]]:11 -> [[# @LINE+1]]:19
13+
_ = toRename
14+
}
15+
}
16+
}

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,7 @@ static bool passCursorInfoForDecl(
11721172
});
11731173
return false;
11741174
}
1175+
11751176
if (MainInfo.VD != OrigInfo.VD && !OrigInfo.Unavailable) {
11761177
CursorSymbolInfo &CtorSymbol = Symbols.emplace_back();
11771178
if (auto Err =
@@ -1182,16 +1183,21 @@ static bool passCursorInfoForDecl(
11821183
Symbols.pop_back();
11831184
}
11841185
}
1185-
for (auto D : Info.getShorthandShadowedDecls()) {
1186-
CursorSymbolInfo &SymbolInfo = Symbols.emplace_back();
1187-
DeclInfo DInfo(D, Type(), /*IsRef=*/true, /*IsDynamic=*/false,
1188-
ArrayRef<NominalTypeDecl *>(), Invoc);
1189-
if (auto Err =
1190-
fillSymbolInfo(SymbolInfo, DInfo, Info.getLoc(), AddSymbolGraph,
1191-
Lang, Invoc, PreviousSnaps, Allocator)) {
1192-
// Ignore but make sure to remove the partially-filled symbol
1193-
llvm::handleAllErrors(std::move(Err), [](const llvm::StringError &E) {});
1194-
Symbols.pop_back();
1186+
1187+
// Add in shadowed declarations if on a decl. For references just go to the
1188+
// actual declaration.
1189+
if (!Info.isRef()) {
1190+
for (auto D : Info.getShorthandShadowedDecls()) {
1191+
CursorSymbolInfo &SymbolInfo = Symbols.emplace_back();
1192+
DeclInfo DInfo(D, Type(), /*IsRef=*/true, /*IsDynamic=*/false,
1193+
ArrayRef<NominalTypeDecl *>(), Invoc);
1194+
if (auto Err =
1195+
fillSymbolInfo(SymbolInfo, DInfo, Info.getLoc(), AddSymbolGraph,
1196+
Lang, Invoc, PreviousSnaps, Allocator)) {
1197+
// Ignore but make sure to remove the partially-filled symbol
1198+
llvm::handleAllErrors(std::move(Err), [](const llvm::StringError &E) {});
1199+
Symbols.pop_back();
1200+
}
11951201
}
11961202
}
11971203

0 commit comments

Comments
 (0)