Commit f663bb6e authored by Simon Zünd's avatar Simon Zünd Committed by Commit Bot

[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/+/1569434Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60942}
parent 72b28e65
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment