[tintd] Implement TextDocumentDefinitionRequest
Bug: tint:2127
Change-Id: I65f3de4b818a503421610a6539ab61b7cfa8cec3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/179105
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/wgsl/ls/BUILD.bazel b/src/tint/lang/wgsl/ls/BUILD.bazel
index 67c8c2a..db8b7b4 100644
--- a/src/tint/lang/wgsl/ls/BUILD.bazel
+++ b/src/tint/lang/wgsl/ls/BUILD.bazel
@@ -39,6 +39,7 @@
cc_library(
name = "ls",
srcs = [
+ "definition.cc",
"diagnostics.cc",
"document.cc",
"file.cc",
@@ -96,7 +97,9 @@
name = "test",
alwayslink = True,
srcs = [
+ "definition_test.cc",
"diagnostics_test.cc",
+ "helpers_test.cc",
"helpers_test.h",
"symbols_test.cc",
],
diff --git a/src/tint/lang/wgsl/ls/BUILD.cmake b/src/tint/lang/wgsl/ls/BUILD.cmake
index 47dc1f4..f39c345 100644
--- a/src/tint/lang/wgsl/ls/BUILD.cmake
+++ b/src/tint/lang/wgsl/ls/BUILD.cmake
@@ -41,6 +41,7 @@
# Condition: TINT_BUILD_TINTD AND TINT_BUILD_WGSL_READER
################################################################################
tint_add_target(tint_lang_wgsl_ls lib
+ lang/wgsl/ls/definition.cc
lang/wgsl/ls/diagnostics.cc
lang/wgsl/ls/document.cc
lang/wgsl/ls/file.cc
@@ -104,7 +105,9 @@
# Condition: TINT_BUILD_TINTD AND TINT_BUILD_WGSL_READER
################################################################################
tint_add_target(tint_lang_wgsl_ls_test test
+ lang/wgsl/ls/definition_test.cc
lang/wgsl/ls/diagnostics_test.cc
+ lang/wgsl/ls/helpers_test.cc
lang/wgsl/ls/helpers_test.h
lang/wgsl/ls/symbols_test.cc
)
diff --git a/src/tint/lang/wgsl/ls/BUILD.gn b/src/tint/lang/wgsl/ls/BUILD.gn
index 52190eb..ce8653f 100644
--- a/src/tint/lang/wgsl/ls/BUILD.gn
+++ b/src/tint/lang/wgsl/ls/BUILD.gn
@@ -44,6 +44,7 @@
if (tint_build_tintd && tint_build_wgsl_reader) {
libtint_source_set("ls") {
sources = [
+ "definition.cc",
"diagnostics.cc",
"document.cc",
"file.cc",
@@ -96,7 +97,9 @@
if (tint_build_tintd && tint_build_wgsl_reader) {
tint_unittests_source_set("unittests") {
sources = [
+ "definition_test.cc",
"diagnostics_test.cc",
+ "helpers_test.cc",
"helpers_test.h",
"symbols_test.cc",
]
diff --git a/src/tint/lang/wgsl/ls/definition.cc b/src/tint/lang/wgsl/ls/definition.cc
new file mode 100644
index 0000000..5d68a2f
--- /dev/null
+++ b/src/tint/lang/wgsl/ls/definition.cc
@@ -0,0 +1,52 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/wgsl/ls/server.h"
+
+#include "src/tint/lang/wgsl/ls/utils.h"
+
+namespace lsp = langsvr::lsp;
+
+namespace tint::wgsl::ls {
+
+typename lsp::TextDocumentDefinitionRequest::ResultType //
+Server::Handle(const lsp::TextDocumentDefinitionRequest& r) {
+ typename lsp::TextDocumentDefinitionRequest::SuccessType result = lsp::Null{};
+
+ if (auto file = files_.Get(r.text_document.uri)) {
+ if (auto def = (*file)->Definition(Conv(r.position))) {
+ lsp::Location loc;
+ loc.range = Conv(def->range);
+ loc.uri = r.text_document.uri;
+ result = lsp::Definition{loc};
+ }
+ }
+
+ return result;
+}
+
+} // namespace tint::wgsl::ls
diff --git a/src/tint/lang/wgsl/ls/definition_test.cc b/src/tint/lang/wgsl/ls/definition_test.cc
new file mode 100644
index 0000000..5a630a1
--- /dev/null
+++ b/src/tint/lang/wgsl/ls/definition_test.cc
@@ -0,0 +1,175 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string_view>
+
+#include "gmock/gmock.h"
+
+#include "langsvr/lsp/lsp.h"
+#include "langsvr/lsp/primitives.h"
+#include "langsvr/lsp/printer.h"
+#include "src/tint/lang/wgsl/ls/helpers_test.h"
+#include "src/tint/utils/text/unicode.h"
+
+namespace tint::wgsl::ls {
+namespace {
+
+namespace lsp = langsvr::lsp;
+
+using LsDefinitionTest = LsTestWithParam<std::string_view>;
+TEST_P(LsDefinitionTest, Symbols) {
+ auto parsed = ParseMarkers(GetParam());
+ ASSERT_EQ(parsed.positions.size(), 1u);
+ ASSERT_LE(parsed.ranges.size(), 1u);
+
+ lsp::TextDocumentDefinitionRequest req{};
+ req.text_document.uri = OpenDocument(parsed.clean);
+ req.position = parsed.positions[0];
+
+ for (auto& n : diagnostics_) {
+ for (auto& d : n.diagnostics) {
+ if (d.severity == lsp::DiagnosticSeverity::kError) {
+ FAIL() << "Error: " << d.message << "\nWGSL:\n" << parsed.clean;
+ }
+ }
+ }
+
+ auto future = client_session_.Send(req);
+ ASSERT_EQ(future, langsvr::Success);
+ auto res = future->get();
+ if (parsed.ranges.empty()) {
+ ASSERT_TRUE(res.Is<lsp::Null>());
+ } else {
+ ASSERT_TRUE(res.Is<lsp::Definition>());
+ auto definition = *res.Get<lsp::Definition>();
+ ASSERT_TRUE(definition.Is<lsp::Location>());
+ EXPECT_THAT(definition.Get<lsp::Location>()->uri, req.text_document.uri);
+ EXPECT_THAT(definition.Get<lsp::Location>()->range, parsed.ranges[0]);
+ }
+}
+
+// TODO(bclayton): Type aliases.
+INSTANTIATE_TEST_SUITE_P(,
+ LsDefinitionTest,
+ ::testing::ValuesIn(std::vector<std::string_view>{
+ R"(
+const「CONST」= 42;
+fn f() { _ = ⧘CONST; }
+)", // =========================================
+ R"(
+var<private>「VAR」= 42;
+fn f() { _ = V⧘AR; }
+)", // =========================================
+ R"(
+override「OVERRIDE」= 42;
+fn f() { _ = OVERRID⧘E; }
+)", // =========================================
+ R"(
+struct「STRUCT」{ i : i32 }
+fn f(s : ⧘STRUCT) {}
+)", // =========================================
+ R"(
+struct S {「i」: i32 }
+fn f(s : S) { _ = s.⧘i; }
+)", // =========================================
+ R"(
+fn f(「p」: i32) { _ = ⧘p; }
+)", // =========================================
+ R"(
+fn f() {
+ const「i」= 42;
+ _ = ⧘i;
+}
+)", // =========================================
+ R"(
+fn f() {
+ let「i」= 42;
+ _ = ⧘i;
+}
+)", // =========================================
+ R"(
+fn f() {
+ var「i」= 42;
+ _ = ⧘i;
+}
+)", // =========================================
+ R"(
+fn f() {
+ var i = 42;
+ {
+ var「i」= 42;
+ _ = ⧘i;
+ }
+}
+)", // =========================================
+ R"(
+fn f() {
+ var「i」= 42;
+ {
+ var i = 42;
+ }
+ _ = ⧘i;
+}
+)", // =========================================
+ R"(
+const i = 42;
+fn f() {
+ var「i」= 42;
+ _ = ⧘i;
+}
+)", // =========================================
+ R"(
+const i = 42;
+fn f(「i」: i32) {
+ _ = ⧘i;
+}
+)", // =========================================
+ R"(
+fn「a」() {}
+fn b() { ⧘a(); }
+)", // =========================================
+ R"(
+fn b() { ⧘a(); }
+fn「a」() {}
+)", // =========================================
+ R"(
+fn f() {
+ let「i」= 42;
+ _ = (max(i⧘, 8) * 5);
+}
+)", // =========================================
+ R"(
+const C = m⧘ax(1, 2);
+)", // =========================================
+ R"(
+const C : i⧘32 = 42;
+)"}));
+
+} // namespace
+} // namespace tint::wgsl::ls
diff --git a/src/tint/lang/wgsl/ls/file.cc b/src/tint/lang/wgsl/ls/file.cc
index f89b099..39393fc 100644
--- a/src/tint/lang/wgsl/ls/file.cc
+++ b/src/tint/lang/wgsl/ls/file.cc
@@ -36,6 +36,7 @@
#include "src/tint/lang/wgsl/sem/function_expression.h"
#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
#include "src/tint/lang/wgsl/sem/struct.h"
+#include "src/tint/lang/wgsl/sem/type_expression.h"
#include "src/tint/lang/wgsl/sem/variable.h"
#include "src/tint/utils/rtti/switch.h"
@@ -94,6 +95,18 @@
}
}
};
+ auto struct_ = [&](const sem::Struct* s) {
+ if (include_declaration) {
+ references.push_back(s->Declaration()->name->source.range);
+ }
+ for (auto* node : nodes) {
+ if (auto* ident = node->As<ast::IdentifierExpression>()) {
+ if (program.Sem().Get<sem::Struct>(node) == s) {
+ references.push_back(ident->source.range);
+ }
+ }
+ }
+ };
Switch(
Unwrap(NodeAt<CastableBase>(l)), //
@@ -106,7 +119,11 @@
struct_member(member);
}
},
- [&](const sem::StructMember* m) { struct_member(m); });
+ [&](const sem::StructMember* m) { struct_member(m); },
+ [&](const sem::TypeExpression* te) {
+ return Switch(te->Type(), //
+ [&](const sem::Struct* s) { return struct_(s); });
+ });
return references;
}
@@ -123,7 +140,11 @@
}
return nullptr;
},
- [&](const sem::StructMember* m) { return m->Declaration()->name; });
+ [&](const sem::StructMember* m) { return m->Declaration()->name; },
+ [&](const sem::TypeExpression* te) {
+ return Switch(te->Type(), //
+ [&](const sem::Struct* s) { return s->Declaration()->name; });
+ });
if (ident) {
return TextAndRange{ident->symbol.Name(), ident->source.range};
}
diff --git a/src/tint/lang/wgsl/ls/helpers_test.cc b/src/tint/lang/wgsl/ls/helpers_test.cc
new file mode 100644
index 0000000..d996285
--- /dev/null
+++ b/src/tint/lang/wgsl/ls/helpers_test.cc
@@ -0,0 +1,86 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/wgsl/ls/helpers_test.h"
+
+#include <utility>
+
+namespace tint::wgsl::ls {
+
+namespace lsp = langsvr::lsp;
+
+ParsedMarkers ParseMarkers(std::string_view str) {
+ std::stringstream clean;
+ lsp::Position current_position;
+ std::vector<langsvr::lsp::Position> positions;
+ std::vector<langsvr::lsp::Range> ranges;
+ std::optional<langsvr::lsp::Range> current_range;
+ while (!str.empty()) {
+ auto [codepoint, len] =
+ utf8::Decode(reinterpret_cast<const uint8_t*>(str.data()), str.length());
+ if (codepoint == 0 || len == 0) {
+ break;
+ }
+
+ switch (codepoint) {
+ case '\n':
+ current_position.line++;
+ current_position.character = 0;
+ clean << "\n";
+ break;
+ case U"「"[0]:
+ // Range start. Replace with ' '
+ current_position.character++;
+ current_range = lsp::Range{};
+ current_range->start = current_position;
+ clean << ' ';
+ break;
+ case U"」"[0]:
+ // Range end. Replace with ' '
+ if (current_range) {
+ current_range->end = current_position;
+ ranges.push_back(*current_range);
+ current_range.reset();
+ }
+ clean << ' ';
+ current_position.character++;
+ break;
+ case U"⧘"[0]:
+ // Position. Consume
+ positions.push_back(current_position);
+ break;
+ default:
+ clean << str.substr(0, len);
+ current_position.character++;
+ break;
+ }
+ str = str.substr(len);
+ }
+ return ParsedMarkers{std::move(ranges), std::move(positions), clean.str()};
+}
+
+} // namespace tint::wgsl::ls
diff --git a/src/tint/lang/wgsl/ls/helpers_test.h b/src/tint/lang/wgsl/ls/helpers_test.h
index 08377d2..d98b7a2 100644
--- a/src/tint/lang/wgsl/ls/helpers_test.h
+++ b/src/tint/lang/wgsl/ls/helpers_test.h
@@ -90,6 +90,21 @@
template <typename T>
using LsTestWithParam = LsTestImpl<testing::TestWithParam<T>>;
+/// Result structure of ParseMarkers
+struct ParsedMarkers {
+ /// All parsed ranges, marked up with '「' and '」'. For example: `「my_range」`
+ std::vector<langsvr::lsp::Range> ranges;
+ /// All parsed positions, marked up with '⧘'. For example: `posi⧘tion`
+ std::vector<langsvr::lsp::Position> positions;
+ /// The string with all markup removed.
+ /// '「' and '」' are replaced with whitespace.
+ /// '⧘' are omitted with no replacement characters.
+ std::string clean;
+};
+
+/// ParseMarkers parses location and range markers from the string @p str.
+ParsedMarkers ParseMarkers(std::string_view str);
+
} // namespace tint::wgsl::ls
#endif // SRC_TINT_LANG_WGSL_LS_HELPERS_TEST_H_
diff --git a/src/tint/lang/wgsl/ls/server.cc b/src/tint/lang/wgsl/ls/server.cc
index e8e3791..0e2d9c0 100644
--- a/src/tint/lang/wgsl/ls/server.cc
+++ b/src/tint/lang/wgsl/ls/server.cc
@@ -38,6 +38,7 @@
Server::Server(langsvr::Session& session) : session_(session) {
session.Register([&](const lsp::InitializeRequest&) {
lsp::InitializeResult result;
+ result.capabilities.definition_provider = true;
result.capabilities.document_symbol_provider = [] {
lsp::DocumentSymbolOptions opts;
return opts;
@@ -58,6 +59,7 @@
[&](const lsp::WorkspaceDidChangeConfigurationNotification&) { return langsvr::Success; });
// Request handlers
+ session.Register([&](const lsp::TextDocumentDefinitionRequest& r) { return Handle(r); });
session.Register([&](const lsp::TextDocumentDocumentSymbolRequest& r) { return Handle(r); });
}
diff --git a/src/tint/lang/wgsl/ls/server.h b/src/tint/lang/wgsl/ls/server.h
index ab84a36..03dce58 100644
--- a/src/tint/lang/wgsl/ls/server.h
+++ b/src/tint/lang/wgsl/ls/server.h
@@ -55,6 +55,10 @@
bool ShuttingDown() const { return shutting_down_; }
private:
+ /// Handler for langsvr::lsp::TextDocumentDefinitionRequest
+ typename langsvr::lsp::TextDocumentDefinitionRequest::ResultType //
+ Handle(const langsvr::lsp::TextDocumentDefinitionRequest&);
+
/// Handler for langsvr::lsp::TextDocumentDocumentSymbolRequest
typename langsvr::lsp::TextDocumentDocumentSymbolRequest::ResultType //
Handle(const langsvr::lsp::TextDocumentDocumentSymbolRequest& r);