Skip to content

Commit eb67297

Browse files
committed
[IDE] Report ambiguous cursor info results
1 parent 05b59d1 commit eb67297

File tree

5 files changed

+65
-22
lines changed

5 files changed

+65
-22
lines changed

lib/IDE/CursorInfo.cpp

+33-15
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class NodeFinderExprResult : public NodeFinderResult {
121121
/// Walks the AST, looking for a node at \c LocToResolve. While walking the
122122
/// AST, also gathers information about shorthand shadows.
123123
class NodeFinder : ASTWalker {
124-
SourceFile &SrcFile;
124+
DeclContext &DC;
125125
SourceLoc LocToResolve;
126126

127127
/// As we are walking the tree, this variable is updated to the last seen
@@ -139,11 +139,10 @@ class NodeFinder : ASTWalker {
139139
llvm::DenseMap<ValueDecl *, ValueDecl *> ShorthandShadowedDecls;
140140

141141
public:
142-
NodeFinder(SourceFile &SrcFile, SourceLoc LocToResolve)
143-
: SrcFile(SrcFile), LocToResolve(LocToResolve),
144-
DeclContextStack({&SrcFile}) {}
142+
NodeFinder(DeclContext &DC, SourceLoc LocToResolve)
143+
: DC(DC), LocToResolve(LocToResolve), DeclContextStack({&DC}) {}
145144

146-
void resolve() { SrcFile.walk(*this); }
145+
void resolve() { DC.walkContext(*this); }
147146

148147
std::unique_ptr<NodeFinderResult> takeResult() { return std::move(Result); }
149148

@@ -161,9 +160,7 @@ class NodeFinder : ASTWalker {
161160
}
162161

163162
private:
164-
SourceManager &getSourceMgr() const {
165-
return SrcFile.getASTContext().SourceMgr;
166-
}
163+
SourceManager &getSourceMgr() const { return DC.getASTContext().SourceMgr; }
167164

168165
/// The decl context that is currently being walked.
169166
DeclContext *getCurrentDeclContext() { return DeclContextStack.back(); }
@@ -232,7 +229,8 @@ class NodeFinder : ASTWalker {
232229
switch (E->getKind()) {
233230
case ExprKind::DeclRef:
234231
case ExprKind::UnresolvedDot:
235-
case ExprKind::UnresolvedDeclRef: {
232+
case ExprKind::UnresolvedDeclRef:
233+
case ExprKind::OverloadedDeclRef: {
236234
assert(Result == nullptr);
237235
Result =
238236
std::make_unique<NodeFinderExprResult>(E, getCurrentDeclContext());
@@ -280,13 +278,33 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
280278
};
281279

282280
private:
283-
/// The expression for which we want to provide cursor info results.
284-
Expr *ResolveExpr;
281+
/// The location to resolve and the \c DeclContext to resolve it in.
282+
/// Note that we cannot store the expression to resolve directly because an
283+
/// \c UnresolvedDeclRefExpr might be replaced by an \c OverloadedDeclRefExpr
284+
/// and thus the constraint system solution doesn't know about the
285+
/// \c UnresolvedDeclRefExpr. Instead, we find the expression to resolve in
286+
/// the source file again after expression pre-check has run.
287+
DeclContext &DC;
288+
SourceLoc ResolveLoc;
285289

286290
SmallVector<CursorInfoDeclReference, 1> Results;
287291

292+
Expr *getExprToResolve() {
293+
NodeFinder Finder(DC, ResolveLoc);
294+
Finder.resolve();
295+
auto Result = Finder.takeResult();
296+
if (!Result || Result->getKind() != NodeFinderResultKind::Expr) {
297+
return nullptr;
298+
}
299+
return cast<NodeFinderExprResult>(Result.get())->getExpr();
300+
}
301+
288302
void sawSolutionImpl(const Solution &S) override {
289303
auto &CS = S.getConstraintSystem();
304+
auto ResolveExpr = getExprToResolve();
305+
if (!ResolveExpr) {
306+
return;
307+
}
290308

291309
auto Locator = CS.getConstraintLocator(ResolveExpr);
292310
auto CalleeLocator = S.getCalleeLocator(Locator);
@@ -310,8 +328,8 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
310328
}
311329

312330
public:
313-
CursorInfoTypeCheckSolutionCallback(Expr *ResolveExpr)
314-
: ResolveExpr(ResolveExpr) {}
331+
CursorInfoTypeCheckSolutionCallback(DeclContext &DC, SourceLoc ResolveLoc)
332+
: DC(DC), ResolveLoc(ResolveLoc) {}
315333

316334
ArrayRef<CursorInfoDeclReference> getResults() const { return Results; }
317335
};
@@ -332,7 +350,7 @@ class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks {
332350
getDeclResult(NodeFinderDeclResult *DeclResult, SourceFile *SrcFile,
333351
NodeFinder &Finder) const {
334352
typeCheckDeclAndParentClosures(DeclResult->getDecl());
335-
return new ResolvedValueRefCursorInfo(
353+
auto CursorInfo = new ResolvedValueRefCursorInfo(
336354
SrcFile, RequestedLoc, DeclResult->getDecl(),
337355
/*CtorTyRef=*/nullptr,
338356
/*ExtTyRef=*/nullptr, /*IsRef=*/false, /*Ty=*/Type(),
@@ -352,7 +370,7 @@ class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks {
352370
DeclContext *DC = ExprResult->getDeclContext();
353371

354372
// Type check the statemnt containing E and listen for solutions.
355-
CursorInfoTypeCheckSolutionCallback Callback(E);
373+
CursorInfoTypeCheckSolutionCallback Callback(*DC, RequestedLoc);
356374
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
357375
DC->getASTContext().SolutionCallback, &Callback);
358376
typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC),

lib/Sema/CSSolver.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -1430,7 +1430,10 @@ ConstraintSystem::solve(SyntacticElementTarget &target,
14301430
case SolutionResult::Ambiguous:
14311431
// If salvaging produced an ambiguous result, it has already been
14321432
// diagnosed.
1433-
if (stage == 1) {
1433+
// If we have found an ambiguous solution in the first stage, salvaging
1434+
// won't produce more solutions, so we can inform the solution callback
1435+
// about the current ambiguous solutions straight away.
1436+
if (stage == 1 || Context.SolutionCallback) {
14341437
reportSolutionsToSolutionCallback(solution);
14351438
solution.markAsDiagnosed();
14361439
return None;
@@ -1449,8 +1452,10 @@ ConstraintSystem::solve(SyntacticElementTarget &target,
14491452
LLVM_FALLTHROUGH;
14501453

14511454
case SolutionResult::UndiagnosedError:
1452-
if (shouldSuppressDiagnostics()) {
1453-
reportSolutionsToSolutionCallback(solution);
1455+
/// If we have a SolutionCallback, we are inspecting constraint system
1456+
/// solutions directly and thus also want to receive ambiguous solutions.
1457+
/// Hence always run the second (salvaging) stage.
1458+
if (shouldSuppressDiagnostics() && !Context.SolutionCallback) {
14541459
solution.markAsDiagnosed();
14551460
return None;
14561461
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
func testAmbiguousFunctionReference() {
2+
func foo(a: Int) {}
3+
func foo(a: String) {}
4+
5+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):7 %s -- %s | %FileCheck %s
6+
_ = foo
7+
8+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):7 %s -- %s | %FileCheck %s
9+
_ = foo(a: UInt(1))
10+
}
11+
12+
// CHECK: source.lang.swift.ref.function.free (2:8-2:19)
13+
// CHECK: <Declaration>func foo(a: <Type usr="s:Si">Int</Type>)</Declaration>
14+
// CHECK: SECONDARY SYMBOLS BEGIN
15+
// CHECK: source.lang.swift.ref.function.free (3:8-3:22)
16+
// CHECK: <Declaration>func foo(a: <Type usr="s:SS">String</Type>)</Declaration>
17+
// CHECK: SECONDARY SYMBOLS END

test/SourceKit/Refactoring/basic.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func hasCallToAsyncAlternative(c: ConvertAsync) {
170170
// RUN: %sourcekitd-test -req=cursor -pos=117:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
171171
// RUN: %sourcekitd-test -req=cursor -pos=117:17 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
172172

173-
// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT
173+
// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME
174174

175175
// RUN: %sourcekitd-test -req=cursor -pos=54:10 -end-pos=54:22 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-LOCAL
176176
// RUN: %sourcekitd-test -req=cursor -pos=54:12 -end-pos=54:22 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-SELF-RENAME1
@@ -231,6 +231,8 @@ func hasCallToAsyncAlternative(c: ConvertAsync) {
231231
// CHECK-LOCAL-NOT: Global Rename
232232
// CHECK-LOCAL: ACTIONS END
233233

234+
// CHECK-RENAME: Global Rename
235+
234236
// CHECK-RENAME-EXTRACT: Global Rename
235237
// CHECK-RENAME-EXTRACT: Extract Method
236238

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -1915,15 +1915,15 @@ static void deliverCursorInfoResults(
19151915
// TODO: Implement delivery of other result types as more cursor info kinds
19161916
// are migrated to be completion-like.
19171917
CursorInfoData Data;
1918-
for (auto ResolvedCursorInfo : Results.getResult().ResolvedCursorInfos) {
1918+
for (auto ResolvedCursorInfo : Results->ResolvedCursorInfos) {
19191919
if (auto Result = dyn_cast_or_null<ResolvedValueRefCursorInfo>(
19201920
ResolvedCursorInfo)) {
19211921
std::string Diagnostic; // Unused
19221922
addCursorInfoForDecl(Data, Result, AddRefactorings, AddSymbolGraph,
19231923
Lang, Invoc, Diagnostic, /*PreviousSnaps=*/{});
19241924
}
1925-
Data.DidReuseAST = Results->DidReuseAST;
19261925
}
1926+
Data.DidReuseAST = Results->DidReuseAST;
19271927
if (!Data.Symbols.empty()) {
19281928
Receiver(RequestResult<CursorInfoData>::fromResult(Data));
19291929
}
@@ -2008,7 +2008,8 @@ void SwiftLangSupport::getCursorInfo(
20082008
std::unique_ptr<llvm::MemoryBuffer> UnresolvedInputFile =
20092009
getASTManager()->getMemoryBuffer(RealInputFilePath, fileSystem,
20102010
InputFileError);
2011-
if (UnresolvedInputFile) {
2011+
// The solver-based implementation doesn't support range based cursor info.
2012+
if (UnresolvedInputFile && Length == 0) {
20122013
auto SolverBasedReceiver = [&](const RequestResult<CursorInfoData> &Res) {
20132014
SolverBasedProducedResult = true;
20142015
Receiver(Res);

0 commit comments

Comments
 (0)