[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:
Simon Zünd 2019-04-23 10:47:33 +02:00 committed by Commit Bot
parent 72b28e658a
commit f663bb6e95
6 changed files with 133 additions and 11 deletions

View File

@ -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");
}

View File

@ -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

View File

@ -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 {

View File

@ -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])) {

View File

@ -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;

View File

@ -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