Update intrinsics in the grammar.

The intrinsic methods were removed from the WGSL grammar and are treated
as builtin functions. This Cl updates Tint to match.

Bug: tint:41
Change-Id: I3f9ff6c17f1ca57ad159d883fd5a966657caeb4f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/22301
Reviewed-by: David Neto <dneto@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 698068f..7c08d05 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -230,8 +230,6 @@
     "src/ast/continue_statement.h",
     "src/ast/decorated_variable.cc",
     "src/ast/decorated_variable.h",
-    "src/ast/derivative_modifier.cc",
-    "src/ast/derivative_modifier.h",
     "src/ast/else_statement.cc",
     "src/ast/else_statement.h",
     "src/ast/entry_point.cc",
@@ -322,14 +320,6 @@
     "src/ast/type_constructor_expression.h",
     "src/ast/uint_literal.cc",
     "src/ast/uint_literal.h",
-    "src/ast/unary_derivative.cc",
-    "src/ast/unary_derivative.h",
-    "src/ast/unary_derivative_expression.cc",
-    "src/ast/unary_derivative_expression.h",
-    "src/ast/unary_method.cc",
-    "src/ast/unary_method.h",
-    "src/ast/unary_method_expression.cc",
-    "src/ast/unary_method_expression.h",
     "src/ast/unary_op.cc",
     "src/ast/unary_op.h",
     "src/ast/unary_op_expression.cc",
@@ -623,8 +613,6 @@
     "src/ast/type/vector_type_test.cc",
     "src/ast/type_constructor_expression_test.cc",
     "src/ast/uint_literal_test.cc",
-    "src/ast/unary_derivative_expression_test.cc",
-    "src/ast/unary_method_expression_test.cc",
     "src/ast/unary_op_expression_test.cc",
     "src/ast/unless_statement_test.cc",
     "src/ast/variable_decl_statement_test.cc",
@@ -737,7 +725,6 @@
     "src/reader/wgsl/parser_impl_const_literal_test.cc",
     "src/reader/wgsl/parser_impl_continue_stmt_test.cc",
     "src/reader/wgsl/parser_impl_continuing_stmt_test.cc",
-    "src/reader/wgsl/parser_impl_derivative_modifier_test.cc",
     "src/reader/wgsl/parser_impl_else_stmt_test.cc",
     "src/reader/wgsl/parser_impl_elseif_stmt_test.cc",
     "src/reader/wgsl/parser_impl_entry_point_decl_test.cc",
@@ -830,7 +817,6 @@
     "src/writer/wgsl/generator_impl_switch_test.cc",
     "src/writer/wgsl/generator_impl_test.cc",
     "src/writer/wgsl/generator_impl_type_test.cc",
-    "src/writer/wgsl/generator_impl_unary_derivative_test.cc",
     "src/writer/wgsl/generator_impl_unary_method_test.cc",
     "src/writer/wgsl/generator_impl_unary_op_test.cc",
     "src/writer/wgsl/generator_impl_unless_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 22fdd35..117063b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -67,8 +67,6 @@
   ast/continue_statement.h
   ast/decorated_variable.cc
   ast/decorated_variable.h
-  ast/derivative_modifier.cc
-  ast/derivative_modifier.h
   ast/else_statement.cc
   ast/else_statement.h
   ast/entry_point.cc
@@ -159,14 +157,6 @@
   ast/type/void_type.h
   ast/uint_literal.cc
   ast/uint_literal.h
-  ast/unary_derivative.cc
-  ast/unary_derivative.h
-  ast/unary_derivative_expression.cc
-  ast/unary_derivative_expression.h
-  ast/unary_method.cc
-  ast/unary_method.h
-  ast/unary_method_expression.cc
-  ast/unary_method_expression.h
   ast/unary_op.cc
   ast/unary_op.h
   ast/unary_op_expression.cc
@@ -303,8 +293,6 @@
   ast/type/vector_type_test.cc
   ast/type_constructor_expression_test.cc
   ast/uint_literal_test.cc
-  ast/unary_derivative_expression_test.cc
-  ast/unary_method_expression_test.cc
   ast/unary_op_expression_test.cc
   ast/unless_statement_test.cc
   ast/variable_decl_statement_test.cc
@@ -368,7 +356,6 @@
     reader/wgsl/parser_impl_const_literal_test.cc
     reader/wgsl/parser_impl_continue_stmt_test.cc
     reader/wgsl/parser_impl_continuing_stmt_test.cc
-    reader/wgsl/parser_impl_derivative_modifier_test.cc
     reader/wgsl/parser_impl_else_stmt_test.cc
     reader/wgsl/parser_impl_elseif_stmt_test.cc
     reader/wgsl/parser_impl_entry_point_decl_test.cc
@@ -478,8 +465,6 @@
     writer/wgsl/generator_impl_return_test.cc
     writer/wgsl/generator_impl_switch_test.cc
     writer/wgsl/generator_impl_type_test.cc
-    writer/wgsl/generator_impl_unary_derivative_test.cc
-    writer/wgsl/generator_impl_unary_method_test.cc
     writer/wgsl/generator_impl_unary_op_test.cc
     writer/wgsl/generator_impl_unless_test.cc
     writer/wgsl/generator_impl_variable_decl_statement_test.cc
