[CursorInfo] Pass primary path to allow comparing previous ASTs

Update the cursor requests to also pass in their primary file. Snapshots
should be compared using this file, not the input buffer name. This
fixes AST re-use when the AST is usable with snapshots.

Resolves rdar://110344363.
This commit is contained in:
Ben Barham
2023-06-23 16:18:59 -07:00
parent 3ddab3e9b0
commit ab95dba720
7 changed files with 105 additions and 62 deletions

View File

@@ -8,6 +8,4 @@
// RUN: %sourcekitd-test \
// RUN: -req=open -text-input %t/empty.swift %t/func.swift -- %t/func.swift == \
// RUN: -req=edit -offset=0 -length=0 -replace="func foo() {}" -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/func.swift -- %t/func.swift == \
// RUN: -req=cursor -offset=5 %t/func.swift -- %t/func.swift == \
// RUN: -req=edit -offset=0 -length=0 -replace="// some comment\n" -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0 %t/func.swift -- %t/func.swift == \
// RUN: -req=cursor -offset=21 %t/func.swift -- %t/func.swift
// RUN: -req=cursor -offset=5 %t/func.swift -- %t/func.swift

View File

@@ -0,0 +1,23 @@
// RUN: %empty-directory(%t)
// RUN: touch %t/empty.swift %t/func.swift
// Check that cursor info re-uses the underlying AST if it's able to based
// off edit snapshots.
// RUN: %sourcekitd-test \
// RUN: -shell -- echo '## State 1' == \
// RUN: -req=open -text-input %t/empty.swift %t/func.swift -- %t/func.swift == \
// RUN: -req=edit -offset=0 -length=0 -replace="func foo() {}" -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0,syntactic_only=1 %t/func.swift -- %t/func.swift == \
// RUN: -req=cursor -offset=5 %t/func.swift -- %t/func.swift == \
// RUN: -shell -- echo '## State 2' == \
// RUN: -req=edit -offset=0 -length=0 -replace="/* some comment */ " -req-opts=enablesyntaxmap=0,enablesubstructure=0,enablediagnostics=0,syntactic_only=1 %t/func.swift -- %t/func.swift == \
// RUN: -req=cursor -offset=24 %t/func.swift -- %t/func.swift | %FileCheck %s
// CHECK: ## State 1
// CHECK: source.lang.swift.decl.function.free
// CHECK: foo()
// CHECK: DID REUSE AST CONTEXT: 0
// CHECK: ## State 2
// CHECK: source.lang.swift.decl.function.free
// CHECK: foo()
// CHECK: DID REUSE AST CONTEXT: 1

View File

@@ -1086,7 +1086,8 @@ public:
Receiver) = 0;
virtual void
getNameInfo(StringRef Filename, unsigned Offset, NameTranslatingInfo &Input,
getNameInfo(StringRef PrimaryFilePath, StringRef InputBufferName,
unsigned Offset, NameTranslatingInfo &Input,
ArrayRef<const char *> Args,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<NameTranslatingInfo> &)>

View File

@@ -660,8 +660,9 @@ public:
Receiver) override;
void getNameInfo(
StringRef Filename, unsigned Offset, NameTranslatingInfo &Input,
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken,
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
NameTranslatingInfo &Input, ArrayRef<const char *> Args,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<NameTranslatingInfo> &)> Receiver)
override;

View File

