Import Tint changes from Dawn

Changes:
  - 61d76ebb5c636a501fd4ec07db56164b7380d589 Update some forward declarations. by dan sinclair <dsinclair@chromium.org>
  - 8b09bc97c0802c4dfcec4a8a7b881123c36e002f tint: Lex abstract integers by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 61d76ebb5c636a501fd4ec07db56164b7380d589
Change-Id: I64955bf7ad1471d0f3d155bd0b20ea857cde4d38
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/92040
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/ast/node.h b/src/tint/ast/node.h
index 41eeefe..19d7682 100644
--- a/src/tint/ast/node.h
+++ b/src/tint/ast/node.h
@@ -19,15 +19,6 @@
 
 #include "src/tint/clone_context.h"
 
-// Forward declarations
-namespace tint {
-class CloneContext;
-}  // namespace tint
-namespace tint::sem {
-class Type;
-class Info;
-}  // namespace tint::sem
-
 namespace tint::ast {
 
 /// AST base class node
diff --git a/src/tint/inspector/resource_binding.h b/src/tint/inspector/resource_binding.h
index 9a54435..6adab5a 100644
--- a/src/tint/inspector/resource_binding.h
+++ b/src/tint/inspector/resource_binding.h
@@ -20,6 +20,11 @@
 #include "src/tint/ast/storage_texture.h"
 #include "src/tint/ast/texture.h"
 
+// Forward declarations
+namespace tint::sem {
+class Type;
+}  // namespace tint::sem
+
 namespace tint::inspector {
 
 /// Container for information about how a resource is bound
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 1b69152..58e3c85 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -678,10 +678,19 @@
 }
 
 Token Lexer::build_token_from_int_if_possible(Source source, size_t start, int32_t base) {
-    int64_t res = strtoll(&at(start), nullptr, base);
+    const char* start_ptr = &at(start);
+    char* end_ptr = nullptr;
+
+    errno = 0;
+    int64_t res = strtoll(start_ptr, &end_ptr, base);
+    const bool overflow = errno == ERANGE;
+
+    if (end_ptr) {
+        advance(end_ptr - start_ptr);
+    }
 
     if (matches(pos(), "u")) {
-        if (CheckedConvert<u32>(AInt(res))) {
+        if (!overflow && CheckedConvert<u32>(AInt(res))) {
             advance(1);
             end_source(source);
             return {Token::Type::kIntLiteral_U, source, res};
@@ -690,7 +699,7 @@
     }
 
     if (matches(pos(), "i")) {
-        if (CheckedConvert<i32>(AInt(res))) {
+        if (!overflow && CheckedConvert<i32>(AInt(res))) {
             advance(1);
             end_source(source);
             return {Token::Type::kIntLiteral_I, source, res};
@@ -698,94 +707,59 @@
         return {Token::Type::kError, source, "value cannot be represented as 'i32'"};
     }
 
-    // TODO(crbug.com/tint/1504): Properly support abstract int:
-    // Change `AbstractIntType` to `int64_t`, update errors to say 'abstract int'.
-    using AbstractIntType = i32;
-    if (CheckedConvert<AbstractIntType>(AInt(res))) {
-        end_source(source);
-        return {Token::Type::kIntLiteral, source, res};
+    end_source(source);
+    if (overflow) {
+        return {Token::Type::kError, source, "value cannot be represented as 'abstract-int'"};
     }
-    return {Token::Type::kError, source, "value cannot be represented as 'i32'"};
+    return {Token::Type::kIntLiteral, source, res};
 }
 
 Token Lexer::try_hex_integer() {
-    constexpr size_t kMaxDigits = 8;  // Valid for both 32-bit integer types
     auto start = pos();
-    auto end = pos();
+    auto curr = start;
 
     auto source = begin_source();
 
-    if (matches(end, "-")) {
-        end++;
+    if (matches(curr, "-")) {
+        curr++;
     }
 
-    if (matches(end, "0x") || matches(end, "0X")) {
-        end += 2;
+    if (matches(curr, "0x") || matches(curr, "0X")) {
+        curr += 2;
     } else {
         return {};
     }
 
-    auto first = end;
-    while (!is_eol() && is_hex(at(end))) {
-        end++;
-
-        auto digits = end - first;
-        if (digits > kMaxDigits) {
-            return {Token::Type::kError, source,
-                    "integer literal (" + std::string{substr(start, end - 1 - start)} +
-                        "...) has too many digits"};
-        }
-    }
-    if (first == end) {
+    if (!is_hex(at(curr))) {
         return {Token::Type::kError, source,
                 "integer or float hex literal has no significant digits"};
     }
 
-    advance(end - start);
-
     return build_token_from_int_if_possible(source, start, 16);
 }
 
 Token Lexer::try_integer() {
-    constexpr size_t kMaxDigits = 10;  // Valid for both 32-bit integer types
     auto start = pos();
-    auto end = start;
+    auto curr = start;
 
     auto source = begin_source();
 
-    if (matches(end, "-")) {
-        end++;
+    if (matches(curr, "-")) {
+        curr++;
     }
 
-    if (end >= length() || !is_digit(at(end))) {
+    if (curr >= length() || !is_digit(at(curr))) {
         return {};
     }
 
-    auto first = end;
     // If the first digit is a zero this must only be zero as leading zeros
     // are not allowed.
-    auto next = first + 1;
-    if (next < length()) {
-        if (at(first) == '0' && is_digit(at(next))) {
-            return {Token::Type::kError, source,
-                    "integer literal (" + std::string{substr(start, end - 1 - start)} +
-                        "...) has leading 0s"};
+    if (auto next = curr + 1; next < length()) {
+        if (at(curr) == '0' && is_digit(at(next))) {
+            return {Token::Type::kError, source, "integer literal cannot have leading 0s"};
         }
     }
 
-    while (end < length() && is_digit(at(end))) {
-        auto digits = end - first;
-        if (digits > kMaxDigits) {
-            return {Token::Type::kError, source,
-                    "integer literal (" + std::string{substr(start, end - 1 - start)} +
-                        "...) has too many digits"};
-        }
-
-        end++;
-    }
-
-    advance(end - start);
-
     return build_token_from_int_if_possible(source, start, 10);
 }
 
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index 801b62c..16ae46d 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -15,6 +15,8 @@
 #include "src/tint/reader/wgsl/lexer.h"
 
 #include <limits>
+#include <tuple>
+#include <vector>
 
 #include "gtest/gtest.h"
 
@@ -586,270 +588,218 @@
     EXPECT_FALSE(t.IsIdentifier());
 }
 
-struct HexSignedIntData {
+////////////////////////////////////////////////////////////////////////////////
+// ParseIntegerTest
+////////////////////////////////////////////////////////////////////////////////
+struct ParseIntegerCase {
     const char* input;
-    int32_t result;
+    int64_t result;
 };
-inline std::ostream& operator<<(std::ostream& out, HexSignedIntData data) {
+
+inline std::ostream& operator<<(std::ostream& out, ParseIntegerCase data) {
     out << std::string(data.input);
     return out;
 }
 
-using IntegerTest_HexSigned = testing::TestWithParam<HexSignedIntData>;
-TEST_P(IntegerTest_HexSigned, NoSuffix) {
-    auto params = GetParam();
+using ParseIntegerTest = testing::TestWithParam<std::tuple<char, ParseIntegerCase>>;
+TEST_P(ParseIntegerTest, Parse) {
+    auto suffix = std::get<0>(GetParam());
+    auto params = std::get<1>(GetParam());
     Source::File file("", params.input);
-    Lexer l(&file);
 
-    auto t = l.next();
-    EXPECT_TRUE(t.Is(Token::Type::kIntLiteral));
-    EXPECT_EQ(t.source().range.begin.line, 1u);
-    EXPECT_EQ(t.source().range.begin.column, 1u);
-    EXPECT_EQ(t.source().range.end.line, 1u);
-    EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
-    EXPECT_EQ(t.to_i64(), params.result);
-}
-TEST_P(IntegerTest_HexSigned, ISuffix) {
-    auto params = GetParam();
-    Source::File file("", std::string(params.input) + "i");
-    Lexer l(&file);
-
-    auto t = l.next();
-    EXPECT_TRUE(t.Is(Token::Type::kIntLiteral_I));
-    EXPECT_EQ(t.source().range.begin.line, 1u);
-    EXPECT_EQ(t.source().range.begin.column, 1u);
-    EXPECT_EQ(t.source().range.end.line, 1u);
-    EXPECT_EQ(t.source().range.end.column, 2u + strlen(params.input));
-    EXPECT_EQ(t.to_i64(), params.result);
-}
-INSTANTIATE_TEST_SUITE_P(
-    LexerTest,
-    IntegerTest_HexSigned,
-    testing::Values(HexSignedIntData{"0x0", 0},
-                    HexSignedIntData{"0X0", 0},
-                    HexSignedIntData{"0x42", 66},
-                    HexSignedIntData{"0X42", 66},
-                    HexSignedIntData{"-0x42", -66},
-                    HexSignedIntData{"-0X42", -66},
-                    HexSignedIntData{"0xeF1Abc9", 250719177},
-                    HexSignedIntData{"0XeF1Abc9", 250719177},
-                    HexSignedIntData{"-0x80000000", std::numeric_limits<int32_t>::min()},
-                    HexSignedIntData{"-0X80000000", std::numeric_limits<int32_t>::min()},
-                    HexSignedIntData{"0x7FFFFFFF", std::numeric_limits<int32_t>::max()},
-                    HexSignedIntData{"0X7FFFFFFF", std::numeric_limits<int32_t>::max()}));
-
-TEST_F(LexerTest, HexPrefixOnly_IsError) {
-    // Could be the start of a hex integer or hex float, but is neither.
-    Source::File file("", "0x");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "integer or float hex literal has no significant digits");
-}
-
-TEST_F(LexerTest, HexPrefixUpperCaseOnly_IsError) {
-    // Could be the start of a hex integer or hex float, but is neither.
-    Source::File file("", "0X");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "integer or float hex literal has no significant digits");
-}
-
-TEST_F(LexerTest, NegativeHexPrefixOnly_IsError) {
-    // Could be the start of a hex integer or hex float, but is neither.
-    Source::File file("", "-0x");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "integer or float hex literal has no significant digits");
-}
-
-TEST_F(LexerTest, NegativeHexPrefixUpperCaseOnly_IsError) {
-    // Could be the start of a hex integer or hex float, but is neither.
-    Source::File file("", "-0X");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "integer or float hex literal has no significant digits");
-}
-
-TEST_F(LexerTest, IntegerTest_HexSignedTooLarge) {
-    Source::File file("", "0x80000000");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "value cannot be represented as 'i32'");
-}
-
-TEST_F(LexerTest, IntegerTest_HexSignedTooSmall) {
-    Source::File file("", "-0x8000000F");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "value cannot be represented as 'i32'");
-}
-
-TEST_F(LexerTest, IntegerTest_HexSignedTooManyDigits) {
-    {
-        Source::File file("", "-0x100000000000000000000000");
-        Lexer l(&file);
-
-        auto t = l.next();
-        ASSERT_TRUE(t.Is(Token::Type::kError));
-        EXPECT_EQ(t.to_str(), "integer literal (-0x10000000...) has too many digits");
+    auto t = Lexer(&file).next();
+    switch (suffix) {
+        case 'i':
+            EXPECT_TRUE(t.Is(Token::Type::kIntLiteral_I));
+            break;
+        case 'u':
+            EXPECT_TRUE(t.Is(Token::Type::kIntLiteral_U));
+            break;
+        case 0:
+            EXPECT_TRUE(t.Is(Token::Type::kIntLiteral));
+            break;
     }
-    {
-        Source::File file("", "0x100000000000000");
-        Lexer l(&file);
-
-        auto t = l.next();
-        ASSERT_TRUE(t.Is(Token::Type::kError));
-        EXPECT_EQ(t.to_str(), "integer literal (0x10000000...) has too many digits");
-    }
-}
-
-struct HexUnsignedIntData {
-    const char* input;
-    uint32_t result;
-};
-inline std::ostream& operator<<(std::ostream& out, HexUnsignedIntData data) {
-    out << std::string(data.input);
-    return out;
-}
-using IntegerTest_HexUnsigned = testing::TestWithParam<HexUnsignedIntData>;
-// TODO(crbug.com/tint/1504): Split into NoSuffix and USuffix
-TEST_P(IntegerTest_HexUnsigned, Matches) {
-    auto params = GetParam();
-    Source::File file("", params.input);
-    Lexer l(&file);
-
-    auto t = l.next();
-    EXPECT_TRUE(t.Is(Token::Type::kIntLiteral_U));
     EXPECT_EQ(t.source().range.begin.line, 1u);
     EXPECT_EQ(t.source().range.begin.column, 1u);
     EXPECT_EQ(t.source().range.end.line, 1u);
     EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
+    ASSERT_FALSE(t.IsError()) << t.to_str();
     EXPECT_EQ(t.to_i64(), params.result);
-
-    t = l.next();
-    EXPECT_TRUE(t.IsEof());
-}
-INSTANTIATE_TEST_SUITE_P(
-    LexerTest,
-    IntegerTest_HexUnsigned,
-    testing::Values(HexUnsignedIntData{"0x0u", 0},
-                    HexUnsignedIntData{"0x42u", 66},
-                    HexUnsignedIntData{"0xeF1Abc9u", 250719177},
-                    HexUnsignedIntData{"0x0u", std::numeric_limits<uint32_t>::min()},
-                    HexUnsignedIntData{"0xFFFFFFFFu", std::numeric_limits<uint32_t>::max()}));
-
-TEST_F(LexerTest, IntegerTest_HexUnsignedTooManyDigits) {
-    Source::File file("", "0x1000000000000000000000u");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "integer literal (0x10000000...) has too many digits");
 }
 
-struct UnsignedIntData {
-    const char* input;
-    uint32_t result;
-};
-inline std::ostream& operator<<(std::ostream& out, UnsignedIntData data) {
-    out << std::string(data.input);
-    return out;
+INSTANTIATE_TEST_SUITE_P(Dec_AInt,
+                         ParseIntegerTest,
+                         testing::Combine(testing::Values('\0'),  // No suffix
+                                          testing::ValuesIn(std::vector<ParseIntegerCase>{
+                                              {"0", 0},
+                                              {"-2", -2},
+                                              {"2", 2},
+                                              {"123", 123},
+                                              {"2147483647", 2147483647},
+                                              {"-2147483648", -2147483648LL},
+                                              {"-9223372036854775808", -9223372036854775807LL - 1},
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(Dec_u32,
+                         ParseIntegerTest,
+                         testing::Combine(testing::Values('u'),  // Suffix
+                                          testing::ValuesIn(std::vector<ParseIntegerCase>{
+                                              {"0u", 0},
+                                              {"123u", 123},
+                                              {"4294967295u", 4294967295ll},
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(Dec_i32,
+                         ParseIntegerTest,
+                         testing::Combine(testing::Values('i'),  // Suffix
+                                          testing::ValuesIn(std::vector<ParseIntegerCase>{
+                                              {"0i", 0u},
+                                              {"-0i", 0u},
+                                              {"123i", 123},
+                                              {"-123i", -123},
+                                              {"2147483647i", 2147483647},
+                                              {"-2147483647i", -2147483647ll},
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(Hex_AInt,
+                         ParseIntegerTest,
+                         testing::Combine(testing::Values('\0'),  // No suffix
+                                          testing::ValuesIn(std::vector<ParseIntegerCase>{
+                                              {"0x0", 0},
+                                              {"0X0", 0},
+                                              {"0x42", 66},
+                                              {"0X42", 66},
+                                              {"-0x42", -66},
+                                              {"-0X42", -66},
+                                              {"0xeF1Abc9", 0xeF1Abc9},
+                                              {"0XeF1Abc9", 0xeF1Abc9},
+                                              {"-0xeF1Abc9", -0xeF1Abc9},
+                                              {"-0XeF1Abc9", -0xeF1Abc9},
+                                              {"0x80000000", 0x80000000},
+                                              {"0X80000000", 0X80000000},
+                                              {"-0x80000000", -0x80000000ll},
+                                              {"-0X80000000", -0X80000000ll},
+                                              {"0x7FFFFFFF", 0x7fffffff},
+                                              {"0X7FFFFFFF", 0x7fffffff},
+                                              {"0x7fffffff", 0x7fffffff},
+                                              {"0x7fffffff", 0x7fffffff},
+                                              {"0x7FfFfFfF", 0x7fffffff},
+                                              {"0X7FfFfFfF", 0x7fffffff},
+                                              {"0x7fffffffffffffff", 0x7fffffffffffffffll},
+                                              {"-0x7fffffffffffffff", -0x7fffffffffffffffll},
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(Hex_u32,
+                         ParseIntegerTest,
+                         testing::Combine(testing::Values('u'),  // Suffix
+                                          testing::ValuesIn(std::vector<ParseIntegerCase>{
+                                              {"0x0u", 0},
+                                              {"0x42u", 66},
+                                              {"0xeF1Abc9u", 250719177},
+                                              {"0xFFFFFFFFu", 0xffffffff},
+                                              {"0XFFFFFFFFu", 0xffffffff},
+                                              {"0xffffffffu", 0xffffffff},
+                                              {"0Xffffffffu", 0xffffffff},
+                                              {"0xfFfFfFfFu", 0xffffffff},
+                                              {"0XfFfFfFfFu", 0xffffffff},
+                                          })));
+
+INSTANTIATE_TEST_SUITE_P(Hex_i32,
+                         ParseIntegerTest,
+                         testing::Combine(testing::Values('i'),  // Suffix
+                                          testing::ValuesIn(std::vector<ParseIntegerCase>{
+                                              {"0x0i", 0},
+                                              {"0x42i", 66},
+                                              {"-0x0i", 0},
+                                              {"-0x42i", -66},
+                                              {"0xeF1Abc9i", 250719177},
+                                              {"-0xeF1Abc9i", -250719177},
+                                              {"0x7FFFFFFFi", 0x7fffffff},
+                                              {"-0x7FFFFFFFi", -0x7fffffff},
+                                              {"0X7FFFFFFFi", 0x7fffffff},
+                                              {"-0X7FFFFFFFi", -0x7fffffff},
+                                              {"0x7fffffffi", 0x7fffffff},
+                                              {"-0x7fffffffi", -0x7fffffff},
+                                              {"0X7fffffffi", 0x7fffffff},
+                                              {"-0X7fffffffi", -0x7fffffff},
+                                              {"0x7FfFfFfFi", 0x7fffffff},
+                                              {"-0x7FfFfFfFi", -0x7fffffff},
+                                              {"0X7FfFfFfFi", 0x7fffffff},
+                                              {"-0X7FfFfFfFi", -0x7fffffff},
+                                          })));
+////////////////////////////////////////////////////////////////////////////////
+// ParseIntegerTest_CannotBeRepresented
+////////////////////////////////////////////////////////////////////////////////
+using ParseIntegerTest_CannotBeRepresented =
+    testing::TestWithParam<std::tuple<const char*, const char*>>;
+TEST_P(ParseIntegerTest_CannotBeRepresented, Parse) {
+    auto type = std::get<0>(GetParam());
+    auto source = std::get<1>(GetParam());
+    Source::File file("", source);
+    auto t = Lexer(&file).next();
+    EXPECT_TRUE(t.Is(Token::Type::kError));
+    auto expect = "value cannot be represented as '" + std::string(type) + "'";
+    EXPECT_EQ(t.to_str(), expect);
 }
-using IntegerTest_Unsigned = testing::TestWithParam<UnsignedIntData>;
-TEST_P(IntegerTest_Unsigned, Matches) {
-    auto params = GetParam();
-    Source::File file("", params.input);
-    Lexer l(&file);
+INSTANTIATE_TEST_SUITE_P(AbstractInt,
+                         ParseIntegerTest_CannotBeRepresented,
+                         testing::Combine(testing::Values("abstract-int"),
+                                          testing::Values("9223372036854775808",
+                                                          "0xFFFFFFFFFFFFFFFF",
+                                                          "0xffffffffffffffff",
+                                                          "0x8000000000000000")));
 
-    auto t = l.next();
-    EXPECT_TRUE(t.Is(Token::Type::kIntLiteral_U));
-    EXPECT_EQ(t.to_i64(), params.result);
-    EXPECT_EQ(t.source().range.begin.line, 1u);
-    EXPECT_EQ(t.source().range.begin.column, 1u);
-    EXPECT_EQ(t.source().range.end.line, 1u);
-    EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
-}
-INSTANTIATE_TEST_SUITE_P(LexerTest,
-                         IntegerTest_Unsigned,
-                         testing::Values(UnsignedIntData{"0u", 0u},
-                                         UnsignedIntData{"123u", 123u},
-                                         UnsignedIntData{"4294967295u", 4294967295u}));
+INSTANTIATE_TEST_SUITE_P(i32,
+                         ParseIntegerTest_CannotBeRepresented,
+                         testing::Combine(testing::Values("i32"),  // type
+                                          testing::Values("2147483648i")));
 
-TEST_F(LexerTest, IntegerTest_UnsignedTooManyDigits) {
-    Source::File file("", "10000000000000000000000u");
-    Lexer l(&file);
+INSTANTIATE_TEST_SUITE_P(u32,
+                         ParseIntegerTest_CannotBeRepresented,
+                         testing::Combine(testing::Values("u32"),         // type
+                                          testing::Values("4294967296u",  //
+                                                          "-1u")));
 
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "integer literal (1000000000...) has too many digits");
-}
-
-struct SignedIntData {
-    const char* input;
-    int32_t result;
-};
-inline std::ostream& operator<<(std::ostream& out, SignedIntData data) {
-    out << std::string(data.input);
-    return out;
-}
-using IntegerTest_Signed = testing::TestWithParam<SignedIntData>;
-TEST_P(IntegerTest_Signed, Matches) {
-    auto params = GetParam();
-    Source::File file("", params.input);
-    Lexer l(&file);
-
-    auto t = l.next();
-    EXPECT_TRUE(t.Is(Token::Type::kIntLiteral));
-    EXPECT_EQ(t.to_i64(), params.result);
-    EXPECT_EQ(t.source().range.begin.line, 1u);
-    EXPECT_EQ(t.source().range.begin.column, 1u);
-    EXPECT_EQ(t.source().range.end.line, 1u);
-    EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
-}
-INSTANTIATE_TEST_SUITE_P(LexerTest,
-                         IntegerTest_Signed,
-                         testing::Values(SignedIntData{"0", 0},
-                                         SignedIntData{"-2", -2},
-                                         SignedIntData{"2", 2},
-                                         SignedIntData{"123", 123},
-                                         SignedIntData{"2147483647", 2147483647},
-                                         SignedIntData{"-2147483648", -2147483648LL}));
-
-TEST_F(LexerTest, IntegerTest_SignedTooManyDigits) {
-    Source::File file("", "-10000000000000000");
-    Lexer l(&file);
-
-    auto t = l.next();
-    ASSERT_TRUE(t.Is(Token::Type::kError));
-    EXPECT_EQ(t.to_str(), "integer literal (-1000000000...) has too many digits");
-}
-
-using IntegerTest_Invalid = testing::TestWithParam<const char*>;
-TEST_P(IntegerTest_Invalid, Parses) {
+////////////////////////////////////////////////////////////////////////////////
+// ParseIntegerTest_LeadingZeros
+////////////////////////////////////////////////////////////////////////////////
+using ParseIntegerTest_LeadingZeros = testing::TestWithParam<const char*>;
+TEST_P(ParseIntegerTest_LeadingZeros, Parse) {
     Source::File file("", GetParam());
-    Lexer l(&file);
-
-    auto t = l.next();
-    EXPECT_FALSE(t.Is(Token::Type::kIntLiteral));
-    EXPECT_FALSE(t.Is(Token::Type::kIntLiteral_U));
-    EXPECT_FALSE(t.Is(Token::Type::kIntLiteral_I));
+    auto t = Lexer(&file).next();
+    EXPECT_TRUE(t.Is(Token::Type::kError));
+    EXPECT_EQ(t.to_str(), "integer literal cannot have leading 0s");
 }
-INSTANTIATE_TEST_SUITE_P(
-    LexerTest,
-    IntegerTest_Invalid,
-    testing::Values("2147483648", "4294967296u", "01234", "0000", "-00", "00u"));
+
+INSTANTIATE_TEST_SUITE_P(LeadingZero,
+                         ParseIntegerTest_LeadingZeros,
+                         testing::Values("01234", "0000", "-00", "00u"));
+
+////////////////////////////////////////////////////////////////////////////////
+// ParseIntegerTest_NoSignificantDigits
+////////////////////////////////////////////////////////////////////////////////
+using ParseIntegerTest_NoSignificantDigits = testing::TestWithParam<const char*>;
+TEST_P(ParseIntegerTest_NoSignificantDigits, Parse) {
+    Source::File file("", GetParam());
+    auto t = Lexer(&file).next();
+    EXPECT_TRUE(t.Is(Token::Type::kError));
+    EXPECT_EQ(t.to_str(), "integer or float hex literal has no significant digits");
+}
+
+INSTANTIATE_TEST_SUITE_P(LeadingZero,
+                         ParseIntegerTest_NoSignificantDigits,
+                         testing::Values("0x",
+                                         "0X",
+                                         "-0x",
+                                         "-0X",
+                                         "0xu",
+                                         "0Xu",
+                                         "-0xu",
+                                         "-0Xu",
+                                         "0xi",
+                                         "0Xi",
+                                         "-0xi",
+                                         "-0Xi"));
 
 struct TokenData {
     const char* input;
diff --git a/src/tint/sem/array.h b/src/tint/sem/array.h
index aeef3ce..7f72d8a 100644
--- a/src/tint/sem/array.h
+++ b/src/tint/sem/array.h
@@ -21,11 +21,6 @@
 #include "src/tint/sem/node.h"
 #include "src/tint/sem/type.h"
 
-// Forward declarations
-namespace tint::ast {
-class Array;
-}  // namespace tint::ast
-
 namespace tint::sem {
 
 /// Array holds the semantic information for Array nodes.
diff --git a/src/tint/sem/block_statement.h b/src/tint/sem/block_statement.h
index e87b225..4f12122 100644
--- a/src/tint/sem/block_statement.h
+++ b/src/tint/sem/block_statement.h
@@ -24,7 +24,6 @@
 namespace tint::ast {
 class BlockStatement;
 class ContinueStatement;
-class Function;
 class Variable;
 }  // namespace tint::ast
 
diff --git a/src/tint/sem/module.h b/src/tint/sem/module.h
index a7b3d45..dffe003 100644
--- a/src/tint/sem/module.h
+++ b/src/tint/sem/module.h
@@ -23,7 +23,6 @@
 // Forward declarations
 namespace tint::ast {
 class Node;
-class Module;
 }  // namespace tint::ast
 
 namespace tint::sem {
diff --git a/src/tint/sem/statement.h b/src/tint/sem/statement.h
index 741a325..bdcb55c 100644
--- a/src/tint/sem/statement.h
+++ b/src/tint/sem/statement.h
@@ -20,7 +20,6 @@
 
 // Forward declarations
 namespace tint::ast {
-class Function;
 class Statement;
 }  // namespace tint::ast
 namespace tint::sem {
diff --git a/src/tint/writer/glsl/generator.h b/src/tint/writer/glsl/generator.h
index 77a7f4b..d48b0bc 100644
--- a/src/tint/writer/glsl/generator.h
+++ b/src/tint/writer/glsl/generator.h
@@ -32,9 +32,6 @@
 namespace tint {
 class Program;
 }  // namespace tint
-namespace tint::writer::glsl {
-class GeneratorImpl;
-}  // namespace tint::writer::glsl
 
 namespace tint::writer::glsl {
 
diff --git a/src/tint/writer/hlsl/generator.h b/src/tint/writer/hlsl/generator.h
index f658c99..f14da6d 100644
--- a/src/tint/writer/hlsl/generator.h
+++ b/src/tint/writer/hlsl/generator.h
@@ -30,9 +30,6 @@
 namespace tint {
 class Program;
 }  // namespace tint
-namespace tint::writer::hlsl {
-class GeneratorImpl;
-}  // namespace tint::writer::hlsl
 
 namespace tint::writer::hlsl {
 
diff --git a/src/tint/writer/msl/generator.h b/src/tint/writer/msl/generator.h
index 43b0cb7..d4208cc 100644
--- a/src/tint/writer/msl/generator.h
+++ b/src/tint/writer/msl/generator.h
@@ -28,9 +28,6 @@
 namespace tint {
 class Program;
 }  // namespace tint
-namespace tint::writer::msl {
-class GeneratorImpl;
-}  // namespace tint::writer::msl
 
 namespace tint::writer::msl {