mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[SourceKit] Add a dedicated request to retrieve the diagnostics of a file
Currently, SourceKit always implicitly sends diagnostics to the client after every edit. This doesn’t match our new cancellation story because diagnostics retrieval is no request and thus can’t be cancelled. Instead, diagnostics retrieval should be a standalone request, which returns a cancellation token like every other request and which can thus also be cancelled as such. The indented transition path here is to change all open and edit requests to be syntactic only. When diagnostics are needed, the new diagnostics request should be used. rdar://83391522
This commit is contained in:
49
test/SourceKit/Diagnostics/diags.swift
Normal file
49
test/SourceKit/Diagnostics/diags.swift
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Use -print-raw-response on the first request so we don't wait for semantic info which will never come.
|
||||||
|
// RUN: %sourcekitd-test -req=open %s -req-opts=syntactic_only=1 -print-raw-response -- %s == \
|
||||||
|
// RUN: -req=stats == \
|
||||||
|
// RUN: -req=diags %s -print-raw-response -- %s == \
|
||||||
|
// RUN: -req=stats == \
|
||||||
|
// RUN: -req=diags %s -print-raw-response -- %s == \
|
||||||
|
// RUN: -req=stats | %FileCheck %s
|
||||||
|
|
||||||
|
func foo(y: String) {
|
||||||
|
foo(y: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We shouldn't build an AST for the syntactic open
|
||||||
|
// CHECK: 0 {{.*}} source.statistic.num-ast-builds
|
||||||
|
|
||||||
|
// Retrieving diagnostics should workd
|
||||||
|
// CHECK: {
|
||||||
|
// CHECK: key.diagnostics: [
|
||||||
|
// CHECK: {
|
||||||
|
// CHECK: key.line: 10,
|
||||||
|
// CHECK: key.column: 10,
|
||||||
|
// CHECK: key.filepath: "{{.*}}",
|
||||||
|
// CHECK: key.severity: source.diagnostic.severity.error,
|
||||||
|
// CHECK: key.id: "cannot_convert_argument_value",
|
||||||
|
// CHECK: key.description: "cannot convert value of type 'Int' to expected argument type 'String'"
|
||||||
|
// CHECK: }
|
||||||
|
// CHECK: ]
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// ... and we should have built an AST for it
|
||||||
|
// CHECK: 1 {{.*}} source.statistic.num-ast-builds
|
||||||
|
|
||||||
|
// Retrieving diagnostics again should workd
|
||||||
|
// CHECK: {
|
||||||
|
// CHECK: key.diagnostics: [
|
||||||
|
// CHECK: {
|
||||||
|
// CHECK: key.line: 10,
|
||||||
|
// CHECK: key.column: 10,
|
||||||
|
// CHECK: key.filepath: "{{.*}}",
|
||||||
|
// CHECK: key.severity: source.diagnostic.severity.error,
|
||||||
|
// CHECK: key.id: "cannot_convert_argument_value",
|
||||||
|
// CHECK: key.description: "cannot convert value of type 'Int' to expected argument type 'String'"
|
||||||
|
// CHECK: }
|
||||||
|
// CHECK: ]
|
||||||
|
// CHECK: }
|
||||||
|
|
||||||
|
// ... but we shouldn't rebuild an AST
|
||||||
|
// CHECK: 1 {{.*}} source.statistic.num-ast-builds
|
||||||
|
// CHECK: 1 {{.*}} source.statistic.num-ast-cache-hits
|
||||||
@@ -500,6 +500,9 @@ struct CursorInfoData {
|
|||||||
llvm::ArrayRef<RefactoringInfo> AvailableActions;
|
llvm::ArrayRef<RefactoringInfo> AvailableActions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The result type of `LangSupport::getDiagnostics`
|
||||||
|
typedef ArrayRef<DiagnosticEntryInfo> DiagnosticsResult;
|
||||||
|
|
||||||
struct RangeInfo {
|
struct RangeInfo {
|
||||||
UIdent RangeKind;
|
UIdent RangeKind;
|
||||||
StringRef ExprType;
|
StringRef ExprType;
|
||||||
@@ -832,6 +835,13 @@ public:
|
|||||||
SourceKitCancellationToken CancellationToken,
|
SourceKitCancellationToken CancellationToken,
|
||||||
std::function<void(const RequestResult<CursorInfoData> &)> Receiver) = 0;
|
std::function<void(const RequestResult<CursorInfoData> &)> Receiver) = 0;
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
getDiagnostics(StringRef InputFile, ArrayRef<const char *> Args,
|
||||||
|
Optional<VFSOptions> VfsOptions,
|
||||||
|
SourceKitCancellationToken CancellationToken,
|
||||||
|
std::function<void(const RequestResult<DiagnosticsResult> &)>
|
||||||
|
Receiver) = 0;
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
getNameInfo(StringRef Filename, unsigned Offset, NameTranslatingInfo &Input,
|
getNameInfo(StringRef Filename, unsigned Offset, NameTranslatingInfo &Input,
|
||||||
ArrayRef<const char *> Args,
|
ArrayRef<const char *> Args,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#ifndef LLVM_SOURCEKIT_SUPPORT_FILESYSTEMPROVIDER_H
|
#ifndef LLVM_SOURCEKIT_SUPPORT_FILESYSTEMPROVIDER_H
|
||||||
#define LLVM_SOURCEKIT_SUPPORT_FILESYSTEMPROVIDER_H
|
#define LLVM_SOURCEKIT_SUPPORT_FILESYSTEMPROVIDER_H
|
||||||
|
|
||||||
|
#include "SourceKit/Core/LangSupport.h"
|
||||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
#include "llvm/ADT/StringMap.h"
|
||||||
|
|||||||
@@ -569,6 +569,13 @@ public:
|
|||||||
std::function<void(const RequestResult<CursorInfoData> &)>
|
std::function<void(const RequestResult<CursorInfoData> &)>
|
||||||
Receiver) override;
|
Receiver) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
getDiagnostics(StringRef InputFile, ArrayRef<const char *> Args,
|
||||||
|
Optional<VFSOptions> VfsOptions,
|
||||||
|
SourceKitCancellationToken CancellationToken,
|
||||||
|
std::function<void(const RequestResult<DiagnosticsResult> &)>
|
||||||
|
Receiver) override;
|
||||||
|
|
||||||
void getNameInfo(
|
void getNameInfo(
|
||||||
StringRef Filename, unsigned Offset, NameTranslatingInfo &Input,
|
StringRef Filename, unsigned Offset, NameTranslatingInfo &Input,
|
||||||
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken,
|
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken,
|
||||||
|
|||||||
@@ -10,12 +10,13 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "SwiftASTManager.h"
|
|
||||||
#include "SwiftLangSupport.h"
|
|
||||||
#include "SourceKit/Support/FileSystemProvider.h"
|
#include "SourceKit/Support/FileSystemProvider.h"
|
||||||
#include "SourceKit/Support/ImmutableTextBuffer.h"
|
#include "SourceKit/Support/ImmutableTextBuffer.h"
|
||||||
#include "SourceKit/Support/Logging.h"
|
#include "SourceKit/Support/Logging.h"
|
||||||
#include "SourceKit/Support/UIdent.h"
|
#include "SourceKit/Support/UIdent.h"
|
||||||
|
#include "SwiftASTManager.h"
|
||||||
|
#include "SwiftEditorDiagConsumer.h"
|
||||||
|
#include "SwiftLangSupport.h"
|
||||||
|
|
||||||
#include "swift/AST/ASTDemangler.h"
|
#include "swift/AST/ASTDemangler.h"
|
||||||
#include "swift/AST/ASTPrinter.h"
|
#include "swift/AST/ASTPrinter.h"
|
||||||
@@ -1588,6 +1589,36 @@ static void resolveCursor(
|
|||||||
CancellationToken, fileSystem);
|
CancellationToken, fileSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void computeDiagnostics(
|
||||||
|
SwiftLangSupport &Lang, StringRef InputFile, SwiftInvocationRef Invok,
|
||||||
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
|
||||||
|
SourceKitCancellationToken CancellationToken,
|
||||||
|
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver) {
|
||||||
|
|
||||||
|
class DiagnosticsConsumer : public SwiftASTConsumer {
|
||||||
|
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DiagnosticsConsumer(
|
||||||
|
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver)
|
||||||
|
: Receiver(Receiver) {}
|
||||||
|
|
||||||
|
void handlePrimaryAST(ASTUnitRef AstUnit) override {
|
||||||
|
unsigned BufferID =
|
||||||
|
AstUnit->getPrimarySourceFile().getBufferID().getValue();
|
||||||
|
auto &DiagConsumer = AstUnit->getEditorDiagConsumer();
|
||||||
|
auto Diagnostics = DiagConsumer.getDiagnosticsForBuffer(BufferID);
|
||||||
|
Receiver(RequestResult<DiagnosticsResult>::fromResult(Diagnostics));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto Consumer = std::make_shared<DiagnosticsConsumer>(std::move(Receiver));
|
||||||
|
|
||||||
|
Lang.getASTManager()->processASTAsync(Invok, std::move(Consumer),
|
||||||
|
/*OncePerASTToken=*/nullptr,
|
||||||
|
CancellationToken, FileSystem);
|
||||||
|
}
|
||||||
|
|
||||||
static void resolveName(
|
static void resolveName(
|
||||||
SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
|
SwiftLangSupport &Lang, StringRef InputFile, unsigned Offset,
|
||||||
SwiftInvocationRef Invok, bool TryExistingAST, NameTranslatingInfo &Input,
|
SwiftInvocationRef Invok, bool TryExistingAST, NameTranslatingInfo &Input,
|
||||||
@@ -1846,6 +1877,33 @@ void SwiftLangSupport::getCursorInfo(
|
|||||||
fileSystem, CancellationToken, Receiver);
|
fileSystem, CancellationToken, Receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SwiftLangSupport::getDiagnostics(
|
||||||
|
StringRef InputFile, ArrayRef<const char *> Args,
|
||||||
|
Optional<VFSOptions> VfsOptions,
|
||||||
|
SourceKitCancellationToken CancellationToken,
|
||||||
|
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver) {
|
||||||
|
std::string FileSystemError;
|
||||||
|
auto FileSystem = getFileSystem(VfsOptions, InputFile, FileSystemError);
|
||||||
|
if (!FileSystem) {
|
||||||
|
Receiver(RequestResult<DiagnosticsResult>::fromError(FileSystemError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InvocationError;
|
||||||
|
SwiftInvocationRef Invok =
|
||||||
|
ASTMgr->getInvocation(Args, InputFile, FileSystem, InvocationError);
|
||||||
|
if (!InvocationError.empty()) {
|
||||||
|
LOG_WARN_FUNC("error creating ASTInvocation: " << InvocationError);
|
||||||
|
}
|
||||||
|
if (!Invok) {
|
||||||
|
Receiver(RequestResult<DiagnosticsResult>::fromError(InvocationError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
computeDiagnostics(*this, InputFile, Invok, FileSystem, CancellationToken,
|
||||||
|
Receiver);
|
||||||
|
}
|
||||||
|
|
||||||
void SwiftLangSupport::getRangeInfo(
|
void SwiftLangSupport::getRangeInfo(
|
||||||
StringRef InputFile, unsigned Offset, unsigned Length,
|
StringRef InputFile, unsigned Offset, unsigned Length,
|
||||||
bool CancelOnSubsequentRequest, ArrayRef<const char *> Args,
|
bool CancelOnSubsequentRequest, ArrayRef<const char *> Args,
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef<const char *> Args) {
|
|||||||
.Case("collect-var-type", SourceKitRequest::CollectVariableType)
|
.Case("collect-var-type", SourceKitRequest::CollectVariableType)
|
||||||
.Case("global-config", SourceKitRequest::GlobalConfiguration)
|
.Case("global-config", SourceKitRequest::GlobalConfiguration)
|
||||||
.Case("dependency-updated", SourceKitRequest::DependencyUpdated)
|
.Case("dependency-updated", SourceKitRequest::DependencyUpdated)
|
||||||
|
.Case("diags", SourceKitRequest::Diagnostics)
|
||||||
#define SEMANTIC_REFACTORING(KIND, NAME, ID) .Case("refactoring." #ID, SourceKitRequest::KIND)
|
#define SEMANTIC_REFACTORING(KIND, NAME, ID) .Case("refactoring." #ID, SourceKitRequest::KIND)
|
||||||
#include "swift/IDE/RefactoringKinds.def"
|
#include "swift/IDE/RefactoringKinds.def"
|
||||||
.Default(SourceKitRequest::None);
|
.Default(SourceKitRequest::None);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ enum class SourceKitRequest {
|
|||||||
CollectVariableType,
|
CollectVariableType,
|
||||||
GlobalConfiguration,
|
GlobalConfiguration,
|
||||||
DependencyUpdated,
|
DependencyUpdated,
|
||||||
|
Diagnostics,
|
||||||
#define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND,
|
#define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND,
|
||||||
#include "swift/IDE/RefactoringKinds.def"
|
#include "swift/IDE/RefactoringKinds.def"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1075,6 +1075,9 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
|
|||||||
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
||||||
RequestDependencyUpdated);
|
RequestDependencyUpdated);
|
||||||
break;
|
break;
|
||||||
|
case SourceKitRequest::Diagnostics:
|
||||||
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDiagnostics);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SourceFile.empty()) {
|
if (!SourceFile.empty()) {
|
||||||
@@ -1305,6 +1308,7 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
|
|||||||
case SourceKitRequest::TypeContextInfo:
|
case SourceKitRequest::TypeContextInfo:
|
||||||
case SourceKitRequest::ConformingMethodList:
|
case SourceKitRequest::ConformingMethodList:
|
||||||
case SourceKitRequest::DependencyUpdated:
|
case SourceKitRequest::DependencyUpdated:
|
||||||
|
case SourceKitRequest::Diagnostics:
|
||||||
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
|
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1465,6 +1469,7 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
|
|||||||
break;
|
break;
|
||||||
case SourceKitRequest::Statistics:
|
case SourceKitRequest::Statistics:
|
||||||
printStatistics(Info, llvm::outs());
|
printStatistics(Info, llvm::outs());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -193,6 +193,9 @@ static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf,
|
|||||||
|
|
||||||
static void reportCursorInfo(const RequestResult<CursorInfoData> &Result, ResponseReceiver Rec);
|
static void reportCursorInfo(const RequestResult<CursorInfoData> &Result, ResponseReceiver Rec);
|
||||||
|
|
||||||
|
static void reportDiagnostics(const RequestResult<DiagnosticsResult> &Result,
|
||||||
|
ResponseReceiver Rec);
|
||||||
|
|
||||||
static void reportExpressionTypeInfo(const RequestResult<ExpressionTypesInFile> &Result,
|
static void reportExpressionTypeInfo(const RequestResult<ExpressionTypesInFile> &Result,
|
||||||
ResponseReceiver Rec);
|
ResponseReceiver Rec);
|
||||||
|
|
||||||
@@ -1277,6 +1280,16 @@ static void handleSemanticRequest(
|
|||||||
Args, CancellationToken, Rec);
|
Args, CancellationToken, Rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ReqUID == RequestDiagnostics) {
|
||||||
|
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
||||||
|
Lang.getDiagnostics(*SourceFile, Args, std::move(vfsOptions),
|
||||||
|
CancellationToken,
|
||||||
|
[Rec](const RequestResult<DiagnosticsResult> &Result) {
|
||||||
|
reportDiagnostics(Result, Rec);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
llvm::raw_svector_ostream OSErr(ErrBuf);
|
llvm::raw_svector_ostream OSErr(ErrBuf);
|
||||||
OSErr << "unknown request: " << UIdentFromSKDUID(ReqUID).getName();
|
OSErr << "unknown request: " << UIdentFromSKDUID(ReqUID).getName();
|
||||||
@@ -1948,6 +1961,27 @@ static void reportCursorInfo(const RequestResult<CursorInfoData> &Result,
|
|||||||
return Rec(RespBuilder.createResponse());
|
return Rec(RespBuilder.createResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// ReportDiagnostics
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
static void reportDiagnostics(const RequestResult<DiagnosticsResult> &Result,
|
||||||
|
ResponseReceiver Rec) {
|
||||||
|
if (Result.isCancelled())
|
||||||
|
return Rec(createErrorRequestCancelled());
|
||||||
|
if (Result.isError())
|
||||||
|
return Rec(createErrorRequestFailed(Result.getError()));
|
||||||
|
|
||||||
|
auto &DiagResults = Result.value();
|
||||||
|
|
||||||
|
ResponseBuilder RespBuilder;
|
||||||
|
auto Dict = RespBuilder.getDictionary();
|
||||||
|
auto DiagArray = Dict.setArray(KeyDiagnostics);
|
||||||
|
for (const auto &DiagInfo : DiagResults)
|
||||||
|
fillDictionaryForDiagnosticInfo(DiagArray.appendDictionary(), DiagInfo);
|
||||||
|
Rec(RespBuilder.createResponse());
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// ReportRangeInfo
|
// ReportRangeInfo
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ UID_REQUESTS = [
|
|||||||
REQUEST('CollectVariableType', 'source.request.variable.type'),
|
REQUEST('CollectVariableType', 'source.request.variable.type'),
|
||||||
REQUEST('GlobalConfiguration', 'source.request.configuration.global'),
|
REQUEST('GlobalConfiguration', 'source.request.configuration.global'),
|
||||||
REQUEST('DependencyUpdated', 'source.request.dependency_updated'),
|
REQUEST('DependencyUpdated', 'source.request.dependency_updated'),
|
||||||
|
REQUEST('Diagnostics', 'source.request.diagnostics'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user