Skip to content

Commit be0b18d

Browse files
committed
[SourceKit] Make sure we reuse ASTContext in function bodies for solver-based cursor info
The main problem that prevented us from reusing the ASTContext was that we weren’t remapping the `LocToResolve` in the temporary buffer that only contains the re-parsed function back to the original buffer. Thus `NodeFinder` couldn’t find the node that we want to get cursor info for. Getting AST reuse to work for top-level items is harder because it currently heavily relies on the `HasCodeCompletion` state being set on the parser result. I’ll try that in a follow-up PR. rdar://103251263
1 parent 873fb73 commit be0b18d

File tree

12 files changed

+258
-31
lines changed

12 files changed

+258
-31
lines changed

include/swift/Basic/SourceManager.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,17 @@ class SourceManager {
166166

167167
SourceLoc getIDEInspectionTargetLoc() const;
168168

169+
/// Returns whether `Range` contains `Loc`. This also respects the
170+
/// `ReplacedRanges`, i.e. if `Loc` is in a range that replaces a range which
171+
/// overlaps `Range`, this also returns `true`.
172+
bool containsRespectingReplacedRanges(SourceRange Range, SourceLoc Loc) const;
173+
174+
/// Returns whether `Enclosing` contains `Inner`. This also respects the
175+
/// `ReplacedRanges`, i.e. if `Inner` is contained in a range that replaces a
176+
/// range which overlaps `Range`, this also returns `true`.
177+
bool rangeContainsRespectingReplacedRanges(SourceRange Enclosing,
178+
SourceRange Inner) const;
179+
169180
const llvm::DenseMap<SourceRange, SourceRange> &getReplacedRanges() const {
170181
return ReplacedRanges;
171182
}

lib/Basic/SourceLoc.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,31 @@ SourceLoc SourceManager::getIDEInspectionTargetLoc() const {
4545
.getAdvancedLoc(IDEInspectionTargetOffset);
4646
}
4747

48+
bool SourceManager::containsRespectingReplacedRanges(SourceRange Range,
49+
SourceLoc Loc) const {
50+
if (Loc.isInvalid() || Range.isInvalid()) {
51+
return false;
52+
}
53+
54+
if (Range.contains(Loc)) {
55+
return true;
56+
}
57+
for (const auto &pair : getReplacedRanges()) {
58+
auto OriginalRange = pair.first;
59+
auto NewRange = pair.second;
60+
if (NewRange.contains(Loc) && Range.overlaps(OriginalRange)) {
61+
return true;
62+
}
63+
}
64+
return false;
65+
}
66+
67+
bool SourceManager::rangeContainsRespectingReplacedRanges(
68+
SourceRange Enclosing, SourceRange Inner) const {
69+
return containsRespectingReplacedRanges(Enclosing, Inner.Start) &&
70+
containsRespectingReplacedRanges(Enclosing, Inner.End);
71+
}
72+
4873
StringRef SourceManager::getDisplayNameForLoc(SourceLoc Loc, bool ForceGeneratedSourceToDisk) const {
4974
// Respect #line first
5075
if (auto VFile = getVirtualFile(Loc))

lib/IDE/CursorInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class NodeFinder : ASTWalker {
140140
DeclContext *getCurrentDeclContext() { return DeclContextStack.back(); }
141141

142142
bool rangeContainsLocToResolve(SourceRange Range) const {
143-
return Range.contains(LocToResolve);
143+
return getSourceMgr().containsRespectingReplacedRanges(Range, LocToResolve);
144144
}
145145

146146
PreWalkAction walkToDeclPre(Decl *D) override {

lib/IDETool/IDEInspectionInstance.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -842,10 +842,9 @@ void swift::ide::IDEInspectionInstance::cursorInfo(
842842
CancellationFlag,
843843
[&](CancellableResult<IDEInspectionInstanceResult> CIResult) {
844844
CIResult.mapAsync<CursorInfoResults>(
845-
[&CancellationFlag, Offset](auto &Result, auto DeliverTransformed) {
845+
[&CancellationFlag](auto &Result, auto DeliverTransformed) {
846846
auto &Mgr = Result.CI->getSourceMgr();
847-
auto RequestedLoc = Mgr.getLocForOffset(
848-
Mgr.getIDEInspectionTargetBufferID(), Offset);
847+
auto RequestedLoc = Mgr.getIDEInspectionTargetLoc();
849848
ConsumerToCallbackAdapter Consumer(
850849
Result.DidReuseAST, CancellationFlag, DeliverTransformed);
851850
std::unique_ptr<IDEInspectionCallbacksFactory> callbacksFactory(

lib/Refactoring/Refactoring.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ class ContextFinder : public SourceEntityWalker {
5252
std::function<bool(ASTNode)> IsContext;
5353
SmallVector<ASTNode, 4> AllContexts;
5454
bool contains(ASTNode Enclosing) {
55-
auto Result = SM.rangeContains(Enclosing.getSourceRange(), Target);
56-
if (Result && IsContext(Enclosing))
55+
auto Result = SM.rangeContainsRespectingReplacedRanges(
56+
Enclosing.getSourceRange(), Target);
57+
if (Result && IsContext(Enclosing)) {
5758
AllContexts.push_back(Enclosing);
59+
}
5860
return Result;
5961
}
6062
public:
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 2):7 %s -- %s == -req=cursor -pos=%(line + 3):7 %s -- %s | %FileCheck %s --check-prefix IN-FUNCTION
2+
func foo() {
3+
let inFunctionA = 1
4+
let inFunctionB = "hi"
5+
}
6+
7+
// IN-FUNCTION: source.lang.swift.decl.var.local
8+
// IN-FUNCTION-NEXT: inFunctionA
9+
// IN-FUNCTION: DID REUSE AST CONTEXT: 0
10+
// IN-FUNCTION: source.lang.swift.decl.var.local
11+
// IN-FUNCTION-NEXT: inFunctionB
12+
// IN-FUNCTION: DID REUSE AST CONTEXT: 1
13+
14+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 3):9 %s -- %s == -req=cursor -pos=%(line + 4):9 %s -- %s | %FileCheck %s --check-prefix IN-INSTANCE-METHOD
15+
struct MyStruct {
16+
func test() {
17+
let inInstanceMethod1 = 2
18+
let inInstanceMethod2 = "hello"
19+
}
20+
}
21+
22+
// IN-INSTANCE-METHOD: source.lang.swift.decl.var.local
23+
// IN-INSTANCE-METHOD-NEXT: inInstanceMethod1
24+
// IN-INSTANCE-METHOD: DID REUSE AST CONTEXT: 0
25+
// IN-INSTANCE-METHOD: source.lang.swift.decl.var.local
26+
// IN-INSTANCE-METHOD-NEXT: inInstanceMethod2
27+
// IN-INSTANCE-METHOD: DID REUSE AST CONTEXT: 1
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// BEGIN State1.swift
2+
3+
func foo() {
4+
let inFunctionA = 1
5+
let inFunctionB = "hi"
6+
}
7+
8+
// BEGIN State2.swift
9+
10+
func foo() {
11+
let newlyAddedMember = 3
12+
let inFunctionA = 1
13+
let inFunctionB = "hi"
14+
}
15+
16+
// BEGIN State3.swift
17+
18+
func foo() {
19+
let inFunctionB = "hi"
20+
}
21+
22+
// BEGIN State4.swift
23+
24+
func foo() {
25+
let myNewName = "hi"
26+
}
27+
28+
// BEGIN State5.swift
29+
30+
func foo(param: Int) {
31+
let myNewName = "hi"
32+
}
33+
34+
// BEGIN State6.swift
35+
36+
func foo(param: Int) {
37+
let myNewName = 7
38+
}
39+
40+
41+
// BEGIN Dummy.swift
42+
43+
// RUN: %empty-directory(%t)
44+
// RUN: %{python} %utils/split_file.py -o %t %s
45+
46+
// RUN: cp %t/State1.swift %t/file.swift
47+
48+
// RUN: %sourcekitd-test \
49+
// RUN: -shell -- echo '## State 1' == \
50+
// RUN: -req=cursor -pos=4:7 %t/file.swift -- %t/file.swift == \
51+
// RUN: -shell -- echo '## State 2' == \
52+
// RUN: -shell -- cp %t/State2.swift %t/file.swift == \
53+
// RUN: -req=cursor -pos=5:7 %t/file.swift -- %t/file.swift == \
54+
// RUN: -shell -- echo '## State 3' == \
55+
// RUN: -shell -- cp %t/State3.swift %t/file.swift == \
56+
// RUN: -req=cursor -pos=3:7 %t/file.swift -- %t/file.swift == \
57+
// RUN: -shell -- echo '## State 4' == \
58+
// RUN: -shell -- cp %t/State4.swift %t/file.swift == \
59+
// RUN: -req=cursor -pos=3:7 %t/file.swift -- %t/file.swift == \
60+
// RUN: -shell -- echo '## State 5' == \
61+
// RUN: -shell -- cp %t/State5.swift %t/file.swift == \
62+
// RUN: -req=cursor -pos=3:7 %t/file.swift -- %t/file.swift == \
63+
// RUN: -shell -- echo '## State 6' == \
64+
// RUN: -shell -- cp %t/State6.swift %t/file.swift == \
65+
// RUN: -req=cursor -pos=3:7 %t/file.swift -- %t/file.swift > %t/response.txt
66+
// RUN: %FileCheck %s < %t/response.txt
67+
68+
// CHECK-LABEL: ## State 1
69+
// CHECK: source.lang.swift.decl.var.local (4:7-4:18)
70+
// CHECK: <Declaration>let inFunctionB: <Type usr="s:SS">String</Type></Declaration>
71+
// CHECK: DID REUSE AST CONTEXT: 0
72+
// CHECK-LABEL: ## State 2
73+
// CHECK: source.lang.swift.decl.var.local (5:7-5:18)
74+
// CHECK: <Declaration>let inFunctionB: <Type usr="s:SS">String</Type></Declaration>
75+
// CHECK: DID REUSE AST CONTEXT: 1
76+
// CHECK-LABEL: ## State 3
77+
// CHECK: source.lang.swift.decl.var.local (3:7-3:18)
78+
// CHECK: <Declaration>let inFunctionB: <Type usr="s:SS">String</Type></Declaration>
79+
// CHECK: DID REUSE AST CONTEXT: 1
80+
// CHECK-LABEL: ## State 4
81+
// CHECK: source.lang.swift.decl.var.local (3:7-3:16)
82+
// CHECK: <Declaration>let myNewName: <Type usr="s:SS">String</Type></Declaration>
83+
// CHECK: DID REUSE AST CONTEXT: 1
84+
// CHECK-LABEL: ## State 5
85+
// CHECK: source.lang.swift.decl.var.local (3:7-3:16)
86+
// CHECK: <Declaration>let myNewName: <Type usr="s:SS">String</Type></Declaration>
87+
// CHECK: DID REUSE AST CONTEXT: 0
88+
// CHECK-LABEL: ## State 6
89+
// CHECK: source.lang.swift.decl.var.local (3:7-3:16)
90+
// CHECK: <Declaration>let myNewName: <Type usr="s:Si">Int</Type></Declaration>
91+
// CHECK: DID REUSE AST CONTEXT: 1
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
func materialize() {
2+
let a = 2
3+
}
4+
5+
func outer() {
6+
func inner() {
7+
}
8+
}
9+
10+
// RUN: %sourcekitd-test -req=cursor -pos=2:6 %s -- %s == \
11+
// RUN: -req=cursor -req-opts=retrieve_refactor_actions=1 -pos=6:8 %s -- %s | %FileCheck %s
12+
13+
// CHECK: source.lang.swift.decl.function.free (6:8-6:15)
14+
// CHECK: inner()
15+
// CHECK: ACTIONS BEGIN
16+
// CHECK-DAG: source.refactoring.kind.rename.local
17+
// CHECK-DAG: Local Rename
18+
// CHECK-DAG: source.refactoring.kind.convert.func-to-async
19+
// CHECK-DAG: Convert Function to Async
20+
// CHECK: ACTIONS END

tools/SourceKit/include/SourceKit/Core/LangSupport.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ class RequestResult {
363363
return RequestResult();
364364
}
365365

366+
bool isValue() const { return type == Value; }
366367
const T &value() const {
367368
assert(type == Value);
368369
return *data;
@@ -588,8 +589,14 @@ struct CursorInfoData {
588589
llvm::ArrayRef<CursorSymbolInfo> Symbols;
589590
/// All available actions on the code under cursor.
590591
llvm::ArrayRef<RefactoringInfo> AvailableActions;
591-
592-
void print(llvm::raw_ostream &OS, std::string Indentation) const {
592+
/// Whether the ASTContext was reused for this cursor info.
593+
bool DidReuseAST = false;
594+
595+
/// If \p ForSolverBasedCursorInfoVerification is \c true, fields that are
596+
/// acceptable to differ between the AST-based and the solver-based result,
597+
/// will be excluded.
598+
void print(llvm::raw_ostream &OS, std::string Indentation,
599+
bool ForSolverBasedCursorInfoVerification = false) const {
593600
OS << Indentation << "CursorInfoData" << '\n';
594601
OS << Indentation << " Symbols:" << '\n';
595602
for (auto Symbol : Symbols) {
@@ -599,6 +606,9 @@ struct CursorInfoData {
599606
for (auto AvailableAction : AvailableActions) {
600607
AvailableAction.print(OS, Indentation + " ");
601608
}
609+
if (!ForSolverBasedCursorInfoVerification) {
610+
OS << Indentation << "DidReuseAST: " << DidReuseAST << '\n';
611+
}
602612
}
603613

604614
SWIFT_DEBUG_DUMP { print(llvm::errs(), ""); }

0 commit comments

Comments
 (0)