@@ -1392,7 +1392,8 @@ class CursorRangeInfoConsumer : public SwiftASTConsumer {
protected:
SwiftLangSupport &Lang;
SwiftInvocationRef ASTInvok;
std::string InputFile;
std::string PrimaryFilePath;
std::string InputBufferName;
unsigned Offset;
unsigned Length;
@@ -1408,11 +1409,13 @@ protected:
}
public:
CursorRangeInfoConsumer(StringRef InputFile, unsigned Offset, unsigned Length,
CursorRangeInfoConsumer(StringRef PrimaryFilePath, StringRef InputBufferName,
unsigned Offset, unsigned Length,
SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
bool TryExistingAST, bool CancelOnSubsequentRequest)
: Lang(Lang), ASTInvok(ASTInvok), InputFile(InputFile.str()),
Offset(Offset), Length(Length), TryExistingAST(TryExistingAST),
: Lang(Lang), ASTInvok(ASTInvok), PrimaryFilePath(PrimaryFilePath.str()),
InputBufferName(InputBufferName.str()), Offset(Offset), Length(Length),
TryExistingAST(TryExistingAST),
CancelOnSubsequentRequest(CancelOnSubsequentRequest) {}
bool canUseASTWithSnapshots(ArrayRef<ImmutableTextSnapshotRef> Snapshots) override {
@@ -1429,7 +1432,7 @@ public:
ImmutableTextSnapshotRef InputSnap;
if (auto EditorDoc = Lang.getEditorDocuments()->findByPath(
InputFile, /*IsRealpath=*/true))
PrimaryFilePath, /*IsRealpath=*/true))
InputSnap = EditorDoc->getLatestSnapshot();
if (!InputSnap)
return false;
@@ -1498,8 +1501,8 @@ static SourceFile *retrieveInputFile(StringRef inputBufferName,
}
static void resolveCursor(
SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
unsigned Length, bool Actionables, bool SymbolGraph,
SwiftLangSupport &Lang, StringRef PrimaryFile, StringRef InputBufferName,
unsigned Offset, unsigned Length, bool Actionables, bool SymbolGraph,
SwiftInvocationRef Invok, bool TryExistingAST,
bool CancelOnSubsequentRequest,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fileSystem,
@@ -1516,20 +1519,22 @@ static void resolveCursor(
public:
CursorInfoConsumer(
StringRef InputFile, unsigned Offset, unsigned Length, bool Actionables,
bool SymbolGraph, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
StringRef PrimaryFile, StringRef InputBufferName, unsigned Offset,
unsigned Length, bool Actionables, bool SymbolGraph,
SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
bool TryExistingAST, bool CancelOnSubsequentRequest,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<CursorInfoData> &)> Receiver)
: CursorRangeInfoConsumer(InputFile, Offset, Length, Lang, ASTInvok,
TryExistingAST, CancelOnSubsequentRequest),
: CursorRangeInfoConsumer(PrimaryFile, InputBufferName, Offset, Length,
Lang, ASTInvok, TryExistingAST,
CancelOnSubsequentRequest),
Actionables(Actionables), SymbolGraph(SymbolGraph),
CancellationToken(CancellationToken), Receiver(std::move(Receiver)) {}
void handlePrimaryAST(ASTUnitRef AstUnit) override {
auto &CompIns = AstUnit->getCompilerInstance();
SourceFile *SF = retrieveInputFile(InputFile, CompIns);
SourceFile *SF = retrieveInputFile(InputBufferName, CompIns);
if (!SF) {
Receiver(RequestResult<CursorInfoData>::fromError(
"Unable to find input file"));
@@ -1604,14 +1609,14 @@ static void resolveCursor(
cast<ResolvedValueRefCursorInfo>(CursorInfo), Actionables,
SymbolGraph, Actions, Lang, CompInvok, Diagnostic,
getPreviousASTSnaps(),
/*DidReuseAST=*/false, Receiver);
/*DidReuseAST=*/!getPreviousASTSnaps().empty(), Receiver);
if (!Success) {
if (!getPreviousASTSnaps().empty()) {
// Attempt again using the up-to-date AST.
resolveCursor(Lang, InputFile, Offset, Length, Actionables,
SymbolGraph, ASTInvok, /*TryExistingAST=*/false,
CancelOnSubsequentRequest, SM.getFileSystem(),
CancellationToken, Receiver);
resolveCursor(Lang, PrimaryFilePath, InputBufferName, Offset,
Length, Actionables, SymbolGraph, ASTInvok,
/*TryExistingAST=*/false, CancelOnSubsequentRequest,
SM.getFileSystem(), CancellationToken, Receiver);
} else {
CursorInfoData Info;
Info.InternalDiagnostic = Diagnostic;
@@ -1662,8 +1667,9 @@ static void resolveCursor(
};
auto Consumer = std::make_shared<CursorInfoConsumer>(
InputFile, Offset, Length, Actionables, SymbolGraph, Lang, Invok,
TryExistingAST, CancelOnSubsequentRequest, CancellationToken, Receiver);
PrimaryFile, InputBufferName, Offset, Length, Actionables, SymbolGraph,
Lang, Invok, TryExistingAST, CancelOnSubsequentRequest, CancellationToken,
Receiver);
/// FIXME: When request cancellation is implemented and Xcode adopts it,
/// don't use 'OncePerASTToken'.
@@ -1710,8 +1716,9 @@ static void computeDiagnostics(
}
static void resolveName(
SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
SwiftInvocationRef Invok, bool TryExistingAST, NameTranslatingInfo &Input,
SwiftLangSupport &Lang, StringRef PrimaryFilePath,
StringRef InputBufferName, unsigned Offset, SwiftInvocationRef Invok,
bool TryExistingAST, NameTranslatingInfo &Input,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<NameTranslatingInfo> &)> Receiver) {
assert(Invok);
@@ -1723,13 +1730,14 @@ static void resolveName(
public:
NameInfoConsumer(
StringRef InputFile, unsigned Offset, SwiftLangSupport &Lang,
SwiftInvocationRef ASTInvok, bool TryExistingAST,
NameTranslatingInfo Input, SourceKitCancellationToken CancellationToken,
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
bool TryExistingAST, NameTranslatingInfo Input,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<NameTranslatingInfo> &)>
Receiver)
: CursorRangeInfoConsumer(InputFile, Offset, 0, Lang, ASTInvok,
TryExistingAST,
: CursorRangeInfoConsumer(PrimaryFilePath, InputBufferName, Offset, 0,
Lang, ASTInvok, TryExistingAST,
/*CancelOnSubsequentRequest=*/false),
Input(std::move(Input)), CancellationToken(CancellationToken),
Receiver(std::move(Receiver)) {}
@@ -1737,7 +1745,7 @@ static void resolveName(
void handlePrimaryAST(ASTUnitRef AstUnit) override {
auto &CompIns = AstUnit->getCompilerInstance();
SourceFile *SF = retrieveInputFile(InputFile, CompIns);
SourceFile *SF = retrieveInputFile(InputBufferName, CompIns);
if (!SF) {
Receiver(RequestResult<NameTranslatingInfo>::fromError(
"Unable to find input file"));
@@ -1778,9 +1786,9 @@ static void resolveName(
if (!Success) {
if (!getPreviousASTSnaps().empty()) {
// Attempt again using the up-to-date AST.
resolveName(Lang, InputFile, Offset, ASTInvok,
/*TryExistingAST=*/false, Input, CancellationToken,
Receiver);
resolveName(
Lang, PrimaryFilePath, InputBufferName, Offset, ASTInvok,
/*TryExistingAST=*/false, Input, CancellationToken, Receiver);
} else {
NameTranslatingInfo Info;
Info.InternalDiagnostic = Diagnostic;
@@ -1813,8 +1821,8 @@ static void resolveName(
};
auto Consumer = std::make_shared<NameInfoConsumer>(
InputFile, Offset, Lang, Invok, TryExistingAST, Input, CancellationToken,
Receiver);
PrimaryFilePath, InputBufferName, Offset, Lang, Invok, TryExistingAST,
Input, CancellationToken, Receiver);
Lang.getASTManager()->processASTAsync(
Invok, std::move(Consumer), /*OncePerASTToken=*/nullptr,
@@ -1822,8 +1830,9 @@ static void resolveName(
}
static void
resolveRange(SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
unsigned Length, SwiftInvocationRef Invok, bool TryExistingAST,
resolveRange(SwiftLangSupport &Lang, StringRef PrimaryFilePath,
StringRef InputBufferName, unsigned Offset, unsigned Length,
SwiftInvocationRef Invok, bool TryExistingAST,
bool CancelOnSubsequentRequest,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<RangeInfo> &)> Receiver) {
@@ -1835,20 +1844,21 @@ resolveRange(SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
public:
RangeInfoConsumer(
StringRef InputFile, unsigned Offset, unsigned Length,
SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
unsigned Length, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
bool TryExistingAST, bool CancelOnSubsequentRequest,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<RangeInfo> &)> Receiver)
: CursorRangeInfoConsumer(InputFile, Offset, Length, Lang, ASTInvok,
TryExistingAST, CancelOnSubsequentRequest),
: CursorRangeInfoConsumer(PrimaryFilePath, InputBufferName, Offset,
Length, Lang, ASTInvok, TryExistingAST,
CancelOnSubsequentRequest),
CancellationToken(CancellationToken), Receiver(std::move(Receiver)) {}
void handlePrimaryAST(ASTUnitRef AstUnit) override {
// FIXME: Implement tracing
auto &CompIns = AstUnit->getCompilerInstance();
SourceFile *SF = retrieveInputFile(InputFile, CompIns);
SourceFile *SF = retrieveInputFile(InputBufferName, CompIns);
if (!SF) {
Receiver(
RequestResult<RangeInfo>::fromError("Unable to find input file"));
@@ -1892,7 +1902,8 @@ resolveRange(SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
case RangeKind::Invalid:
if (!getPreviousASTSnaps().empty()) {
// Attempt again using the up-to-date AST.
resolveRange(Lang, InputFile, Offset, Length, ASTInvok,
resolveRange(Lang, PrimaryFilePath, InputBufferName, Offset, Length,
ASTInvok,
/*TryExistingAST=*/false, CancelOnSubsequentRequest,
CancellationToken, Receiver);
} else {
@@ -1913,8 +1924,8 @@ resolveRange(SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
};
auto Consumer = std::make_shared<RangeInfoConsumer>(
InputFile, Offset, Length, Lang, Invok, TryExistingAST,
CancelOnSubsequentRequest, CancellationToken, Receiver);
PrimaryFilePath, InputBufferName, Offset, Length, Lang, Invok,
TryExistingAST, CancelOnSubsequentRequest, CancellationToken, Receiver);
/// FIXME: When request cancellation is implemented and Xcode adopts it,
/// don't use 'OncePerASTToken'.
static const char OncePerASTToken = 0;
@@ -2097,8 +2108,8 @@ void SwiftLangSupport::getCursorInfo(
}
};
resolveCursor(*this, InputBufferName, Offset, Length, Actionables,
SymbolGraph, Invok, /*TryExistingAST=*/true,
resolveCursor(*this, PrimaryFilePath, InputBufferName, Offset, Length,
Actionables, SymbolGraph, Invok, /*TryExistingAST=*/true,
CancelOnSubsequentRequest, fileSystem, CancellationToken,
ASTBasedReceiver);
}
@@ -2152,17 +2163,18 @@ void SwiftLangSupport::getRangeInfo(
Receiver(RequestResult<RangeInfo>::fromError("Invalid range length."));
return;
}
resolveRange(*this, InputBufferName, Offset, Length, Invok,
resolveRange(*this, PrimaryFilePath, InputBufferName, Offset, Length, Invok,
/*TryExistingAST=*/true, CancelOnSubsequentRequest,
CancellationToken, Receiver);
}
void SwiftLangSupport::getNameInfo(
StringRef InputFile, unsigned Offset, NameTranslatingInfo &Input,
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken,
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
NameTranslatingInfo &Input, ArrayRef<const char *> Args,
SourceKitCancellationToken CancellationToken,
std::function<void(const RequestResult<NameTranslatingInfo> &)> Receiver) {
if (auto IFaceGenRef = IFaceGenContexts.get(InputFile)) {
if (auto IFaceGenRef = IFaceGenContexts.get(PrimaryFilePath)) {
IFaceGenRef->accessASTAsync([IFaceGenRef, Offset, Input, Receiver] {
SwiftInterfaceGenContext::ResolvedEntity Entity;
Entity = IFaceGenRef->resolveEntityForOffset(Offset);
@@ -2187,15 +2199,15 @@ void SwiftLangSupport::getNameInfo(
std::string Error;
SwiftInvocationRef Invok =
ASTMgr->getTypecheckInvocation(Args, InputFile, Error);
ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error);
if (!Invok) {
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
Receiver(RequestResult<NameTranslatingInfo>::fromError(Error));
return;
}
resolveName(*this, InputFile, Offset, Invok, /*TryExistingAST=*/true, Input,
CancellationToken, Receiver);
resolveName(*this, PrimaryFilePath, InputBufferName, Offset, Invok,
/*TryExistingAST=*/true, Input, CancellationToken, Receiver);
}
static void resolveCursorFromUSR(

View File

@@ -1379,16 +1379,24 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
KeepResponseAlive = true;
break;
case SourceKitRequest::Edit:
if (Opts.Length == 0 && Opts.ReplaceText->empty()) {
// Length=0, replace="" is a nop and will not trigger sema.
case SourceKitRequest::Edit: {
// Length=0, replace="" is a nop and will not trigger sema.
bool SyntacticOnly = Opts.Length == 0 && Opts.ReplaceText->empty();
for (const std::string &ReqOpt : Opts.RequestOptions) {
if (SyntacticOnly)
break;
if (ReqOpt.find("syntactic_only=1") != std::string::npos) {
SyntacticOnly = true;
}
}
if (SyntacticOnly) {
printRawResponse(Resp);
} else {
getSemanticInfo(Info, SourceFile);
KeepResponseAlive = true;
}
break;
}
case SourceKitRequest::DemangleNames:
printDemangleResults(sourcekitd_response_get_value(Resp), outs());
break;

View File

@@ -1735,7 +1735,7 @@ handleRequestNameTranslation(const RequestDict &Req,
llvm::transform(Selectors, std::back_inserter(Input.ArgNames),
[](const char *C) { return StringRef(C); });
return Lang.getNameInfo(
*PrimaryFilePath, Offset, Input, Args, CancellationToken,
*PrimaryFilePath, "", Offset, Input, Args, CancellationToken,
[Rec](const RequestResult<NameTranslatingInfo> &Result) {
reportNameInfo(Result, Rec);
});