diff --git a/src/ast/derivative_modifier.cc b/src/ast/derivative_modifier.cc
deleted file mode 100644
index 66a7f35..0000000
--- a/src/ast/derivative_modifier.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/derivative_modifier.h"
-
-namespace tint {
-namespace ast {
-
-std::ostream& operator<<(std::ostream& out, DerivativeModifier mod) {
-  switch (mod) {
-    case DerivativeModifier::kNone: {
-      out << "none";
-      break;
-    }
-    case DerivativeModifier::kFine: {
-      out << "fine";
-      break;
-    }
-    case DerivativeModifier::kCoarse: {
-      out << "coarse";
-      break;
-    }
-  }
-  return out;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/derivative_modifier.h b/src/ast/derivative_modifier.h
deleted file mode 100644
index 83789e4..0000000
--- a/src/ast/derivative_modifier.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_AST_DERIVATIVE_MODIFIER_H_
-#define SRC_AST_DERIVATIVE_MODIFIER_H_
-
-#include <ostream>
-
-namespace tint {
-namespace ast {
-
-/// The derivative modifier
-enum class DerivativeModifier { kNone = -1, kFine, kCoarse };
-
-std::ostream& operator<<(std::ostream& out, DerivativeModifier mod);
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_DERIVATIVE_MODIFIER_H_
diff --git a/src/ast/expression.cc b/src/ast/expression.cc
index 71074e6..bbbc7ad 100644
--- a/src/ast/expression.cc
+++ b/src/ast/expression.cc
@@ -25,8 +25,6 @@
 #include "src/ast/identifier_expression.h"
 #include "src/ast/member_accessor_expression.h"
 #include "src/ast/type/alias_type.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 
 namespace tint {
@@ -78,14 +76,6 @@
   return false;
 }
 
-bool Expression::IsUnaryDerivative() const {
-  return false;
-}
-
-bool Expression::IsUnaryMethod() const {
-  return false;
-}
-
 bool Expression::IsUnaryOp() const {
   return false;
 }
@@ -129,16 +119,6 @@
   return static_cast<const MemberAccessorExpression*>(this);
 }
 
-const UnaryDerivativeExpression* Expression::AsUnaryDerivative() const {
-  assert(IsUnaryDerivative());
-  return static_cast<const UnaryDerivativeExpression*>(this);
-}
-
-const UnaryMethodExpression* Expression::AsUnaryMethod() const {
-  assert(IsUnaryMethod());
-  return static_cast<const UnaryMethodExpression*>(this);
-}
-
 const UnaryOpExpression* Expression::AsUnaryOp() const {
   assert(IsUnaryOp());
   return static_cast<const UnaryOpExpression*>(this);
@@ -184,16 +164,6 @@
   return static_cast<MemberAccessorExpression*>(this);
 }
 
-UnaryDerivativeExpression* Expression::AsUnaryDerivative() {
-  assert(IsUnaryDerivative());
-  return static_cast<UnaryDerivativeExpression*>(this);
-}
-
-UnaryMethodExpression* Expression::AsUnaryMethod() {
-  assert(IsUnaryMethod());
-  return static_cast<UnaryMethodExpression*>(this);
-}
-
 UnaryOpExpression* Expression::AsUnaryOp() {
   assert(IsUnaryOp());
   return static_cast<UnaryOpExpression*>(this);
diff --git a/src/ast/expression.h b/src/ast/expression.h
index f82fe9b..43c4c40 100644
--- a/src/ast/expression.h
+++ b/src/ast/expression.h
@@ -32,8 +32,6 @@
 class IdentifierExpression;
 class ConstructorExpression;
 class MemberAccessorExpression;
-class UnaryDerivativeExpression;
-class UnaryMethodExpression;
 class UnaryOpExpression;
 
 /// Base expression class
@@ -63,10 +61,6 @@
   virtual bool IsMemberAccessor() const;
   /// @returns true if this is a binary expression
   virtual bool IsBinary() const;
-  /// @returns true if this is a unary derivative expression
-  virtual bool IsUnaryDerivative() const;
-  /// @returns true if this is a unary method expression
-  virtual bool IsUnaryMethod() const;
   /// @returns true if this is a unary op expression
   virtual bool IsUnaryOp() const;
 
@@ -86,10 +80,6 @@
   const MemberAccessorExpression* AsMemberAccessor() const;
   /// @returns the expression as a binary expression
   const BinaryExpression* AsBinary() const;
-  /// @returns the expression as a unary derivative expression
-  const UnaryDerivativeExpression* AsUnaryDerivative() const;
-  /// @returns the expression as a unary method expression
-  const UnaryMethodExpression* AsUnaryMethod() const;
   /// @returns the expression as a unary op expression
   const UnaryOpExpression* AsUnaryOp() const;
 
@@ -109,10 +99,6 @@
   MemberAccessorExpression* AsMemberAccessor();
   /// @returns the expression as a binary expression
   BinaryExpression* AsBinary();
-  /// @returns the expression as a unary derivative expression
-  UnaryDerivativeExpression* AsUnaryDerivative();
-  /// @returns the expression as a unary method expression
-  UnaryMethodExpression* AsUnaryMethod();
   /// @returns the expression as a unary op expression
   UnaryOpExpression* AsUnaryOp();
 
diff --git a/src/ast/unary_derivative.cc b/src/ast/unary_derivative.cc
deleted file mode 100644
index 82ae23c..0000000
--- a/src/ast/unary_derivative.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/unary_derivative.h"
-
-namespace tint {
-namespace ast {
-
-std::ostream& operator<<(std::ostream& out, UnaryDerivative mod) {
-  switch (mod) {
-    case UnaryDerivative::kDpdx: {
-      out << "dpdx";
-      break;
-    }
-    case UnaryDerivative::kDpdy: {
-      out << "dpdy";
-      break;
-    }
-    case UnaryDerivative::kFwidth: {
-      out << "fwidth";
-      break;
-    }
-  }
-  return out;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/unary_derivative.h b/src/ast/unary_derivative.h
deleted file mode 100644
index 741834c..0000000
--- a/src/ast/unary_derivative.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_AST_UNARY_DERIVATIVE_H_
-#define SRC_AST_UNARY_DERIVATIVE_H_
-
-#include <ostream>
-
-namespace tint {
-namespace ast {
-
-/// The unary derivative
-enum class UnaryDerivative { kDpdx = 0, kDpdy, kFwidth };
-
-std::ostream& operator<<(std::ostream& out, UnaryDerivative mod);
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_UNARY_DERIVATIVE_H_
diff --git a/src/ast/unary_derivative_expression.cc b/src/ast/unary_derivative_expression.cc
deleted file mode 100644
index 23c13d5..0000000
--- a/src/ast/unary_derivative_expression.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/unary_derivative_expression.h"
-
-namespace tint {
-namespace ast {
-
-UnaryDerivativeExpression::UnaryDerivativeExpression() : Expression() {}
-
-UnaryDerivativeExpression::UnaryDerivativeExpression(
-    UnaryDerivative op,
-    DerivativeModifier mod,
-    std::unique_ptr<Expression> param)
-    : Expression(), op_(op), modifier_(mod), param_(std::move(param)) {}
-
-UnaryDerivativeExpression::UnaryDerivativeExpression(
-    const Source& source,
-    UnaryDerivative op,
-    DerivativeModifier mod,
-    std::unique_ptr<Expression> param)
-    : Expression(source), op_(op), modifier_(mod), param_(std::move(param)) {}
-
-UnaryDerivativeExpression::UnaryDerivativeExpression(
-    UnaryDerivativeExpression&&) = default;
-
-UnaryDerivativeExpression::~UnaryDerivativeExpression() = default;
-
-bool UnaryDerivativeExpression::IsUnaryDerivative() const {
-  return true;
-}
-
-bool UnaryDerivativeExpression::IsValid() const {
-  if (param_ == nullptr || !param_->IsValid()) {
-    return false;
-  }
-  return true;
-}
-
-void UnaryDerivativeExpression::to_str(std::ostream& out, size_t indent) const {
-  make_indent(out, indent);
-  out << "UnaryDerivative{" << std::endl;
-  make_indent(out, indent + 2);
-  out << op_ << std::endl;
-  make_indent(out, indent + 2);
-  out << modifier_ << std::endl;
-  param_->to_str(out, indent + 2);
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/unary_derivative_expression.h b/src/ast/unary_derivative_expression.h
deleted file mode 100644
index 2076eaf..0000000
--- a/src/ast/unary_derivative_expression.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_AST_UNARY_DERIVATIVE_EXPRESSION_H_
-#define SRC_AST_UNARY_DERIVATIVE_EXPRESSION_H_
-
-#include <memory>
-#include <utility>
-
-#include "src/ast/derivative_modifier.h"
-#include "src/ast/expression.h"
-#include "src/ast/literal.h"
-#include "src/ast/unary_derivative.h"
-
-namespace tint {
-namespace ast {
-
-/// A unary derivative expression
-class UnaryDerivativeExpression : public Expression {
- public:
-  /// Constructor
-  UnaryDerivativeExpression();
-  /// Constructor
-  /// @param op the op
-  /// @param mod the derivative modifier
-  /// @param param the param
-  UnaryDerivativeExpression(UnaryDerivative op,
-                            DerivativeModifier mod,
-                            std::unique_ptr<Expression> param);
-  /// Constructor
-  /// @param source the unary derivative expression source
-  /// @param op the op
-  /// @param mod the derivative modifier
-  /// @param param the param
-  UnaryDerivativeExpression(const Source& source,
-                            UnaryDerivative op,
-                            DerivativeModifier mod,
-                            std::unique_ptr<Expression> param);
-  /// Move constructor
-  UnaryDerivativeExpression(UnaryDerivativeExpression&&);
-  ~UnaryDerivativeExpression() override;
-
-  /// Sets the op
-  /// @param op the op
-  void set_op(UnaryDerivative op) { op_ = op; }
-  /// @returns the op
-  UnaryDerivative op() const { return op_; }
-
-  /// Sets the derivative modifier
-  /// @param mod the modifier
-  void set_modifier(DerivativeModifier mod) { modifier_ = mod; }
-  /// @returns the derivative modifier
-  DerivativeModifier modifier() const { return modifier_; }
-
-  /// Sets the param
-  /// @param param the param
-  void set_param(std::unique_ptr<Expression> param) {
-    param_ = std::move(param);
-  }
-  /// @returns the param
-  Expression* param() const { return param_.get(); }
-
-  /// @returns true if this is an as expression
-  bool IsUnaryDerivative() const override;
-
-  /// @returns true if the node is valid
-  bool IsValid() const override;
-
-  /// Writes a representation of the node to the output stream
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(std::ostream& out, size_t indent) const override;
-
- private:
-  UnaryDerivativeExpression(const UnaryDerivativeExpression&) = delete;
-
-  UnaryDerivative op_ = UnaryDerivative::kDpdx;
-  DerivativeModifier modifier_ = DerivativeModifier::kNone;
-  std::unique_ptr<Expression> param_;
-};
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_UNARY_DERIVATIVE_EXPRESSION_H_
diff --git a/src/ast/unary_derivative_expression_test.cc b/src/ast/unary_derivative_expression_test.cc
deleted file mode 100644
index 96d1e46..0000000
--- a/src/ast/unary_derivative_expression_test.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/unary_derivative_expression.h"
-
-#include <sstream>
-
-#include "gtest/gtest.h"
-#include "src/ast/identifier_expression.h"
-
-namespace tint {
-namespace ast {
-namespace {
-
-using UnaryDerivativeExpressionTest = testing::Test;
-
-TEST_F(UnaryDerivativeExpressionTest, Creation) {
-  auto ident = std::make_unique<IdentifierExpression>("ident");
-  auto* ident_ptr = ident.get();
-
-  UnaryDerivativeExpression d(UnaryDerivative::kDpdy,
-                              DerivativeModifier::kCoarse, std::move(ident));
-  EXPECT_EQ(d.param(), ident_ptr);
-  EXPECT_EQ(d.modifier(), DerivativeModifier::kCoarse);
-  EXPECT_EQ(d.op(), UnaryDerivative::kDpdy);
-}
-
-TEST_F(UnaryDerivativeExpressionTest, Creation_WithSource) {
-  auto ident = std::make_unique<IdentifierExpression>("ident");
-
-  UnaryDerivativeExpression d(Source{20, 2}, UnaryDerivative::kDpdy,
-                              DerivativeModifier::kCoarse, std::move(ident));
-  auto src = d.source();
-  EXPECT_EQ(src.line, 20u);
-  EXPECT_EQ(src.column, 2u);
-}
-
-TEST_F(UnaryDerivativeExpressionTest, IsUnaryDerivative) {
-  UnaryDerivativeExpression d;
-  EXPECT_TRUE(d.IsUnaryDerivative());
-}
-
-TEST_F(UnaryDerivativeExpressionTest, IsValid) {
-  auto ident = std::make_unique<IdentifierExpression>("ident");
-  UnaryDerivativeExpression d(UnaryDerivative::kDpdy,
-                              DerivativeModifier::kCoarse, std::move(ident));
-  EXPECT_TRUE(d.IsValid());
-}
-
-TEST_F(UnaryDerivativeExpressionTest, IsValid_NullParam) {
-  UnaryDerivativeExpression d(UnaryDerivative::kDpdy,
-                              DerivativeModifier::kCoarse, nullptr);
-  EXPECT_FALSE(d.IsValid());
-}
-
-TEST_F(UnaryDerivativeExpressionTest, IsValid_InvalidParam) {
-  auto ident = std::make_unique<IdentifierExpression>("");
-  UnaryDerivativeExpression d(UnaryDerivative::kDpdy,
-                              DerivativeModifier::kCoarse, std::move(ident));
-  EXPECT_FALSE(d.IsValid());
-}
-
-TEST_F(UnaryDerivativeExpressionTest, ToStr) {
-  auto ident = std::make_unique<IdentifierExpression>("ident");
-  UnaryDerivativeExpression d(UnaryDerivative::kDpdy,
-                              DerivativeModifier::kCoarse, std::move(ident));
-  std::ostringstream out;
-  d.to_str(out, 2);
-  EXPECT_EQ(out.str(), R"(  UnaryDerivative{
-    dpdy
-    coarse
-    Identifier{ident}
-  }
-)");
-}
-
-}  // namespace
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/unary_method.cc b/src/ast/unary_method.cc
deleted file mode 100644
index 7ff4653..0000000
--- a/src/ast/unary_method.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/unary_method.h"
-
-namespace tint {
-namespace ast {
-
-std::ostream& operator<<(std::ostream& out, UnaryMethod mod) {
-  switch (mod) {
-    case UnaryMethod::kAny: {
-      out << "any";
-      break;
-    }
-    case UnaryMethod::kAll: {
-      out << "all";
-      break;
-    }
-    case UnaryMethod::kIsNan: {
-      out << "is_nan";
-      break;
-    }
-    case UnaryMethod::kIsInf: {
-      out << "is_inf";
-      break;
-    }
-    case UnaryMethod::kIsFinite: {
-      out << "is_finite";
-      break;
-    }
-    case UnaryMethod::kIsNormal: {
-      out << "is_normal";
-      break;
-    }
-    case UnaryMethod::kDot: {
-      out << "dot";
-      break;
-    }
-    case UnaryMethod::kOuterProduct: {
-      out << "outer_product";
-      break;
-    }
-  }
-  return out;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/unary_method.h b/src/ast/unary_method.h
deleted file mode 100644
index 3f719ef..0000000
--- a/src/ast/unary_method.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_AST_UNARY_METHOD_H_
-#define SRC_AST_UNARY_METHOD_H_
-
-#include <ostream>
-
-namespace tint {
-namespace ast {
-
-/// The unary methods
-enum class UnaryMethod {
-  kAny = 0,
-  kAll,
-  kIsNan,
-  kIsInf,
-  kIsFinite,
-  kIsNormal,
-  kDot,
-  kOuterProduct
-};
-
-std::ostream& operator<<(std::ostream& out, UnaryMethod mod);
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_UNARY_METHOD_H_
diff --git a/src/ast/unary_method_expression.cc b/src/ast/unary_method_expression.cc
deleted file mode 100644
index 034a8e4..0000000
--- a/src/ast/unary_method_expression.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/unary_method_expression.h"
-
-namespace tint {
-namespace ast {
-
-UnaryMethodExpression::UnaryMethodExpression() : Expression() {}
-
-UnaryMethodExpression::UnaryMethodExpression(UnaryMethod op,
-                                             ExpressionList params)
-    : Expression(), op_(op), params_(std::move(params)) {}
-
-UnaryMethodExpression::UnaryMethodExpression(const Source& source,
-                                             UnaryMethod op,
-                                             ExpressionList params)
-    : Expression(source), op_(op), params_(std::move(params)) {}
-
-UnaryMethodExpression::UnaryMethodExpression(UnaryMethodExpression&&) = default;
-
-UnaryMethodExpression::~UnaryMethodExpression() = default;
-
-bool UnaryMethodExpression::IsUnaryMethod() const {
-  return true;
-}
-
-bool UnaryMethodExpression::IsValid() const {
-  if (params_.empty()) {
-    return false;
-  }
-  for (const auto& p : params_) {
-    if (p == nullptr || !p->IsValid()) {
-      return false;
-    }
-  }
-  return true;
-}
-
-void UnaryMethodExpression::to_str(std::ostream& out, size_t indent) const {
-  make_indent(out, indent);
-
-  out << "UnaryMethod{" << std::endl;
-  make_indent(out, indent + 2);
-  out << op_ << std::endl;
-  for (const auto& param : params_) {
-    param->to_str(out, indent + 2);
-  }
-  make_indent(out, indent);
-  out << "}" << std::endl;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/unary_method_expression.h b/src/ast/unary_method_expression.h
deleted file mode 100644
index 4c63325..0000000
--- a/src/ast/unary_method_expression.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_AST_UNARY_METHOD_EXPRESSION_H_
-#define SRC_AST_UNARY_METHOD_EXPRESSION_H_
-
-#include <utility>
-
-#include "src/ast/expression.h"
-#include "src/ast/literal.h"
-#include "src/ast/unary_method.h"
-
-namespace tint {
-namespace ast {
-
-/// A unary method expression
-class UnaryMethodExpression : public Expression {
- public:
-  /// Constructor
-  UnaryMethodExpression();
-  /// Constructor
-  /// @param op the op
-  /// @param params the params
-  UnaryMethodExpression(UnaryMethod op, ExpressionList params);
-  /// Constructor
-  /// @param source the unary method source
-  /// @param op the op
-  /// @param params the params
-  UnaryMethodExpression(const Source& source,
-                        UnaryMethod op,
-                        ExpressionList params);
-  /// Move constructor
-  UnaryMethodExpression(UnaryMethodExpression&&);
-  ~UnaryMethodExpression() override;
-
-  /// Sets the op
-  /// @param op the op
-  void set_op(UnaryMethod op) { op_ = op; }
-  /// @returns the op
-  UnaryMethod op() const { return op_; }
-
-  /// Sets the params
-  /// @param params the parameters
-  void set_params(ExpressionList params) { params_ = std::move(params); }
-  /// @returns the params
-  const ExpressionList& params() const { return params_; }
-
-  /// @returns true if this is an as expression
-  bool IsUnaryMethod() const override;
-
-  /// @returns true if the node is valid
-  bool IsValid() const override;
-
-  /// Writes a representation of the node to the output stream
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(std::ostream& out, size_t indent) const override;
-
- private:
-  UnaryMethodExpression(const UnaryMethodExpression&) = delete;
-
-  UnaryMethod op_ = UnaryMethod::kAny;
-  ExpressionList params_;
-};
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_UNARY_METHOD_EXPRESSION_H_
diff --git a/src/ast/unary_method_expression_test.cc b/src/ast/unary_method_expression_test.cc
deleted file mode 100644
index 1a2d9ed..0000000
--- a/src/ast/unary_method_expression_test.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/unary_method_expression.h"
-
-#include <memory>
-#include <sstream>
-
-#include "gtest/gtest.h"
-#include "src/ast/identifier_expression.h"
-
-namespace tint {
-namespace ast {
-namespace {
-
-using UnaryMethodExpressionTest = testing::Test;
-
-TEST_F(UnaryMethodExpressionTest, Creation) {
-  ExpressionList params;
-  params.push_back(std::make_unique<IdentifierExpression>("ident"));
-
-  auto* ident_ptr = params[0].get();
-
-  UnaryMethodExpression u(UnaryMethod::kAll, std::move(params));
-  EXPECT_EQ(u.op(), UnaryMethod::kAll);
-  ASSERT_EQ(u.params().size(), 1u);
-  EXPECT_EQ(u.params()[0].get(), ident_ptr);
-}
-
-TEST_F(UnaryMethodExpressionTest, Creation_WithSource) {
-  ExpressionList params;
-  params.push_back(std::make_unique<IdentifierExpression>("ident"));
-
-  UnaryMethodExpression u(Source{20, 2}, UnaryMethod::kAll, std::move(params));
-  auto src = u.source();
-  EXPECT_EQ(src.line, 20u);
-  EXPECT_EQ(src.column, 2u);
-}
-
-TEST_F(UnaryMethodExpressionTest, IsUnaryMethod) {
-  UnaryMethodExpression u;
-  EXPECT_TRUE(u.IsUnaryMethod());
-}
-
-TEST_F(UnaryMethodExpressionTest, IsValid) {
-  ExpressionList params;
-  params.push_back(std::make_unique<IdentifierExpression>("ident"));
-
-  UnaryMethodExpression u(UnaryMethod::kAll, std::move(params));
-  EXPECT_TRUE(u.IsValid());
-}
-
-TEST_F(UnaryMethodExpressionTest, IsValid_NullParam) {
-  ExpressionList params;
-  params.push_back(std::make_unique<IdentifierExpression>("ident"));
-  params.push_back(nullptr);
-
-  UnaryMethodExpression u(UnaryMethod::kAll, std::move(params));
-  EXPECT_FALSE(u.IsValid());
-}
-
-TEST_F(UnaryMethodExpressionTest, IsValid_InvalidParam) {
-  ExpressionList params;
-  params.push_back(std::make_unique<IdentifierExpression>(""));
-
-  UnaryMethodExpression u(UnaryMethod::kAll, std::move(params));
-  EXPECT_FALSE(u.IsValid());
-}
-
-TEST_F(UnaryMethodExpressionTest, IsValid_EmptyParams) {
-  UnaryMethodExpression u;
-  u.set_op(UnaryMethod::kAll);
-  EXPECT_FALSE(u.IsValid());
-}
-
-TEST_F(UnaryMethodExpressionTest, ToStr) {
-  ExpressionList params;
-  params.push_back(std::make_unique<IdentifierExpression>("ident"));
-
-  UnaryMethodExpression u(UnaryMethod::kAll, std::move(params));
-  std::ostringstream out;
-  u.to_str(out, 2);
-  EXPECT_EQ(out.str(), R"(  UnaryMethod{
-    all
-    Identifier{ident}
-  }
-)");
-}
-
-}  // namespace
-}  // namespace ast
-}  // namespace tint
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index 5d11237..ae3e67f 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -468,10 +468,6 @@
 }
 
 Token Lexer::check_keyword(const Source& source, const std::string& str) {
-  if (str == "all")
-    return {Token::Type::kAll, source, "all"};
-  if (str == "any")
-    return {Token::Type::kAny, source, "any"};
   if (str == "array")
     return {Token::Type::kArray, source, "array"};
   if (str == "as")
@@ -498,16 +494,8 @@
     return {Token::Type::kContinue, source, "continue"};
   if (str == "continuing")
     return {Token::Type::kContinuing, source, "continuing"};
-  if (str == "coarse")
-    return {Token::Type::kCoarse, source, "coarse"};
   if (str == "default")
     return {Token::Type::kDefault, source, "default"};
-  if (str == "dot")
-    return {Token::Type::kDot, source, "dot"};
-  if (str == "dpdx")
-    return {Token::Type::kDpdx, source, "dpdx"};
-  if (str == "dpdy")
-    return {Token::Type::kDpdy, source, "dpdy"};
   if (str == "else")
     return {Token::Type::kElse, source, "else"};
   if (str == "elseif")
@@ -520,16 +508,12 @@
     return {Token::Type::kFallthrough, source, "fallthrough"};
   if (str == "false")
     return {Token::Type::kFalse, source, "false"};
-  if (str == "fine")
-    return {Token::Type::kFine, source, "fine"};
   if (str == "fn")
     return {Token::Type::kFn, source, "fn"};
   if (str == "fragment")
     return {Token::Type::kFragment, source, "fragment"};
   if (str == "function")
     return {Token::Type::kFunction, source, "function"};
-  if (str == "fwidth")
-    return {Token::Type::kFwidth, source, "fwidth"};
   if (str == "i32")
     return {Token::Type::kI32, source, "i32"};
   if (str == "if")
@@ -540,14 +524,6 @@
     return {Token::Type::kImport, source, "import"};
   if (str == "in")
     return {Token::Type::kIn, source, "in"};
-  if (str == "is_nan")
-    return {Token::Type::kIsNan, source, "is_nan"};
-  if (str == "is_inf")
-    return {Token::Type::kIsInf, source, "is_inf"};
-  if (str == "is_finite")
-    return {Token::Type::kIsFinite, source, "is_finite"};
-  if (str == "is_normal")
-    return {Token::Type::kIsNormal, source, "is_normal"};
   if (str == "kill")
     return {Token::Type::kKill, source, "kill"};
   if (str == "location")
@@ -576,8 +552,6 @@
     return {Token::Type::kOffset, source, "offset"};
   if (str == "out")
     return {Token::Type::kOut, source, "out"};
-  if (str == "outer_product")
-    return {Token::Type::kOuterProduct, source, "outer_product"};
   if (str == "private")
     return {Token::Type::kPrivate, source, "private"};
   if (str == "ptr")
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
index 0b514f9..12170dc 100644
--- a/src/reader/wgsl/lexer_test.cc
+++ b/src/reader/wgsl/lexer_test.cc
@@ -408,82 +408,69 @@
 INSTANTIATE_TEST_SUITE_P(
     LexerTest,
     KeywordTest,
-    testing::Values(
-        TokenData{"all", Token::Type::kAll},
-        TokenData{"any", Token::Type::kAny},
-        TokenData{"array", Token::Type::kArray},
-        TokenData{"as", Token::Type::kAs},
-        TokenData{"binding", Token::Type::kBinding},
-        TokenData{"block", Token::Type::kBlock},
-        TokenData{"bool", Token::Type::kBool},
-        TokenData{"break", Token::Type::kBreak},
-        TokenData{"builtin", Token::Type::kBuiltin},
-        TokenData{"case", Token::Type::kCase},
-        TokenData{"cast", Token::Type::kCast},
-        TokenData{"compute", Token::Type::kCompute},
-        TokenData{"const", Token::Type::kConst},
-        TokenData{"continue", Token::Type::kContinue},
-        TokenData{"continuing", Token::Type::kContinuing},
-        TokenData{"coarse", Token::Type::kCoarse},
-        TokenData{"default", Token::Type::kDefault},
-        TokenData{"dot", Token::Type::kDot},
-        TokenData{"dpdx", Token::Type::kDpdx},
-        TokenData{"dpdy", Token::Type::kDpdy},
-        TokenData{"else", Token::Type::kElse},
-        TokenData{"elseif", Token::Type::kElseIf},
-        TokenData{"entry_point", Token::Type::kEntryPoint},
-        TokenData{"f32", Token::Type::kF32},
-        TokenData{"fallthrough", Token::Type::kFallthrough},
-        TokenData{"false", Token::Type::kFalse},
-        TokenData{"fine", Token::Type::kFine},
-        TokenData{"fn", Token::Type::kFn},
-        TokenData{"fragment", Token::Type::kFragment},
-        TokenData{"function", Token::Type::kFunction},
-        TokenData{"fwidth", Token::Type::kFwidth},
-        TokenData{"i32", Token::Type::kI32},
-        TokenData{"if", Token::Type::kIf},
-        TokenData{"image", Token::Type::kImage},
-        TokenData{"import", Token::Type::kImport},
-        TokenData{"in", Token::Type::kIn},
-        TokenData{"is_nan", Token::Type::kIsNan},
-        TokenData{"is_inf", Token::Type::kIsInf},
-        TokenData{"is_finite", Token::Type::kIsFinite},
-        TokenData{"is_normal", Token::Type::kIsNormal},
-        TokenData{"kill", Token::Type::kKill},
-        TokenData{"location", Token::Type::kLocation},
-        TokenData{"loop", Token::Type::kLoop},
-        TokenData{"mat2x2", Token::Type::kMat2x2},
-        TokenData{"mat2x3", Token::Type::kMat2x3},
-        TokenData{"mat2x4", Token::Type::kMat2x4},
-        TokenData{"mat3x2", Token::Type::kMat3x2},
-        TokenData{"mat3x3", Token::Type::kMat3x3},
-        TokenData{"mat3x4", Token::Type::kMat3x4},
-        TokenData{"mat4x2", Token::Type::kMat4x2},
-        TokenData{"mat4x3", Token::Type::kMat4x3},
-        TokenData{"mat4x4", Token::Type::kMat4x4},
-        TokenData{"offset", Token::Type::kOffset},
-        TokenData{"out", Token::Type::kOut},
-        TokenData{"outer_product", Token::Type::kOuterProduct},
-        TokenData{"private", Token::Type::kPrivate},
-        TokenData{"ptr", Token::Type::kPtr},
-        TokenData{"return", Token::Type::kReturn},
-        TokenData{"set", Token::Type::kSet},
-        TokenData{"storage_buffer", Token::Type::kStorageBuffer},
-        TokenData{"struct", Token::Type::kStruct},
-        TokenData{"switch", Token::Type::kSwitch},
-        TokenData{"true", Token::Type::kTrue},
-        TokenData{"type", Token::Type::kType},
-        TokenData{"u32", Token::Type::kU32},
-        TokenData{"uniform", Token::Type::kUniform},
-        TokenData{"uniform_constant", Token::Type::kUniformConstant},
-        TokenData{"unless", Token::Type::kUnless},
-        TokenData{"var", Token::Type::kVar},
-        TokenData{"vec2", Token::Type::kVec2},
-        TokenData{"vec3", Token::Type::kVec3},
-        TokenData{"vec4", Token::Type::kVec4},
-        TokenData{"vertex", Token::Type::kVertex},
-        TokenData{"void", Token::Type::kVoid},
-        TokenData{"workgroup", Token::Type::kWorkgroup}));
+    testing::Values(TokenData{"array", Token::Type::kArray},
+                    TokenData{"as", Token::Type::kAs},
+                    TokenData{"binding", Token::Type::kBinding},
+                    TokenData{"block", Token::Type::kBlock},
+                    TokenData{"bool", Token::Type::kBool},
+                    TokenData{"break", Token::Type::kBreak},
+                    TokenData{"builtin", Token::Type::kBuiltin},
+                    TokenData{"case", Token::Type::kCase},
+                    TokenData{"cast", Token::Type::kCast},
+                    TokenData{"compute", Token::Type::kCompute},
+                    TokenData{"const", Token::Type::kConst},
+                    TokenData{"continue", Token::Type::kContinue},
+                    TokenData{"continuing", Token::Type::kContinuing},
+                    TokenData{"default", Token::Type::kDefault},
+                    TokenData{"else", Token::Type::kElse},
+                    TokenData{"elseif", Token::Type::kElseIf},
+                    TokenData{"entry_point", Token::Type::kEntryPoint},
+                    TokenData{"f32", Token::Type::kF32},
+                    TokenData{"fallthrough", Token::Type::kFallthrough},
+                    TokenData{"false", Token::Type::kFalse},
+                    TokenData{"fn", Token::Type::kFn},
+                    TokenData{"fragment", Token::Type::kFragment},
+                    TokenData{"function", Token::Type::kFunction},
+                    TokenData{"i32", Token::Type::kI32},
+                    TokenData{"if", Token::Type::kIf},
+                    TokenData{"image", Token::Type::kImage},
+                    TokenData{"import", Token::Type::kImport},
+                    TokenData{"in", Token::Type::kIn},
+                    TokenData{"kill", Token::Type::kKill},
+                    TokenData{"location", Token::Type::kLocation},
+                    TokenData{"loop", Token::Type::kLoop},
+                    TokenData{"mat2x2", Token::Type::kMat2x2},
+                    TokenData{"mat2x3", Token::Type::kMat2x3},
+                    TokenData{"mat2x4", Token::Type::kMat2x4},
+                    TokenData{"mat3x2", Token::Type::kMat3x2},
+                    TokenData{"mat3x3", Token::Type::kMat3x3},
+                    TokenData{"mat3x4", Token::Type::kMat3x4},
+                    TokenData{"mat4x2", Token::Type::kMat4x2},
+                    TokenData{"mat4x3", Token::Type::kMat4x3},
+                    TokenData{"mat4x4", Token::Type::kMat4x4},
+                    TokenData{"offset", Token::Type::kOffset},
+                    TokenData{"out", Token::Type::kOut},
+                    TokenData{"private", Token::Type::kPrivate},
+                    TokenData{"ptr", Token::Type::kPtr},
+                    TokenData{"return", Token::Type::kReturn},
+                    TokenData{"set", Token::Type::kSet},
+                    TokenData{"storage_buffer", Token::Type::kStorageBuffer},
+                    TokenData{"struct", Token::Type::kStruct},
+                    TokenData{"switch", Token::Type::kSwitch},
+                    TokenData{"true", Token::Type::kTrue},
+                    TokenData{"type", Token::Type::kType},
+                    TokenData{"u32", Token::Type::kU32},
+                    TokenData{"uniform", Token::Type::kUniform},
+                    TokenData{"uniform_constant",
+                              Token::Type::kUniformConstant},
+                    TokenData{"unless", Token::Type::kUnless},
+                    TokenData{"var", Token::Type::kVar},
+                    TokenData{"vec2", Token::Type::kVec2},
+                    TokenData{"vec3", Token::Type::kVec3},
+                    TokenData{"vec4", Token::Type::kVec4},
+                    TokenData{"vertex", Token::Type::kVertex},
+                    TokenData{"void", Token::Type::kVoid},
+                    TokenData{"workgroup", Token::Type::kWorkgroup}));
 
 using KeywordTest_Reserved = testing::TestWithParam<const char*>;
 TEST_P(KeywordTest_Reserved, Parses) {
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 0cfd6e5..589e4f7 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -57,10 +57,6 @@
 #include "src/ast/type/void_type.h"
 #include "src/ast/type_constructor_expression.h"
 #include "src/ast/uint_literal.h"
-#include "src/ast/unary_derivative.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/variable_decl_statement.h"
@@ -2338,30 +2334,6 @@
 //   : postfix_expression
 //   | MINUS unary_expression
 //   | BANG unary_expression
-//   | ANY PAREN_LEFT IDENT PAREN_RIGHT
-//   | ALL PAREN_LEFT IDENT PAREN_RIGHT
-//   | IS_NAN PAREN_LEFT IDENT PAREN_RIGHT
-//   | IS_INF PAREN_LEFT IDENT PAREN_RIGHT
-//   | IS_FINITE PAREN_LEFT IDENT PAREN_RIGHT
-//   | IS_NORMAL PAREN_LEFT IDENT PAREN_RIGHT
-//   | DOT PAREN_LEFT IDENT COMMA IDENT PAREN_RIGHT
-//   | OUTER_PRODUCT PAREN_LEFT IDENT COMMA IDENT PAREN_RIGHT
-//   | DPDX (LESS_THAN derivative_modifier GREATER_THAN)?
-//           PAREN_LEFT IDENT PAREN_RIGHT
-//   | DPDY (LESS_THAN derivative_modifier GREATER_THAN)?
-//           PAREN_LEFT IDENT PAREN_RIGHT
-//   | FWIDTH (LESS_THAN derivative_modifier GREATER_THAN)?
-//           PAREN_LEFT IDENT PAREN_RIGHT
-// # | unord_greater_than_equal(a, b)
-// # | unord_greater_than(a, b)
-// # | unord_less_than_equal(a, b)
-// # | unord_less_than(a, b)
-// # | unord_not_equal(a, b)
-// # | unord_equal(a, b)
-// # | signed_greater_than_equal(a, b)
-// # | signed_greater_than(a, b)
-// # | signed_less_than_equal(a, b)
-// # | signed_less_than(a, b)
 std::unique_ptr<ast::Expression> ParserImpl::unary_expression() {
   auto t = peek();
   auto source = t.source();
@@ -2385,158 +2357,9 @@
     return std::make_unique<ast::UnaryOpExpression>(source, op,
                                                     std::move(expr));
   }
-  if (t.IsAny() || t.IsAll() || t.IsIsNan() || t.IsIsInf() || t.IsIsFinite() ||
-      t.IsIsNormal()) {
-    next();  // Consume the peek
-
-    auto op = ast::UnaryMethod::kAny;
-    if (t.IsAll())
-      op = ast::UnaryMethod::kAll;
-    else if (t.IsIsNan())
-      op = ast::UnaryMethod::kIsNan;
-    else if (t.IsIsInf())
-      op = ast::UnaryMethod::kIsInf;
-    else if (t.IsIsFinite())
-      op = ast::UnaryMethod::kIsFinite;
-    else if (t.IsIsNormal())
-      op = ast::UnaryMethod::kIsNormal;
-
-    t = next();
-    if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for method call");
-      return nullptr;
-    }
-
-    t = next();
-    if (!t.IsIdentifier()) {
-      set_error(t, "missing identifier for method call");
-      return nullptr;
-    }
-    ast::ExpressionList ident;
-    ident.push_back(
-        std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
-
-    t = next();
-    if (!t.IsParenRight()) {
-      set_error(t, "missing ) for method call");
-      return nullptr;
-    }
-    return std::make_unique<ast::UnaryMethodExpression>(source, op,
-                                                        std::move(ident));
-  }
-  if (t.IsDot() || t.IsOuterProduct()) {
-    next();  // Consume the peek
-
-    auto op = ast::UnaryMethod::kDot;
-    if (t.IsOuterProduct())
-      op = ast::UnaryMethod::kOuterProduct;
-
-    t = next();
-    if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for method call");
-      return nullptr;
-    }
-
-    t = next();
-    if (!t.IsIdentifier()) {
-      set_error(t, "missing identifier for method call");
-      return nullptr;
-    }
-    ast::ExpressionList ident;
-    ident.push_back(
-        std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
-
-    t = next();
-    if (!t.IsComma()) {
-      set_error(t, "missing , for method call");
-      return nullptr;
-    }
-
-    t = next();
-    if (!t.IsIdentifier()) {
-      set_error(t, "missing identifier for method call");
-      return nullptr;
-    }
-    ident.push_back(
-        std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
-
-    t = next();
-    if (!t.IsParenRight()) {
-      set_error(t, "missing ) for method call");
-      return nullptr;
-    }
-
-    return std::make_unique<ast::UnaryMethodExpression>(source, op,
-                                                        std::move(ident));
-  }
-  if (t.IsDpdx() || t.IsDpdy() || t.IsFwidth()) {
-    next();  // Consume the peek
-
-    auto op = ast::UnaryDerivative::kDpdx;
-    if (t.IsDpdy())
-      op = ast::UnaryDerivative::kDpdy;
-    else if (t.IsFwidth())
-      op = ast::UnaryDerivative::kFwidth;
-
-    t = next();
-    auto mod = ast::DerivativeModifier::kNone;
-    if (t.IsLessThan()) {
-      mod = derivative_modifier();
-      if (has_error())
-        return nullptr;
-      if (mod == ast::DerivativeModifier::kNone) {
-        set_error(peek(), "unable to parse derivative modifier");
-        return nullptr;
-      }
-
-      t = next();
-      if (!t.IsGreaterThan()) {
-        set_error(t, "missing > for derivative modifier");
-        return nullptr;
-      }
-      t = next();
-    }
-    if (!t.IsParenLeft()) {
-      set_error(t, "missing ( for derivative method");
-      return nullptr;
-    }
-
-    t = next();
-    if (!t.IsIdentifier()) {
-      set_error(t, "missing identifier for derivative method");
-      return nullptr;
-    }
-    auto ident =
-        std::make_unique<ast::IdentifierExpression>(source, t.to_str());
-
-    t = next();
-    if (!t.IsParenRight()) {
-      set_error(t, "missing ) for derivative method");
-      return nullptr;
-    }
-
-    return std::make_unique<ast::UnaryDerivativeExpression>(source, op, mod,
-                                                            std::move(ident));
-  }
   return postfix_expression();
 }
 
-// derivative_modifier
-//   : FINE
-//   | COARSE
-ast::DerivativeModifier ParserImpl::derivative_modifier() {
-  auto t = peek();
-  if (t.IsFine()) {
-    next();  // Consume the peek
-    return ast::DerivativeModifier::kFine;
-  }
-  if (t.IsCoarse()) {
-    next();  // Consume the peek
-    return ast::DerivativeModifier::kCoarse;
-  }
-  return ast::DerivativeModifier::kNone;
-}
-
 // multiplicative_expr
 //   :
 //   | STAR unary_expression multiplicative_expr
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 3767686..fc88037 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -24,7 +24,6 @@
 #include "src/ast/assignment_statement.h"
 #include "src/ast/builtin.h"
 #include "src/ast/constructor_expression.h"
-#include "src/ast/derivative_modifier.h"
 #include "src/ast/else_statement.h"
 #include "src/ast/entry_point.h"
 #include "src/ast/function.h"
@@ -246,9 +245,6 @@
   /// Parses a `unary_expression` grammar element
   /// @returns the parsed expression or nullptr
   std::unique_ptr<ast::Expression> unary_expression();
-  /// Parses a `derivative_modifier` grammar element
-  /// @returns the modifier or DerivativeModifier::kNone if none matched
-  ast::DerivativeModifier derivative_modifier();
   /// Parses the recursive part of the `multiplicative_expression`
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
diff --git a/src/reader/wgsl/parser_impl_argument_expression_list_test.cc b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
index 4061b11..efbd731 100644
--- a/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
+++ b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
@@ -17,8 +17,6 @@
 #include "src/ast/identifier_expression.h"
 #include "src/ast/int_literal.h"
 #include "src/ast/scalar_constructor_expression.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/reader/wgsl/parser_impl.h"
 #include "src/reader/wgsl/parser_impl_test_helper.h"
diff --git a/src/reader/wgsl/parser_impl_derivative_modifier_test.cc b/src/reader/wgsl/parser_impl_derivative_modifier_test.cc
deleted file mode 100644
index 29b5785e..0000000
--- a/src/reader/wgsl/parser_impl_derivative_modifier_test.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "gtest/gtest.h"
-#include "src/ast/derivative_modifier.h"
-#include "src/reader/wgsl/parser_impl.h"
-#include "src/reader/wgsl/parser_impl_test_helper.h"
-
-namespace tint {
-namespace reader {
-namespace wgsl {
-namespace {
-
-struct DerivativeModifierData {
-  const char* input;
-  ast::DerivativeModifier result;
-};
-inline std::ostream& operator<<(std::ostream& out,
-                                DerivativeModifierData data) {
-  out << std::string(data.input);
-  return out;
-}
-
-class DerivativeModifierTest
-    : public testing::TestWithParam<DerivativeModifierData> {
- public:
-  DerivativeModifierTest() = default;
-  ~DerivativeModifierTest() override = default;
-
-  void SetUp() override { ctx_.Reset(); }
-
-  void TearDown() override { impl_ = nullptr; }
-
-  ParserImpl* parser(const std::string& str) {
-    impl_ = std::make_unique<ParserImpl>(&ctx_, str);
-    return impl_.get();
-  }
-
- private:
-  std::unique_ptr<ParserImpl> impl_;
-  Context ctx_;
-};
-
-TEST_P(DerivativeModifierTest, Parses) {
-  auto params = GetParam();
-  auto* p = parser(params.input);
-
-  auto mod = p->derivative_modifier();
-  ASSERT_FALSE(p->has_error());
-  EXPECT_EQ(mod, params.result);
-
-  auto t = p->next();
-  EXPECT_TRUE(t.IsEof());
-}
-INSTANTIATE_TEST_SUITE_P(
-    ParserImplTest,
-    DerivativeModifierTest,
-    testing::Values(
-        DerivativeModifierData{"fine", ast::DerivativeModifier::kFine},
-        DerivativeModifierData{"coarse", ast::DerivativeModifier::kCoarse}));
-
-TEST_F(ParserImplTest, DerivativeModifier_NoMatch) {
-  auto* p = parser("not-a-modifier");
-  auto stage = p->derivative_modifier();
-  ASSERT_EQ(stage, ast::DerivativeModifier::kNone);
-
-  auto t = p->next();
-  EXPECT_TRUE(t.IsIdentifier());
-  EXPECT_EQ(t.to_str(), "not");
-}
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace reader
-}  // namespace tint
diff --git a/src/reader/wgsl/parser_impl_postfix_expression_test.cc b/src/reader/wgsl/parser_impl_postfix_expression_test.cc
index 6f9ff51..960bdf3 100644
--- a/src/reader/wgsl/parser_impl_postfix_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_postfix_expression_test.cc
@@ -19,8 +19,6 @@
 #include "src/ast/int_literal.h"
 #include "src/ast/member_accessor_expression.h"
 #include "src/ast/scalar_constructor_expression.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/reader/wgsl/parser_impl.h"
 #include "src/reader/wgsl/parser_impl_test_helper.h"
diff --git a/src/reader/wgsl/parser_impl_primary_expression_test.cc b/src/reader/wgsl/parser_impl_primary_expression_test.cc
index 80f2940..1be32d7 100644
--- a/src/reader/wgsl/parser_impl_primary_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_primary_expression_test.cc
@@ -23,8 +23,6 @@
 #include "src/ast/type/f32_type.h"
 #include "src/ast/type/i32_type.h"
 #include "src/ast/type_constructor_expression.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/reader/wgsl/parser_impl.h"
 #include "src/reader/wgsl/parser_impl_test_helper.h"
diff --git a/src/reader/wgsl/parser_impl_unary_expression_test.cc b/src/reader/wgsl/parser_impl_unary_expression_test.cc
index a11bc78..fad15fc 100644
--- a/src/reader/wgsl/parser_impl_unary_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_unary_expression_test.cc
@@ -17,8 +17,6 @@
 #include "src/ast/identifier_expression.h"
 #include "src/ast/int_literal.h"
 #include "src/ast/scalar_constructor_expression.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/reader/wgsl/parser_impl.h"
 #include "src/reader/wgsl/parser_impl_test_helper.h"
@@ -99,732 +97,6 @@
   EXPECT_EQ(p->error(), "1:2: unable to parse right side of ! expression");
 }
 
-TEST_F(ParserImplTest, UnaryExpression_Any) {
-  auto* p = parser("any(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kAny);
-  ASSERT_EQ(u->params().size(), 1u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Any_MissingParenLeft) {
-  auto* p = parser("any a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Any_MissingParenRight) {
-  auto* p = parser("any(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Any_MissingIdentifier) {
-  auto* p = parser("any()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Any_InvalidIdentifier) {
-  auto* p = parser("any(123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_All) {
-  auto* p = parser("all(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kAll);
-  ASSERT_EQ(u->params().size(), 1u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_All_MissingParenLeft) {
-  auto* p = parser("all a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_All_MissingParenRight) {
-  auto* p = parser("all(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_All_MissingIdentifier) {
-  auto* p = parser("all()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_All_InvalidIdentifier) {
-  auto* p = parser("all(123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNan) {
-  auto* p = parser("is_nan(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kIsNan);
-  ASSERT_EQ(u->params().size(), 1u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNan_MissingParenLeft) {
-  auto* p = parser("is_nan a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNan_MissingParenRight) {
-  auto* p = parser("is_nan(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:9: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNan_MissingIdentifier) {
-  auto* p = parser("is_nan()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNan_InvalidIdentifier) {
-  auto* p = parser("is_nan(123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsInf) {
-  auto* p = parser("is_inf(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kIsInf);
-  ASSERT_EQ(u->params().size(), 1u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsInf_MissingParenLeft) {
-  auto* p = parser("is_inf a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsInf_MissingParenRight) {
-  auto* p = parser("is_inf(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:9: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsInf_MissingIdentifier) {
-  auto* p = parser("is_inf()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsInf_InvalidIdentifier) {
-  auto* p = parser("is_inf(123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsFinite) {
-  auto* p = parser("is_finite(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kIsFinite);
-  ASSERT_EQ(u->params().size(), 1u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsFinite_MissingParenLeft) {
-  auto* p = parser("is_finite a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsFinite_MissingParenRight) {
-  auto* p = parser("is_finite(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:12: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsFinite_MissingIdentifier) {
-  auto* p = parser("is_finite()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsFinite_InvalidIdentifier) {
-  auto* p = parser("is_finite(123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNormal) {
-  auto* p = parser("is_normal(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kIsNormal);
-  ASSERT_EQ(u->params().size(), 1u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNormal_MissingParenLeft) {
-  auto* p = parser("is_normal a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNormal_MissingParenRight) {
-  auto* p = parser("is_normal(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:12: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNormal_MissingIdentifier) {
-  auto* p = parser("is_normal()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_IsNormal_InvalidIdentifier) {
-  auto* p = parser("is_normal(123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:11: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot) {
-  auto* p = parser("dot(a, b)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kDot);
-  ASSERT_EQ(u->params().size(), 2u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-
-  ASSERT_TRUE(u->params()[1]->IsIdentifier());
-  ident = u->params()[1]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "b");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot_MissingParenLeft) {
-  auto* p = parser("dot a, b)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot_MissingParenRight) {
-  auto* p = parser("dot(a, b");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:9: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot_MissingFirstIdentifier) {
-  auto* p = parser("dot(, a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot_MissingSecondIdentifier) {
-  auto* p = parser("dot(a, )");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot_MissingComma) {
-  auto* p = parser("dot(a b)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:7: missing , for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot_InvalidFirstIdentifier) {
-  auto* p = parser("dot(123, b)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:5: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dot_InvalidSecondIdentifier) {
-  auto* p = parser("dot(a, 123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct) {
-  auto* p = parser("outer_product(a, b)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryMethod());
-
-  auto* u = e->AsUnaryMethod();
-  ASSERT_EQ(u->op(), ast::UnaryMethod::kOuterProduct);
-  ASSERT_EQ(u->params().size(), 2u);
-  ASSERT_TRUE(u->params()[0]->IsIdentifier());
-  auto* ident = u->params()[0]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-
-  ASSERT_TRUE(u->params()[1]->IsIdentifier());
-  ident = u->params()[1]->AsIdentifier();
-  EXPECT_EQ(ident->name(), "b");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingParenLeft) {
-  auto* p = parser("outer_product a, b)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:15: missing ( for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingParenRight) {
-  auto* p = parser("outer_product(a, b");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:19: missing ) for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingFirstIdentifier) {
-  auto* p = parser("outer_product(, b)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:15: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingSecondIdentifier) {
-  auto* p = parser("outer_product(a, )");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:18: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingComma) {
-  auto* p = parser("outer_product(a b)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:17: missing , for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct_InvalidFirstIdentifier) {
-  auto* p = parser("outer_product(123, b)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:15: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_OuterProduct_InvalidSecondIdentifier) {
-  auto* p = parser("outer_product(a, 123)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:18: missing identifier for method call");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_NoModifier) {
-  auto* p = parser("dpdx(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryDerivative());
-
-  auto* deriv = e->AsUnaryDerivative();
-  EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdx);
-  EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kNone);
-
-  ASSERT_NE(deriv->param(), nullptr);
-  ASSERT_TRUE(deriv->param()->IsIdentifier());
-  auto* ident = deriv->param()->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_WithModifier) {
-  auto* p = parser("dpdx<coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryDerivative());
-
-  auto* deriv = e->AsUnaryDerivative();
-  EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdx);
-  EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kCoarse);
-
-  ASSERT_NE(deriv->param(), nullptr);
-  ASSERT_TRUE(deriv->param()->IsIdentifier());
-  auto* ident = deriv->param()->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingLessThan) {
-  auto* p = parser("dpdx coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_InvalidModifier) {
-  auto* p = parser("dpdx<invalid>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: unable to parse derivative modifier");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_EmptyModifer) {
-  auto* p = parser("dpdx coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingGreaterThan) {
-  auto* p = parser("dpdx<coarse (a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:13: missing > for derivative modifier");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_MisisngLeftParen) {
-  auto* p = parser("dpdx<coarse>a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:13: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingRightParen) {
-  auto* p = parser("dpdx<coarse>(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:15: missing ) for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingIdentifier) {
-  auto* p = parser("dpdx<coarse>()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:14: missing identifier for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdx_InvalidIdentifeir) {
-  auto* p = parser("dpdx<coarse>(12345)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:14: missing identifier for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_NoModifier) {
-  auto* p = parser("dpdy(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryDerivative());
-
-  auto* deriv = e->AsUnaryDerivative();
-  EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdy);
-  EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kNone);
-
-  ASSERT_NE(deriv->param(), nullptr);
-  ASSERT_TRUE(deriv->param()->IsIdentifier());
-  auto* ident = deriv->param()->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_WithModifier) {
-  auto* p = parser("dpdy<fine>(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryDerivative());
-
-  auto* deriv = e->AsUnaryDerivative();
-  EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdy);
-  EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kFine);
-
-  ASSERT_NE(deriv->param(), nullptr);
-  ASSERT_TRUE(deriv->param()->IsIdentifier());
-  auto* ident = deriv->param()->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingLessThan) {
-  auto* p = parser("dpdy coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_InvalidModifier) {
-  auto* p = parser("dpdy<invalid>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: unable to parse derivative modifier");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_EmptyModifer) {
-  auto* p = parser("dpdy coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:6: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingGreaterThan) {
-  auto* p = parser("dpdy<coarse (a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:13: missing > for derivative modifier");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_MisisngLeftParen) {
-  auto* p = parser("dpdy<coarse>a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:13: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingRightParen) {
-  auto* p = parser("dpdy<coarse>(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:15: missing ) for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingIdentifier) {
-  auto* p = parser("dpdy<coarse>()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:14: missing identifier for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Dpdy_InvalidIdentifeir) {
-  auto* p = parser("dpdy<coarse>(12345)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:14: missing identifier for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_NoModifier) {
-  auto* p = parser("fwidth(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryDerivative());
-
-  auto* deriv = e->AsUnaryDerivative();
-  EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kFwidth);
-  EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kNone);
-
-  ASSERT_NE(deriv->param(), nullptr);
-  ASSERT_TRUE(deriv->param()->IsIdentifier());
-  auto* ident = deriv->param()->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_WithModifier) {
-  auto* p = parser("fwidth<coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsUnaryDerivative());
-
-  auto* deriv = e->AsUnaryDerivative();
-  EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kFwidth);
-  EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kCoarse);
-
-  ASSERT_NE(deriv->param(), nullptr);
-  ASSERT_TRUE(deriv->param()->IsIdentifier());
-  auto* ident = deriv->param()->AsIdentifier();
-  EXPECT_EQ(ident->name(), "a");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingLessThan) {
-  auto* p = parser("fwidth coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_InvalidModifier) {
-  auto* p = parser("fwidth<invalid>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: unable to parse derivative modifier");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_EmptyModifer) {
-  auto* p = parser("fwidth coarse>(a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:8: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingGreaterThan) {
-  auto* p = parser("fwidth<coarse (a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:15: missing > for derivative modifier");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_MisisngLeftParen) {
-  auto* p = parser("fwidth<coarse>a)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:15: missing ( for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingRightParen) {
-  auto* p = parser("fwidth<coarse>(a");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:17: missing ) for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingIdentifier) {
-  auto* p = parser("fwidth<coarse>()");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:16: missing identifier for derivative method");
-}
-
-TEST_F(ParserImplTest, UnaryExpression_Fwidht_InvalidIdentifeir) {
-  auto* p = parser("fwidth<coarse>(12345)");
-  auto e = p->unary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:16: missing identifier for derivative method");
-}
-
 }  // namespace
 }  // namespace wgsl
 }  // namespace reader
diff --git a/src/reader/wgsl/parser_impl_variable_decoration_test.cc b/src/reader/wgsl/parser_impl_variable_decoration_test.cc
index 4fc9e5c..dc1a5b0 100644
--- a/src/reader/wgsl/parser_impl_variable_decoration_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_decoration_test.cc
@@ -116,7 +116,6 @@
   EXPECT_EQ(p->error(), "1:8: expected identifier for builtin");
 }
 
-
 TEST_F(ParserImplTest, VariableDecoration_Builtin_InvalidValue) {
   auto* p = parser("builtin other_thingy");
   auto deco = p->variable_decoration();
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index 300fbe4..2976a95 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -105,10 +105,6 @@
     case Token::Type::kXor:
       return "^";
 
-    case Token::Type::kAll:
-      return "all";
-    case Token::Type::kAny:
-      return "any";
     case Token::Type::kArray:
       return "array";
     case Token::Type::kAs:
@@ -135,16 +131,8 @@
       return "continue";
     case Token::Type::kContinuing:
       return "continuing";
-    case Token::Type::kCoarse:
-      return "coarse";
     case Token::Type::kDefault:
       return "default";
-    case Token::Type::kDot:
-      return "dot";
-    case Token::Type::kDpdx:
-      return "dpdx";
-    case Token::Type::kDpdy:
-      return "dpdy";
     case Token::Type::kElse:
       return "else";
     case Token::Type::kElseIf:
@@ -157,16 +145,12 @@
       return "fallthrough";
     case Token::Type::kFalse:
       return "false";
-    case Token::Type::kFine:
-      return "fine";
     case Token::Type::kFn:
       return "fn";
     case Token::Type::kFragment:
       return "fragment";
     case Token::Type::kFunction:
       return "function";
-    case Token::Type::kFwidth:
-      return "fwidth";
     case Token::Type::kI32:
       return "i32";
     case Token::Type::kIf:
@@ -177,14 +161,6 @@
       return "import";
     case Token::Type::kIn:
       return "in";
-    case Token::Type::kIsNan:
-      return "is_nan";
-    case Token::Type::kIsInf:
-      return "is_inf";
-    case Token::Type::kIsFinite:
-      return "is_finite";
-    case Token::Type::kIsNormal:
-      return "is_normal";
     case Token::Type::kKill:
       return "kill";
     case Token::Type::kLocation:
@@ -213,8 +189,6 @@
       return "offset";
     case Token::Type::kOut:
       return "out";
-    case Token::Type::kOuterProduct:
-      return "outer_product";
     case Token::Type::kPremerge:
       return "premerge";
     case Token::Type::kPrivate:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index 53a37cd..f5a2c5f 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -116,10 +116,6 @@
     /// A '^'
     kXor,
 
-    /// A 'all'
-    kAll,
-    /// A 'any'
-    kAny,
     /// A 'array'
     kArray,
     /// A 'as'
@@ -146,16 +142,8 @@
     kContinue,
     /// A 'continuing'
     kContinuing,
-    /// A 'coarse'
-    kCoarse,
     /// A 'default'
     kDefault,
-    /// A 'dot'
-    kDot,
-    /// A 'dpdx'
-    kDpdx,
-    /// A 'dpdy'
-    kDpdy,
     /// A 'else'
     kElse,
     /// A 'elseif'
@@ -168,16 +156,12 @@
     kFallthrough,
     /// A 'false'
     kFalse,
-    /// A 'fine'
-    kFine,
     /// A 'fn'
     kFn,
     /// A 'fragment'
     kFragment,
     /// A 'function'
     kFunction,
-    /// A 'fwidth'
-    kFwidth,
     /// A 'i32'
     kI32,
     /// A 'if'
@@ -188,14 +172,6 @@
     kImport,
     /// A 'in'
     kIn,
-    /// A 'is_nan'
-    kIsNan,
-    /// A 'is_inf'
-    kIsInf,
-    /// A 'is_finite'
-    kIsFinite,
-    /// A 'is_normal'
-    kIsNormal,
     /// A 'kill'
     kKill,
     /// A 'location'
@@ -224,8 +200,6 @@
     kOffset,
     /// A 'out'
     kOut,
-    /// A 'outer_product'
-    kOuterProduct,
     /// A 'premerge'
     kPremerge,
     /// A 'private'
@@ -401,10 +375,6 @@
   /// @returns true if token is a '^'
   bool IsXor() const { return type_ == Type::kXor; }
 
-  /// @returns true if token is a 'all'
-  bool IsAll() const { return type_ == Type::kAll; }
-  /// @returns true if token is a 'any'
-  bool IsAny() const { return type_ == Type::kAny; }
   /// @returns true if token is a 'array'
   bool IsArray() const { return type_ == Type::kArray; }
   /// @returns true if token is a 'as'
@@ -423,8 +393,6 @@
   bool IsCase() const { return type_ == Type::kCase; }
   /// @returns true if token is a 'cast'
   bool IsCast() const { return type_ == Type::kCast; }
-  /// @returns true if token is 'coarse'
-  bool IsCoarse() const { return type_ == Type::kCoarse; }
   /// @returns true if token is a 'compute'
   bool IsCompute() const { return type_ == Type::kCompute; }
   /// @returns true if token is a 'const'
@@ -435,12 +403,6 @@
   bool IsContinuing() const { return type_ == Type::kContinuing; }
   /// @returns true if token is a 'default'
   bool IsDefault() const { return type_ == Type::kDefault; }
-  /// @returns true if token is a 'dot'
-  bool IsDot() const { return type_ == Type::kDot; }
-  /// @returns true if token is a 'dpdx'
-  bool IsDpdx() const { return type_ == Type::kDpdx; }
-  /// @returns true if token is a 'dpdy'
-  bool IsDpdy() const { return type_ == Type::kDpdy; }
   /// @returns true if token is a 'else'
   bool IsElse() const { return type_ == Type::kElse; }
   /// @returns true if token is a 'elseif'
@@ -453,16 +415,12 @@
   bool IsFallthrough() const { return type_ == Type::kFallthrough; }
   /// @returns true if token is a 'false'
   bool IsFalse() const { return type_ == Type::kFalse; }
-  /// @returns true if token is a 'fine'
-  bool IsFine() const { return type_ == Type::kFine; }
   /// @returns true if token is a 'fn'
   bool IsFn() const { return type_ == Type::kFn; }
   /// @returns true if token is a 'fragment'
   bool IsFragment() const { return type_ == Type::kFragment; }
   /// @returns true if token is a 'function'
   bool IsFunction() const { return type_ == Type::kFunction; }
-  /// @returns true if token is a 'fwidth'
-  bool IsFwidth() const { return type_ == Type::kFwidth; }
   /// @returns true if token is a 'i32'
   bool IsI32() const { return type_ == Type::kI32; }
   /// @returns true if token is a 'if'
@@ -473,14 +431,6 @@
   bool IsImport() const { return type_ == Type::kImport; }
   /// @returns true if token is a 'in'
   bool IsIn() const { return type_ == Type::kIn; }
-  /// @returns true if token is a 'is_nan'
-  bool IsIsNan() const { return type_ == Type::kIsNan; }
-  /// @returns true if token is a 'is_inf'
-  bool IsIsInf() const { return type_ == Type::kIsInf; }
-  /// @returns true if token is a 'is_finite'
-  bool IsIsFinite() const { return type_ == Type::kIsFinite; }
-  /// @returns true if token is a 'is_normal'
-  bool IsIsNormal() const { return type_ == Type::kIsNormal; }
   /// @returns true if token is a 'kill'
   bool IsKill() const { return type_ == Type::kKill; }
   /// @returns true if token is a 'location'
@@ -509,8 +459,6 @@
   bool IsOffset() const { return type_ == Type::kOffset; }
   /// @returns true if token is a 'out'
   bool IsOut() const { return type_ == Type::kOut; }
-  /// @returns true if token is a 'outer_product'
-  bool IsOuterProduct() const { return type_ == Type::kOuterProduct; }
   /// @returns true if token is a 'private'
   bool IsPrivate() const { return type_ == Type::kPrivate; }
   /// @returns true if token is a 'ptr'
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 0def870..beddba4 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -42,13 +42,30 @@
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/vector_type.h"
 #include "src/ast/type_constructor_expression.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/unless_statement.h"
 #include "src/ast/variable_decl_statement.h"
 
 namespace tint {
+namespace {
+
+bool IsDerivative(const std::string& name) {
+  return name == "dpdx" || name == "dpdx_fine" || name == "dpdx_coarse" ||
+         name == "dpdy" || name == "dpdy_fine" || name == "dpdy_coarse" ||
+         name == "fwidth" || name == "fwidth_fine" || name == "fwidth_coarse";
+}
+
+bool IsFloatIntrinsic(const std::string& name) {
+  return name == "is_finite" || name == "is_inf" || name == "is_nan" ||
+         name == "is_normal";
+}
+
+bool IsIntrinsic(const std::string& name) {
+  return IsDerivative(name) || name == "all" || name == "any" ||
+         IsFloatIntrinsic(name) || name == "dot" || name == "outer_product";
+}
+
+}  // namespace
 
 TypeDeterminer::TypeDeterminer(Context* ctx, ast::Module* mod)
     : ctx_(*ctx), mod_(mod) {}
@@ -253,12 +270,6 @@
   if (expr->IsMemberAccessor()) {
     return DetermineMemberAccessor(expr->AsMemberAccessor());
   }
-  if (expr->IsUnaryDerivative()) {
-    return DetermineUnaryDerivative(expr->AsUnaryDerivative());
-  }
-  if (expr->IsUnaryMethod()) {
-    return DetermineUnaryMethod(expr->AsUnaryMethod());
-  }
   if (expr->IsUnaryOp()) {
     return DetermineUnaryOp(expr->AsUnaryOp());
   }
@@ -318,7 +329,11 @@
   if (expr->func()->IsIdentifier()) {
     auto* ident = expr->func()->AsIdentifier();
 
-    if (ident->has_path()) {
+    if (IsIntrinsic(ident->name())) {
+      if (!DetermineIntrinsic(ident->name(), expr))
+        return false;
+
+    } else if (ident->has_path()) {
       auto* imp = mod_->FindImportByName(ident->path());
       if (imp == nullptr) {
         set_error(expr->source(), "Unable to find import for " + ident->name());
@@ -355,6 +370,75 @@
   return true;
 }
 
+bool TypeDeterminer::DetermineIntrinsic(const std::string& name,
+                                        ast::CallExpression* expr) {
+  if (IsDerivative(name)) {
+    if (expr->params().size() != 1) {
+      set_error(expr->source(), "incorrect number of parameters for " + name);
+      return false;
+    }
+
+    // The result type must be the same as the type of the parameter.
+    auto& param = expr->params()[0];
+    if (!DetermineResultType(param.get())) {
+      return false;
+    }
+    expr->func()->set_result_type(param->result_type()->UnwrapPtrIfNeeded());
+    return true;
+  }
+  if (name == "any" || name == "all") {
+    expr->func()->set_result_type(
+        ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>()));
+    return true;
+  }
+  if (IsFloatIntrinsic(name)) {
+    if (expr->params().size() != 1) {
+      set_error(expr->source(), "incorrect number of parameters for " + name);
+      return false;
+    }
+
+    auto* bool_type =
+        ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>());
+
+    auto* param_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
+    if (param_type->IsVector()) {
+      expr->func()->set_result_type(
+          ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(
+              bool_type, param_type->AsVector()->size())));
+    } else {
+      expr->func()->set_result_type(bool_type);
+    }
+    return true;
+  }
+  if (name == "dot") {
+    expr->func()->set_result_type(
+        ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
+    return true;
+  }
+  if (name == "outer_product") {
+    if (expr->params().size() != 2) {
+      set_error(expr->source(),
+                "incorrect number of parameters for outer_product");
+      return false;
+    }
+
+    auto* param0_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
+    auto* param1_type = expr->params()[1]->result_type()->UnwrapPtrIfNeeded();
+    if (!param0_type->IsVector() || !param1_type->IsVector()) {
+      set_error(expr->source(), "invalid parameter type for outer_product");
+      return false;
+    }
+
+    expr->func()->set_result_type(
+        ctx_.type_mgr().Get(std::make_unique<ast::type::MatrixType>(
+            ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()),
+            param0_type->AsVector()->size(), param1_type->AsVector()->size())));
+    return true;
+  }
+
+  return false;
+}
+
 bool TypeDeterminer::DetermineCast(ast::CastExpression* expr) {
   if (!DetermineResultType(expr->expr())) {
     return false;
@@ -550,77 +634,6 @@
   return false;
 }
 
-bool TypeDeterminer::DetermineUnaryDerivative(
-    ast::UnaryDerivativeExpression* expr) {
-  // The result type must be the same as the type of the parameter.
-  if (!DetermineResultType(expr->param())) {
-    return false;
-  }
-  expr->set_result_type(expr->param()->result_type()->UnwrapPtrIfNeeded());
-  return true;
-}
-
-bool TypeDeterminer::DetermineUnaryMethod(ast::UnaryMethodExpression* expr) {
-  if (!DetermineResultType(expr->params())) {
-    return false;
-  }
-
-  switch (expr->op()) {
-    case ast::UnaryMethod::kAny:
-    case ast::UnaryMethod::kAll: {
-      expr->set_result_type(
-          ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>()));
-      break;
-    }
-    case ast::UnaryMethod::kIsNan:
-    case ast::UnaryMethod::kIsInf:
-    case ast::UnaryMethod::kIsFinite:
-    case ast::UnaryMethod::kIsNormal: {
-      if (expr->params().empty()) {
-        set_error(expr->source(), "incorrect number of parameters");
-        return false;
-      }
-
-      auto* bool_type =
-          ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>());
-      auto* param_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
-      if (param_type->IsVector()) {
-        expr->set_result_type(
-            ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(
-                bool_type, param_type->AsVector()->size())));
-      } else {
-        expr->set_result_type(bool_type);
-      }
-      break;
-    }
-    case ast::UnaryMethod::kDot: {
-      expr->set_result_type(
-          ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
-      break;
-    }
-    case ast::UnaryMethod::kOuterProduct: {
-      if (expr->params().size() != 2) {
-        set_error(expr->source(),
-                  "incorrect number of parameters for outer product");
-        return false;
-      }
-      auto* param0_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
-      auto* param1_type = expr->params()[1]->result_type()->UnwrapPtrIfNeeded();
-      if (!param0_type->IsVector() || !param1_type->IsVector()) {
-        set_error(expr->source(), "invalid parameter type for outer product");
-        return false;
-      }
-      expr->set_result_type(
-          ctx_.type_mgr().Get(std::make_unique<ast::type::MatrixType>(
-              ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()),
-              param0_type->AsVector()->size(),
-              param1_type->AsVector()->size())));
-      break;
-    }
-  }
-  return true;
-}
-
 bool TypeDeterminer::DetermineUnaryOp(ast::UnaryOpExpression* expr) {
   // Result type matches the parameter type.
   if (!DetermineResultType(expr->expr())) {
diff --git a/src/type_determiner.h b/src/type_determiner.h
index 770e00b..7f62a57 100644
--- a/src/type_determiner.h
+++ b/src/type_determiner.h
@@ -34,8 +34,6 @@
 class Function;
 class IdentifierExpression;
 class MemberAccessorExpression;
-class UnaryDerivativeExpression;
-class UnaryMethodExpression;
 class UnaryOpExpression;
 class Variable;
 
@@ -114,9 +112,8 @@
   bool DetermineCast(ast::CastExpression* expr);
   bool DetermineConstructor(ast::ConstructorExpression* expr);
   bool DetermineIdentifier(ast::IdentifierExpression* expr);
+  bool DetermineIntrinsic(const std::string& name, ast::CallExpression* expr);
   bool DetermineMemberAccessor(ast::MemberAccessorExpression* expr);
-  bool DetermineUnaryDerivative(ast::UnaryDerivativeExpression* expr);
-  bool DetermineUnaryMethod(ast::UnaryMethodExpression* expr);
   bool DetermineUnaryOp(ast::UnaryOpExpression* expr);
 
   Context& ctx_;
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index bb44112..5104709 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -51,8 +51,6 @@
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/vector_type.h"
 #include "src/ast/type_constructor_expression.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/unless_statement.h"
 #include "src/ast/variable_decl_statement.h"
@@ -1369,13 +1367,34 @@
   EXPECT_EQ(mat->columns(), 4u);
 }
 
-using UnaryDerivativeExpressionTest =
-    TypeDeterminerTestWithParam<ast::UnaryDerivative>;
-TEST_P(UnaryDerivativeExpressionTest, Expr_UnaryDerivative) {
-  auto derivative = GetParam();
+using IntrinsicDerivativeTest = TypeDeterminerTestWithParam<std::string>;
+TEST_P(IntrinsicDerivativeTest, Scalar) {
+  auto name = GetParam();
 
   ast::type::F32Type f32;
 
+  auto var =
+      std::make_unique<ast::Variable>("ident", ast::StorageClass::kNone, &f32);
+  mod()->AddGlobalVariable(std::move(var));
+
+  // Register the global
+  EXPECT_TRUE(td()->Determine());
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("ident"));
+
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
+  EXPECT_TRUE(td()->DetermineResultType(&expr));
+
+  ASSERT_NE(expr.result_type(), nullptr);
+  ASSERT_TRUE(expr.result_type()->IsF32());
+}
+
+TEST_P(IntrinsicDerivativeTest, Vector) {
+  auto name = GetParam();
+
+  ast::type::F32Type f32;
   ast::type::VectorType vec4(&f32, 4);
 
   auto var =
@@ -1385,25 +1404,75 @@
   // Register the global
   EXPECT_TRUE(td()->Determine());
 
-  ast::UnaryDerivativeExpression der(
-      derivative, ast::DerivativeModifier::kNone,
-      std::make_unique<ast::IdentifierExpression>("ident"));
-  EXPECT_TRUE(td()->DetermineResultType(&der));
-  ASSERT_NE(der.result_type(), nullptr);
-  ASSERT_TRUE(der.result_type()->IsVector());
-  EXPECT_TRUE(der.result_type()->AsVector()->type()->IsF32());
-  EXPECT_EQ(der.result_type()->AsVector()->size(), 4u);
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("ident"));
+
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
+  EXPECT_TRUE(td()->DetermineResultType(&expr));
+
+  ASSERT_NE(expr.result_type(), nullptr);
+  ASSERT_TRUE(expr.result_type()->IsVector());
+  EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsF32());
+  EXPECT_EQ(expr.result_type()->AsVector()->size(), 4u);
+}
+
+TEST_P(IntrinsicDerivativeTest, MissingParam) {
+  auto name = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec4(&f32, 4);
+
+  // Register the global
+  EXPECT_TRUE(td()->Determine());
+
+  ast::ExpressionList call_params;
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
+  EXPECT_FALSE(td()->DetermineResultType(&expr));
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for " + name);
+}
+
+TEST_P(IntrinsicDerivativeTest, ToomManyParams) {
+  auto name = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec4(&f32, 4);
+
+  auto var1 = std::make_unique<ast::Variable>("ident1",
+                                              ast::StorageClass::kNone, &vec4);
+  auto var2 = std::make_unique<ast::Variable>("ident2",
+                                              ast::StorageClass::kNone, &vec4);
+  mod()->AddGlobalVariable(std::move(var1));
+  mod()->AddGlobalVariable(std::move(var2));
+
+  // Register the global
+  EXPECT_TRUE(td()->Determine());
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("ident1"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("ident2"));
+
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
+  EXPECT_FALSE(td()->DetermineResultType(&expr));
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for " + name);
 }
 INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
-                         UnaryDerivativeExpressionTest,
-                         testing::Values(ast::UnaryDerivative::kDpdx,
-                                         ast::UnaryDerivative::kDpdy,
-                                         ast::UnaryDerivative::kFwidth));
+                         IntrinsicDerivativeTest,
+                         testing::Values("dpdx",
+                                         "dpdx_coarse",
+                                         "dpdx_fine",
+                                         "dpdy",
+                                         "dpdy_coarse",
+                                         "dpdy_fine",
+                                         "fwidth",
+                                         "fwidth_coarse",
+                                         "fwidth_fine"));
 
-using UnaryMethodExpressionBoolTest =
-    TypeDeterminerTestWithParam<ast::UnaryMethod>;
-TEST_P(UnaryMethodExpressionBoolTest, Expr_UnaryMethod_Any) {
-  auto op = GetParam();
+using Intrinsic = TypeDeterminerTestWithParam<std::string>;
+TEST_P(Intrinsic, Test) {
+  auto name = GetParam();
 
   ast::type::BoolType bool_type;
   ast::type::VectorType vec3(&bool_type, 3);
@@ -1412,27 +1481,26 @@
                                              &vec3);
   mod()->AddGlobalVariable(std::move(var));
 
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
 
-  ast::UnaryMethodExpression exp(op, std::move(params));
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
 
   // Register the variable
   EXPECT_TRUE(td()->Determine());
 
-  EXPECT_TRUE(td()->DetermineResultType(&exp));
-  ASSERT_NE(exp.result_type(), nullptr);
-  EXPECT_TRUE(exp.result_type()->IsBool());
+  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  ASSERT_NE(expr.result_type(), nullptr);
+  EXPECT_TRUE(expr.result_type()->IsBool());
 }
 INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
-                         UnaryMethodExpressionBoolTest,
-                         testing::Values(ast::UnaryMethod::kAny,
-                                         ast::UnaryMethod::kAll));
+                         Intrinsic,
+                         testing::Values("any", "all"));
 
-using UnaryMethodExpressionVecTest =
-    TypeDeterminerTestWithParam<ast::UnaryMethod>;
-TEST_P(UnaryMethodExpressionVecTest, Expr_UnaryMethod_Bool) {
-  auto op = GetParam();
+using Intrinsic_FloatMethod = TypeDeterminerTestWithParam<std::string>;
+TEST_P(Intrinsic_FloatMethod, Vector) {
+  auto name = GetParam();
 
   ast::type::F32Type f32;
   ast::type::VectorType vec3(&f32, 3);
@@ -1441,22 +1509,24 @@
                                              &vec3);
   mod()->AddGlobalVariable(std::move(var));
 
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
 
-  ast::UnaryMethodExpression exp(op, std::move(params));
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
 
   // Register the variable
   EXPECT_TRUE(td()->Determine());
+  EXPECT_TRUE(td()->DetermineResultType(&expr));
 
-  EXPECT_TRUE(td()->DetermineResultType(&exp));
-  ASSERT_NE(exp.result_type(), nullptr);
-  ASSERT_TRUE(exp.result_type()->IsVector());
-  EXPECT_TRUE(exp.result_type()->AsVector()->type()->IsBool());
-  EXPECT_EQ(exp.result_type()->AsVector()->size(), 3u);
+  ASSERT_NE(expr.result_type(), nullptr);
+  ASSERT_TRUE(expr.result_type()->IsVector());
+  EXPECT_TRUE(expr.result_type()->AsVector()->type()->IsBool());
+  EXPECT_EQ(expr.result_type()->AsVector()->size(), 3u);
 }
-TEST_P(UnaryMethodExpressionVecTest, Expr_UnaryMethod_Vec) {
-  auto op = GetParam();
+
+TEST_P(Intrinsic_FloatMethod, Scalar) {
+  auto name = GetParam();
 
   ast::type::F32Type f32;
 
@@ -1464,26 +1534,65 @@
       std::make_unique<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
   mod()->AddGlobalVariable(std::move(var));
 
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
 
-  ast::UnaryMethodExpression exp(op, std::move(params));
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
 
   // Register the variable
   EXPECT_TRUE(td()->Determine());
-
-  EXPECT_TRUE(td()->DetermineResultType(&exp));
-  ASSERT_NE(exp.result_type(), nullptr);
-  EXPECT_TRUE(exp.result_type()->IsBool());
+  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  ASSERT_NE(expr.result_type(), nullptr);
+  EXPECT_TRUE(expr.result_type()->IsBool());
 }
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
-                         UnaryMethodExpressionVecTest,
-                         testing::Values(ast::UnaryMethod::kIsInf,
-                                         ast::UnaryMethod::kIsNan,
-                                         ast::UnaryMethod::kIsFinite,
-                                         ast::UnaryMethod::kIsNormal));
 
-TEST_F(TypeDeterminerTest, Expr_UnaryMethod_Dot) {
+TEST_P(Intrinsic_FloatMethod, MissingParam) {
+  auto name = GetParam();
+
+  ast::type::F32Type f32;
+
+  auto var =
+      std::make_unique<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
+  mod()->AddGlobalVariable(std::move(var));
+
+  ast::ExpressionList call_params;
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
+
+  // Register the variable
+  EXPECT_TRUE(td()->Determine());
+  EXPECT_FALSE(td()->DetermineResultType(&expr));
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for " + name);
+}
+
+TEST_P(Intrinsic_FloatMethod, TooManyParams) {
+  auto name = GetParam();
+
+  ast::type::F32Type f32;
+
+  auto var =
+      std::make_unique<ast::Variable>("my_var", ast::StorageClass::kNone, &f32);
+  mod()->AddGlobalVariable(std::move(var));
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
+
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(name),
+                           std::move(call_params));
+
+  // Register the variable
+  EXPECT_TRUE(td()->Determine());
+  EXPECT_FALSE(td()->DetermineResultType(&expr));
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for " + name);
+}
+INSTANTIATE_TEST_SUITE_P(
+    TypeDeterminerTest,
+    Intrinsic_FloatMethod,
+    testing::Values("is_inf", "is_nan", "is_finite", "is_normal"));
+
+TEST_F(TypeDeterminerTest, Intrinsic_Dot) {
   ast::type::F32Type f32;
   ast::type::VectorType vec3(&f32, 3);
 
@@ -1491,21 +1600,21 @@
                                              &vec3);
   mod()->AddGlobalVariable(std::move(var));
 
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
-  params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("my_var"));
 
-  ast::UnaryMethodExpression exp(ast::UnaryMethod::kDot, std::move(params));
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>("dot"),
+                           std::move(call_params));
 
   // Register the variable
   EXPECT_TRUE(td()->Determine());
-
-  EXPECT_TRUE(td()->DetermineResultType(&exp));
-  ASSERT_NE(exp.result_type(), nullptr);
-  EXPECT_TRUE(exp.result_type()->IsF32());
+  EXPECT_TRUE(td()->DetermineResultType(&expr));
+  ASSERT_NE(expr.result_type(), nullptr);
+  EXPECT_TRUE(expr.result_type()->IsF32());
 }
 
-TEST_F(TypeDeterminerTest, Expr_UnaryMethod_OuterProduct) {
+TEST_F(TypeDeterminerTest, Intrinsic_OuterProduct) {
   ast::type::F32Type f32;
   ast::type::VectorType vec3(&f32, 3);
   ast::type::VectorType vec2(&f32, 2);
@@ -1517,25 +1626,73 @@
   mod()->AddGlobalVariable(std::move(var1));
   mod()->AddGlobalVariable(std::move(var2));
 
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::IdentifierExpression>("v3"));
-  params.push_back(std::make_unique<ast::IdentifierExpression>("v2"));
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("v3"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("v2"));
 
-  ast::UnaryMethodExpression exp(ast::UnaryMethod::kOuterProduct,
-                                 std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("outer_product"),
+      std::move(call_params));
 
   // Register the variable
   EXPECT_TRUE(td()->Determine());
+  EXPECT_TRUE(td()->DetermineResultType(&expr));
 
-  EXPECT_TRUE(td()->DetermineResultType(&exp));
-  ASSERT_NE(exp.result_type(), nullptr);
-  ASSERT_TRUE(exp.result_type()->IsMatrix());
-  auto* mat = exp.result_type()->AsMatrix();
+  ASSERT_NE(expr.result_type(), nullptr);
+  ASSERT_TRUE(expr.result_type()->IsMatrix());
+
+  auto* mat = expr.result_type()->AsMatrix();
   EXPECT_TRUE(mat->type()->IsF32());
   EXPECT_EQ(mat->rows(), 3u);
   EXPECT_EQ(mat->columns(), 2u);
 }
 
+TEST_F(TypeDeterminerTest, Intrinsic_OuterProduct_TooFewParams) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec3(&f32, 3);
+  ast::type::VectorType vec2(&f32, 2);
+
+  auto var2 =
+      std::make_unique<ast::Variable>("v2", ast::StorageClass::kNone, &vec2);
+  mod()->AddGlobalVariable(std::move(var2));
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("v2"));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("outer_product"),
+      std::move(call_params));
+
+  // Register the variable
+  EXPECT_TRUE(td()->Determine());
+  EXPECT_FALSE(td()->DetermineResultType(&expr));
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for outer_product");
+}
+
+TEST_F(TypeDeterminerTest, Intrinsic_OuterProduct_TooManyParams) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec3(&f32, 3);
+  ast::type::VectorType vec2(&f32, 2);
+
+  auto var2 =
+      std::make_unique<ast::Variable>("v2", ast::StorageClass::kNone, &vec2);
+  mod()->AddGlobalVariable(std::move(var2));
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("v2"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("v2"));
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("v2"));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("outer_product"),
+      std::move(call_params));
+
+  // Register the variable
+  EXPECT_TRUE(td()->Determine());
+  EXPECT_FALSE(td()->DetermineResultType(&expr));
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for outer_product");
+}
+
 using UnaryOpExpressionTest = TypeDeterminerTestWithParam<ast::UnaryOp>;
 TEST_P(UnaryOpExpressionTest, Expr_UnaryOp) {
   auto op = GetParam();
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index e3ddceb..c83759e 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -54,8 +54,6 @@
 #include "src/ast/type/vector_type.h"
 #include "src/ast/type_constructor_expression.h"
 #include "src/ast/uint_literal.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/ast/unary_method_expression.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/unless_statement.h"
 #include "src/ast/variable_decl_statement.h"
@@ -166,12 +164,6 @@
   if (expr->IsMemberAccessor()) {
     return EmitMemberAccessor(expr->AsMemberAccessor());
   }
-  if (expr->IsUnaryDerivative()) {
-    return EmitUnaryDerivative(expr->AsUnaryDerivative());
-  }
-  if (expr->IsUnaryMethod()) {
-    return EmitUnaryMethod(expr->AsUnaryMethod());
-  }
   if (expr->IsUnaryOp()) {
     return EmitUnaryOp(expr->AsUnaryOp());
   }
@@ -595,78 +587,6 @@
   return true;
 }
 
-bool GeneratorImpl::EmitUnaryDerivative(ast::UnaryDerivativeExpression* expr) {
-  switch (expr->op()) {
-    case ast::UnaryDerivative::kDpdx:
-      out_ << "dpdx";
-      break;
-    case ast::UnaryDerivative::kDpdy:
-      out_ << "dpdy";
-      break;
-    case ast::UnaryDerivative::kFwidth:
-      out_ << "fwidth";
-      break;
-  }
-
-  if (expr->modifier() != ast::DerivativeModifier::kNone) {
-    out_ << "<" << expr->modifier() << ">";
-  }
-
-  out_ << "(";
-
-  if (!EmitExpression(expr->param())) {
-    return false;
-  }
-
-  out_ << ")";
-  return true;
-}
-
-bool GeneratorImpl::EmitUnaryMethod(ast::UnaryMethodExpression* expr) {
-  switch (expr->op()) {
-    case ast::UnaryMethod::kAny:
-      out_ << "any";
-      break;
-    case ast::UnaryMethod::kAll:
-      out_ << "all";
-      break;
-    case ast::UnaryMethod::kIsNan:
-      out_ << "is_nan";
-      break;
-    case ast::UnaryMethod::kIsInf:
-      out_ << "is_inf";
-      break;
-    case ast::UnaryMethod::kIsFinite:
-      out_ << "is_finite";
-      break;
-    case ast::UnaryMethod::kIsNormal:
-      out_ << "is_normal";
-      break;
-    case ast::UnaryMethod::kDot:
-      out_ << "dot";
-      break;
-    case ast::UnaryMethod::kOuterProduct:
-      out_ << "outer_product";
-      break;
-  }
-  out_ << "(";
-
-  bool first = true;
-  for (const auto& param : expr->params()) {
-    if (!first) {
-      out_ << ", ";
-    }
-    first = false;
-
-    if (!EmitExpression(param.get())) {
-      return false;
-    }
-  }
-  out_ << ")";
-
-  return true;
-}
-
 bool GeneratorImpl::EmitUnaryOp(ast::UnaryOpExpression* expr) {
   switch (expr->op()) {
     case ast::UnaryOp::kNot:
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index d9282bd..b262a6d 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -190,14 +190,6 @@
   /// @param expr the type constructor expression
   /// @returns true if the constructor is emitted
   bool EmitTypeConstructor(ast::TypeConstructorExpression* expr);
-  /// Handles a unary derivative expression
-  /// @param expr the expression to emit
-  /// @returns true if the expression was emitted
-  bool EmitUnaryDerivative(ast::UnaryDerivativeExpression* expr);
-  /// Handles a unary method expression
-  /// @param expr the expression to emit
-  /// @returns true if the expression was emitted
-  bool EmitUnaryMethod(ast::UnaryMethodExpression* expr);
   /// Handles a unary op expression
   /// @param expr the expression to emit
   /// @returns true if the expression was emitted
diff --git a/src/writer/wgsl/generator_impl_unary_derivative_test.cc b/src/writer/wgsl/generator_impl_unary_derivative_test.cc
deleted file mode 100644
index b7af8dc..0000000
--- a/src/writer/wgsl/generator_impl_unary_derivative_test.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <memory>
-
-#include "gtest/gtest.h"
-#include "src/ast/identifier_expression.h"
-#include "src/ast/unary_derivative_expression.h"
-#include "src/writer/wgsl/generator_impl.h"
-
-namespace tint {
-namespace writer {
-namespace wgsl {
-namespace {
-
-struct UnaryDerivativeData {
-  const char* name;
-  ast::UnaryDerivative derivative;
-};
-inline std::ostream& operator<<(std::ostream& out, UnaryDerivativeData data) {
-  out << data.derivative;
-  return out;
-}
-using UnaryDerivativeTest = testing::TestWithParam<UnaryDerivativeData>;
-TEST_P(UnaryDerivativeTest, Emit_ModifierNone) {
-  auto params = GetParam();
-
-  auto expr = std::make_unique<ast::IdentifierExpression>("expr");
-
-  ast::UnaryDerivativeExpression derivative(
-      params.derivative, ast::DerivativeModifier::kNone, std::move(expr));
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitExpression(&derivative)) << g.error();
-  EXPECT_EQ(g.result(), std::string(params.name) + "(expr)");
-}
-TEST_P(UnaryDerivativeTest, Emit_ModifierFine) {
-  auto params = GetParam();
-
-  auto expr = std::make_unique<ast::IdentifierExpression>("expr");
-
-  ast::UnaryDerivativeExpression derivative(
-      params.derivative, ast::DerivativeModifier::kFine, std::move(expr));
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitExpression(&derivative)) << g.error();
-  EXPECT_EQ(g.result(), std::string(params.name) + "<fine>(expr)");
-}
-TEST_P(UnaryDerivativeTest, Emit_ModifierCoarse) {
-  auto params = GetParam();
-
-  auto expr = std::make_unique<ast::IdentifierExpression>("expr");
-
-  ast::UnaryDerivativeExpression derivative(
-      params.derivative, ast::DerivativeModifier::kCoarse, std::move(expr));
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitExpression(&derivative)) << g.error();
-  EXPECT_EQ(g.result(), std::string(params.name) + "<coarse>(expr)");
-}
-INSTANTIATE_TEST_SUITE_P(
-    GeneratorImplTest,
-    UnaryDerivativeTest,
-    testing::Values(UnaryDerivativeData{"dpdx", ast::UnaryDerivative::kDpdx},
-                    UnaryDerivativeData{"dpdy", ast::UnaryDerivative::kDpdy},
-                    UnaryDerivativeData{"fwidth",
-                                        ast::UnaryDerivative::kFwidth}));
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace writer
-}  // namespace tint
diff --git a/src/writer/wgsl/generator_impl_unary_method_test.cc b/src/writer/wgsl/generator_impl_unary_method_test.cc
deleted file mode 100644
index 83eff87..0000000
--- a/src/writer/wgsl/generator_impl_unary_method_test.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <memory>
-
-#include "gtest/gtest.h"
-#include "src/ast/identifier_expression.h"
-#include "src/ast/unary_method_expression.h"
-#include "src/writer/wgsl/generator_impl.h"
-
-namespace tint {
-namespace writer {
-namespace wgsl {
-namespace {
-
-struct UnaryMethodData {
-  const char* name;
-  ast::UnaryMethod method;
-};
-inline std::ostream& operator<<(std::ostream& out, UnaryMethodData data) {
-  out << data.method;
-  return out;
-}
-using UnaryMethodTest = testing::TestWithParam<UnaryMethodData>;
-TEST_P(UnaryMethodTest, Emit) {
-  auto params = GetParam();
-
-  auto expr = std::make_unique<ast::IdentifierExpression>("expr");
-  ast::ExpressionList ops;
-  ops.push_back(std::move(expr));
-
-  ast::UnaryMethodExpression method(params.method, std::move(ops));
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitExpression(&method)) << g.error();
-  EXPECT_EQ(g.result(), std::string(params.name) + "(expr)");
-}
-INSTANTIATE_TEST_SUITE_P(
-    GeneratorImplTest,
-    UnaryMethodTest,
-    testing::Values(UnaryMethodData{"any", ast::UnaryMethod::kAny},
-                    UnaryMethodData{"all", ast::UnaryMethod::kAll},
-                    UnaryMethodData{"is_nan", ast::UnaryMethod::kIsNan},
-                    UnaryMethodData{"is_finite", ast::UnaryMethod::kIsFinite},
-                    UnaryMethodData{"is_normal", ast::UnaryMethod::kIsNormal}));
-
-using UnaryMethodTest_MultiParam = testing::TestWithParam<UnaryMethodData>;
-TEST_P(UnaryMethodTest_MultiParam, Emit) {
-  auto params = GetParam();
-
-  auto expr1 = std::make_unique<ast::IdentifierExpression>("expr1");
-  auto expr2 = std::make_unique<ast::IdentifierExpression>("expr2");
-  ast::ExpressionList ops;
-  ops.push_back(std::move(expr1));
-  ops.push_back(std::move(expr2));
-
-  ast::UnaryMethodExpression method(params.method, std::move(ops));
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitExpression(&method)) << g.error();
-  EXPECT_EQ(g.result(), std::string(params.name) + "(expr1, expr2)");
-}
-INSTANTIATE_TEST_SUITE_P(
-    GeneratorImplTest,
-    UnaryMethodTest_MultiParam,
-    testing::Values(UnaryMethodData{"dot", ast::UnaryMethod::kDot},
-                    UnaryMethodData{"outer_product",
-                                    ast::UnaryMethod::kOuterProduct}));
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace writer
-}  // namespace tint