[tintd] Implement lsp::TextDocumentSignatureHelpRequest
Bug: tint:2127
Change-Id: I60865e10fcee1824bc4c6d41274aebac7a44d3e3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/180483
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@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 53c566d..25ddd43 100644
--- a/src/tint/lang/wgsl/ls/BUILD.bazel
+++ b/src/tint/lang/wgsl/ls/BUILD.bazel
@@ -54,6 +54,7 @@
"serve.cc",
"server.cc",
"set_trace.cc",
+ "signature_help.cc",
"symbols.cc",
],
hdrs = [
@@ -67,12 +68,14 @@
"//src/tint/api/common",
"//src/tint/lang/core",
"//src/tint/lang/core/constant",
+ "//src/tint/lang/core/intrinsic",
"//src/tint/lang/core/ir",
"//src/tint/lang/core/type",
"//src/tint/lang/wgsl",
"//src/tint/lang/wgsl/ast",
"//src/tint/lang/wgsl/common",
"//src/tint/lang/wgsl/features",
+ "//src/tint/lang/wgsl/intrinsic",
"//src/tint/lang/wgsl/program",
"//src/tint/lang/wgsl/sem",
"//src/tint/utils/containers",
@@ -116,6 +119,7 @@
"references_test.cc",
"rename_test.cc",
"sem_tokens_test.cc",
+ "signature_help_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 456469f..611c64d 100644
--- a/src/tint/lang/wgsl/ls/BUILD.cmake
+++ b/src/tint/lang/wgsl/ls/BUILD.cmake
@@ -60,6 +60,7 @@
lang/wgsl/ls/server.cc
lang/wgsl/ls/server.h
lang/wgsl/ls/set_trace.cc
+ lang/wgsl/ls/signature_help.cc
lang/wgsl/ls/symbols.cc
lang/wgsl/ls/utils.h
)
@@ -68,12 +69,14 @@
tint_api_common
tint_lang_core
tint_lang_core_constant
+ tint_lang_core_intrinsic
tint_lang_core_ir
tint_lang_core_type
tint_lang_wgsl
tint_lang_wgsl_ast
tint_lang_wgsl_common
tint_lang_wgsl_features
+ tint_lang_wgsl_intrinsic
tint_lang_wgsl_program
tint_lang_wgsl_sem
tint_utils_containers
@@ -124,6 +127,7 @@
lang/wgsl/ls/references_test.cc
lang/wgsl/ls/rename_test.cc
lang/wgsl/ls/sem_tokens_test.cc
+ lang/wgsl/ls/signature_help_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 dc692f8..6dde581 100644
--- a/src/tint/lang/wgsl/ls/BUILD.gn
+++ b/src/tint/lang/wgsl/ls/BUILD.gn
@@ -63,6 +63,7 @@
"server.cc",
"server.h",
"set_trace.cc",
+ "signature_help.cc",
"symbols.cc",
"utils.h",
]
@@ -71,12 +72,14 @@
"${tint_src_dir}/api/common",
"${tint_src_dir}/lang/core",
"${tint_src_dir}/lang/core/constant",
+ "${tint_src_dir}/lang/core/intrinsic",
"${tint_src_dir}/lang/core/ir",
"${tint_src_dir}/lang/core/type",
"${tint_src_dir}/lang/wgsl",
"${tint_src_dir}/lang/wgsl/ast",
"${tint_src_dir}/lang/wgsl/common",
"${tint_src_dir}/lang/wgsl/features",
+ "${tint_src_dir}/lang/wgsl/intrinsic",
"${tint_src_dir}/lang/wgsl/program",
"${tint_src_dir}/lang/wgsl/sem",
"${tint_src_dir}/utils/containers",
@@ -116,6 +119,7 @@
"references_test.cc",
"rename_test.cc",
"sem_tokens_test.cc",
+ "signature_help_test.cc",
"symbols_test.cc",
]
deps = [
diff --git a/src/tint/lang/wgsl/ls/file.h b/src/tint/lang/wgsl/ls/file.h
index 51f2004..7e6c367 100644
--- a/src/tint/lang/wgsl/ls/file.h
+++ b/src/tint/lang/wgsl/ls/file.h
@@ -31,10 +31,15 @@
#include <limits>
#include <memory>
#include <string>
+#include <type_traits>
#include <vector>
#include "src/tint/lang/wgsl/ast/node.h"
+#include "src/tint/lang/wgsl/ls/utils.h"
#include "src/tint/lang/wgsl/program/program.h"
+#include "src/tint/lang/wgsl/sem/expression.h"
+#include "src/tint/lang/wgsl/sem/load.h"
+#include "src/tint/lang/wgsl/sem/materialize.h"
#include "src/tint/utils/diagnostic/source.h"
namespace tint::wgsl::ls {
@@ -73,9 +78,24 @@
/// @returns the definition of the symbol at the location @p l in the file.
std::optional<DefinitionResult> Definition(Source::Location l);
+ /// Behaviour of NodeAt()
+ enum class UnwrapMode {
+ /// NodeAt will Unwrap() the semantic node when searching for the template type.
+ kNoUnwrap,
+ /// NodeAt will not Unwrap() the semantic node when searching for the template type.
+ kUnwrap
+ };
+
+ // Default UnwrapMode is to unwrap, unless searching for a sem::Materialize or sem::Load
+ template <typename T>
+ static constexpr UnwrapMode DefaultUnwrapMode =
+ (std::is_same_v<T, sem::Materialize> || std::is_same_v<T, sem::Load>)
+ ? UnwrapMode::kNoUnwrap
+ : UnwrapMode::kUnwrap;
+
/// @returns the inner-most semantic node at the location @p l in the file.
/// @tparam T the type or subtype of the node to scan for.
- template <typename T = sem::Node>
+ template <typename T = sem::Node, UnwrapMode UNWRAP_MODE = DefaultUnwrapMode<T>>
const T* NodeAt(Source::Location l) const {
// TODO(bclayton): This is a brute-force search. Optimize.
size_t best_len = std::numeric_limits<uint32_t>::max();
@@ -83,12 +103,15 @@
for (auto* node : nodes) {
if (node->source.range.begin.line == node->source.range.end.line &&
node->source.range.begin <= l && node->source.range.end >= l) {
- if (auto* sem =
- As<T, CastFlags::kDontErrorOnImpossibleCast>(program.Sem().Get(node))) {
+ auto* sem = program.Sem().Get(node);
+ if constexpr (UNWRAP_MODE == UnwrapMode::kUnwrap) {
+ sem = Unwrap(sem);
+ }
+ if (auto* cast = As<T, CastFlags::kDontErrorOnImpossibleCast>(sem)) {
size_t len = node->source.range.end.column - node->source.range.begin.column;
if (len < best_len) {
best_len = len;
- best_node = sem;
+ best_node = cast;
}
}
}
diff --git a/src/tint/lang/wgsl/ls/hover.cc b/src/tint/lang/wgsl/ls/hover.cc
index ae8b2f3..5ffbe47 100644
--- a/src/tint/lang/wgsl/ls/hover.cc
+++ b/src/tint/lang/wgsl/ls/hover.cc
@@ -180,7 +180,7 @@
return lsp::Null{};
}
- auto* node = (*file)->NodeAt<CastableBase>(Conv(r.position));
+ auto* node = (*file)->NodeAt<CastableBase, File::UnwrapMode::kNoUnwrap>(Conv(r.position));
if (!node) {
return lsp::Null{};
}
diff --git a/src/tint/lang/wgsl/ls/server.cc b/src/tint/lang/wgsl/ls/server.cc
index f5aa72f..6b526de 100644
--- a/src/tint/lang/wgsl/ls/server.cc
+++ b/src/tint/lang/wgsl/ls/server.cc
@@ -69,6 +69,10 @@
}
return opts;
}();
+ result.capabilities.signature_help_provider = [] {
+ lsp::SignatureHelpOptions opts;
+ return opts;
+ }();
return result;
});
@@ -97,6 +101,7 @@
session.Register([&](const lsp::TextDocumentRenameRequest& r) { return Handle(r); });
session.Register(
[&](const lsp::TextDocumentSemanticTokensFullRequest& r) { return Handle(r); });
+ session.Register([&](const lsp::TextDocumentSignatureHelpRequest& 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 5fa616c..e0df2f3 100644
--- a/src/tint/lang/wgsl/ls/server.h
+++ b/src/tint/lang/wgsl/ls/server.h
@@ -103,6 +103,10 @@
langsvr::Result<langsvr::SuccessType> //
Handle(const langsvr::lsp::SetTraceNotification&);
+ /// Handler for langsvr::lsp::TextDocumentSignatureHelpRequest
+ typename langsvr::lsp::TextDocumentSignatureHelpRequest::ResultType //
+ Handle(const langsvr::lsp::TextDocumentSignatureHelpRequest&);
+
/// Handler for langsvr::lsp::TextDocumentDidOpenNotification
langsvr::Result<langsvr::SuccessType> //
Handle(const langsvr::lsp::TextDocumentDidOpenNotification&);
diff --git a/src/tint/lang/wgsl/ls/signature_help.cc b/src/tint/lang/wgsl/ls/signature_help.cc
new file mode 100644
index 0000000..401e621
--- /dev/null
+++ b/src/tint/lang/wgsl/ls/signature_help.cc
@@ -0,0 +1,213 @@
+// 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/core/intrinsic/table.h"
+#include "src/tint/lang/wgsl/intrinsic/dialect.h"
+#include "src/tint/lang/wgsl/ls/utils.h"
+#include "src/tint/lang/wgsl/sem/call.h"
+#include "src/tint/utils/rtti/switch.h"
+#include "src/tint/utils/text/string_stream.h"
+
+namespace lsp = langsvr::lsp;
+
+namespace tint::wgsl::ls {
+
+namespace {
+
+std::vector<lsp::ParameterInformation> Params(const core::intrinsic::TableData& data,
+ const core::intrinsic::OverloadInfo& overload) {
+ std::vector<lsp::ParameterInformation> params;
+ for (size_t i = 0; i < overload.num_parameters; i++) {
+ lsp::ParameterInformation param_out;
+ auto& param_in = data[overload.parameters + i];
+ if (param_in.usage != core::ParameterUsage::kNone) {
+ param_out.label = std::string(core::ToString(param_in.usage));
+ } else {
+ param_out.label = "param-" + std::to_string(i);
+ }
+ params.push_back(std::move(param_out));
+ }
+ return params;
+}
+
+size_t CalcParamIndex(const Source& call_source, const Source::Location& carat) {
+ size_t index = 0;
+ int depth = 0;
+
+ auto start = call_source.range.begin;
+ auto end = std::min(call_source.range.end, carat);
+ auto& lines = call_source.file->content.lines;
+
+ for (auto line = start.line; line <= end.line; line++) {
+ auto start_column = line == start.line ? start.column : 0;
+ auto end_column = line == end.line ? end.column : 0;
+ auto text = lines[line - 1].substr(start_column - 1, end_column - start_column);
+ for (char c : text) {
+ switch (c) {
+ case '(':
+ case '[':
+ depth++;
+ break;
+ case ')':
+ case ']':
+ depth--;
+ break;
+ case ',':
+ if (depth == 1) {
+ index++;
+ }
+ }
+ }
+ }
+ return index;
+}
+
+void PrintOverload(StyledText& ss,
+ core::intrinsic::Context& context,
+ const core::intrinsic::OverloadInfo& overload,
+ std::string_view intrinsic_name) {
+ // Restore old style before returning.
+ auto prev_style = ss.Style();
+ TINT_DEFER(ss << prev_style);
+
+ core::intrinsic::TemplateState templates;
+
+ auto earliest_eval_stage = core::EvaluationStage::kConstant;
+
+ ss << style::Code << style::Function(intrinsic_name);
+
+ if (overload.num_explicit_templates > 0) {
+ ss << "<";
+ for (size_t i = 0; i < overload.num_explicit_templates; i++) {
+ const auto& tmpl = context.data[overload.templates + i];
+ if (i > 0) {
+ ss << ", ";
+ }
+ ss << style::Type(tmpl.name) << " ";
+ }
+ ss << ">";
+ }
+
+ ss << "(";
+ for (size_t i = 0; i < overload.num_parameters; i++) {
+ const auto& parameter = context.data[overload.parameters + i];
+ auto* matcher_indices = context.data[parameter.matcher_indices];
+
+ if (i > 0) {
+ ss << ", ";
+ }
+
+ if (parameter.usage != core::ParameterUsage::kNone) {
+ ss << style::Variable(parameter.usage, ": ");
+ }
+ context.Match(templates, overload, matcher_indices, earliest_eval_stage).PrintType(ss);
+ }
+ ss << ")";
+ if (overload.return_matcher_indices.IsValid()) {
+ ss << " -> ";
+ auto* matcher_indices = context.data[overload.return_matcher_indices];
+ context.Match(templates, overload, matcher_indices, earliest_eval_stage).PrintType(ss);
+ }
+
+ bool first = true;
+ auto separator = [&] {
+ ss << style::Plain(first ? " where:\n " : "\n ");
+ first = false;
+ };
+
+ for (size_t i = 0; i < overload.num_templates; i++) {
+ auto& tmpl = context.data[overload.templates + i];
+ if (auto* matcher_indices = context.data[tmpl.matcher_indices]) {
+ separator();
+
+ ss << style::Type(tmpl.name) << style::Plain(" is ");
+ if (tmpl.kind == core::intrinsic::TemplateInfo::Kind::kType) {
+ context.Match(templates, overload, matcher_indices, earliest_eval_stage)
+ .PrintType(ss);
+ } else {
+ context.Match(templates, overload, matcher_indices, earliest_eval_stage)
+ .PrintNum(ss);
+ }
+ }
+ }
+}
+
+} // namespace
+
+typename lsp::TextDocumentSignatureHelpRequest::ResultType //
+Server::Handle(const lsp::TextDocumentSignatureHelpRequest& r) {
+ auto file = files_.Get(r.text_document.uri);
+ if (!file) {
+ return lsp::Null{};
+ }
+
+ auto& program = (*file)->program;
+ auto pos = Conv(r.position);
+
+ auto call = (*file)->NodeAt<sem::Call>(pos);
+ if (!call) {
+ return lsp::Null{};
+ }
+
+ lsp::SignatureHelp help;
+ help.active_parameter = CalcParamIndex(call->Declaration()->source, pos);
+ Switch(call->Target(), //
+ [&](const sem::BuiltinFn* target) {
+ auto& data = wgsl::intrinsic::Dialect::kData;
+ auto& builtins = data.builtins;
+ auto& intrinsic_info = builtins[static_cast<size_t>(target->Fn())];
+ std::string name{wgsl::str(target->Fn())};
+
+ for (size_t i = 0; i < intrinsic_info.num_overloads; i++) {
+ auto& overload = data[intrinsic_info.overloads + i];
+
+ auto params = Params(data, overload);
+
+ auto type_mgr = core::type::Manager::Wrap(program.Types());
+ auto symbols = SymbolTable::Wrap(program.Symbols());
+
+ StyledText ss;
+ core::intrinsic::Context ctx{data, type_mgr, symbols};
+ PrintOverload(ss, ctx, overload, name);
+
+ lsp::SignatureInformation sig;
+ sig.parameters = params;
+ sig.label = ss.Plain();
+ help.signatures.push_back(sig);
+
+ if (&overload == &target->Overload()) {
+ help.active_signature = i;
+ }
+ }
+ });
+
+ return help;
+}
+
+} // namespace tint::wgsl::ls
diff --git a/src/tint/lang/wgsl/ls/signature_help_test.cc b/src/tint/lang/wgsl/ls/signature_help_test.cc
new file mode 100644
index 0000000..ceaf52a
--- /dev/null
+++ b/src/tint/lang/wgsl/ls/signature_help_test.cc
@@ -0,0 +1,281 @@
+// 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"
+
+namespace tint::wgsl::ls {
+namespace {
+
+namespace lsp = langsvr::lsp;
+
+std::vector<lsp::SignatureInformation> MaxSignatures() {
+ std::vector<lsp::SignatureInformation> out;
+
+ {
+ std::vector<lsp::ParameterInformation> parameters;
+ parameters.push_back(lsp::ParameterInformation{
+ /* label */ lsp::String("param-0"),
+ /* documentation */ {},
+ });
+ parameters.push_back(lsp::ParameterInformation{
+ /* label */ lsp::String("param-1"),
+ /* documentation */ {},
+ });
+
+ lsp::SignatureInformation sig{};
+ sig.label = R"('max(T, T) -> T' where:
+ 'T' is 'abstract-float', 'abstract-int', 'f32', 'i32', 'u32' or 'f16')";
+ sig.parameters = std::move(parameters);
+
+ out.push_back(std::move(sig));
+ }
+
+ {
+ std::vector<lsp::ParameterInformation> parameters;
+ parameters.push_back(lsp::ParameterInformation{
+ /* label */ lsp::String("param-0"),
+ /* documentation */ {},
+ });
+ parameters.push_back(lsp::ParameterInformation{
+ /* label */ lsp::String("param-1"),
+ /* documentation */ {},
+ });
+
+ lsp::SignatureInformation sig{};
+ sig.label = R"('max(vecN<T>, vecN<T>) -> vecN<T>' where:
+ 'T' is 'abstract-float', 'abstract-int', 'f32', 'i32', 'u32' or 'f16')";
+ sig.parameters = std::move(parameters);
+
+ out.push_back(std::move(sig));
+ }
+
+ return out;
+}
+
+struct Case {
+ std::string_view markup;
+ std::vector<lsp::SignatureInformation> signatures;
+ lsp::Uinteger active_signature = 0;
+ lsp::Uinteger active_parameter = 0;
+};
+
+std::ostream& operator<<(std::ostream& stream, const Case& c) {
+ return stream << "wgsl: '" << c.markup << "'";
+}
+
+using LsSignatureHelpTest = LsTestWithParam<Case>;
+TEST_P(LsSignatureHelpTest, SignatureHelp) {
+ auto parsed = ParseMarkers(GetParam().markup);
+ ASSERT_EQ(parsed.ranges.size(), 0u);
+ ASSERT_EQ(parsed.positions.size(), 1u);
+
+ lsp::TextDocumentSignatureHelpRequest 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 (GetParam().signatures.empty()) {
+ ASSERT_TRUE(res.Is<lsp::Null>());
+ } else {
+ ASSERT_TRUE(res.Is<lsp::SignatureHelp>());
+ auto& got = *res.Get<lsp::SignatureHelp>();
+ EXPECT_EQ(got.signatures, GetParam().signatures);
+ EXPECT_EQ(got.active_signature, GetParam().active_signature);
+ EXPECT_EQ(got.active_parameter, GetParam().active_parameter);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ LsSignatureHelpTest,
+ ::testing::ValuesIn(std::vector<Case>{
+ {
+ R"(
+const C = max(⧘1, 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C : i32 = max(⧘1, 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(⧘1, 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(1⧘, 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(1,⧘ 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+const C = max(1, 2⧘);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+const C = max(⧘ vec2(1), vec2(2));
+)",
+ MaxSignatures(),
+ /* active_signature */ 1,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(⧘ vec3(1), vec3(2));
+)",
+ MaxSignatures(),
+ /* active_signature */ 1,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(vec4(1) ⧘, vec4(2));
+)",
+ MaxSignatures(),
+ /* active_signature */ 1,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(vec2(1),⧘ vec2(2));
+)",
+ MaxSignatures(),
+ /* active_signature */ 1,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+const C = max(vec3(1), vec3(2) ⧘);
+)",
+ MaxSignatures(),
+ /* active_signature */ 1,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+const C = min(max(1, ⧘2), max(3, 4));
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+fn f() {
+ let x = max(1, ⧘2);
+}
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+const C = max( (1 + (2⧘) * 3), 4);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(1, (2 + (3⧘) * 4));
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+const C = max( array(1,2,3)[1⧘], 2);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 0,
+ }, // =========================================
+ {
+ R"(
+const C = max(1, array(1,2,3)[⧘2]);
+)",
+ MaxSignatures(),
+ /* active_signature */ 0,
+ /* active_parameter */ 1,
+ }, // =========================================
+ {
+ R"(
+const C = max(1, 2) ⧘;
+)",
+ {},
+ },
+ }));
+
+} // namespace
+} // namespace tint::wgsl::ls