[torque-ls] Send compilation errors to the client
This CL implements the first set of diagnostic notifications. When Torque compilation fails, the language server translates the Torque error into a diagnostics notification and pushes it to the client. Note that per specification, the server is responsible to manage the state of all published diagnostics. This means that the server is also responsible for clearing out previous notifications if they become stale. Bug: v8:8880 Change-Id: Ief46dc1d94d1e5b7fa3e0048df494bfc05974031 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1569434 Reviewed-by: Sigurd Schneider <sigurds@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/master@{#60942}
This commit is contained in:
parent
72b28e658a
commit
f663bb6e95
@ -19,6 +19,7 @@ namespace torque {
|
||||
|
||||
DEFINE_CONTEXTUAL_VARIABLE(Logger)
|
||||
DEFINE_CONTEXTUAL_VARIABLE(TorqueFileList)
|
||||
DEFINE_CONTEXTUAL_VARIABLE(DiagnosticsFiles)
|
||||
|
||||
namespace ls {
|
||||
|
||||
@ -63,7 +64,61 @@ void WriteMessage(JsonValue& message) {
|
||||
|
||||
namespace {
|
||||
|
||||
void RecompileTorque() {
|
||||
void ResetCompilationErrorDiagnostics(MessageWriter writer) {
|
||||
for (const SourceId& source : DiagnosticsFiles::Get()) {
|
||||
PublishDiagnosticsNotification notification;
|
||||
notification.set_method("textDocument/publishDiagnostics");
|
||||
|
||||
std::string error_file = SourceFileMap::GetSource(source);
|
||||
notification.params().set_uri(error_file);
|
||||
// Trigger empty array creation.
|
||||
USE(notification.params().diagnostics_size());
|
||||
|
||||
writer(notification.GetJsonValue());
|
||||
}
|
||||
}
|
||||
|
||||
void SendCompilationErrorDiagnostics(const TorqueError& error,
|
||||
MessageWriter writer) {
|
||||
PublishDiagnosticsNotification notification;
|
||||
notification.set_method("textDocument/publishDiagnostics");
|
||||
|
||||
std::string error_file =
|
||||
error.position ? SourceFileMap::GetSource(error.position->source)
|
||||
: "<unknown>";
|
||||
notification.params().set_uri(error_file);
|
||||
|
||||
Diagnostic diagnostic = notification.params().add_diagnostics();
|
||||
diagnostic.set_severity(Diagnostic::kError);
|
||||
diagnostic.set_message(error.message);
|
||||
diagnostic.set_source("Torque Compiler");
|
||||
|
||||
if (error.position) {
|
||||
Range range = diagnostic.range();
|
||||
range.start().set_line(error.position->start.line);
|
||||
range.start().set_character(error.position->start.column);
|
||||
range.end().set_line(error.position->end.line);
|
||||
range.end().set_character(error.position->end.column);
|
||||
}
|
||||
writer(notification.GetJsonValue());
|
||||
|
||||
if (error.position) DiagnosticsFiles::Get().push_back(error.position->source);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void CompilationFinished(TorqueCompilerResult result, MessageWriter writer) {
|
||||
LanguageServerData::Get() = result.language_server_data;
|
||||
SourceFileMap::Get() = result.source_file_map;
|
||||
|
||||
if (result.error) {
|
||||
SendCompilationErrorDiagnostics(*result.error, writer);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void RecompileTorque(MessageWriter writer) {
|
||||
Logger::Log("[info] Start compilation run ...\n");
|
||||
|
||||
TorqueCompilerOptions options;
|
||||
@ -74,10 +129,14 @@ void RecompileTorque() {
|
||||
|
||||
TorqueCompilerResult result = CompileTorque(TorqueFileList::Get(), options);
|
||||
|
||||
LanguageServerData::Get() = result.language_server_data;
|
||||
SourceFileMap::Get() = result.source_file_map;
|
||||
|
||||
Logger::Log("[info] Finished compilation run ...\n");
|
||||
|
||||
CompilationFinished(result, writer);
|
||||
}
|
||||
|
||||
void RecompileTorqueWithDiagnostics(MessageWriter writer) {
|
||||
ResetCompilationErrorDiagnostics(writer);
|
||||
RecompileTorque(writer);
|
||||
}
|
||||
|
||||
void HandleInitializeRequest(InitializeRequest request, MessageWriter writer) {
|
||||
@ -115,7 +174,8 @@ void HandleInitializedNotification(MessageWriter writer) {
|
||||
writer(request.GetJsonValue());
|
||||
}
|
||||
|
||||
void HandleTorqueFileListNotification(TorqueFileListNotification notification) {
|
||||
void HandleTorqueFileListNotification(TorqueFileListNotification notification,
|
||||
MessageWriter writer) {
|
||||
CHECK_EQ(notification.params().object()["files"].tag, JsonValue::ARRAY);
|
||||
|
||||
std::vector<std::string>& files = TorqueFileList::Get();
|
||||
@ -145,7 +205,7 @@ void HandleTorqueFileListNotification(TorqueFileListNotification notification) {
|
||||
return a < b;
|
||||
});
|
||||
|
||||
RecompileTorque();
|
||||
RecompileTorqueWithDiagnostics(writer);
|
||||
}
|
||||
|
||||
void HandleGotoDefinitionRequest(GotoDefinitionRequest request,
|
||||
@ -186,10 +246,10 @@ void HandleGotoDefinitionRequest(GotoDefinitionRequest request,
|
||||
}
|
||||
|
||||
void HandleChangeWatchedFilesNotification(
|
||||
DidChangeWatchedFilesNotification notification) {
|
||||
DidChangeWatchedFilesNotification notification, MessageWriter writer) {
|
||||
// TODO(szuend): Implement updates to the TorqueFile list when create/delete
|
||||
// notifications are received. Currently we simply re-compile.
|
||||
RecompileTorque();
|
||||
RecompileTorqueWithDiagnostics(writer);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -213,13 +273,13 @@ void HandleMessage(JsonValue& raw_message, MessageWriter writer) {
|
||||
HandleInitializedNotification(writer);
|
||||
} else if (method == "torque/fileList") {
|
||||
HandleTorqueFileListNotification(
|
||||
TorqueFileListNotification(request.GetJsonValue()));
|
||||
TorqueFileListNotification(request.GetJsonValue()), writer);
|
||||
} else if (method == "textDocument/definition") {
|
||||
HandleGotoDefinitionRequest(GotoDefinitionRequest(request.GetJsonValue()),
|
||||
writer);
|
||||
} else if (method == "workspace/didChangeWatchedFiles") {
|
||||
HandleChangeWatchedFilesNotification(
|
||||
DidChangeWatchedFilesNotification(request.GetJsonValue()));
|
||||
DidChangeWatchedFilesNotification(request.GetJsonValue()), writer);
|
||||
} else {
|
||||
Logger::Log("[error] Message of type ", method, " is not handled!\n\n");
|
||||
}
|
||||
|
@ -7,10 +7,19 @@
|
||||
|
||||
#include "src/base/macros.h"
|
||||
#include "src/torque/ls/json.h"
|
||||
#include "src/torque/source-positions.h"
|
||||
#include "src/torque/torque-compiler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
// A list of source Ids for which the LS provided diagnostic information
|
||||
// after the last compile. The LS is responsible for syncing diagnostic
|
||||
// information with the client. Before updated information can be sent,
|
||||
// old diagnostic messages have to be reset.
|
||||
DECLARE_CONTEXTUAL_VARIABLE(DiagnosticsFiles, std::vector<SourceId>);
|
||||
|
||||
namespace ls {
|
||||
|
||||
// The message handler might send responses or follow up requests.
|
||||
@ -19,6 +28,10 @@ using MessageWriter = void (*)(JsonValue& message);
|
||||
|
||||
V8_EXPORT_PRIVATE void HandleMessage(JsonValue& raw_message, MessageWriter);
|
||||
|
||||
// Called when a compilation run finishes. Exposed for testability.
|
||||
V8_EXPORT_PRIVATE void CompilationFinished(TorqueCompilerResult result,
|
||||
MessageWriter);
|
||||
|
||||
} // namespace ls
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
|
@ -254,6 +254,31 @@ class TextDocumentPositionParams : public NestedJsonAccessor {
|
||||
JSON_OBJECT_ACCESSORS(JsonPosition, position)
|
||||
};
|
||||
|
||||
class Diagnostic : public NestedJsonAccessor {
|
||||
public:
|
||||
using NestedJsonAccessor::NestedJsonAccessor;
|
||||
|
||||
enum DiagnosticSeverity {
|
||||
kError = 1,
|
||||
kWarning = 2,
|
||||
kInformation = 3,
|
||||
kHint = 4
|
||||
};
|
||||
|
||||
JSON_OBJECT_ACCESSORS(Range, range)
|
||||
JSON_INT_ACCESSORS(severity)
|
||||
JSON_STRING_ACCESSORS(source)
|
||||
JSON_STRING_ACCESSORS(message)
|
||||
};
|
||||
|
||||
class PublishDiagnosticsParams : public NestedJsonAccessor {
|
||||
public:
|
||||
using NestedJsonAccessor::NestedJsonAccessor;
|
||||
|
||||
JSON_STRING_ACCESSORS(uri)
|
||||
JSON_ARRAY_OBJECT_ACCESSORS(Diagnostic, diagnostics)
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Request : public Message {
|
||||
public:
|
||||
@ -269,6 +294,7 @@ using RegistrationRequest = Request<RegistrationParams>;
|
||||
using TorqueFileListNotification = Request<FileListParams>;
|
||||
using GotoDefinitionRequest = Request<TextDocumentPositionParams>;
|
||||
using DidChangeWatchedFilesNotification = Request<DidChangeWatchedFilesParams>;
|
||||
using PublishDiagnosticsNotification = Request<PublishDiagnosticsParams>;
|
||||
|
||||
template <class T>
|
||||
class Response : public Message {
|
||||
|
@ -22,6 +22,7 @@ int WrappedMain(int argc, const char** argv) {
|
||||
TorqueFileList::Scope files_scope;
|
||||
LanguageServerData::Scope server_data_scope;
|
||||
SourceFileMap::Scope source_file_map_scope;
|
||||
DiagnosticsFiles::Scope diagnostics_files_scope;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp("-l", argv[i])) {
|
||||
|
@ -52,7 +52,7 @@ bool IsSnakeCase(const std::string& s);
|
||||
bool IsValidNamespaceConstName(const std::string& s);
|
||||
bool IsValidTypeName(const std::string& s);
|
||||
|
||||
struct TorqueError : public std::exception {
|
||||
struct TorqueError {
|
||||
explicit TorqueError(const std::string& message) : message(message) {}
|
||||
|
||||
std::string message;
|
||||
|
@ -111,6 +111,28 @@ TEST(LanguageServerMessage, GotoDefinition) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(LanguageServerMessage, CompilationErrorSendsDiagnostics) {
|
||||
LanguageServerData::Scope server_data_scope;
|
||||
SourceFileMap::Scope source_file_map_scope;
|
||||
|
||||
TorqueCompilerResult result;
|
||||
result.error = TorqueError("compilation failed somehow");
|
||||
result.source_file_map = SourceFileMap::Get();
|
||||
|
||||
CompilationFinished(result, [](JsonValue& raw_response) {
|
||||
PublishDiagnosticsNotification notification(raw_response);
|
||||
|
||||
EXPECT_EQ(notification.method(), "textDocument/publishDiagnostics");
|
||||
ASSERT_FALSE(notification.IsNull("params"));
|
||||
EXPECT_EQ(notification.params().uri(), "<unknown>");
|
||||
|
||||
ASSERT_GT(notification.params().diagnostics_size(), static_cast<size_t>(0));
|
||||
Diagnostic diagnostic = notification.params().diagnostics(0);
|
||||
EXPECT_EQ(diagnostic.severity(), Diagnostic::kError);
|
||||
EXPECT_EQ(diagnostic.message(), "compilation failed somehow");
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace ls
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
|
Loading…
Reference in New Issue
Block a user