Rename 'decoration' to 'attribute'

This matches (mostly) the term used in the WGSL spec.

Change-Id: Ie148a1ca8498698e91fdbb60e1aeb0d509b80630
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/78786
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/transform/add_spirv_block_decoration.cc b/src/transform/add_spirv_block_attribute.cc
similarity index 72%
rename from src/transform/add_spirv_block_decoration.cc
rename to src/transform/add_spirv_block_attribute.cc
index 1bb9f85..24a1903 100644
--- a/src/transform/add_spirv_block_decoration.cc
+++ b/src/transform/add_spirv_block_attribute.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/transform/add_spirv_block_decoration.h"
+#include "src/transform/add_spirv_block_attribute.h"
 
 #include <unordered_map>
 #include <unordered_set>
@@ -22,20 +22,20 @@
 #include "src/sem/variable.h"
 #include "src/utils/map.h"
 
-TINT_INSTANTIATE_TYPEINFO(tint::transform::AddSpirvBlockDecoration);
+TINT_INSTANTIATE_TYPEINFO(tint::transform::AddSpirvBlockAttribute);
 TINT_INSTANTIATE_TYPEINFO(
-    tint::transform::AddSpirvBlockDecoration::SpirvBlockDecoration);
+    tint::transform::AddSpirvBlockAttribute::SpirvBlockAttribute);
 
 namespace tint {
 namespace transform {
 
-AddSpirvBlockDecoration::AddSpirvBlockDecoration() = default;
+AddSpirvBlockAttribute::AddSpirvBlockAttribute() = default;
 
-AddSpirvBlockDecoration::~AddSpirvBlockDecoration() = default;
+AddSpirvBlockAttribute::~AddSpirvBlockAttribute() = default;
 
-void AddSpirvBlockDecoration::Run(CloneContext& ctx,
-                                  const DataMap&,
-                                  DataMap&) const {
+void AddSpirvBlockAttribute::Run(CloneContext& ctx,
+                                 const DataMap&,
+                                 DataMap&) const {
   auto& sem = ctx.src->Sem();
 
   // Collect the set of structs that are nested in other types.
@@ -75,13 +75,13 @@
       // need to wrap it first.
       auto* wrapper = utils::GetOrCreate(wrapper_structs, ty, [&]() {
         auto* block =
-            ctx.dst->ASTNodes().Create<SpirvBlockDecoration>(ctx.dst->ID());
+            ctx.dst->ASTNodes().Create<SpirvBlockAttribute>(ctx.dst->ID());
         auto wrapper_name = ctx.src->Symbols().NameFor(var->symbol) + "_block";
         auto* ret = ctx.dst->create<ast::Struct>(
             ctx.dst->Symbols().New(wrapper_name),
             ast::StructMemberList{
                 ctx.dst->Member(kMemberName, CreateASTTypeFor(ctx, ty))},
-            ast::DecorationList{block});
+            ast::AttributeList{block});
         ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), var, ret);
         return ret;
       });
@@ -95,30 +95,27 @@
             ctx.dst->MemberAccessor(ctx.Clone(var->symbol), kMemberName));
       }
     } else {
-      // Add a block decoration to this struct directly.
+      // Add a block attribute to this struct directly.
       auto* block =
-          ctx.dst->ASTNodes().Create<SpirvBlockDecoration>(ctx.dst->ID());
-      ctx.InsertFront(str->Declaration()->decorations, block);
+          ctx.dst->ASTNodes().Create<SpirvBlockAttribute>(ctx.dst->ID());
+      ctx.InsertFront(str->Declaration()->attributes, block);
     }
   }
 
   ctx.Clone();
 }
 
-AddSpirvBlockDecoration::SpirvBlockDecoration::SpirvBlockDecoration(
-    ProgramID pid)
+AddSpirvBlockAttribute::SpirvBlockAttribute::SpirvBlockAttribute(ProgramID pid)
     : Base(pid) {}
