[tintd] Implement TextDocumentReferencesRequest
Bug: tint:2127
Change-Id: Ic9a979ee7b55afc90cace949b0f3ab2f19a9dafb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/179403
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/tint/lang/wgsl/ls/BUILD.bazel b/src/tint/lang/wgsl/ls/BUILD.bazel
index db8b7b4..3449dc0 100644
--- a/src/tint/lang/wgsl/ls/BUILD.bazel
+++ b/src/tint/lang/wgsl/ls/BUILD.bazel
@@ -43,6 +43,7 @@
"diagnostics.cc",
"document.cc",
"file.cc",
+ "references.cc",
"serve.cc",
"server.cc",
"symbols.cc",
@@ -101,6 +102,7 @@
"diagnostics_test.cc",
"helpers_test.cc",
"helpers_test.h",
+ "references_test.cc",
"symbols_test.cc",
],
deps = [
diff --git a/src/tint/lang/wgsl/ls/BUILD.cmake b/src/tint/lang/wgsl/ls/BUILD.cmake
index f39c345..8cf5edc 100644
--- a/src/tint/lang/wgsl/ls/BUILD.cmake
+++ b/src/tint/lang/wgsl/ls/BUILD.cmake
@@ -46,6 +46,7 @@
lang/wgsl/ls/document.cc
lang/wgsl/ls/file.cc
lang/wgsl/ls/file.h
+ lang/wgsl/ls/references.cc
lang/wgsl/ls/serve.cc
lang/wgsl/ls/serve.h
lang/wgsl/ls/server.cc
@@ -109,6 +110,7 @@
lang/wgsl/ls/diagnostics_test.cc
lang/wgsl/ls/helpers_test.cc
lang/wgsl/ls/helpers_test.h
+ lang/wgsl/ls/references_test.cc
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 ce8653f..e018f9c 100644
--- a/src/tint/lang/wgsl/ls/BUILD.gn
+++ b/src/tint/lang/wgsl/ls/BUILD.gn
@@ -49,6 +49,7 @@
"document.cc",
"file.cc",
"file.h",
+ "references.cc",
"serve.cc",
"serve.h",
"server.cc",
@@ -101,6 +102,7 @@
"diagnostics_test.cc",
"helpers_test.cc",
"helpers_test.h",
+ "references_test.cc",
"symbols_test.cc",
]
deps = [
diff --git a/src/tint/lang/wgsl/ls/file.cc b/src/tint/lang/wgsl/ls/file.cc
index 39393fc..f586d16 100644
--- a/src/tint/lang/wgsl/ls/file.cc
+++ b/src/tint/lang/wgsl/ls/file.cc
@@ -101,8 +101,10 @@
}
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);
+ if (auto* te = program.Sem().Get<sem::TypeExpression>(node)) {
+ if (te->Type() == s) {
+ references.push_back(ident->source.range);
+ }
}
}
}
diff --git a/src/tint/lang/wgsl/ls/references.cc b/src/tint/lang/wgsl/ls/references.cc
new file mode 100644
index 0000000..9eda4d6
--- /dev/null
+++ b/src/tint/lang/wgsl/ls/references.cc
@@ -0,0 +1,57 @@
+// 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"
+#include "src/tint/utils/rtti/switch.h"
+
+namespace lsp = langsvr::lsp;
+
+namespace tint::wgsl::ls {
+
+typename lsp::TextDocumentReferencesRequest::ResultType //
+Server::Handle(const lsp::TextDocumentReferencesRequest& r) {
+ typename lsp::TextDocumentReferencesRequest::SuccessType result = lsp::Null{};
+
+ if (auto file = files_.Get(r.text_document.uri)) {
+ std::vector<lsp::Location> out;
+ for (auto& ref : (*file)->References(Conv(r.position), r.context.include_declaration)) {
+ lsp::Location loc;
+ loc.range = Conv(ref);
+ loc.uri = r.text_document.uri;
+ out.push_back(std::move(loc));
+ }
+ if (!out.empty()) {
+ result = out;
+ }
+ }
+
+ return result;
+}
+
+} // namespace tint::wgsl::ls
diff --git a/src/tint/lang/wgsl/ls/references_test.cc b/src/tint/lang/wgsl/ls/references_test.cc
new file mode 100644
index 0000000..1053eb2
--- /dev/null
+++ b/src/tint/lang/wgsl/ls/references_test.cc
@@ -0,0 +1,294 @@
+// 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;
+
+struct Case {
+ bool include_declaration;
+ std::string_view markup;
+};
+
+std::ostream& operator<<(std::ostream& stream, const Case& c) {
+ return stream << "wgsl: '" << c.markup << "'";
+}
+
+using LsReferencesTest = LsTestWithParam<Case>;
+TEST_P(LsReferencesTest, Symbols) {
+ auto parsed = ParseMarkers(GetParam().markup);
+ ASSERT_EQ(parsed.positions.size(), 1u);
+
+ lsp::TextDocumentReferencesRequest req{};
+ req.text_document.uri = OpenDocument(parsed.clean);
+ req.context.include_declaration = GetParam().include_declaration;
+ 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<std::vector<lsp::Location>>());
+ std::vector<lsp::Range> got_ranges;
+ for (auto& location : *res.Get<std::vector<lsp::Location>>()) {
+ EXPECT_EQ(location.uri, req.text_document.uri);
+ got_ranges.push_back(location.range);
+ }
+ EXPECT_THAT(got_ranges, testing::UnorderedElementsAreArray(parsed.ranges));
+ }
+}
+
+// TODO(bclayton): Type aliases.
+INSTANTIATE_TEST_SUITE_P(IncludeDeclaration,
+ LsReferencesTest,
+ ::testing::ValuesIn(std::vector<Case>{
+ {/* include_declaration */ true, R"(
+const「CONST」= 42;
+fn f() { _ =「⧘CONST」; }
+const C =「CONST」;
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+var<private>「VAR」= 42;
+fn f() { _ =「V⧘AR」; }
+fn g() { _ = 「VAR」; }
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+override「OVERRIDE」= 42;
+fn f() { _ =「OVERRID⧘E」+「OVERRIDE」; }
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+struct「STRUCT」{ i : i32 }
+fn f(s :「⧘STRUCT」) { var v : 「STRUCT」; }
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+struct S {「i」: i32 }
+fn f(s : S) { _ = s.「⧘i」; }
+fn g(s : S) { _ = s.「i」; }
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn f(「p」: i32) { _ =「⧘p」* 「p」; }
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn f() {
+ const「i」= 42;
+ _ =「⧘i」*「i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn f() {
+ let「i」= 42;
+ _ =「⧘i」+「i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn f() {
+ var「i」= 42;
+ 「i」=「⧘i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn f() {
+ var i = 42;
+ {
+ var「i」= 42;
+ 「i」=「⧘i」;
+ }
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn f() {
+ var「i」= 42;
+ {
+ var i = 42;
+ }
+ 「i」=「⧘i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+const i = 42;
+fn f() {
+ var「i」= 42;
+ 「i」=「⧘i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+const i = 42;
+fn f(「i」: i32) {
+ _ =「⧘i」*「i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn「a」() {}
+fn b() { 「⧘a」(); }
+fn c() { 「a」(); }
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn b() { 「a⧘」(); }
+fn「a」() {}
+fn c() { 「a」(); }
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+fn f() {
+ let「i」= 42;
+ _ = (max(「i⧘」, 「i」) * 「i」);
+}
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+const C = m⧘ax(1, 2);
+)"}, // =========================================
+ {/* include_declaration */ true, R"(
+const C : i⧘32 = 42;
+)"},
+ }));
+
+INSTANTIATE_TEST_SUITE_P(ExcludeDeclaration,
+ LsReferencesTest,
+ ::testing::ValuesIn(std::vector<Case>{
+ {/* include_declaration */ false, R"(
+const CONST = 42;
+fn f() { _ =「⧘CONST」; }
+const C =「CONST」;
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+var<private> VAR = 42;
+fn f() { _ =「V⧘AR」; }
+fn g() { _ = 「VAR」; }
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+override OVERRIDE = 42;
+fn f() { _ =「OVERRID⧘E」+「OVERRIDE」; }
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+struct STRUCT { i : i32 }
+fn f(s :「⧘STRUCT」) { var v : 「STRUCT」; }
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+struct S { i : i32 }
+fn f(s : S) { _ = s.「⧘i」; }
+fn g(s : S) { _ = s.「i」; }
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn f( p : i32) { _ =「⧘p」* 「p」; }
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn f() {
+ const i = 42;
+ _ =「⧘i」*「i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn f() {
+ let i = 42;
+ _ =「⧘i」+「i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn f() {
+ var i = 42;
+ 「i」=「⧘i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn f() {
+ var i = 42;
+ {
+ var i = 42;
+ 「i」=「⧘i」;
+ }
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn f() {
+ var i = 42;
+ {
+ var i = 42;
+ }
+ 「i」=「⧘i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+const i = 42;
+fn f() {
+ var i = 42;
+ 「i」=「⧘i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+const i = 42;
+fn f( i : i32) {
+ _ =「⧘i」*「i」;
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn a() {}
+fn b() { 「⧘a」(); }
+fn c() { 「a」(); }
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn b() { 「a⧘」(); }
+fn a() {}
+fn c() { 「a」(); }
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+fn f() {
+ let i = 42;
+ _ = (max(「i⧘」, 「i」) * 「i」);
+}
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+const C = m⧘ax(1, 2);
+)"}, // =========================================
+ {/* include_declaration */ false, R"(
+const C : i⧘32 = 42;
+)"},
+ }));
+
+} // namespace
+} // namespace tint::wgsl::ls
diff --git a/src/tint/lang/wgsl/ls/server.cc b/src/tint/lang/wgsl/ls/server.cc
index 0e2d9c0..faf5870 100644
--- a/src/tint/lang/wgsl/ls/server.cc
+++ b/src/tint/lang/wgsl/ls/server.cc
@@ -43,6 +43,10 @@
lsp::DocumentSymbolOptions opts;
return opts;
}();
+ result.capabilities.references_provider = [] {
+ lsp::ReferenceOptions opts;
+ return opts;
+ }();
return result;
});
@@ -61,6 +65,7 @@
// Request handlers
session.Register([&](const lsp::TextDocumentDefinitionRequest& r) { return Handle(r); });
session.Register([&](const lsp::TextDocumentDocumentSymbolRequest& r) { return Handle(r); });
+ session.Register([&](const lsp::TextDocumentReferencesRequest& r) { return Handle(r); });
}
Server::~Server() = default;
diff --git a/src/tint/lang/wgsl/ls/server.h b/src/tint/lang/wgsl/ls/server.h
index 03dce58..3335c7c 100644
--- a/src/tint/lang/wgsl/ls/server.h
+++ b/src/tint/lang/wgsl/ls/server.h
@@ -63,6 +63,10 @@
typename langsvr::lsp::TextDocumentDocumentSymbolRequest::ResultType //
Handle(const langsvr::lsp::TextDocumentDocumentSymbolRequest& r);
+ /// Handler for langsvr::lsp::TextDocumentReferencesRequest
+ typename langsvr::lsp::TextDocumentReferencesRequest::ResultType //
+ Handle(const langsvr::lsp::TextDocumentReferencesRequest&);
+
/// Handler for langsvr::lsp::TextDocumentDidOpenNotification
langsvr::Result<langsvr::SuccessType> //
Handle(const langsvr::lsp::TextDocumentDidOpenNotification&);
diff --git a/src/tint/lang/wgsl/ls/symbols_test.cc b/src/tint/lang/wgsl/ls/symbols_test.cc
index 8899adf..33820c9 100644
--- a/src/tint/lang/wgsl/ls/symbols_test.cc
+++ b/src/tint/lang/wgsl/ls/symbols_test.cc
@@ -44,6 +44,10 @@
const std::vector<lsp::DocumentSymbol> symbols;
};
+std::ostream& operator<<(std::ostream& stream, const Case& c) {
+ return stream << "wgsl: '" << c.wgsl << "'";
+}
+
struct Symbol : lsp::DocumentSymbol {
explicit Symbol(std::string_view n) { name = n; }
@@ -69,10 +73,6 @@
}
};
-std::ostream& operator<<(std::ostream& stream, const Case& c) {
- return stream << "wgsl: '" << c.wgsl << "'";
-}
-
using LsSymbolsTest = LsTestWithParam<Case>;
TEST_P(LsSymbolsTest, Symbols) {
lsp::TextDocumentDocumentSymbolRequest req{};