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 { ...@@ -19,6 +19,7 @@ namespace torque {
DEFINE_CONTEXTUAL_VARIABLE(Logger) DEFINE_CONTEXTUAL_VARIABLE(Logger)
DEFINE_CONTEXTUAL_VARIABLE(TorqueFileList) DEFINE_CONTEXTUAL_VARIABLE(TorqueFileList)
DEFINE_CONTEXTUAL_VARIABLE(DiagnosticsFiles)
namespace ls { namespace ls {
...@@ -63,7 +64,61 @@ void WriteMessage(JsonValue& message) { ...@@ -63,7 +64,61 @@ void WriteMessage(JsonValue& message) {
namespace { 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"); Logger::Log("[info] Start compilation run ...\n");
TorqueCompilerOptions options; TorqueCompilerOptions options;
...@@ -74,10 +129,14 @@ void RecompileTorque() { ...@@ -74,10 +129,14 @@ void RecompileTorque() {
TorqueCompilerResult result = CompileTorque(TorqueFileList::Get(), options); 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"); Logger::Log("[info] Finished compilation run ...\n");
CompilationFinished(result, writer);
}
void RecompileTorqueWithDiagnostics(MessageWriter writer) {
ResetCompilationErrorDiagnostics(writer);
RecompileTorque(writer);
} }
void HandleInitializeRequest(InitializeRequest request, MessageWriter writer) { void HandleInitializeRequest(InitializeRequest request, MessageWriter writer) {
...@@ -115,7 +174,8 @@ void HandleInitializedNotification(MessageWriter writer) { ...@@ -115,7 +174,8 @@ void HandleInitializedNotification(MessageWriter writer) {
writer(request.GetJsonValue()); writer(request.GetJsonValue());
} }
void HandleTorqueFileListNotification(TorqueFileListNotification notification) { void HandleTorqueFileListNotification(TorqueFileListNotification notification,
MessageWriter writer) {
CHECK_EQ(notification.params().object()["files"].tag, JsonValue::ARRAY); CHECK_EQ(notification.params().object()["files"].tag, JsonValue::ARRAY);
std::vector<std::string>& files = TorqueFileList::Get(); std::vector<std::string>& files = TorqueFileList::Get();
...@@ -145,7 +205,7 @@ void HandleTorqueFileListNotification(TorqueFileListNotification notification) { ...@@ -145,7 +205,7 @@ void HandleTorqueFileListNotification(TorqueFileListNotification notification) {
return a < b; return a < b;
}); });
RecompileTorque(); RecompileTorqueWithDiagnostics(writer);
} }
void HandleGotoDefinitionRequest(GotoDefinitionRequest request, void HandleGotoDefinitionRequest(GotoDefinitionRequest request,
...@@ -186,10 +246,10 @@ void HandleGotoDefinitionRequest(GotoDefinitionRequest request, ...@@ -186,10 +246,10 @@ void HandleGotoDefinitionRequest(GotoDefinitionRequest request,
} }
void HandleChangeWatchedFilesNotification( void HandleChangeWatchedFilesNotification(
DidChangeWatchedFilesNotification notification) { DidChangeWatchedFilesNotification notification, MessageWriter writer) {
// TODO(szuend): Implement updates to the TorqueFile list when create/delete // TODO(szuend): Implement updates to the TorqueFile list when create/delete
// notifications are received. Currently we simply re-compile. // notifications are received. Currently we simply re-compile.
RecompileTorque(); RecompileTorqueWithDiagnostics(writer);
} }
} // namespace } // namespace
...@@ -213,13 +273,13 @@ void HandleMessage(JsonValue& raw_message, MessageWriter writer) { ...@@ -213,13 +273,13 @@ void HandleMessage(JsonValue& raw_message, MessageWriter writer) {
HandleInitializedNotification(writer); HandleInitializedNotification(writer);
} else if (method == "torque/fileList") { } else if (method == "torque/fileList") {
HandleTorqueFileListNotification( HandleTorqueFileListNotification(
TorqueFileListNotification(request.GetJsonValue())); TorqueFileListNotification(request.GetJsonValue()), writer);
} else if (method == "textDocument/definition") { } else if (method == "textDocument/definition") {
HandleGotoDefinitionRequest(GotoDefinitionRequest(request.GetJsonValue()), HandleGotoDefinitionRequest(GotoDefinitionRequest(request.GetJsonValue()),
writer); writer);
} else if (method == "workspace/didChangeWatchedFiles") { } else if (method == "workspace/didChangeWatchedFiles") {
HandleChangeWatchedFilesNotification( HandleChangeWatchedFilesNotification(
DidChangeWatchedFilesNotification(request.GetJsonValue())); DidChangeWatchedFilesNotification(request.GetJsonValue()), writer);
} else { } else {
Logger::Log("[error] Message of type ", method, " is not handled!\n\n"); Logger::Log("[error] Message of type ", method, " is not handled!\n\n");
} }
......
...@@ -7,10 +7,19 @@ ...@@ -7,10 +7,19 @@
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/torque/ls/json.h" #include "src/torque/ls/json.h"
#include "src/torque/source-positions.h"
#include "src/torque/torque-compiler.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
namespace torque { 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 { namespace ls {
// The message handler might send responses or follow up requests. // The message handler might send responses or follow up requests.
...@@ -19,6 +28,10 @@ using MessageWriter = void (*)(JsonValue& message); ...@@ -19,6 +28,10 @@ using MessageWriter = void (*)(JsonValue& message);
V8_EXPORT_PRIVATE void HandleMessage(JsonValue& raw_message, MessageWriter); 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 ls
} // namespace torque } // namespace torque
} // namespace internal } // namespace internal
......
...@@ -254,6 +254,31 @@ class TextDocumentPositionParams : public NestedJsonAccessor { ...@@ -254,6 +254,31 @@ class TextDocumentPositionParams : public NestedJsonAccessor {
JSON_OBJECT_ACCESSORS(JsonPosition, position) 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> template <class T>
class Request : public Message { class Request : public Message {
public: public:
...@@ -269,6 +294,7 @@ using RegistrationRequest = Request<RegistrationParams>; ...@@ -269,6 +294,7 @@ using RegistrationRequest = Request<RegistrationParams>;
using TorqueFileListNotification = Request<FileListParams>; using TorqueFileListNotification = Request<FileListParams>;
using GotoDefinitionRequest = Request<TextDocumentPositionParams>; using GotoDefinitionRequest = Request<TextDocumentPositionParams>;
using DidChangeWatchedFilesNotification = Request<DidChangeWatchedFilesParams>; using DidChangeWatchedFilesNotification = Request<DidChangeWatchedFilesParams>;
using PublishDiagnosticsNotification = Request<PublishDiagnosticsParams>;
template <class T> template <class T>
class Response : public Message { class Response : public Message {
......
...@@ -22,6 +22,7 @@ int WrappedMain(int argc, const char** argv) { ...@@ -22,6 +22,7 @@ int WrappedMain(int argc, const char** argv) {
TorqueFileList::Scope files_scope; TorqueFileList::Scope files_scope;
LanguageServerData::Scope server_data_scope; LanguageServerData::Scope server_data_scope;
SourceFileMap::Scope source_file_map_scope; SourceFileMap::Scope source_file_map_scope;
DiagnosticsFiles::Scope diagnostics_files_scope;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (!strcmp("-l", argv[i])) { if (!strcmp("-l", argv[i])) {
......
...@@ -52,7 +52,7 @@ bool IsSnakeCase(const std::string& s); ...@@ -52,7 +52,7 @@ bool IsSnakeCase(const std::string& s);
bool IsValidNamespaceConstName(const std::string& s); bool IsValidNamespaceConstName(const std::string& s);
bool IsValidTypeName(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) {} explicit TorqueError(const std::string& message) : message(message) {}
std::string message; std::string message;
......
...@@ -111,6 +111,28 @@ TEST(LanguageServerMessage, GotoDefinition) { ...@@ -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 ls
} // namespace torque } // namespace torque
} // namespace internal } // 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