Import Tint changes from Dawn

Changes:
  - e5a62a88b182f3186d0e80e74209f37a18f1d41c [tintd] Add more comments by Ben Clayton <bclayton@google.com>
  - de85a6154309afb1910a2adce669aba89798e319 [tintd] Handle multi-line node spans by Ben Clayton <bclayton@google.com>
  - 95b0765dbdde149c273c7057e52ad78a5ed2b559 Fix issue with external texture mapping in MSL. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: e5a62a88b182f3186d0e80e74209f37a18f1d41c
Change-Id: Ife436df9b7adc4870148651faa8c196e554e82f1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/178921
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc
index 6cbaec6..f1bd0c3 100644
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc
+++ b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc
@@ -174,6 +174,12 @@
 
             // Replace the original texture_external binding with a texture_2d<f32> binding.
             auto cloned_attributes = ctx.Clone(global->attributes);
+
+            // Allow the originating binding to have collisions.
+            if (new_binding_points->allow_collisions) {
+                cloned_attributes.Push(b.Disable(DisabledValidation::kBindingPointCollision));
+            }
+
             const Expression* cloned_initializer = ctx.Clone(global->initializer);
 
             auto* replacement = b.Var(
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
index 27f2acb..f89523c 100644
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
@@ -176,6 +176,83 @@
     EXPECT_EQ(expect, str(got));
 }
 