-AddSpirvBlockDecoration::SpirvBlockDecoration::~SpirvBlockDecoration() =
-    default;
-std::string AddSpirvBlockDecoration::SpirvBlockDecoration::InternalName()
-    const {
+AddSpirvBlockAttribute::SpirvBlockAttribute::~SpirvBlockAttribute() = default;
+std::string AddSpirvBlockAttribute::SpirvBlockAttribute::InternalName() const {
   return "spirv_block";
 }
 
-const AddSpirvBlockDecoration::SpirvBlockDecoration*
-AddSpirvBlockDecoration::SpirvBlockDecoration::Clone(CloneContext* ctx) const {
+const AddSpirvBlockAttribute::SpirvBlockAttribute*
+AddSpirvBlockAttribute::SpirvBlockAttribute::Clone(CloneContext* ctx) const {
   return ctx->dst->ASTNodes()
-      .Create<AddSpirvBlockDecoration::SpirvBlockDecoration>(ctx->dst->ID());
+      .Create<AddSpirvBlockAttribute::SpirvBlockAttribute>(ctx->dst->ID());
 }
 
 }  // namespace transform
diff --git a/src/transform/add_spirv_block_decoration.h b/src/transform/add_spirv_block_attribute.h
similarity index 68%
rename from src/transform/add_spirv_block_decoration.h
rename to src/transform/add_spirv_block_attribute.h
index 047c55e..eb9ac82 100644
--- a/src/transform/add_spirv_block_decoration.h
+++ b/src/transform/add_spirv_block_attribute.h
@@ -12,51 +12,51 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SRC_TRANSFORM_ADD_SPIRV_BLOCK_DECORATION_H_
-#define SRC_TRANSFORM_ADD_SPIRV_BLOCK_DECORATION_H_
+#ifndef SRC_TRANSFORM_ADD_SPIRV_BLOCK_ATTRIBUTE_H_
+#define SRC_TRANSFORM_ADD_SPIRV_BLOCK_ATTRIBUTE_H_
 
 #include <string>
 
-#include "src/ast/internal_decoration.h"
+#include "src/ast/internal_attribute.h"
 #include "src/transform/transform.h"
 
 namespace tint {
 namespace transform {
 
-/// AddSpirvBlockDecoration is a transform that adds an
+/// AddSpirvBlockAttribute is a transform that adds an
 /// `@internal(spirv_block)` attribute to any structure that is used as the
 /// store type of a buffer. If that structure is nested inside another structure
 /// or an array, then it is wrapped inside another structure which gets the
 /// `@internal(spirv_block)` attribute instead.
-class AddSpirvBlockDecoration
-    : public Castable<AddSpirvBlockDecoration, Transform> {
+class AddSpirvBlockAttribute
+    : public Castable<AddSpirvBlockAttribute, Transform> {
  public:
-  /// SpirvBlockDecoration is an InternalDecoration that is used to decorate a
-  // structure that needs a SPIR-V block decoration.
-  class SpirvBlockDecoration
-      : public Castable<SpirvBlockDecoration, ast::InternalDecoration> {
+  /// SpirvBlockAttribute is an InternalAttribute that is used to decorate a
+  // structure that needs a SPIR-V block attribute.
+  class SpirvBlockAttribute
+      : public Castable<SpirvBlockAttribute, ast::InternalAttribute> {
    public:
     /// Constructor
     /// @param program_id the identifier of the program that owns this node
-    explicit SpirvBlockDecoration(ProgramID program_id);
+    explicit SpirvBlockAttribute(ProgramID program_id);
     /// Destructor
-    ~SpirvBlockDecoration() override;
+    ~SpirvBlockAttribute() override;
 
-    /// @return a short description of the internal decoration which will be
+    /// @return a short description of the internal attribute which will be
     /// displayed as `@internal(<name>)`
     std::string InternalName() const override;
 
     /// Performs a deep clone of this object using the CloneContext `ctx`.
     /// @param ctx the clone context
     /// @return the newly cloned object
-    const SpirvBlockDecoration* Clone(CloneContext* ctx) const override;
+    const SpirvBlockAttribute* Clone(CloneContext* ctx) const override;
   };
 
   /// Constructor
-  AddSpirvBlockDecoration();
+  AddSpirvBlockAttribute();
 
   /// Destructor
-  ~AddSpirvBlockDecoration() override;
+  ~AddSpirvBlockAttribute() override;
 
  protected:
   /// Runs the transform using the CloneContext built for transforming a
@@ -73,4 +73,4 @@
 }  // namespace transform
 }  // namespace tint
 
-#endif  // SRC_TRANSFORM_ADD_SPIRV_BLOCK_DECORATION_H_
+#endif  // SRC_TRANSFORM_ADD_SPIRV_BLOCK_ATTRIBUTE_H_
diff --git a/src/transform/add_spirv_block_decoration_test.cc b/src/transform/add_spirv_block_attribute_test.cc
similarity index 80%
rename from src/transform/add_spirv_block_decoration_test.cc
rename to src/transform/add_spirv_block_attribute_test.cc
index 3a8455f..21dbe19 100644
--- a/src/transform/add_spirv_block_decoration_test.cc
+++ b/src/transform/add_spirv_block_attribute_test.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/transform/add_spirv_block_decoration.h"
+#include "src/transform/add_spirv_block_attribute.h"
 
 #include <memory>
 #include <utility>
@@ -23,18 +23,18 @@
 namespace transform {
 namespace {
 
-using AddSpirvBlockDecorationTest = TransformTest;
+using AddSpirvBlockAttributeTest = TransformTest;
 
-TEST_F(AddSpirvBlockDecorationTest, EmptyModule) {
+TEST_F(AddSpirvBlockAttributeTest, EmptyModule) {
   auto* src = "";
   auto* expect = "";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, Noop_UsedForPrivateVar) {
+TEST_F(AddSpirvBlockAttributeTest, Noop_UsedForPrivateVar) {
   auto* src = R"(
 struct S {
   f : f32;
@@ -49,12 +49,12 @@
 )";
   auto* expect = src;
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, Noop_UsedForShaderIO) {
+TEST_F(AddSpirvBlockAttributeTest, Noop_UsedForShaderIO) {
   auto* src = R"(
 struct S {
   @location(0)
@@ -68,12 +68,12 @@
 )";
   auto* expect = src;
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, BasicScalar) {
+TEST_F(AddSpirvBlockAttributeTest, BasicScalar) {
   auto* src = R"(
 @group(0) @binding(0)
 var<uniform> u : f32;
@@ -97,12 +97,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, BasicArray) {
+TEST_F(AddSpirvBlockAttributeTest, BasicArray) {
   auto* src = R"(
 @group(0) @binding(0)
 var<uniform> u : array<vec4<f32>, 4u>;
@@ -126,12 +126,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, BasicArray_Alias) {
+TEST_F(AddSpirvBlockAttributeTest, BasicArray_Alias) {
   auto* src = R"(
 type Numbers = array<vec4<f32>, 4u>;
 
@@ -159,12 +159,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, BasicStruct) {
+TEST_F(AddSpirvBlockAttributeTest, BasicStruct) {
   auto* src = R"(
 struct S {
   f : f32;
@@ -192,12 +192,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, Nested_OuterBuffer_InnerNotBuffer) {
+TEST_F(AddSpirvBlockAttributeTest, Nested_OuterBuffer_InnerNotBuffer) {
   auto* src = R"(
 struct Inner {
   f : f32;
@@ -233,12 +233,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, Nested_OuterBuffer_InnerBuffer) {
+TEST_F(AddSpirvBlockAttributeTest, Nested_OuterBuffer_InnerBuffer) {
   auto* src = R"(
 struct Inner {
   f : f32;
@@ -286,12 +286,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, Nested_OuterNotBuffer_InnerBuffer) {
+TEST_F(AddSpirvBlockAttributeTest, Nested_OuterNotBuffer_InnerBuffer) {
   auto* src = R"(
 struct Inner {
   f : f32;
@@ -337,12 +337,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, Nested_InnerUsedForMultipleBuffers) {
+TEST_F(AddSpirvBlockAttributeTest, Nested_InnerUsedForMultipleBuffers) {
   auto* src = R"(
 struct Inner {
   f : f32;
@@ -397,12 +397,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, StructInArray) {
+TEST_F(AddSpirvBlockAttributeTest, StructInArray) {
   auto* src = R"(
 struct S {
   f : f32;
@@ -436,12 +436,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, StructInArray_MultipleBuffers) {
+TEST_F(AddSpirvBlockAttributeTest, StructInArray_MultipleBuffers) {
   auto* src = R"(
 struct S {
   f : f32;
@@ -482,12 +482,12 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(AddSpirvBlockDecorationTest, Aliases_Nested_OuterBuffer_InnerBuffer) {
+TEST_F(AddSpirvBlockAttributeTest, Aliases_Nested_OuterBuffer_InnerBuffer) {
   auto* src = R"(
 struct Inner {
   f : f32;
@@ -543,7 +543,7 @@
 }
 )";
 
-  auto got = Run<AddSpirvBlockDecoration>(src);
+  auto got = Run<AddSpirvBlockAttribute>(src);
 
   EXPECT_EQ(expect, str(got));
 }
diff --git a/src/transform/array_length_from_uniform.cc b/src/transform/array_length_from_uniform.cc
index 80edb88..5ab30a7 100644
--- a/src/transform/array_length_from_uniform.cc
+++ b/src/transform/array_length_from_uniform.cc
@@ -154,9 +154,9 @@
       buffer_size_ubo = ctx.dst->Global(
           ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct),
           ast::StorageClass::kUniform,
-          ast::DecorationList{
-              ctx.dst->create<ast::GroupDecoration>(cfg->ubo_binding.group),
-              ctx.dst->create<ast::BindingDecoration>(
+          ast::AttributeList{
+              ctx.dst->create<ast::GroupAttribute>(cfg->ubo_binding.group),
+              ctx.dst->create<ast::BindingAttribute>(
                   cfg->ubo_binding.binding)});
     }
     return buffer_size_ubo;
diff --git a/src/transform/binding_remapper.cc b/src/transform/binding_remapper.cc
index 178753b..91c8ebb 100644
--- a/src/transform/binding_remapper.cc
+++ b/src/transform/binding_remapper.cc
@@ -18,7 +18,7 @@
 #include <unordered_set>
 #include <utility>
 
-#include "src/ast/disable_validation_decoration.h"
+#include "src/ast/disable_validation_attribute.h"
 #include "src/program_builder.h"
 #include "src/sem/function.h"
 #include "src/sem/variable.h"
@@ -62,12 +62,12 @@
   }
 
   // A set of post-remapped binding points that need to be decorated with a
-  // DisableValidationDecoration to disable binding-point-collision validation
-  std::unordered_set<sem::BindingPoint> add_collision_deco;
+  // DisableValidationAttribute to disable binding-point-collision validation
+  std::unordered_set<sem::BindingPoint> add_collision_attr;
 
   if (remappings->allow_collisions) {
     // Scan for binding point collisions generated by this transform.
-    // Populate all collisions in the `add_collision_deco` set.
+    // Populate all collisions in the `add_collision_attr` set.
     for (auto* func_ast : ctx.src->AST().Functions()) {
       if (!func_ast->IsEntryPoint()) {
         continue;
@@ -83,12 +83,12 @@
             // Remapped
             BindingPoint to = bp_it->second;
             if (binding_point_counts[to]++) {
-              add_collision_deco.emplace(to);
+              add_collision_attr.emplace(to);
             }
           } else {
             // No remapping
             if (binding_point_counts[from]++) {
-              add_collision_deco.emplace(from);
+              add_collision_attr.emplace(from);
             }
           }
         }
@@ -105,14 +105,14 @@
       // The binding point after remapping
       BindingPoint bp = from;
 
-      // Replace any group or binding decorations.
+      // Replace any group or binding attributes.
       // Note: This has to be performed *before* remapping access controls, as
-      // `ctx.Clone(var->decorations)` depend on these replacements.
+      // `ctx.Clone(var->attributes)` depend on these replacements.
       auto bp_it = remappings->binding_points.find(from);
       if (bp_it != remappings->binding_points.end()) {
         BindingPoint to = bp_it->second;
-        auto* new_group = ctx.dst->create<ast::GroupDecoration>(to.group);
-        auto* new_binding = ctx.dst->create<ast::BindingDecoration>(to.binding);
+        auto* new_group = ctx.dst->create<ast::GroupAttribute>(to.group);
+        auto* new_binding = ctx.dst->create<ast::BindingAttribute>(to.binding);
 
         ctx.Replace(binding_point.group, new_group);
         ctx.Replace(binding_point.binding, new_binding);
@@ -143,16 +143,15 @@
         auto* new_var = ctx.dst->create<ast::Variable>(
             ctx.Clone(var->source), ctx.Clone(var->symbol),
             var->declared_storage_class, ac, inner_ty, var->is_const,
-            ctx.Clone(var->constructor), ctx.Clone(var->decorations));
+            ctx.Clone(var->constructor), ctx.Clone(var->attributes));
         ctx.Replace(var, new_var);
       }
 
-      // Add `DisableValidationDecoration`s if required
-      if (add_collision_deco.count(bp)) {
-        auto* decoration =
+      // Add `DisableValidationAttribute`s if required
+      if (add_collision_attr.count(bp)) {
+        auto* attribute =
             ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision);
-        ctx.InsertBefore(var->decorations, *var->decorations.begin(),
-                         decoration);
+        ctx.InsertBefore(var->attributes, *var->attributes.begin(), attribute);
       }
     }
   }
diff --git a/src/transform/binding_remapper_test.cc b/src/transform/binding_remapper_test.cc
index 3145f13..2a8d7eb 100644
--- a/src/transform/binding_remapper_test.cc
+++ b/src/transform/binding_remapper_test.cc
@@ -179,7 +179,7 @@
 }
 
 // TODO(crbug.com/676): Possibly enable if the spec allows for access
-// decorations in type aliases. If not, just remove.
+// attributes in type aliases. If not, just remove.
 TEST_F(BindingRemapperTest, DISABLED_RemapAccessControlsWithAliases) {
   auto* src = R"(
 struct S {
diff --git a/src/transform/calculate_array_length.cc b/src/transform/calculate_array_length.cc
index b365f06..92604a1 100644
--- a/src/transform/calculate_array_length.cc
+++ b/src/transform/calculate_array_length.cc
@@ -18,7 +18,7 @@
 #include <utility>
 
 #include "src/ast/call_statement.h"
-#include "src/ast/disable_validation_decoration.h"
+#include "src/ast/disable_validation_attribute.h"
 #include "src/program_builder.h"
 #include "src/sem/block_statement.h"
 #include "src/sem/call.h"
@@ -109,16 +109,16 @@
               ctx.dst->create<ast::Variable>(
                   ctx.dst->Sym("buffer"), ast::StorageClass::kStorage,
                   ast::Access::kUndefined, type, true, nullptr,
-                  ast::DecorationList{disable_validation}),
+                  ast::AttributeList{disable_validation}),
               ctx.dst->Param("result",
                              ctx.dst->ty.pointer(ctx.dst->ty.u32(),
                                                  ast::StorageClass::kFunction)),
           },
           ctx.dst->ty.void_(), nullptr,
-          ast::DecorationList{
+          ast::AttributeList{
               ctx.dst->ASTNodes().Create<BufferSizeIntrinsic>(ctx.dst->ID()),
           },
-          ast::DecorationList{});
+          ast::AttributeList{});
       // Insert the intrinsic function after the structure or array structure
       // element type. TODO(crbug.com/tint/1266): Once we allow out-of-order
       // declarations, this can be simplified.
diff --git a/src/transform/calculate_array_length.h b/src/transform/calculate_array_length.h
index 1ae8474..525a2e8 100644
--- a/src/transform/calculate_array_length.h
+++ b/src/transform/calculate_array_length.h
@@ -17,7 +17,7 @@
 
 #include <string>
 
-#include "src/ast/internal_decoration.h"
+#include "src/ast/internal_attribute.h"
 #include "src/transform/transform.h"
 
 namespace tint {
@@ -34,10 +34,10 @@
 /// * SimplifyPointers
 class CalculateArrayLength : public Castable<CalculateArrayLength, Transform> {
  public:
-  /// BufferSizeIntrinsic is an InternalDecoration that's applied to intrinsic
+  /// BufferSizeIntrinsic is an InternalAttribute that's applied to intrinsic
   /// functions used to obtain the runtime size of a storage buffer.
   class BufferSizeIntrinsic
-      : public Castable<BufferSizeIntrinsic, ast::InternalDecoration> {
+      : public Castable<BufferSizeIntrinsic, ast::InternalAttribute> {
    public:
     /// Constructor
     /// @param program_id the identifier of the program that owns this node
diff --git a/src/transform/canonicalize_entry_point_io.cc b/src/transform/canonicalize_entry_point_io.cc
index 974e456..e00c1a0 100644
--- a/src/transform/canonicalize_entry_point_io.cc
+++ b/src/transform/canonicalize_entry_point_io.cc
@@ -20,7 +20,7 @@
 #include <utility>
 #include <vector>
 
-#include "src/ast/disable_validation_decoration.h"
+#include "src/ast/disable_validation_attribute.h"
 #include "src/program_builder.h"
 #include "src/sem/function.h"
 #include "src/transform/unshadow.h"
@@ -41,10 +41,10 @@
 // those with builtin attributes.
 bool StructMemberComparator(const ast::StructMember* a,
                             const ast::StructMember* b) {
-  auto* a_loc = ast::GetDecoration<ast::LocationDecoration>(a->decorations);
-  auto* b_loc = ast::GetDecoration<ast::LocationDecoration>(b->decorations);
-  auto* a_blt = ast::GetDecoration<ast::BuiltinDecoration>(a->decorations);
-  auto* b_blt = ast::GetDecoration<ast::BuiltinDecoration>(b->decorations);
+  auto* a_loc = ast::GetAttribute<ast::LocationAttribute>(a->attributes);
+  auto* b_loc = ast::GetAttribute<ast::LocationAttribute>(b->attributes);
+  auto* a_blt = ast::GetAttribute<ast::BuiltinAttribute>(a->attributes);
+  auto* b_blt = ast::GetAttribute<ast::BuiltinAttribute>(b->attributes);
   if (a_loc) {
     if (!b_loc) {
       // `a` has location attribute and `b` does not: `a` goes first.
@@ -62,15 +62,15 @@
   }
 }
 
-// Returns true if `deco` is a shader IO decoration.
-bool IsShaderIODecoration(const ast::Decoration* deco) {
-  return deco->IsAnyOf<ast::BuiltinDecoration, ast::InterpolateDecoration,
-                       ast::InvariantDecoration, ast::LocationDecoration>();
+// Returns true if `attr` is a shader IO attribute.
+bool IsShaderIOAttribute(const ast::Attribute* attr) {
+  return attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute,
+                       ast::InvariantAttribute, ast::LocationAttribute>();
 }
 
-// Returns true if `decos` contains a `sample_mask` builtin.
-bool HasSampleMask(const ast::DecorationList& decos) {
-  auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(decos);
+// Returns true if `attrs` contains a `sample_mask` builtin.
+bool HasSampleMask(const ast::AttributeList& attrs) {
+  auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attrs);
   return builtin && builtin->builtin == ast::Builtin::kSampleMask;
 }
 
@@ -85,7 +85,7 @@
     /// The type of the output value.
     const ast::Type* type;
     /// The shader IO attributes.
-    ast::DecorationList attributes;
+    ast::AttributeList attributes;
     /// The value itself.
     const ast::Expression* value;
   };
@@ -128,20 +128,20 @@
         func_ast(function),
         func_sem(ctx.src->Sem().Get(function)) {}
 
-  /// Clones the shader IO decorations from `src`.
-  /// @param src the decorations to clone
-  /// @param do_interpolate whether to clone InterpolateDecoration
-  /// @return the cloned decorations
-  ast::DecorationList CloneShaderIOAttributes(const ast::DecorationList& src,
-                                              bool do_interpolate) {
-    ast::DecorationList new_decorations;
-    for (auto* deco : src) {
-      if (IsShaderIODecoration(deco) &&
-          (do_interpolate || !deco->Is<ast::InterpolateDecoration>())) {
-        new_decorations.push_back(ctx.Clone(deco));
+  /// Clones the shader IO attributes from `src`.
+  /// @param src the attributes to clone
+  /// @param do_interpolate whether to clone InterpolateAttribute
+  /// @return the cloned attributes
+  ast::AttributeList CloneShaderIOAttributes(const ast::AttributeList& src,
+                                             bool do_interpolate) {
+    ast::AttributeList new_attributes;
+    for (auto* attr : src) {
+      if (IsShaderIOAttribute(attr) &&
+          (do_interpolate || !attr->Is<ast::InterpolateAttribute>())) {
+        new_attributes.push_back(ctx.Clone(attr));
       }
     }
-    return new_decorations;
+    return new_attributes;
   }
 
   /// Create or return a symbol for the wrapper function's struct parameter.
@@ -160,7 +160,7 @@
   /// @returns an expression which evaluates to the value of the shader input
   const ast::Expression* AddInput(std::string name,
                                   const sem::Type* type,
-                                  ast::DecorationList attributes) {
+                                  ast::AttributeList attributes) {
     auto* ast_type = CreateASTTypeFor(ctx, type);
     if (cfg.shader_style == ShaderStyle::kSpirv ||
         cfg.shader_style == ShaderStyle::kGlsl) {
@@ -169,8 +169,8 @@
       // TODO(crbug.com/tint/1224): Remove this once a flat interpolation
       // attribute is required for integers.
       if (type->is_integer_scalar_or_vector() &&
-          ast::HasDecoration<ast::LocationDecoration>(attributes) &&
-          !ast::HasDecoration<ast::InterpolateDecoration>(attributes) &&
+          ast::HasAttribute<ast::LocationAttribute>(attributes) &&
+          !ast::HasAttribute<ast::InterpolateAttribute>(attributes) &&
           func_ast->PipelineStage() == ast::PipelineStage::kFragment) {
         attributes.push_back(ctx.dst->Interpolate(
             ast::InterpolationType::kFlat, ast::InterpolationSampling::kNone));
@@ -182,7 +182,7 @@
 
       // In GLSL, if it's a builtin, override the name with the
       // corresponding gl_ builtin name
-      auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(attributes);
+      auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
       if (cfg.shader_style == ShaderStyle::kGlsl && builtin) {
         name = GLSLBuiltinToString(builtin->builtin, func_ast->PipelineStage());
       }
@@ -207,7 +207,7 @@
                       std::move(attributes));
       return value;
     } else if (cfg.shader_style == ShaderStyle::kMsl &&
-               ast::HasDecoration<ast::BuiltinDecoration>(attributes)) {
+               ast::HasAttribute<ast::BuiltinAttribute>(attributes)) {
       // If this input is a builtin and we are targeting MSL, then add it to the
       // parameter list and pass it directly to the inner function.
       Symbol symbol = input_names.emplace(name).second
@@ -234,7 +234,7 @@
   /// @param value the value of the shader output
   void AddOutput(std::string name,
                  const sem::Type* type,
-                 ast::DecorationList attributes,
+                 ast::AttributeList attributes,
                  const ast::Expression* value) {
     // Vulkan requires that integer user-defined vertex outputs are
     // always decorated with `Flat`.
@@ -242,8 +242,8 @@
     // attribute is required for integers.
     if (cfg.shader_style == ShaderStyle::kSpirv &&
         type->is_integer_scalar_or_vector() &&
-        ast::HasDecoration<ast::LocationDecoration>(attributes) &&
-        !ast::HasDecoration<ast::InterpolateDecoration>(attributes) &&
+        ast::HasAttribute<ast::LocationAttribute>(attributes) &&
+        !ast::HasAttribute<ast::InterpolateAttribute>(attributes) &&
         func_ast->PipelineStage() == ast::PipelineStage::kVertex) {
       attributes.push_back(ctx.dst->Interpolate(
           ast::InterpolationType::kFlat, ast::InterpolationSampling::kNone));
@@ -251,7 +251,7 @@
 
     // In GLSL, if it's a builtin, override the name with the
     // corresponding gl_ builtin name
-    auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(attributes);
+    auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
     if (cfg.shader_style == ShaderStyle::kGlsl && builtin) {
       name = GLSLBuiltinToString(builtin->builtin, func_ast->PipelineStage());
     }
@@ -272,11 +272,11 @@
   void ProcessNonStructParameter(const sem::Parameter* param) {
     // Remove the shader IO attributes from the inner function parameter, and
     // attach them to the new object instead.
-    ast::DecorationList attributes;
-    for (auto* deco : param->Declaration()->decorations) {
-      if (IsShaderIODecoration(deco)) {
-        ctx.Remove(param->Declaration()->decorations, deco);
-        attributes.push_back(ctx.Clone(deco));
+    ast::AttributeList attributes;
+    for (auto* attr : param->Declaration()->attributes) {
+      if (IsShaderIOAttribute(attr)) {
+        ctx.Remove(param->Declaration()->attributes, attr);
+        attributes.push_back(ctx.Clone(attr));
       }
     }
 
@@ -305,14 +305,14 @@
       auto* member_ast = member->Declaration();
       auto name = ctx.src->Symbols().NameFor(member_ast->symbol);
 
-      // In GLSL, do not add interpolation decorations on vertex input
+      // In GLSL, do not add interpolation attributes on vertex input
       bool do_interpolate = true;
       if (cfg.shader_style == ShaderStyle::kGlsl &&
           func_ast->PipelineStage() == ast::PipelineStage::kVertex) {
         do_interpolate = false;
       }
       auto attributes =
-          CloneShaderIOAttributes(member_ast->decorations, do_interpolate);
+          CloneShaderIOAttributes(member_ast->attributes, do_interpolate);
       auto* input_expr = AddInput(name, member->Type(), std::move(attributes));
       inner_struct_values.push_back(input_expr);
     }
@@ -330,7 +330,7 @@
   void ProcessReturnType(const sem::Type* inner_ret_type,
                          Symbol original_result) {
     bool do_interpolate = true;
-    // In GLSL, do not add interpolation decorations on fragment output
+    // In GLSL, do not add interpolation attributes on fragment output
     if (cfg.shader_style == ShaderStyle::kGlsl &&
         func_ast->PipelineStage() == ast::PipelineStage::kFragment) {
       do_interpolate = false;
@@ -345,7 +345,7 @@
         auto* member_ast = member->Declaration();
         auto name = ctx.src->Symbols().NameFor(member_ast->symbol);
         auto attributes =
-            CloneShaderIOAttributes(member_ast->decorations, do_interpolate);
+            CloneShaderIOAttributes(member_ast->attributes, do_interpolate);
 
         // Extract the original structure member.
         AddOutput(name, member->Type(), std::move(attributes),
@@ -353,7 +353,7 @@
       }
     } else if (!inner_ret_type->Is<sem::Void>()) {
       auto attributes = CloneShaderIOAttributes(
-          func_ast->return_type_decorations, do_interpolate);
+          func_ast->return_type_attributes, do_interpolate);
 
       // Propagate the non-struct return value as is.
       AddOutput("value", func_sem->ReturnType(), std::move(attributes),
@@ -406,7 +406,7 @@
     // Create the new struct type.
     auto struct_name = ctx.dst->Sym();
     auto* in_struct = ctx.dst->create<ast::Struct>(
-        struct_name, wrapper_struct_param_members, ast::DecorationList{});
+        struct_name, wrapper_struct_param_members, ast::AttributeList{});
     ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), func_ast, in_struct);
 
     // Create a new function parameter using this struct type.
@@ -446,7 +446,7 @@
 
     // Create the new struct type.
     auto* out_struct = ctx.dst->create<ast::Struct>(
-        ctx.dst->Sym(), wrapper_struct_output_members, ast::DecorationList{});
+        ctx.dst->Sym(), wrapper_struct_output_members, ast::AttributeList{});
     ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), func_ast, out_struct);
 
     // Create the output struct object, assign its members, and return it.
@@ -464,7 +464,7 @@
   void CreateGlobalOutputVariables() {
     for (auto& outval : wrapper_output_values) {
       // Disable validation for use of the `output` storage class.
-      ast::DecorationList attributes = std::move(outval.attributes);
+      ast::AttributeList attributes = std::move(outval.attributes);
       attributes.push_back(
           ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
 
@@ -505,7 +505,7 @@
     auto* inner_function = ctx.dst->create<ast::Function>(
         inner_name, ctx.Clone(func_ast->params),
         ctx.Clone(func_ast->return_type), ctx.Clone(func_ast->body),
-        ast::DecorationList{}, ast::DecorationList{});
+        ast::AttributeList{}, ast::AttributeList{});
     ctx.Replace(func_ast, inner_function);
 
     // Call the function.
@@ -617,8 +617,8 @@
 
     auto* wrapper_func = ctx.dst->create<ast::Function>(
         name, wrapper_ep_parameters, wrapper_ret_type(),
-        ctx.dst->Block(wrapper_body), ctx.Clone(func_ast->decorations),
-        ast::DecorationList{});
+        ctx.dst->Block(wrapper_body), ctx.Clone(func_ast->attributes),
+        ast::AttributeList{});
     ctx.InsertAfter(ctx.src->AST().GlobalDeclarations(), func_ast,
                     wrapper_func);
   }
@@ -699,9 +699,9 @@
   for (auto* ty : ctx.src->AST().TypeDecls()) {
     if (auto* struct_ty = ty->As<ast::Struct>()) {
       for (auto* member : struct_ty->members) {
-        for (auto* deco : member->decorations) {
-          if (IsShaderIODecoration(deco)) {
-            ctx.Remove(member->decorations, deco);
+        for (auto* attr : member->attributes) {
+          if (IsShaderIOAttribute(attr)) {
+            ctx.Remove(member->attributes, attr);
           }
         }
       }
diff --git a/src/transform/canonicalize_entry_point_io_test.cc b/src/transform/canonicalize_entry_point_io_test.cc
index 3d89959..38bd91d 100644
--- a/src/transform/canonicalize_entry_point_io_test.cc
+++ b/src/transform/canonicalize_entry_point_io_test.cc
@@ -1306,7 +1306,7 @@
   EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutDecorations) {
+TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes) {
   auto* src = R"(
 struct FragmentInput {
   @size(16) @location(1) value : f32;
diff --git a/src/transform/combine_samplers.cc b/src/transform/combine_samplers.cc
index ac48cdb..6d202f6 100644
--- a/src/transform/combine_samplers.cc
+++ b/src/transform/combine_samplers.cc
@@ -75,14 +75,14 @@
   /// resolver, but are then ignored and removed by the GLSL writer.
   const ast::Variable* placeholder_samplers_[2] = {};
 
-  /// Group and binding decorations used by all combined sampler globals.
+  /// Group and binding attributes used by all combined sampler globals.
   /// Group 0 and binding 0 are used, with collisions disabled.
-  /// @returns the newly-created decoration list
-  ast::DecorationList Decorations() const {
-    auto decorations = ctx.dst->GroupAndBinding(0, 0);
-    decorations.push_back(
+  /// @returns the newly-created attribute list
+  ast::AttributeList Attributes() const {
+    auto attributes = ctx.dst->GroupAndBinding(0, 0);
+    attributes.push_back(
         ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision));
-    return decorations;
+    return attributes;
   }
 
   /// Constructor
@@ -113,7 +113,7 @@
     }
     const ast::Type* type = CreateASTTypeFor(ctx, texture_var->Type());
     Symbol symbol = ctx.dst->Symbols().New(name);
-    return ctx.dst->Global(symbol, type, Decorations());
+    return ctx.dst->Global(symbol, type, Attributes());
   }
 
   /// Creates placeholder global sampler variables.
@@ -125,7 +125,7 @@
                            ? "placeholder_comparison_sampler"
                            : "placeholder_sampler";
     Symbol symbol = ctx.dst->Symbols().New(name);
-    return ctx.dst->Global(symbol, type, Decorations());
+    return ctx.dst->Global(symbol, type, Attributes());
   }
 
   /// Performs the transformation
@@ -141,9 +141,9 @@
       } else if (auto binding_point = var->BindingPoint()) {
         if (binding_point.group->value == 0 &&
             binding_point.binding->value == 0) {
-          auto* decoration =
+          auto* attribute =
               ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision);
-          ctx.InsertFront(var->decorations, decoration);
+          ctx.InsertFront(var->attributes, attribute);
         }
       }
     }
@@ -194,11 +194,11 @@
         auto symbol = ctx.Clone(src->symbol);
         auto* return_type = ctx.Clone(src->return_type);
         auto* body = ctx.Clone(src->body);
-        auto decorations = ctx.Clone(src->decorations);
-        auto return_type_decorations = ctx.Clone(src->return_type_decorations);
+        auto attributes = ctx.Clone(src->attributes);
+        auto return_type_attributes = ctx.Clone(src->return_type_attributes);
         return ctx.dst->create<ast::Function>(
-            symbol, params, return_type, body, std::move(decorations),
-            std::move(return_type_decorations));
+            symbol, params, return_type, body, std::move(attributes),
+            std::move(return_type_attributes));
       }
       return nullptr;
     });
diff --git a/src/transform/decompose_memory_access.cc b/src/transform/decompose_memory_access.cc
index 5ebef49..21eb28b 100644
--- a/src/transform/decompose_memory_access.cc
+++ b/src/transform/decompose_memory_access.cc
@@ -22,7 +22,7 @@
 
 #include "src/ast/assignment_statement.h"
 #include "src/ast/call_statement.h"
-#include "src/ast/disable_validation_decoration.h"
+#include "src/ast/disable_validation_attribute.h"
 #include "src/ast/type_name.h"
 #include "src/ast/unary_op.h"
 #include "src/block_allocator.h"
@@ -191,7 +191,7 @@
   return false;
 }
 
-/// @returns a DecomposeMemoryAccess::Intrinsic decoration that can be applied
+/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied
 /// to a stub function to load the type `ty`.
 DecomposeMemoryAccess::Intrinsic* IntrinsicLoadFor(
     ProgramBuilder* builder,
@@ -206,7 +206,7 @@
       type);
 }
 
-/// @returns a DecomposeMemoryAccess::Intrinsic decoration that can be applied
+/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied
 /// to a stub function to store the type `ty`.
 DecomposeMemoryAccess::Intrinsic* IntrinsicStoreFor(
     ProgramBuilder* builder,
@@ -221,7 +221,7 @@
       storage_class, type);
 }
 
-/// @returns a DecomposeMemoryAccess::Intrinsic decoration that can be applied
+/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied
 /// to a stub function for the atomic op and the type `ty`.
 DecomposeMemoryAccess::Intrinsic* IntrinsicAtomicFor(ProgramBuilder* builder,
                                                      sem::IntrinsicType ity,
@@ -458,7 +458,7 @@
               b.create<ast::Variable>(b.Sym("buffer"), storage_class,
                                       var_user->Variable()->Access(),
                                       buf_ast_ty, true, nullptr,
-                                      ast::DecorationList{disable_validation}),
+                                      ast::AttributeList{disable_validation}),
               b.Param("offset", b.ty.u32()),
           };
 
@@ -469,11 +469,11 @@
             auto* el_ast_ty = CreateASTTypeFor(ctx, el_ty);
             auto* func = b.create<ast::Function>(
                 name, params, el_ast_ty, nullptr,
-                ast::DecorationList{
+                ast::AttributeList{
                     intrinsic,
                     b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
                 },
-                ast::DecorationList{});
+                ast::AttributeList{});
             b.AST().AddFunction(func);
           } else if (auto* arr_ty = el_ty->As<sem::Array>()) {
             // fn load_func(buf : buf_ty, offset : u32) -> array<T, N> {
@@ -557,7 +557,7 @@
               b.create<ast::Variable>(b.Sym("buffer"), storage_class,
                                       var_user->Variable()->Access(),
                                       buf_ast_ty, true, nullptr,
-                                      ast::DecorationList{disable_validation}),
+                                      ast::AttributeList{disable_validation}),
               b.Param("offset", b.ty.u32()),
               b.Param("value", el_ast_ty),
           };
@@ -568,11 +568,11 @@
                   IntrinsicStoreFor(ctx.dst, storage_class, el_ty)) {
             auto* func = b.create<ast::Function>(
                 name, params, b.ty.void_(), nullptr,
-                ast::DecorationList{
+                ast::AttributeList{
                     intrinsic,
                     b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
                 },
-                ast::DecorationList{});
+                ast::AttributeList{});
             b.AST().AddFunction(func);
           } else {
             ast::StatementList body;
@@ -657,7 +657,7 @@
           b.create<ast::Variable>(b.Sym("buffer"), ast::StorageClass::kStorage,
                                   var_user->Variable()->Access(), buf_ast_ty,
                                   true, nullptr,
-                                  ast::DecorationList{disable_validation}),
+                                  ast::AttributeList{disable_validation}),
           b.Param("offset", b.ty.u32()),
       };
 
@@ -678,11 +678,11 @@
       auto* ret_ty = CreateASTTypeFor(ctx, intrinsic->ReturnType());
       auto* func = b.create<ast::Function>(
           b.Sym(), params, ret_ty, nullptr,
-          ast::DecorationList{
+          ast::AttributeList{
               atomic,
               b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
           },
-          ast::DecorationList{});
+          ast::AttributeList{});
 
       b.AST().AddFunction(func);
       return func->symbol;
diff --git a/src/transform/decompose_memory_access.h b/src/transform/decompose_memory_access.h
index 2139f4b..ecf4be5 100644
--- a/src/transform/decompose_memory_access.h
+++ b/src/transform/decompose_memory_access.h
@@ -17,7 +17,7 @@
 
 #include <string>
 
-#include "src/ast/internal_decoration.h"
+#include "src/ast/internal_attribute.h"
 #include "src/transform/transform.h"
 
 namespace tint {
@@ -33,11 +33,11 @@
 class DecomposeMemoryAccess
     : public Castable<DecomposeMemoryAccess, Transform> {
  public:
-  /// Intrinsic is an InternalDecoration that's used to decorate a stub function
+  /// Intrinsic is an InternalAttribute that's used to decorate a stub function
   /// so that the HLSL transforms this into calls to
   /// `[RW]ByteAddressBuffer.Load[N]()` or `[RW]ByteAddressBuffer.Store[N]()`,
   /// with a possible cast.
-  class Intrinsic : public Castable<Intrinsic, ast::InternalDecoration> {
+  class Intrinsic : public Castable<Intrinsic, ast::InternalAttribute> {
    public:
     /// Intrinsic op
     enum class Op {
@@ -81,7 +81,7 @@
     /// Destructor
     ~Intrinsic() override;
 
-    /// @return a short description of the internal decoration which will be
+    /// @return a short description of the internal attribute which will be
     /// displayed as `@internal(<name>)`
     std::string InternalName() const override;
 
diff --git a/src/transform/decompose_strided_matrix.cc b/src/transform/decompose_strided_matrix.cc
index 669efc8..bfa9d96 100644
--- a/src/transform/decompose_strided_matrix.cc
+++ b/src/transform/decompose_strided_matrix.cc
@@ -84,12 +84,12 @@
         if (!matrix) {
           continue;
         }
-        auto* deco = ast::GetDecoration<ast::StrideDecoration>(
-            member->Declaration()->decorations);
-        if (!deco) {
+        auto* attr = ast::GetAttribute<ast::StrideAttribute>(
+            member->Declaration()->attributes);
+        if (!attr) {
           continue;
         }
-        uint32_t stride = deco->stride;
+        uint32_t stride = attr->stride;
         if (matrix->ColumnStride() == stride) {
           continue;
         }
diff --git a/src/transform/decompose_strided_matrix.h b/src/transform/decompose_strided_matrix.h
index e753577..ea7b25d 100644
--- a/src/transform/decompose_strided_matrix.h
+++ b/src/transform/decompose_strided_matrix.h
@@ -21,10 +21,10 @@
 namespace transform {
 
 /// DecomposeStridedMatrix transforms replaces matrix members of storage or
-/// uniform buffer structures, that have a [[stride]] decoration, into an array
+/// uniform buffer structures, that have a [[stride]] attribute, into an array
 /// of N column vectors.
 /// This transform is used by the SPIR-V reader to handle the SPIR-V
-/// MatrixStride decoration.
+/// MatrixStride attribute.
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
diff --git a/src/transform/decompose_strided_matrix_test.cc b/src/transform/decompose_strided_matrix_test.cc
index fe3db50..e2af90c 100644
--- a/src/transform/decompose_strided_matrix_test.cc
+++ b/src/transform/decompose_strided_matrix_test.cc
@@ -18,7 +18,7 @@
 #include <utility>
 #include <vector>
 
-#include "src/ast/disable_validation_decoration.h"
+#include "src/ast/disable_validation_attribute.h"
 #include "src/program_builder.h"
 #include "src/transform/simplify_pointers.h"
 #include "src/transform/test_helper.h"
@@ -57,7 +57,7 @@
 TEST_F(DecomposeStridedMatrixTest, ReadUniformMatrix) {
   // struct S {
   //   @offset(16) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<uniform> s : S;
@@ -73,9 +73,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(16),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(16),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform,
@@ -118,7 +118,7 @@
 TEST_F(DecomposeStridedMatrixTest, ReadUniformColumn) {
   // struct S {
   //   @offset(16) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<uniform> s : S;
@@ -134,9 +134,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(16),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(16),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform,
@@ -175,7 +175,7 @@
 TEST_F(DecomposeStridedMatrixTest, ReadUniformMatrix_DefaultStride) {
   // struct S {
   //   @offset(16) @stride(8)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<uniform> s : S;
@@ -191,9 +191,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(16),
-                  b.create<ast::StrideDecoration>(8),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(16),
+                  b.create<ast::StrideAttribute>(8),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform,
@@ -233,7 +233,7 @@
 TEST_F(DecomposeStridedMatrixTest, ReadStorageMatrix) {
   // struct S {
   //   @offset(8) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<storage, read_write> s : S;
@@ -249,9 +249,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(8),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(8),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage,
@@ -294,7 +294,7 @@
 TEST_F(DecomposeStridedMatrixTest, ReadStorageColumn) {
   // struct S {
   //   @offset(16) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<storage, read_write> s : S;
@@ -310,9 +310,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(16),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(16),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage,
@@ -351,7 +351,7 @@
 TEST_F(DecomposeStridedMatrixTest, WriteStorageMatrix) {
   // struct S {
   //   @offset(8) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<storage, read_write> s : S;
@@ -367,9 +367,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(8),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(8),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage,
@@ -413,7 +413,7 @@
 TEST_F(DecomposeStridedMatrixTest, WriteStorageColumn) {
   // struct S {
   //   @offset(8) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<storage, read_write> s : S;
@@ -429,9 +429,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(8),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(8),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage,
@@ -470,7 +470,7 @@
 TEST_F(DecomposeStridedMatrixTest, ReadWriteViaPointerLets) {
   // struct S {
   //   @offset(8) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // @group(0) @binding(0) var<storage, read_write> s : S;
@@ -492,9 +492,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(8),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(8),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage,
@@ -554,7 +554,7 @@
 TEST_F(DecomposeStridedMatrixTest, ReadPrivateMatrix) {
   // struct S {
   //   @offset(8) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // var<private> s : S;
@@ -570,9 +570,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(8),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(8),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kPrivate);
@@ -611,7 +611,7 @@
 TEST_F(DecomposeStridedMatrixTest, WritePrivateMatrix) {
   // struct S {
   //   @offset(8) @stride(32)
-  //   @internal(ignore_stride_decoration)
+  //   @internal(ignore_stride_attribute)
   //   m : mat2x2<f32>;
   // };
   // var<private> s : S;
@@ -627,9 +627,9 @@
           b.Member(
               "m", b.ty.mat2x2<f32>(),
               {
-                  b.create<ast::StructMemberOffsetDecoration>(8),
-                  b.create<ast::StrideDecoration>(32),
-                  b.Disable(ast::DisabledValidation::kIgnoreStrideDecoration),
+                  b.create<ast::StructMemberOffsetAttribute>(8),
+                  b.create<ast::StrideAttribute>(32),
+                  b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
               }),
       });
   b.Global("s", b.ty.Of(S), ast::StorageClass::kPrivate);
diff --git a/src/transform/external_texture_transform.cc b/src/transform/external_texture_transform.cc
index 1d71260..d24149e 100644
--- a/src/transform/external_texture_transform.cc
+++ b/src/transform/external_texture_transform.cc
@@ -115,11 +115,11 @@
         auto clonedSrc = ctx.Clone(var->source);
         auto clonedSym = ctx.Clone(var->symbol);
         auto* clonedConstructor = ctx.Clone(var->constructor);
-        auto clonedDecorations = ctx.Clone(var->decorations);
+        auto clonedAttributes = ctx.Clone(var->attributes);
         auto* newVar = ctx.dst->create<ast::Variable>(
             clonedSrc, clonedSym, var->declared_storage_class,
             var->declared_access, newType, var->is_const, clonedConstructor,
-            clonedDecorations);
+            clonedAttributes);
 
         ctx.Replace(var, newVar);
       }
diff --git a/src/transform/first_index_offset.cc b/src/transform/first_index_offset.cc
index 9719013..e6a069d 100644
--- a/src/transform/first_index_offset.cc
+++ b/src/transform/first_index_offset.cc
@@ -88,9 +88,9 @@
   // parameters) or structure member accesses.
   for (auto* node : ctx.src->ASTNodes().Objects()) {
     if (auto* var = node->As<ast::Variable>()) {
-      for (auto* dec : var->decorations) {
-        if (auto* builtin_dec = dec->As<ast::BuiltinDecoration>()) {
-          ast::Builtin builtin = builtin_dec->builtin;
+      for (auto* attr : var->attributes) {
+        if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
+          ast::Builtin builtin = builtin_attr->builtin;
           if (builtin == ast::Builtin::kVertexIndex) {
             auto* sem_var = ctx.src->Sem().Get(var);
             builtin_vars.emplace(sem_var, kFirstVertexName);
@@ -105,9 +105,9 @@
       }
     }
     if (auto* member = node->As<ast::StructMember>()) {
-      for (auto* dec : member->decorations) {
-        if (auto* builtin_dec = dec->As<ast::BuiltinDecoration>()) {
-          ast::Builtin builtin = builtin_dec->builtin;
+      for (auto* attr : member->attributes) {
+        if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
+          ast::Builtin builtin = builtin_attr->builtin;
           if (builtin == ast::Builtin::kVertexIndex) {
             auto* sem_mem = ctx.src->Sem().Get(member);
             builtin_members.emplace(sem_mem, kFirstVertexName);
@@ -147,9 +147,9 @@
     Symbol buffer_name = ctx.dst->Sym();
     ctx.dst->Global(buffer_name, ctx.dst->ty.Of(struct_),
                     ast::StorageClass::kUniform, nullptr,
-                    ast::DecorationList{
-                        ctx.dst->create<ast::BindingDecoration>(ub_binding),
-                        ctx.dst->create<ast::GroupDecoration>(ub_group),
+                    ast::AttributeList{
+                        ctx.dst->create<ast::BindingAttribute>(ub_binding),
+                        ctx.dst->create<ast::GroupAttribute>(ub_group),
                     });
 
     // Fix up all references to the builtins with the offsets
diff --git a/src/transform/glsl.cc b/src/transform/glsl.cc
index dde44e6..eeedf6a 100644
--- a/src/transform/glsl.cc
+++ b/src/transform/glsl.cc
@@ -18,7 +18,7 @@
 
 #include "src/program_builder.h"
 #include "src/transform/add_empty_entry_point.h"
-#include "src/transform/add_spirv_block_decoration.h"
+#include "src/transform/add_spirv_block_attribute.h"
 #include "src/transform/binding_remapper.h"
 #include "src/transform/calculate_array_length.h"
 #include "src/transform/canonicalize_entry_point_io.h"
@@ -94,7 +94,7 @@
 
   manager.Add<PadArrayElements>();
   manager.Add<AddEmptyEntryPoint>();
-  manager.Add<AddSpirvBlockDecoration>();
+  manager.Add<AddSpirvBlockAttribute>();
 
   data.Add<CanonicalizeEntryPointIO::Config>(
       CanonicalizeEntryPointIO::ShaderStyle::kGlsl);
diff --git a/src/transform/module_scope_var_to_entry_point_param.cc b/src/transform/module_scope_var_to_entry_point_param.cc
index 84bd553..4601f9c 100644
--- a/src/transform/module_scope_var_to_entry_point_param.cc
+++ b/src/transform/module_scope_var_to_entry_point_param.cc
@@ -19,7 +19,7 @@
 #include <utility>
 #include <vector>
 
-#include "src/ast/disable_validation_decoration.h"
+#include "src/ast/disable_validation_attribute.h"
 #include "src/program_builder.h"
 #include "src/sem/call.h"
 #include "src/sem/function.h"
@@ -191,15 +191,15 @@
             // parameter. Disable entry point parameter validation.
             auto* disable_validation =
                 ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter);
-            auto decos = ctx.Clone(var->Declaration()->decorations);
-            decos.push_back(disable_validation);
-            auto* param = ctx.dst->Param(new_var_symbol, store_type(), decos);
+            auto attrs = ctx.Clone(var->Declaration()->attributes);
+            attrs.push_back(disable_validation);
+            auto* param = ctx.dst->Param(new_var_symbol, store_type(), attrs);
             ctx.InsertFront(func_ast->params, param);
           } else if (sc == ast::StorageClass::kStorage ||
                      sc == ast::StorageClass::kUniform) {
             // Variables into the Storage and Uniform storage classes are
             // redeclared as entry point parameters with a pointer type.
-            auto attributes = ctx.Clone(var->Declaration()->decorations);
+            auto attributes = ctx.Clone(var->Declaration()->attributes);
             attributes.push_back(ctx.dst->Disable(
                 ast::DisabledValidation::kEntryPointParameter));
             attributes.push_back(
@@ -258,7 +258,7 @@
             auto* constructor = ctx.Clone(var->Declaration()->constructor);
             auto* local_var =
                 ctx.dst->Var(new_var_symbol, store_type(), sc, constructor,
-                             ast::DecorationList{disable_validation});
+                             ast::AttributeList{disable_validation});
             ctx.InsertFront(func_ast->body->statements,
                             ctx.dst->Decl(local_var));
           }
@@ -266,7 +266,7 @@
           // For a regular function, redeclare the variable as a parameter.
           // Use a pointer for non-handle types.
           auto* param_type = store_type();
-          ast::DecorationList attributes;
+          ast::AttributeList attributes;
           if (!var->Type()->UnwrapRef()->is_handle()) {
             param_type = ctx.dst->ty.pointer(
                 param_type, sc, var->Declaration()->declared_access);
diff --git a/src/transform/multiplanar_external_texture.cc b/src/transform/multiplanar_external_texture.cc
index e88507a..a4d2deb 100644
--- a/src/transform/multiplanar_external_texture.cc
+++ b/src/transform/multiplanar_external_texture.cc
@@ -85,10 +85,10 @@
         return nullptr;
       }
 
-      // If the decorations are empty, then this must be a texture_external
+      // If the attributes are empty, then this must be a texture_external
       // passed as a function parameter. These variables are transformed
       // elsewhere.
-      if (var->decorations.empty()) {
+      if (var->attributes.empty()) {
         return nullptr;
       }
 
@@ -136,12 +136,12 @@
 
       // Replace the original texture_external binding with a texture_2d<f32>
       // binding.
-      ast::DecorationList cloned_decorations = ctx.Clone(var->decorations);
+      ast::AttributeList cloned_attributes = ctx.Clone(var->attributes);
       const ast::Expression* cloned_constructor = ctx.Clone(var->constructor);
 
       return b.Var(syms.plane_0,
                    b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
-                   cloned_constructor, cloned_decorations);
+                   cloned_constructor, cloned_attributes);
     });
 
     // Transform the original textureLoad and textureSampleLevel calls into
diff --git a/src/transform/num_workgroups_from_uniform.cc b/src/transform/num_workgroups_from_uniform.cc
index f2d411d..180d306 100644
--- a/src/transform/num_workgroups_from_uniform.cc
+++ b/src/transform/num_workgroups_from_uniform.cc
@@ -55,8 +55,8 @@
 bool NumWorkgroupsFromUniform::ShouldRun(const Program* program,
                                          const DataMap&) const {
   for (auto* node : program->ASTNodes().Objects()) {
-    if (auto* deco = node->As<ast::BuiltinDecoration>()) {
-      if (deco->builtin == ast::Builtin::kNumWorkgroups) {
+    if (auto* attr = node->As<ast::BuiltinAttribute>()) {
+      if (attr->builtin == ast::Builtin::kNumWorkgroups) {
         return true;
       }
     }
@@ -94,8 +94,8 @@
       }
 
       for (auto* member : str->Members()) {
-        auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(
-            member->Declaration()->decorations);
+        auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(
+            member->Declaration()->attributes);
         if (!builtin || builtin->builtin != ast::Builtin::kNumWorkgroups) {
           continue;
         }
@@ -134,7 +134,7 @@
       num_workgroups_ubo = ctx.dst->Global(
           ctx.dst->Sym(), ctx.dst->ty.Of(num_workgroups_struct),
           ast::StorageClass::kUniform,
-          ast::DecorationList{ctx.dst->GroupAndBinding(
+          ast::AttributeList{ctx.dst->GroupAndBinding(
               cfg->ubo_binding.group, cfg->ubo_binding.binding)});
     }
     return num_workgroups_ubo;
diff --git a/src/transform/pad_array_elements.h b/src/transform/pad_array_elements.h
index fe77978..5b1df32 100644
--- a/src/transform/pad_array_elements.h
+++ b/src/transform/pad_array_elements.h
@@ -23,7 +23,7 @@
 /// PadArrayElements is a transform that replaces array types with an explicit
 /// stride that is larger than the implicit stride, with an array of a new
 /// structure type. This structure holds with a single field of the element
-/// type, decorated with a [[size]] decoration to pad the structure to the
+/// type, decorated with a `@size` attribute to pad the structure to the
 /// required array stride. The new array types have no explicit stride,
 /// structure size is equal to the desired stride.
 /// Array index expressions and constructors are also adjusted to deal with this
diff --git a/src/transform/single_entry_point.cc b/src/transform/single_entry_point.cc
index fd7bac9..914585a 100644
--- a/src/transform/single_entry_point.cc
+++ b/src/transform/single_entry_point.cc
@@ -78,17 +78,17 @@
     } else if (auto* var = decl->As<ast::Variable>()) {
       if (referenced_vars.count(var)) {
         if (var->is_const) {
-          if (auto* deco = ast::GetDecoration<ast::OverrideDecoration>(
-                  var->decorations)) {
+          if (auto* attr =
+                  ast::GetAttribute<ast::OverrideAttribute>(var->attributes)) {
             // It is an overridable constant
-            if (!deco->has_value) {
-              // If the decoration doesn't have numeric ID specified explicitly
-              // Make their ids explicitly assigned in the decoration so that
+            if (!attr->has_value) {
+              // If the attribute doesn't have numeric ID specified explicitly
+              // Make their ids explicitly assigned in the attribute so that
               // they won't be affected by other stripped away constants
               auto* global = sem.Get(var)->As<sem::GlobalVariable>();
               const auto* new_deco =
-                  ctx.dst->Override(deco->source, global->ConstantId());
-              ctx.Replace(deco, new_deco);
+                  ctx.dst->Override(attr->source, global->ConstantId());
+              ctx.Replace(attr, new_deco);
             }
           }
         }
diff --git a/src/transform/transform.cc b/src/transform/transform.cc
index 7ef3100..fb360fd 100644
--- a/src/transform/transform.cc
+++ b/src/transform/transform.cc
@@ -107,14 +107,14 @@
   }
   if (auto* a = ty->As<sem::Array>()) {
     auto* el = CreateASTTypeFor(ctx, a->ElemType());
-    ast::DecorationList decos;
+    ast::AttributeList attrs;
     if (!a->IsStrideImplicit()) {
-      decos.emplace_back(ctx.dst->create<ast::StrideDecoration>(a->Stride()));
+      attrs.emplace_back(ctx.dst->create<ast::StrideAttribute>(a->Stride()));
     }
     if (a->IsRuntimeSized()) {
-      return ctx.dst->ty.array(el, nullptr, std::move(decos));
+      return ctx.dst->ty.array(el, nullptr, std::move(attrs));
     } else {
-      return ctx.dst->ty.array(el, a->Count(), std::move(decos));
+      return ctx.dst->ty.array(el, a->Count(), std::move(attrs));
     }
   }
   if (auto* s = ty->As<sem::Struct>()) {
diff --git a/src/transform/transform_test.cc b/src/transform/transform_test.cc
index 053e6b3..8098e0c 100644
--- a/src/transform/transform_test.cc
+++ b/src/transform/transform_test.cc
@@ -82,7 +82,7 @@
   });
   ASSERT_TRUE(arr->Is<ast::Array>());
   ASSERT_TRUE(arr->As<ast::Array>()->type->Is<ast::F32>());
-  ASSERT_EQ(arr->As<ast::Array>()->decorations.size(), 0u);
+  ASSERT_EQ(arr->As<ast::Array>()->attributes.size(), 0u);
 
   auto* size = arr->As<ast::Array>()->count->As<ast::IntLiteralExpression>();
   ASSERT_NE(size, nullptr);
@@ -95,14 +95,11 @@
   });
   ASSERT_TRUE(arr->Is<ast::Array>());
   ASSERT_TRUE(arr->As<ast::Array>()->type->Is<ast::F32>());
-  ASSERT_EQ(arr->As<ast::Array>()->decorations.size(), 1u);
-  ASSERT_TRUE(
-      arr->As<ast::Array>()->decorations[0]->Is<ast::StrideDecoration>());
-  ASSERT_EQ(arr->As<ast::Array>()
-                ->decorations[0]
-                ->As<ast::StrideDecoration>()
-                ->stride,
-            64u);
+  ASSERT_EQ(arr->As<ast::Array>()->attributes.size(), 1u);
+  ASSERT_TRUE(arr->As<ast::Array>()->attributes[0]->Is<ast::StrideAttribute>());
+  ASSERT_EQ(
+      arr->As<ast::Array>()->attributes[0]->As<ast::StrideAttribute>()->stride,
+      64u);
 
   auto* size = arr->As<ast::Array>()->count->As<ast::IntLiteralExpression>();
   ASSERT_NE(size, nullptr);
diff --git a/src/transform/unshadow.cc b/src/transform/unshadow.cc
index 7d94fd7..4c9a795 100644
--- a/src/transform/unshadow.cc
+++ b/src/transform/unshadow.cc
@@ -54,10 +54,10 @@
       auto source = ctx.Clone(decl->source);
       auto* type = ctx.Clone(decl->type);
       auto* constructor = ctx.Clone(decl->constructor);
-      auto decorations = ctx.Clone(decl->decorations);
+      auto attributes = ctx.Clone(decl->attributes);
       return ctx.dst->create<ast::Variable>(
           source, symbol, decl->declared_storage_class, decl->declared_access,
-          type, decl->is_const, constructor, decorations);
+          type, decl->is_const, constructor, attributes);
     };
 
     ctx.ReplaceAll([&](const ast::Variable* var) -> const ast::Variable* {
diff --git a/src/transform/vertex_pulling.cc b/src/transform/vertex_pulling.cc
index 6305020..a284ce5 100644
--- a/src/transform/vertex_pulling.cc
+++ b/src/transform/vertex_pulling.cc
@@ -265,9 +265,9 @@
       ctx.dst->Global(
           GetVertexBufferName(i), ctx.dst->ty.Of(struct_type),
           ast::StorageClass::kStorage, ast::Access::kRead,
-          ast::DecorationList{
-              ctx.dst->create<ast::BindingDecoration>(i),
-              ctx.dst->create<ast::GroupDecoration>(cfg.pulling_group),
+          ast::AttributeList{
+              ctx.dst->create<ast::BindingAttribute>(i),
+              ctx.dst->create<ast::GroupAttribute>(cfg.pulling_group),
           });
     }
   }
@@ -723,7 +723,7 @@
   void ProcessNonStructParameter(const ast::Function* func,
                                  const ast::Variable* param) {
     if (auto* location =
-            ast::GetDecoration<ast::LocationDecoration>(param->decorations)) {
+            ast::GetAttribute<ast::LocationAttribute>(param->attributes)) {
       // Create a function-scope variable to replace the parameter.
       auto func_var_sym = ctx.Clone(param->symbol);
       auto* func_var_type = ctx.Clone(param->type);
@@ -734,8 +734,8 @@
       info.expr = [this, func_var]() { return ctx.dst->Expr(func_var); };
       info.type = ctx.src->Sem().Get(param)->Type();
       location_info[location->value] = info;
-    } else if (auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(
-                   param->decorations)) {
+    } else if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(
+                   param->attributes)) {
       // Check for existing vertex_index and instance_index builtins.
       if (builtin->builtin == ast::Builtin::kVertexIndex) {
         vertex_index_expr = [this, param]() {
@@ -776,16 +776,16 @@
         return ctx.dst->MemberAccessor(param_sym, member_sym);
       };
 
-      if (auto* location = ast::GetDecoration<ast::LocationDecoration>(
-              member->decorations)) {
+      if (auto* location =
+              ast::GetAttribute<ast::LocationAttribute>(member->attributes)) {
         // Capture mapping from location to struct member.
         LocationInfo info;
         info.expr = member_expr;
         info.type = ctx.src->Sem().Get(member)->Type();
         location_info[location->value] = info;
         has_locations = true;
-      } else if (auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(
-                     member->decorations)) {
+      } else if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(
+                     member->attributes)) {
         // Check for existing vertex_index and instance_index builtins.
         if (builtin->builtin == ast::Builtin::kVertexIndex) {
           vertex_index_expr = member_expr;
@@ -815,9 +815,9 @@
       for (auto* member : members_to_clone) {
         auto member_sym = ctx.Clone(member->symbol);
         auto* member_type = ctx.Clone(member->type);
-        auto member_decos = ctx.Clone(member->decorations);
+        auto member_attrs = ctx.Clone(member->attributes);
         new_members.push_back(
-            ctx.dst->Member(member_sym, member_type, std::move(member_decos)));
+            ctx.dst->Member(member_sym, member_type, std::move(member_attrs)));
       }
       auto* new_struct = ctx.dst->Structure(ctx.dst->Sym(), new_members);
 
@@ -889,11 +889,11 @@
     auto func_sym = ctx.Clone(func->symbol);
     auto* ret_type = ctx.Clone(func->return_type);
     auto* body = ctx.Clone(func->body);
-    auto decos = ctx.Clone(func->decorations);
-    auto ret_decos = ctx.Clone(func->return_type_decorations);
+    auto attrs = ctx.Clone(func->attributes);
+    auto ret_attrs = ctx.Clone(func->return_type_attributes);
     auto* new_func = ctx.dst->create<ast::Function>(
         func->source, func_sym, new_function_parameters, ret_type, body,
-        std::move(decos), std::move(ret_decos));
+        std::move(attrs), std::move(ret_attrs));
     ctx.Replace(func, new_func);
   }
 };
diff --git a/src/transform/vertex_pulling.h b/src/transform/vertex_pulling.h
index bf6b230..2bd45c2 100644
--- a/src/transform/vertex_pulling.h
+++ b/src/transform/vertex_pulling.h
@@ -113,7 +113,7 @@
 
 /// Converts a program to use vertex pulling
 ///
-/// Variables which accept vertex input are var<in> with a location decoration.
+/// Variables which accept vertex input are var<in> with a location attribute.
 /// This transform will convert those to be assigned from storage buffers
 /// instead. The intention is to allow vertex input to rely on a storage buffer
 /// clamping pass for out of bounds reads. We bind the storage buffers as arrays
diff --git a/src/transform/wrap_arrays_in_structs.cc b/src/transform/wrap_arrays_in_structs.cc
index d62c702..938789f 100644
--- a/src/transform/wrap_arrays_in_structs.cc
+++ b/src/transform/wrap_arrays_in_structs.cc
@@ -149,12 +149,12 @@
 
     // Construct the single structure field type
     info.array_type = [=](CloneContext& c) {
-      ast::DecorationList decos;
+      ast::AttributeList attrs;
       if (!array->IsStrideImplicit()) {
-        decos.emplace_back(
-            c.dst->create<ast::StrideDecoration>(array->Stride()));
+        attrs.emplace_back(
+            c.dst->create<ast::StrideAttribute>(array->Stride()));
       }
-      return c.dst->ty.array(el_type(c), array->Count(), std::move(decos));
+      return c.dst->ty.array(el_type(c), array->Count(), std::move(attrs));
     };
 
     // Structure() will create and append the ast::Struct to the
diff --git a/src/transform/zero_init_workgroup_memory.cc b/src/transform/zero_init_workgroup_memory.cc
index f4109f0..6b20fc0 100644
--- a/src/transform/zero_init_workgroup_memory.cc
+++ b/src/transform/zero_init_workgroup_memory.cc
@@ -20,7 +20,7 @@
 #include <utility>
 #include <vector>
 
-#include "src/ast/workgroup_decoration.h"
+#include "src/ast/workgroup_attribute.h"
 #include "src/program_builder.h"
 #include "src/sem/atomic_type.h"
 #include "src/sem/function.h"
@@ -116,7 +116,7 @@
     auto& sem = ctx.src->Sem();
 
     CalculateWorkgroupSize(
-        ast::GetDecoration<ast::WorkgroupDecoration>(fn->decorations));
+        ast::GetAttribute<ast::WorkgroupAttribute>(fn->attributes));
 
     // Generate a list of statements to zero initialize each of the
     // workgroup storage variables used by `fn`. This will populate #statements.
@@ -140,7 +140,7 @@
     std::function<const ast::Expression*()> local_index;
     for (auto* param : fn->params) {
       if (auto* builtin =
-              ast::GetDecoration<ast::BuiltinDecoration>(param->decorations)) {
+              ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
         if (builtin->builtin == ast::Builtin::kLocalInvocationIndex) {
           local_index = [=] { return b.Expr(ctx.Clone(param->symbol)); };
           break;
@@ -149,8 +149,8 @@
 
       if (auto* str = sem.Get(param)->Type()->As<sem::Struct>()) {
         for (auto* member : str->Members()) {
-          if (auto* builtin = ast::GetDecoration<ast::BuiltinDecoration>(
-                  member->Declaration()->decorations)) {
+          if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(
+                  member->Declaration()->attributes)) {
             if (builtin->builtin == ast::Builtin::kLocalInvocationIndex) {
               local_index = [=] {
                 auto* param_expr = b.Expr(ctx.Clone(param->symbol));
@@ -359,12 +359,12 @@
 
   /// CalculateWorkgroupSize initializes the members #workgroup_size_const and
   /// #workgroup_size_expr with the linear workgroup size.
-  /// @param deco the workgroup decoration applied to the entry point function
-  void CalculateWorkgroupSize(const ast::WorkgroupDecoration* deco) {
+  /// @param attr the workgroup attribute applied to the entry point function
+  void CalculateWorkgroupSize(const ast::WorkgroupAttribute* attr) {
     bool is_signed = false;
     workgroup_size_const = 1u;
     workgroup_size_expr = nullptr;
-    for (auto* expr : deco->Values()) {
+    for (auto* expr : attr->Values()) {
       if (!expr) {
         continue;
       }