+TEST_F(MultiplanarExternalTextureTest, Collisions) {
+    auto* src = R"(
+@group(0) @binding(0) var myTexture: texture_external;
+
+@fragment
+fn fragmentMain() -> @location(0) vec4f {
+  let result = textureLoad(myTexture, vec2u(1, 1));
+  return vec4f(1);
+})";
+
+    auto* expect = R"(
+struct GammaTransferParams {
+  G : f32,
+  A : f32,
+  B : f32,
+  C : f32,
+  D : f32,
+  E : f32,
+  F : f32,
+  padding : u32,
+}
+
+struct ExternalTextureParams {
+  numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
+  yuvToRgbConversionMatrix : mat3x4<f32>,
+  gammaDecodeParams : GammaTransferParams,
+  gammaEncodeParams : GammaTransferParams,
+  gamutConversionMatrix : mat3x3<f32>,
+  coordTransformationMatrix : mat3x2<f32>,
+}
+
+@internal(disable_validation__binding_point_collision) @group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
+
+@internal(disable_validation__binding_point_collision) @group(0) @binding(0) var<uniform> ext_tex_params : ExternalTextureParams;
+
+@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var myTexture : texture_2d<f32>;
+
+fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
+  let cond = (abs(v) < vec3<f32>(params.D));
+  let t = (sign(v) * ((params.C * abs(v)) + params.F));
+  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
+  return select(f, t, cond);
+}
+
+fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<u32>, params : ExternalTextureParams) -> vec4<f32> {
+  let coord1 = (coord >> vec2<u32>(1));
+  var color : vec4<f32>;
+  if ((params.numPlanes == 1)) {
+    color = textureLoad(plane0, coord, 0).rgba;
+  } else {
+    color = vec4<f32>((vec4<f32>(textureLoad(plane0, coord, 0).r, textureLoad(plane1, coord1, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
+  }
+  if ((params.doYuvToRgbConversionOnly == 0)) {
+    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
+    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
+    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
+  }
+  return color;
+}
+
+@fragment
+fn fragmentMain() -> @location(0) vec4f {
+  let result = textureLoadExternal(myTexture, ext_tex_plane_1, vec2u(1, 1), ext_tex_params);
+  return vec4f(1);
+}
+)";
+
+    DataMap data;
+    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+        MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 0}}}},
+        /* allow collisions */ true);
+    auto got = Run<MultiplanarExternalTexture>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
 // Tests that the transform works with a textureDimensions call.
 TEST_F(MultiplanarExternalTextureTest, Dimensions_OutOfOrder) {
     auto* src = R"(
diff --git a/src/tint/lang/wgsl/ls/file.h b/src/tint/lang/wgsl/ls/file.h
index 7e6c367..7f31ad9 100644
--- a/src/tint/lang/wgsl/ls/file.h
+++ b/src/tint/lang/wgsl/ls/file.h
@@ -28,6 +28,8 @@
 #ifndef SRC_TINT_LANG_WGSL_LS_FILE_H_
 #define SRC_TINT_LANG_WGSL_LS_FILE_H_
 
+#include <cstddef>
+#include <cstdint>
 #include <limits>
 #include <memory>
 #include <string>
@@ -98,17 +100,16 @@
     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();
+        size_t best_len = std::numeric_limits<size_t>::max();
         const T* best_node = nullptr;
         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 (node->source.range.begin <= l && node->source.range.end >= l) {
                 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;
+                    size_t len = node->source.range.Length(source->content);
                     if (len < best_len) {
                         best_len = len;
                         best_node = cast;
diff --git a/src/tint/lang/wgsl/ls/hover.cc b/src/tint/lang/wgsl/ls/hover.cc
index 5ffbe47..fe9f123 100644
--- a/src/tint/lang/wgsl/ls/hover.cc
+++ b/src/tint/lang/wgsl/ls/hover.cc
@@ -64,6 +64,7 @@
 
 namespace {
 
+/// @returns a lsp::MarkedStringWithLanguage with the content @p wgsl, using the language `wgsl`
 lsp::MarkedStringWithLanguage WGSL(std::string wgsl) {
     lsp::MarkedStringWithLanguage str;
     str.language = "wgsl";
@@ -71,6 +72,7 @@
     return str;
 }
 
+/// PrintConstant() writes the constant value @p val as a WGSL value to the StringStream @p ss
 void PrintConstant(const core::constant::Value* val, StringStream& ss) {
     Switch(
         val,  //
@@ -98,6 +100,7 @@
         });
 }
 
+/// Variable() writes the hover information for the variable @p v to @p out
 void Variable(const sem::Variable* v, std::vector<lsp::MarkedString>& out) {
     StringStream ss;
     auto* kind = Switch(
@@ -122,6 +125,7 @@
     out.push_back(WGSL(ss.str()));
 }
 
+/// Function() writes the hover information for the function @p f to @p out
 void Function(const sem::Function* f, std::vector<lsp::MarkedString>& out) {
     StringStream ss;
     ss << "fn " << f->Declaration()->name->symbol.NameView();
@@ -142,6 +146,8 @@
     out.push_back(WGSL(ss.str()));
 }
 
+/// Call() writes the hover information for a call to the function with the name @p name with
+/// semantic info @p c, to @p out
 void Call(std::string_view name, const sem::Call* c, std::vector<lsp::MarkedString>& out) {
     StringStream ss;
     ss << name << "(";
@@ -165,6 +171,7 @@
     out.push_back(WGSL(ss.str()));
 }
 
+/// Constant() writes the hover information for the constant value @p val to @p out
 void Constant(const core::constant::Value* val, std::vector<lsp::MarkedString>& out) {
     StringStream ss;
     PrintConstant(val, ss);
diff --git a/src/tint/lang/wgsl/ls/sem_tokens.cc b/src/tint/lang/wgsl/ls/sem_tokens.cc
index 3accd26..ddbd5a5 100644
--- a/src/tint/lang/wgsl/ls/sem_tokens.cc
+++ b/src/tint/lang/wgsl/ls/sem_tokens.cc
@@ -50,12 +50,17 @@
 
 namespace {
 
+/// Token describes a single semantic token, as returned by TextDocumentSemanticTokensFullRequest.
 struct Token {
+    /// The start position of the token
     lsp::Position position;
+    /// The kind of token. Maps to enumerators in SemToken.
     size_t kind = 0;
+    /// The length of the token in UTF-8 codepoints.
     size_t length = 0;
 };
 
+/// @returns a Token built from the source range @p range with the kind @p kind
 Token TokenFromRange(const tint::Source::Range& range, SemToken::Kind kind) {
     Token tok;
     tok.position = Conv(range.begin);
@@ -64,6 +69,8 @@
     return tok;
 }
 
+/// @returns the token kind for the expression @p expr, or nullptr if the expression does not have a
+/// token kind.
 std::optional<SemToken::Kind> TokenKindFor(const sem::Expression* expr) {
     return Switch<std::optional<SemToken::Kind>>(
         Unwrap(expr),  //
@@ -75,6 +82,7 @@
         [](tint::Default) { return std::nullopt; });
 }
 
+/// @returns all the semantic tokens in the file @p file, in sequential order.
 std::vector<Token> Tokens(File& file) {
     std::vector<Token> tokens;
     auto& sem = file.program.Sem();
@@ -102,6 +110,9 @@
                 tokens.push_back(TokenFromRange(a->member->source.range, SemToken::kMember));
             });
     }
+    std::sort(tokens.begin(), tokens.end(),
+              [](const Token& a, const Token& b) { return a.position < b.position; });
+
     return tokens;
 }
 
@@ -117,9 +128,6 @@
         Token last;
 
         auto tokens = Tokens(**file);
-        std::sort(tokens.begin(), tokens.end(),
-                  [](const Token& a, const Token& b) { return a.position < b.position; });
-
         for (auto tok : tokens) {
             if (last.position.line != tok.position.line) {
                 last.position.character = 0;
diff --git a/src/tint/lang/wgsl/ls/signature_help.cc b/src/tint/lang/wgsl/ls/signature_help.cc
index 401e621..7644c74 100644
--- a/src/tint/lang/wgsl/ls/signature_help.cc
+++ b/src/tint/lang/wgsl/ls/signature_help.cc
@@ -27,6 +27,7 @@
 
 #include "src/tint/lang/wgsl/ls/server.h"
 
+#include "langsvr/lsp/comparators.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"
@@ -40,8 +41,10 @@
 
 namespace {
 
-std::vector<lsp::ParameterInformation> Params(const core::intrinsic::TableData& data,
-                                              const core::intrinsic::OverloadInfo& overload) {
+/// @returns the parameter information for all the parameters of the intrinsic overload @p overload.
+std::vector<lsp::ParameterInformation> Params(const core::intrinsic::OverloadInfo& overload) {
+    auto& data = wgsl::intrinsic::Dialect::kData;
+
     std::vector<lsp::ParameterInformation> params;
     for (size_t i = 0; i < overload.num_parameters; i++) {
         lsp::ParameterInformation param_out;
@@ -56,18 +59,22 @@
     return params;
 }
 
-size_t CalcParamIndex(const Source& call_source, const Source::Location& carat) {
+/// @returns the zero-based index of the parameter at with the cursor at @p position, for a call
+/// with the source @p call_source.
+size_t CalcParamIndex(const Source& call_source, const Source::Location& position) {
     size_t index = 0;
     int depth = 0;
 
-    auto start = call_source.range.begin;
-    auto end = std::min(call_source.range.end, carat);
+    auto range = Conv(call_source.range);
+    auto start = range.start;
+    auto end = std::min(range.end, Conv(position));
     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 (auto line_idx = start.line; line_idx <= end.line; line_idx++) {
+        auto& line = lines[line_idx];
+        auto start_character = (line_idx == start.line) ? start.character : 0;
+        auto end_character = (line_idx == end.line) ? end.character : line.size();
+        auto text = line.substr(start_character, end_character - start_character);
         for (char c : text) {
             switch (c) {
                 case '(':
@@ -88,6 +95,8 @@
     return index;
 }
 
+/// PrintOverload() emits a description of the intrinsic overload @p overload of the function with
+/// name @p intrinsic_name to @p ss.
 void PrintOverload(StyledText& ss,
                    core::intrinsic::Context& context,
                    const core::intrinsic::OverloadInfo& overload,
@@ -187,7 +196,7 @@
                for (size_t i = 0; i < intrinsic_info.num_overloads; i++) {
                    auto& overload = data[intrinsic_info.overloads + i];
 
-                   auto params = Params(data, overload);
+                   auto params = Params(overload);
 
                    auto type_mgr = core::type::Manager::Wrap(program.Types());
                    auto symbols = SymbolTable::Wrap(program.Symbols());
diff --git a/src/tint/lang/wgsl/ls/signature_help_test.cc b/src/tint/lang/wgsl/ls/signature_help_test.cc
index ceaf52a..31fcaba 100644
--- a/src/tint/lang/wgsl/ls/signature_help_test.cc
+++ b/src/tint/lang/wgsl/ls/signature_help_test.cc
@@ -271,6 +271,28 @@
                              },  // =========================================
                              {
                                  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) ⧘;
 )",
                                  {},
diff --git a/src/tint/utils/diagnostic/source.cc b/src/tint/utils/diagnostic/source.cc
index 3dc439b..b183f8a 100644
--- a/src/tint/utils/diagnostic/source.cc
+++ b/src/tint/utils/diagnostic/source.cc
@@ -31,6 +31,7 @@
 #include <string_view>
 #include <utility>
 
+#include "src/tint/utils/ice/ice.h"
 #include "src/tint/utils/text/string_stream.h"
 #include "src/tint/utils/text/unicode.h"
 
@@ -190,4 +191,25 @@
     return out.str();
 }
 
+size_t Source::Range::Length(const FileContent& content) const {
+    TINT_ASSERT_OR_RETURN_VALUE(begin <= end, 0);
+    TINT_ASSERT_OR_RETURN_VALUE(begin.column > 0, 0);
+    TINT_ASSERT_OR_RETURN_VALUE(begin.line > 0, 0);
+    TINT_ASSERT_OR_RETURN_VALUE(end.line <= 1 + content.lines.size(), 0);
+    TINT_ASSERT_OR_RETURN_VALUE(end.column <= 1 + content.lines[end.line - 1].size(), 0);
+
+    if (end.line == begin.line) {
+        return end.column - begin.column;
+    }
+
+    size_t len = (content.lines[begin.line - 1].size() + 1 - begin.column) +  // first line
+                 (end.column - 1) +                                           // last line
+                 end.line - begin.line;                                       // newlines
+
+    for (size_t line = begin.line + 1; line < end.line; line++) {
+        len += content.lines[line - 1].size();  // whole-lines
+    }
+    return len;
+}
+
 }  // namespace tint
diff --git a/src/tint/utils/diagnostic/source.h b/src/tint/utils/diagnostic/source.h
index afef58c..e644122 100644
--- a/src/tint/utils/diagnostic/source.h
+++ b/src/tint/utils/diagnostic/source.h
@@ -42,7 +42,7 @@
 /// Source describes a range of characters within a source file.
 class Source {
   public:
-    /// FileContent describes the content of a source file encoded using utf-8.
+    /// FileContent describes the content of a source file encoded using UTF-8.
     class FileContent {
       public:
         /// Constructs the FileContent with the given file content.
@@ -151,7 +151,7 @@
         inline constexpr Range(const Location& b, const Location& e) : begin(b), end(e) {}
 
         /// Return a column-shifted Range
-        /// @param n the number of characters to shift by
+        /// @param n the number of UTF-8 codepoint to shift by
         /// @returns a Range with a #begin and #end column shifted by `n`
         inline Range operator+(uint32_t n) const {
             return Range{{begin.line, begin.column + n}, {end.line, end.column + n}};
@@ -169,9 +169,16 @@
         /// @returns true if `this` == `rhs`
         inline bool operator!=(const Range& rhs) const { return !(*this == rhs); }
 
-        /// The location of the first character in the range.
+        /// @param content the file content that this range belongs to
+        /// @returns the length of the range in UTF-8 codepoints, treating all line-break sequences
+        /// as a single code-point.
+        /// @see https://www.w3.org/TR/WGSL/#blankspace-and-line-breaks for the definition of a line
+        /// break.
+        size_t Length(const FileContent& content) const;
+
+        /// The location of the first UTF-8 codepoint in the range.
         Location begin;
-        /// The location of one-past the last character in the range.
+        /// The location of one-past the last UTF-8 codepoint in the range.
         Location end;
     };
 
diff --git a/src/tint/utils/diagnostic/source_test.cc b/src/tint/utils/diagnostic/source_test.cc
index cb50053..42b4e01 100644
--- a/src/tint/utils/diagnostic/source_test.cc
+++ b/src/tint/utils/diagnostic/source_test.cc
@@ -28,6 +28,7 @@
 #include "src/tint/utils/diagnostic/source.h"
 
 #include <memory>
+#include <string_view>
 #include <utility>
 
 #include "gtest/gtest.h"
@@ -35,7 +36,7 @@
 namespace tint {
 namespace {
 
-static constexpr const char* kSource = R"(line one
+static constexpr std::string_view kSource = R"(line one
 line two
 line three)";
 
@@ -81,7 +82,7 @@
 #define kLS "\xE2\x80\xA8"
 #define kPS "\xE2\x80\xA9"
 
-using LineBreakTest = testing::TestWithParam<const char*>;
+using LineBreakTest = testing::TestWithParam<std::string_view>;
 TEST_P(LineBreakTest, Single) {
     std::string src = "line one";
     src += GetParam();
@@ -108,5 +109,50 @@
                          LineBreakTest,
                          testing::Values(kVTab, kFF, kNL, kLS, kPS, kLF, kCR, kCR kLF));
 
+using RangeLengthTest = testing::TestWithParam<std::pair<Source::Range, size_t>>;
+TEST_P(RangeLengthTest, Test) {
+    Source::FileContent fc("X" kLF       // 1
+                           "XX" kCR kLF  // 2
+                           "X" kCR       // 3
+                               kLS       // 4
+                           "XX"          // 5
+    );
+    auto& range = GetParam().first;
+    auto expected_length = GetParam().second;
+    EXPECT_EQ(range.Length(fc), expected_length);
+}
+
+INSTANTIATE_TEST_SUITE_P(SingleLine,
+                         RangeLengthTest,
+                         testing::Values(  //
+
+                             std::make_pair(Source::Range{{1, 1}, {1, 1}}, 0),
+                             std::make_pair(Source::Range{{2, 1}, {2, 1}}, 0),
+                             std::make_pair(Source::Range{{3, 1}, {3, 1}}, 0),
+                             std::make_pair(Source::Range{{4, 1}, {4, 1}}, 0),
+                             std::make_pair(Source::Range{{5, 1}, {5, 1}}, 0),
+
+                             std::make_pair(Source::Range{{1, 1}, {1, 2}}, 1),
+                             std::make_pair(Source::Range{{2, 1}, {2, 3}}, 2),
+                             std::make_pair(Source::Range{{3, 1}, {3, 2}}, 1),
+                             std::make_pair(Source::Range{{5, 1}, {5, 3}}, 2),
+
+                             std::make_pair(Source::Range{{1, 2}, {1, 2}}, 0),
+                             std::make_pair(Source::Range{{2, 2}, {2, 3}}, 1),
+                             std::make_pair(Source::Range{{3, 2}, {3, 2}}, 0),
+                             std::make_pair(Source::Range{{5, 2}, {5, 3}}, 1)));
+
+INSTANTIATE_TEST_SUITE_P(MultiLine,
+                         RangeLengthTest,
+                         testing::Values(  //
+
+                             std::make_pair(Source::Range{{1, 1}, {2, 1}}, 2),
+                             std::make_pair(Source::Range{{2, 1}, {3, 1}}, 3),
+                             std::make_pair(Source::Range{{3, 1}, {4, 1}}, 2),
+                             std::make_pair(Source::Range{{4, 1}, {5, 1}}, 1),
+
+                             std::make_pair(Source::Range{{1, 1}, {5, 3}}, 10),
+                             std::make_pair(Source::Range{{2, 2}, {5, 2}}, 6)));
+
 }  // namespace
 }  // namespace tint