tint: Clean up Func() usage

Drop the vector typename from the initializer lists. These don't really
provide any significant help in understanding the arguments types, as
the list of element types can be easily inferred.

This is done to simplify the refactor ast::VariableList ->
ast::ParameterList.

Bug: tint:1580
Change-Id: Ibf8564ca9b2bafd2eaa2e4aa54c29be6bbe7a682
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/93600
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
diff --git a/src/tint/ast/function_test.cc b/src/tint/ast/function_test.cc
index bd05fd8..6b79af9 100644
--- a/src/tint/ast/function_test.cc
+++ b/src/tint/ast/function_test.cc
@@ -26,11 +26,10 @@
 using FunctionTest = TestHelper;
 
 TEST_F(FunctionTest, Creation) {
-    VariableList params;
-    params.push_back(Param("var", ty.i32()));
+    VariableList params{Param("var", ty.i32())};
     auto* var = params[0];
 
-    auto* f = Func("func", params, ty.void_(), StatementList{}, AttributeList{});
+    auto* f = Func("func", params, ty.void_(), {});
     EXPECT_EQ(f->symbol, Symbols().Get("func"));
     ASSERT_EQ(f->params.size(), 1u);
     EXPECT_TRUE(f->return_type->Is<ast::Void>());
@@ -38,11 +37,9 @@
 }
 
 TEST_F(FunctionTest, Creation_WithSource) {
-    VariableList params;
-    params.push_back(Param("var", ty.i32()));
+    VariableList params{Param("var", ty.i32())};
 
-    auto* f = Func(Source{Source::Location{20, 2}}, "func", params, ty.void_(), StatementList{},
-                   AttributeList{});
+    auto* f = Func(Source{Source::Location{20, 2}}, "func", params, ty.void_(), {});
     auto src = f->source;
     EXPECT_EQ(src.range.begin.line, 20u);
     EXPECT_EQ(src.range.begin.column, 2u);
@@ -52,7 +49,7 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            b.Func("", VariableList{}, b.ty.void_(), StatementList{}, AttributeList{});
+            b.Func("", {}, b.ty.void_(), {});
         },
         "internal compiler error");
 }
@@ -61,7 +58,7 @@
     EXPECT_FATAL_FAILURE(
         {
             ProgramBuilder b;
-            b.Func("f", VariableList{}, nullptr, StatementList{}, AttributeList{});
+            b.Func("f", {}, nullptr, {});
         },
         "internal compiler error");
 }
@@ -74,7 +71,7 @@
             params.push_back(b.Param("var", b.ty.i32()));
             params.push_back(nullptr);
 
-            b.Func("f", params, b.ty.void_(), StatementList{}, AttributeList{});
+            b.Func("f", params, b.ty.void_(), {});
         },
         "internal compiler error");
 }
@@ -84,7 +81,7 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.Func(b2.Sym("func"), VariableList{}, b1.ty.void_(), StatementList{});
+            b1.Func(b2.Sym("func"), {}, b1.ty.void_(), {});
         },
         "internal compiler error");
 }
@@ -94,8 +91,7 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.Func("func", VariableList{b2.Param("var", b2.ty.i32())}, b1.ty.void_(),
-                    StatementList{});
+            b1.Func("func", {b2.Param("var", b2.ty.i32())}, b1.ty.void_(), {});
         },
         "internal compiler error");
 }
@@ -105,8 +101,8 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.Func("func", VariableList{}, b1.ty.void_(), StatementList{},
-                    AttributeList{
+            b1.Func("func", {}, b1.ty.void_(), {},
+                    {
                         b2.WorkgroupSize(2_i, 4_i, 6_i),
                     });
         },
@@ -118,8 +114,8 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.Func("func", VariableList{}, b1.ty.void_(), StatementList{}, AttributeList{},
-                    AttributeList{
+            b1.Func("func", {}, b1.ty.void_(), {}, {},
+                    {
                         b2.WorkgroupSize(2_i, 4_i, 6_i),
                     });
         },
@@ -133,7 +129,7 @@
             VariableList params;
             params.push_back(b.Var("var", b.ty.i32(), ast::StorageClass::kNone));
 
-            b.Func("f", params, b.ty.void_(), StatementList{}, AttributeList{});
+            b.Func("f", params, b.ty.void_(), {});
         },
         "internal compiler error");
 }
@@ -141,7 +137,7 @@
 using FunctionListTest = TestHelper;
 
 TEST_F(FunctionListTest, FindSymbol) {
-    auto* func = Func("main", VariableList{}, ty.f32(), StatementList{}, ast::AttributeList{});
+    auto* func = Func("main", {}, ty.f32(), {});
     FunctionList list;
     list.Add(func);
     EXPECT_EQ(func, list.Find(Symbols().Register("main")));
@@ -153,12 +149,12 @@
 }
 
 TEST_F(FunctionListTest, FindSymbolStage) {
-    auto* fs = Func("main", VariableList{}, ty.f32(), StatementList{},
-                    ast::AttributeList{
+    auto* fs = Func("main", {}, ty.f32(), {},
+                    {
                         Stage(PipelineStage::kFragment),
                     });
-    auto* vs = Func("main", VariableList{}, ty.f32(), StatementList{},
-                    ast::AttributeList{
+    auto* vs = Func("main", {}, ty.f32(), {},
+                    {
                         Stage(PipelineStage::kVertex),
                     });
     FunctionList list;
@@ -170,8 +166,8 @@
 
 TEST_F(FunctionListTest, FindSymbolStageMissing) {
     FunctionList list;
-    list.Add(Func("main", VariableList{}, ty.f32(), StatementList{},
-                  ast::AttributeList{
+    list.Add(Func("main", {}, ty.f32(), {},
+                  {
                       Stage(PipelineStage::kFragment),
                   }));
     EXPECT_EQ(nullptr, list.Find(Symbols().Register("main"), PipelineStage::kVertex));
@@ -179,8 +175,8 @@
 
 TEST_F(FunctionListTest, HasStage) {
     FunctionList list;
-    list.Add(Func("main", VariableList{}, ty.f32(), StatementList{},
-                  ast::AttributeList{
+    list.Add(Func("main", {}, ty.f32(), {},
+                  {
                       Stage(PipelineStage::kFragment),
                   }));
     EXPECT_TRUE(list.HasStage(PipelineStage::kFragment));
diff --git a/src/tint/ast/module_test.cc b/src/tint/ast/module_test.cc
index 6ac610e..5944f2a 100644
--- a/src/tint/ast/module_test.cc
+++ b/src/tint/ast/module_test.cc
@@ -26,7 +26,7 @@
 }
 
 TEST_F(ModuleTest, LookupFunction) {
-    auto* func = Func("main", VariableList{}, ty.f32(), StatementList{}, ast::AttributeList{});
+    auto* func = Func("main", {}, ty.f32(), {});
 
     Program program(std::move(*this));
     EXPECT_EQ(func, program.AST().Functions().Find(program.Symbols().Get("main")));
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index 986e169..fb09522 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -154,7 +154,7 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, OneEntryPoint) {
-    MakeEmptyBodyFunction("foo", ast::AttributeList{
+    MakeEmptyBodyFunction("foo", {
                                      Stage(ast::PipelineStage::kFragment),
                                  });
 
@@ -172,12 +172,14 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) {
-    MakeEmptyBodyFunction("foo", ast::AttributeList{
+    MakeEmptyBodyFunction("foo", {
                                      Stage(ast::PipelineStage::kFragment),
                                  });
 
-    MakeEmptyBodyFunction(
-        "bar", ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    MakeEmptyBodyFunction("bar", {
+                                     Stage(ast::PipelineStage::kCompute),
+                                     WorkgroupSize(1_i),
+                                 });
 
     // TODO(dsinclair): Update to run the namer transform when available.
 
@@ -198,12 +200,14 @@
 TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) {
     MakeEmptyBodyFunction("func", {});
 
-    MakeCallerBodyFunction(
-        "foo", {"func"},
-        ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    MakeCallerBodyFunction("foo", {"func"},
+                           {
+                               Stage(ast::PipelineStage::kCompute),
+                               WorkgroupSize(1_i),
+                           });
 
     MakeCallerBodyFunction("bar", {"func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -224,8 +228,10 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) {
-    MakeEmptyBodyFunction("foo", ast::AttributeList{Stage(ast::PipelineStage::kCompute),
-                                                    WorkgroupSize(8_i, 2_i, 1_i)});
+    MakeEmptyBodyFunction("foo", {
+                                     Stage(ast::PipelineStage::kCompute),
+                                     WorkgroupSize(8_i, 2_i, 1_i),
+                                 });
 
     Inspector& inspector = Build();
 
@@ -241,8 +247,10 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) {
-    MakeEmptyBodyFunction("foo",
-                          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(8_i, 2_i, 1_i)});
+    MakeEmptyBodyFunction("foo", {
+                                     Stage(ast::PipelineStage::kCompute),
+                                     WorkgroupSize(8_i, 2_i, 1_i),
+                                 });
 
     Inspector& inspector = Build();
 
@@ -261,7 +269,7 @@
     MakeEmptyBodyFunction("func", {});
 
     MakeCallerBodyFunction("foo", {"func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -282,8 +290,16 @@
     std::function<const ast::Type*()> tint_type = GetTypeFunction(component, composition);
 
     auto* in_var = Param("in_var", tint_type(), {Location(0u), Flat()});
-    Func("foo", {in_var}, tint_type(), {Return("in_var")}, {Stage(ast::PipelineStage::kFragment)},
-         {Location(0u)});
+    Func("foo", {in_var}, tint_type(),
+         {
+             Return("in_var"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0u),
+         });
     Inspector& inspector = Build();
 
     auto result = inspector.GetEntryPoints();
@@ -317,8 +333,16 @@
     auto* in_var0 = Param("in_var0", ty.u32(), {Location(0u), Flat()});
     auto* in_var1 = Param("in_var1", ty.u32(), {Location(1u), Flat()});
     auto* in_var4 = Param("in_var4", ty.u32(), {Location(4u), Flat()});
-    Func("foo", {in_var0, in_var1, in_var4}, ty.u32(), {Return("in_var0")},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0u)});
+    Func("foo", {in_var0, in_var1, in_var4}, ty.u32(),
+         {
+             Return("in_var0"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0u),
+         });
     Inspector& inspector = Build();
 
     auto result = inspector.GetEntryPoints();
@@ -352,12 +376,28 @@
 
 TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) {
     auto* in_var_foo = Param("in_var_foo", ty.u32(), {Location(0u), Flat()});
-    Func("foo", {in_var_foo}, ty.u32(), {Return("in_var_foo")},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0u)});
+    Func("foo", {in_var_foo}, ty.u32(),
+         {
+             Return("in_var_foo"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0u),
+         });
 
     auto* in_var_bar = Param("in_var_bar", ty.u32(), {Location(0u), Flat()});
-    Func("bar", {in_var_bar}, ty.u32(), {Return("in_var_bar")},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(1u)});
+    Func("bar", {in_var_bar}, ty.u32(),
+         {
+             Return("in_var_bar"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(1u),
+         });
 
     Inspector& inspector = Build();
 
@@ -396,8 +436,16 @@
 TEST_F(InspectorGetEntryPointTest, BuiltInsNotStageVariables) {
     auto* in_var0 = Param("in_var0", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
     auto* in_var1 = Param("in_var1", ty.f32(), {Location(0u)});
-    Func("foo", {in_var0, in_var1}, ty.f32(), {Return("in_var1")},
-         {Stage(ast::PipelineStage::kFragment)}, {Builtin(ast::Builtin::kFragDepth)});
+    Func("foo", {in_var0, in_var1}, ty.f32(),
+         {
+             Return("in_var1"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Builtin(ast::Builtin::kFragDepth),
+         });
     Inspector& inspector = Build();
 
     auto result = inspector.GetEntryPoints();
@@ -416,8 +464,17 @@
 
 TEST_F(InspectorGetEntryPointTest, InOutStruct) {
     auto* interface = MakeInOutStruct("interface", {{"a", 0u}, {"b", 1u}});
-    Func("foo", {Param("param", ty.Of(interface))}, ty.Of(interface), {Return("param")},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("foo",
+         {
+             Param("param", ty.Of(interface)),
+         },
+         ty.Of(interface),
+         {
+             Return("param"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
     Inspector& inspector = Build();
 
     auto result = inspector.GetEntryPoints();
@@ -448,10 +505,17 @@
 
 TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutSharedStruct) {
     auto* interface = MakeInOutStruct("interface", {{"a", 0u}, {"b", 1u}});
-    Func("foo", {}, ty.Of(interface), {Return(Construct(ty.Of(interface)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("foo", {}, ty.Of(interface),
+         {
+             Return(Construct(ty.Of(interface))),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
     Func("bar", {Param("param", ty.Of(interface))}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
     Inspector& inspector = Build();
 
     auto result = inspector.GetEntryPoints();
@@ -488,9 +552,19 @@
     auto* struct_a = MakeInOutStruct("struct_a", {{"a", 0u}, {"b", 1u}});
     auto* struct_b = MakeInOutStruct("struct_b", {{"a", 2u}});
     Func("foo",
-         {Param("param_a", ty.Of(struct_a)), Param("param_b", ty.Of(struct_b)),
-          Param("param_c", ty.f32(), {Location(3u)}), Param("param_d", ty.f32(), {Location(4u)})},
-         ty.Of(struct_a), {Return("param_a")}, {Stage(ast::PipelineStage::kFragment)});
+         {
+             Param("param_a", ty.Of(struct_a)),
+             Param("param_b", ty.Of(struct_b)),
+             Param("param_c", ty.f32(), {Location(3u)}),
+             Param("param_d", ty.f32(), {Location(4u)}),
+         },
+         ty.Of(struct_a),
+         {
+             Return("param_a"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
     Inspector& inspector = Build();
 
     auto result = inspector.GetEntryPoints();
@@ -533,7 +607,10 @@
 
 TEST_F(InspectorGetEntryPointTest, OverridableConstantUnreferenced) {
     AddOverridableConstantWithoutID("foo", ty.f32(), nullptr);
-    MakeEmptyBodyFunction("ep_func", {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    MakeEmptyBodyFunction("ep_func", {
+                                         Stage(ast::PipelineStage::kCompute),
+                                         WorkgroupSize(1_i),
+                                     });
 
     Inspector& inspector = Build();
 
@@ -546,7 +623,10 @@
 TEST_F(InspectorGetEntryPointTest, OverridableConstantReferencedByEntryPoint) {
     AddOverridableConstantWithoutID("foo", ty.f32(), nullptr);
     MakePlainGlobalReferenceBodyFunction("ep_func", "foo", ty.f32(),
-                                         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                                         {
+                                             Stage(ast::PipelineStage::kCompute),
+                                             WorkgroupSize(1_i),
+                                         });
 
     Inspector& inspector = Build();
 
@@ -561,7 +641,10 @@
     AddOverridableConstantWithoutID("foo", ty.f32(), nullptr);
     MakePlainGlobalReferenceBodyFunction("callee_func", "foo", ty.f32(), {});
     MakeCallerBodyFunction("ep_func", {"callee_func"},
-                           {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                           {
+                               Stage(ast::PipelineStage::kCompute),
+                               WorkgroupSize(1_i),
+                           });
 
     Inspector& inspector = Build();
 
@@ -577,7 +660,10 @@
     AddOverridableConstantWithID("bar", 2, ty.f32(), nullptr);
     MakePlainGlobalReferenceBodyFunction("callee_func", "foo", ty.f32(), {});
     MakeCallerBodyFunction("ep_func", {"callee_func"},
-                           {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                           {
+                               Stage(ast::PipelineStage::kCompute),
+                               WorkgroupSize(1_i),
+                           });
 
     Inspector& inspector = Build();
 
@@ -601,7 +687,10 @@
     MakePlainGlobalReferenceBodyFunction("i32_func", "i32_var", ty.i32(), {});
 
     MakeCallerBodyFunction("ep_func", {"bool_func", "float_func", "u32_func", "i32_func"},
-                           {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                           {
+                               Stage(ast::PipelineStage::kCompute),
+                               WorkgroupSize(1_i),
+                           });
 
     Inspector& inspector = Build();
 
@@ -625,7 +714,10 @@
 TEST_F(InspectorGetEntryPointTest, OverridableConstantInitialized) {
     AddOverridableConstantWithoutID("foo", ty.f32(), Expr(0_f));
     MakePlainGlobalReferenceBodyFunction("ep_func", "foo", ty.f32(),
-                                         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                                         {
+                                             Stage(ast::PipelineStage::kCompute),
+                                             WorkgroupSize(1_i),
+                                         });
 
     Inspector& inspector = Build();
 
@@ -640,7 +732,10 @@
 TEST_F(InspectorGetEntryPointTest, OverridableConstantUninitialized) {
     AddOverridableConstantWithoutID("foo", ty.f32(), nullptr);
     MakePlainGlobalReferenceBodyFunction("ep_func", "foo", ty.f32(),
-                                         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                                         {
+                                             Stage(ast::PipelineStage::kCompute),
+                                             WorkgroupSize(1_i),
+                                         });
 
     Inspector& inspector = Build();
 
@@ -661,7 +756,10 @@
     MakePlainGlobalReferenceBodyFunction("id_func", "foo_id", ty.f32(), {});
 
     MakeCallerBodyFunction("ep_func", {"no_id_func", "id_func"},
-                           {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                           {
+                               Stage(ast::PipelineStage::kCompute),
+                               WorkgroupSize(1_i),
+                           });
 
     Inspector& inspector = Build();
 
@@ -681,7 +779,10 @@
     auto* foo_struct_type = MakeUniformBufferType("foo_type", {ty.i32()});
     AddUniformBuffer("foo_ub", ty.Of(foo_struct_type), 0, 0);
     MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
-    MakeCallerBodyFunction("ep_func", {"ub_func"}, {Stage(ast::PipelineStage::kFragment)});
+    MakeCallerBodyFunction("ep_func", {"ub_func"},
+                           {
+                               Stage(ast::PipelineStage::kFragment),
+                           });
 
     Inspector& inspector = Build();
 
@@ -692,7 +793,9 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, BuiltinNotReferenced) {
-    MakeEmptyBodyFunction("ep_func", {Stage(ast::PipelineStage::kFragment)});
+    MakeEmptyBodyFunction("ep_func", {
+                                         Stage(ast::PipelineStage::kFragment),
+                                     });
 
     Inspector& inspector = Build();
 
@@ -709,7 +812,13 @@
 
 TEST_F(InspectorGetEntryPointTest, InputSampleMaskSimpleReferenced) {
     auto* in_var = Param("in_var", ty.u32(), {Builtin(ast::Builtin::kSampleMask)});
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func", {in_var}, ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -723,9 +832,18 @@
     ast::StructMemberList members;
     members.push_back(Member("inner_position", ty.u32(), {Builtin(ast::Builtin::kSampleMask)}));
     Structure("in_struct", members);
-    auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
 
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.type_name("in_struct"), {}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -736,8 +854,17 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, OutputSampleMaskSimpleReferenced) {
-    auto* in_var = Param("in_var", ty.u32(), {Builtin(ast::Builtin::kSampleMask)});
-    Func("ep_func", {in_var}, ty.u32(), {Return("in_var")}, {Stage(ast::PipelineStage::kFragment)},
+    Func("ep_func",
+         {
+             Param("in_var", ty.u32(), {Builtin(ast::Builtin::kSampleMask)}),
+         },
+         ty.u32(),
+         {
+             Return("in_var"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
          {Builtin(ast::Builtin::kSampleMask)});
 
     Inspector& inspector = Build();
@@ -749,13 +876,19 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, OutputSampleMaskStructReferenced) {
-    ast::StructMemberList members;
-    members.push_back(Member("inner_sample_mask", ty.u32(), {Builtin(ast::Builtin::kSampleMask)}));
-    Structure("out_struct", members);
+    Structure("out_struct",
+              {
+                  Member("inner_sample_mask", ty.u32(), {Builtin(ast::Builtin::kSampleMask)}),
+              });
 
     Func("ep_func", {}, ty.type_name("out_struct"),
-         {Decl(Var("out_var", ty.type_name("out_struct"))), Return("out_var")},
-         {Stage(ast::PipelineStage::kFragment)}, {});
+         {
+             Decl(Var("out_var", ty.type_name("out_struct"))),
+             Return("out_var"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -766,8 +899,17 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, InputPositionSimpleReferenced) {
-    auto* in_var = Param("in_var", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -778,12 +920,22 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, InputPositionStructReferenced) {
-    ast::StructMemberList members;
-    members.push_back(Member("inner_position", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}));
-    Structure("in_struct", members);
-    auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
+    Structure("in_struct",
+              {
+                  Member("inner_position", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
+              });
 
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.type_name("in_struct"), {}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -794,8 +946,17 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, FrontFacingSimpleReferenced) {
-    auto* in_var = Param("in_var", ty.bool_(), {Builtin(ast::Builtin::kFrontFacing)});
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.bool_(), {Builtin(ast::Builtin::kFrontFacing)}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -806,12 +967,22 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, FrontFacingStructReferenced) {
-    ast::StructMemberList members;
-    members.push_back(Member("inner_position", ty.bool_(), {Builtin(ast::Builtin::kFrontFacing)}));
-    Structure("in_struct", members);
-    auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
+    Structure("in_struct",
+              {
+                  Member("inner_position", ty.bool_(), {Builtin(ast::Builtin::kFrontFacing)}),
+              });
 
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.type_name("in_struct"), {}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -822,8 +993,17 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, SampleIndexSimpleReferenced) {
-    auto* in_var = Param("in_var", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -834,12 +1014,22 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, SampleIndexStructReferenced) {
-    ast::StructMemberList members;
-    members.push_back(Member("inner_position", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)}));
-    Structure("in_struct", members);
-    auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
+    Structure("in_struct",
+              {
+                  Member("inner_position", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)}),
+              });
 
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.type_name("in_struct"), {}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -850,8 +1040,14 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, NumWorkgroupsSimpleReferenced) {
-    auto* in_var = Param("in_var", ty.vec3<u32>(), {Builtin(ast::Builtin::kNumWorkgroups)});
-    Func("ep_func", {in_var}, ty.void_(), {Return()},
+    Func("ep_func",
+         {
+             Param("in_var", ty.vec3<u32>(), {Builtin(ast::Builtin::kNumWorkgroups)}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)}, {});
 
     Inspector& inspector = Build();
@@ -863,13 +1059,19 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, NumWorkgroupsStructReferenced) {
-    ast::StructMemberList members;
-    members.push_back(
-        Member("inner_position", ty.vec3<u32>(), {Builtin(ast::Builtin::kNumWorkgroups)}));
-    Structure("in_struct", members);
-    auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
+    Structure("in_struct",
+              {
+                  Member("inner_position", ty.vec3<u32>(), {Builtin(ast::Builtin::kNumWorkgroups)}),
+              });
 
-    Func("ep_func", {in_var}, ty.void_(), {Return()},
+    Func("ep_func",
+         {
+             Param("in_var", ty.type_name("in_struct"), {}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)}, {});
 
     Inspector& inspector = Build();
@@ -881,12 +1083,21 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, ImplicitInterpolate) {
-    ast::StructMemberList members;
-    members.push_back(Member("struct_inner", ty.f32(), {Location(0)}));
-    Structure("in_struct", members);
-    auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
+    Structure("in_struct", {
+                               Member("struct_inner", ty.f32(), {Location(0)}),
+                           });
 
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.type_name("in_struct"), {}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -900,13 +1111,23 @@
 
 TEST_P(InspectorGetEntryPointInterpolateTest, Test) {
     auto& params = GetParam();
-    ast::StructMemberList members;
-    members.push_back(Member("struct_inner", ty.f32(),
-                             {Interpolate(params.in_type, params.in_sampling), Location(0)}));
-    Structure("in_struct", members);
-    auto* in_var = Param("in_var", ty.type_name("in_struct"), {});
+    Structure("in_struct",
+              {
+                  Member("struct_inner", ty.f32(),
+                         {Interpolate(params.in_type, params.in_sampling), Location(0)}),
+              });
 
-    Func("ep_func", {in_var}, ty.void_(), {Return()}, {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("ep_func",
+         {
+             Param("in_var", ty.type_name("in_struct"), {}),
+         },
+         ty.void_(),
+         {
+             Return(),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     Inspector& inspector = Build();
 
@@ -1074,8 +1295,10 @@
 }
 
 TEST_F(InspectorGetStorageSizeTest, Empty) {
-    MakeEmptyBodyFunction(
-        "ep_func", ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    MakeEmptyBodyFunction("ep_func", {
+                                         Stage(ast::PipelineStage::kCompute),
+                                         WorkgroupSize(1_i),
+                                     });
     Inspector& inspector = Build();
     EXPECT_EQ(0u, inspector.GetStorageSize("ep_func"));
 }
@@ -1090,7 +1313,10 @@
              Decl(Let("sb", nullptr, Expr("sb_var"))),
              Decl(Let("rosb", nullptr, Expr("rosb_var"))),
          },
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
     Inspector& inspector = Build();
 
@@ -1111,7 +1337,7 @@
     MakeStructVariableReferenceBodyFunction("rosb_func", "rosb_var", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"ub_func", "sb_func", "rosb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kCompute),
                                WorkgroupSize(1_i),
                            });
@@ -1127,7 +1353,10 @@
          {
              Decl(Let("ub", nullptr, Expr("ub_var"))),
          },
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
     Inspector& inspector = Build();
 
@@ -1141,7 +1370,10 @@
          {
              Decl(Let("ub", nullptr, Expr("ub_var"))),
          },
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
     Inspector& inspector = Build();
 
@@ -1150,7 +1382,7 @@
 
 TEST_F(InspectorGetResourceBindingsTest, Empty) {
     MakeCallerBodyFunction("ep_func", {},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1199,7 +1431,7 @@
     MakeCallerBodyFunction(
         "ep_func",
         {"ub_func", "sb_func", "rosb_func", "s_func", "cs_func", "depth_ms_func", "st_func"},
-        ast::AttributeList{
+        {
             Stage(ast::PipelineStage::kFragment),
         });
 
@@ -1262,7 +1494,7 @@
     MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1278,7 +1510,7 @@
     MakePlainGlobalReferenceBodyFunction("ub_func", "foo_ub", ty.i32(), {});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1302,7 +1534,7 @@
     MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1327,7 +1559,7 @@
                                             {{0, ty.i32()}, {1, ty.u32()}, {2, ty.f32()}});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1351,7 +1583,7 @@
     MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.vec3<f32>()}});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1373,7 +1605,7 @@
     MakePlainGlobalReferenceBodyFunction("ub_func", "foo_ub", ty.vec3<f32>(), {});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1408,10 +1640,14 @@
         return create<ast::CallStatement>(Call(callee));
     };
 
-    Func("ep_func", ast::VariableList(), ty.void_(),
-         ast::StatementList{FuncCall("ub_foo_func"), FuncCall("ub_bar_func"),
-                            FuncCall("ub_baz_func"), Return()},
-         ast::AttributeList{
+    Func("ep_func", {}, ty.void_(),
+         {
+             FuncCall("ub_foo_func"),
+             FuncCall("ub_bar_func"),
+             FuncCall("ub_baz_func"),
+             Return(),
+         },
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1452,7 +1688,7 @@
     MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1474,7 +1710,7 @@
     MakePlainGlobalReferenceBodyFunction("sb_func", "foo_sb", ty.i32(), {});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1498,7 +1734,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1527,7 +1763,7 @@
                                             {{0, ty.i32()}, {1, ty.u32()}, {2, ty.f32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1566,14 +1802,14 @@
         return create<ast::CallStatement>(Call(callee));
     };
 
-    Func("ep_func", ast::VariableList(), ty.void_(),
-         ast::StatementList{
+    Func("ep_func", {}, ty.void_(),
+         {
              FuncCall("sb_foo_func"),
              FuncCall("sb_bar_func"),
              FuncCall("sb_baz_func"),
              Return(),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1609,7 +1845,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1636,7 +1872,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1660,7 +1896,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.vec3<f32>()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1682,7 +1918,7 @@
     MakePlainGlobalReferenceBodyFunction("ub_func", "foo_ub", ty.vec3<f32>(), {});
 
     MakeCallerBodyFunction("ep_func", {"ub_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1706,7 +1942,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1724,7 +1960,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1763,14 +1999,14 @@
         return create<ast::CallStatement>(Call(callee));
     };
 
-    Func("ep_func", ast::VariableList(), ty.void_(),
-         ast::StatementList{
+    Func("ep_func", {}, ty.void_(),
+         {
              FuncCall("sb_foo_func"),
              FuncCall("sb_bar_func"),
              FuncCall("sb_baz_func"),
              Return(),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1809,7 +2045,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1836,7 +2072,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1860,7 +2096,7 @@
     MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
     MakeCallerBodyFunction("ep_func", {"sb_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1878,7 +2114,7 @@
     AddGlobalVariable("foo_coords", ty.f32());
 
     MakeSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords", ty.f32(),
-                                     ast::AttributeList{
+                                     {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -1894,7 +2130,7 @@
 }
 
 TEST_F(InspectorGetSamplerResourceBindingsTest, NoSampler) {
-    MakeEmptyBodyFunction("ep_func", ast::AttributeList{
+    MakeEmptyBodyFunction("ep_func", {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -1916,7 +2152,7 @@
                                      ty.f32(), {});
 
     MakeCallerBodyFunction("ep_func", {"foo_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -1938,7 +2174,7 @@
     AddGlobalVariable("foo_coords", ty.f32());
 
     MakeSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords", ty.f32(),
-                                     ast::AttributeList{
+                                     {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -1957,7 +2193,7 @@
 
     MakeComparisonSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords",
                                                "foo_depth", ty.f32(),
-                                               ast::AttributeList{
+                                               {
                                                    Stage(ast::PipelineStage::kFragment),
                                                });
 
@@ -1978,7 +2214,7 @@
 
     MakeComparisonSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords",
                                                "foo_depth", ty.f32(),
-                                               ast::AttributeList{
+                                               {
                                                    Stage(ast::PipelineStage::kFragment),
                                                });
 
@@ -1994,7 +2230,7 @@
 }
 
 TEST_F(InspectorGetComparisonSamplerResourceBindingsTest, NoSampler) {
-    MakeEmptyBodyFunction("ep_func", ast::AttributeList{
+    MakeEmptyBodyFunction("ep_func", {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -2017,7 +2253,7 @@
                                                "foo_coords", "foo_depth", ty.f32(), {});
 
     MakeCallerBodyFunction("ep_func", {"foo_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kFragment),
                            });
 
@@ -2041,7 +2277,7 @@
 
     MakeComparisonSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords",
                                                "foo_depth", ty.f32(),
-                                               ast::AttributeList{
+                                               {
                                                    Stage(ast::PipelineStage::kFragment),
                                                });
 
@@ -2058,7 +2294,7 @@
     AddGlobalVariable("foo_coords", ty.f32());
 
     MakeSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords", ty.f32(),
-                                     ast::AttributeList{
+                                     {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -2071,7 +2307,7 @@
 }
 
 TEST_F(InspectorGetSampledTextureResourceBindingsTest, Empty) {
-    MakeEmptyBodyFunction("foo", ast::AttributeList{
+    MakeEmptyBodyFunction("foo", {
                                      Stage(ast::PipelineStage::kFragment),
                                  });
 
@@ -2093,7 +2329,7 @@
 
     MakeSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords",
                                      GetBaseType(GetParam().sampled_kind),
-                                     ast::AttributeList{
+                                     {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -2143,7 +2379,7 @@
 
     MakeSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords",
                                      "foo_array_index", GetBaseType(GetParam().sampled_kind),
-                                     ast::AttributeList{
+                                     {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -2179,11 +2415,11 @@
     AddGlobalVariable("foo_coords", coord_type);
     AddGlobalVariable("foo_sample_index", ty.i32());
 
-    Func("ep", ast::VariableList(), ty.void_(),
-         ast::StatementList{
+    Func("ep", {}, ty.void_(),
+         {
              CallStmt(Call("textureLoad", "foo_texture", "foo_coords", "foo_sample_index")),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -2221,7 +2457,7 @@
                                          inspector::ResourceBinding::SampledKind::kUInt}));
 
 TEST_F(InspectorGetMultisampledArrayTextureResourceBindingsTest, Empty) {
-    MakeEmptyBodyFunction("foo", ast::AttributeList{
+    MakeEmptyBodyFunction("foo", {
                                      Stage(ast::PipelineStage::kFragment),
                                  });
 
@@ -2244,7 +2480,7 @@
 
     MakeSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords",
                                      "foo_array_index", GetBaseType(GetParam().sampled_kind),
-                                     ast::AttributeList{
+                                     {
                                          Stage(ast::PipelineStage::kFragment),
                                      });
 
@@ -2276,7 +2512,7 @@
                                          inspector::ResourceBinding::SampledKind::kUInt}));
 
 TEST_F(InspectorGetStorageTextureResourceBindingsTest, Empty) {
-    MakeEmptyBodyFunction("ep", ast::AttributeList{
+    MakeEmptyBodyFunction("ep", {
                                     Stage(ast::PipelineStage::kFragment),
                                 });
 
@@ -2323,7 +2559,9 @@
     ASSERT_FALSE(dim_type == nullptr);
 
     MakeStorageTextureBodyFunction("ep", "st_var", dim_type,
-                                   ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+                                   {
+                                       Stage(ast::PipelineStage::kFragment),
+                                   });
 
     Inspector& inspector = Build();
 
@@ -2403,11 +2641,11 @@
     auto* depth_texture_type = ty.depth_texture(GetParam().type_dim);
     AddResource("dt", depth_texture_type, 0, 0);
 
-    Func("ep", ast::VariableList(), ty.void_(),
-         ast::StatementList{
+    Func("ep", {}, ty.void_(),
+         {
              CallStmt(Call("textureDimensions", "dt")),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -2440,11 +2678,11 @@
     auto* depth_ms_texture_type = ty.depth_multisampled_texture(ast::TextureDimension::k2d);
     AddResource("tex", depth_ms_texture_type, 0, 0);
 
-    Func("ep", ast::VariableList(), ty.void_(),
-         ast::StatementList{
+    Func("ep", {}, ty.void_(),
+         {
              CallStmt(Call("textureDimensions", "tex")),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -2464,11 +2702,11 @@
     auto* external_texture_type = ty.external_texture();
     AddResource("et", external_texture_type, 0, 0);
 
-    Func("ep", ast::VariableList(), ty.void_(),
-         ast::StatementList{
+    Func("ep", {}, ty.void_(),
+         {
              CallStmt(Call("textureDimensions", "et")),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -2744,8 +2982,10 @@
 }
 
 TEST_F(InspectorGetWorkgroupStorageSizeTest, Empty) {
-    MakeEmptyBodyFunction(
-        "ep_func", ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    MakeEmptyBodyFunction("ep_func", {
+                                         Stage(ast::PipelineStage::kCompute),
+                                         WorkgroupSize(1_i),
+                                     });
     Inspector& inspector = Build();
     EXPECT_EQ(0u, inspector.GetWorkgroupStorageSize("ep_func"));
 }
@@ -2755,7 +2995,7 @@
     MakePlainGlobalReferenceBodyFunction("f32_func", "wg_f32", ty.f32(), {});
 
     MakeCallerBodyFunction("ep_func", {"f32_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kCompute),
                                WorkgroupSize(1_i),
                            });
@@ -2777,7 +3017,7 @@
     MakePlainGlobalReferenceBodyFunction("f32_func", "wg_f32", ty.f32(), {});
 
     MakeCallerBodyFunction("ep_func", {"wg_struct_func", "f32_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kCompute),
                                WorkgroupSize(1_i),
                            });
@@ -2793,7 +3033,7 @@
     MakePlainGlobalReferenceBodyFunction("wg_func", "wg_vec3", ty.vec3<f32>(), {});
 
     MakeCallerBodyFunction("ep_func", {"wg_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kCompute),
                                WorkgroupSize(1_i),
                            });
@@ -2814,7 +3054,7 @@
     MakeStructVariableReferenceBodyFunction("wg_struct_func", "wg_struct_var", {{0, ty.f32()}});
 
     MakeCallerBodyFunction("ep_func", {"wg_struct_func"},
-                           ast::AttributeList{
+                           {
                                Stage(ast::PipelineStage::kCompute),
                                WorkgroupSize(1_i),
                            });
diff --git a/src/tint/inspector/test_inspector_builder.cc b/src/tint/inspector/test_inspector_builder.cc
index 074f29c..949ee75 100644
--- a/src/tint/inspector/test_inspector_builder.cc
+++ b/src/tint/inspector/test_inspector_builder.cc
@@ -28,7 +28,7 @@
 InspectorBuilder::~InspectorBuilder() = default;
 
 void InspectorBuilder::MakeEmptyBodyFunction(std::string name, ast::AttributeList attributes) {
-    Func(name, ast::VariableList(), ty.void_(), ast::StatementList{Return()}, attributes);
+    Func(name, {}, ty.void_(), {Return()}, attributes);
 }
 
 void InspectorBuilder::MakeCallerBodyFunction(std::string caller,
@@ -41,7 +41,7 @@
     }
     body.push_back(Return());
 
-    Func(caller, ast::VariableList(), ty.void_(), body, attributes);
+    Func(caller, {}, ty.void_(), body, attributes);
 }
 
 const ast::Struct* InspectorBuilder::MakeInOutStruct(
@@ -67,7 +67,7 @@
     stmts.emplace_back(Assign("local_" + var, var));
     stmts.emplace_back(Return());
 
-    return Func(func, ast::VariableList(), ty.void_(), stmts, attributes);
+    return Func(func, {}, ty.void_(), stmts, attributes);
 }
 
 bool InspectorBuilder::ContainsName(const std::vector<StageVariable>& vec,
@@ -169,7 +169,7 @@
 
     stmts.emplace_back(Return());
 
-    Func(func_name, ast::VariableList(), ty.void_(), stmts, ast::AttributeList{});
+    Func(func_name, {}, ty.void_(), stmts);
 }
 
 void InspectorBuilder::AddSampler(const std::string& name, uint32_t group, uint32_t binding) {
@@ -221,7 +221,7 @@
         Assign(result_name, Call("textureSample", texture_name, sampler_name, coords_name)));
     stmts.emplace_back(Return());
 
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, attributes);
+    return Func(func_name, {}, ty.void_(), stmts, attributes);
 }
 
 const ast::Function* InspectorBuilder::MakeSamplerReferenceBodyFunction(
@@ -242,7 +242,7 @@
                                                      coords_name, array_index)));
     stmts.emplace_back(Return());
 
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, attributes);
+    return Func(func_name, {}, ty.void_(), stmts, attributes);
 }
 
 const ast::Function* InspectorBuilder::MakeComparisonSamplerReferenceBodyFunction(
@@ -262,7 +262,7 @@
                                                      sampler_name, coords_name, depth_name)));
     stmts.emplace_back(Return());
 
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, attributes);
+    return Func(func_name, {}, ty.void_(), stmts, attributes);
 }
 
 const ast::Type* InspectorBuilder::GetBaseType(ResourceBinding::SampledKind sampled_kind) {
@@ -323,7 +323,7 @@
     stmts.emplace_back(Assign("dim", Call("textureDimensions", st_name)));
     stmts.emplace_back(Return());
 
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, attributes);
+    return Func(func_name, {}, ty.void_(), stmts, attributes);
 }
 
 std::function<const ast::Type*()> InspectorBuilder::GetTypeFunction(ComponentType component,
diff --git a/src/tint/program_builder.cc b/src/tint/program_builder.cc
index cd05fd4..aed6ec8 100644
--- a/src/tint/program_builder.cc
+++ b/src/tint/program_builder.cc
@@ -126,9 +126,11 @@
 }
 
 const ast::Function* ProgramBuilder::WrapInFunction(const ast::StatementList stmts) {
-    return Func(
-        "test_function", {}, ty.void_(), std::move(stmts),
-        {create<ast::StageAttribute>(ast::PipelineStage::kCompute), WorkgroupSize(1_i, 1_i, 1_i)});
+    return Func("test_function", {}, ty.void_(), std::move(stmts),
+                {
+                    create<ast::StageAttribute>(ast::PipelineStage::kCompute),
+                    WorkgroupSize(1_i, 1_i, 1_i),
+                });
 }
 
 }  // namespace tint
diff --git a/src/tint/resolver/array_accessor_test.cc b/src/tint/resolver/array_accessor_test.cc
index d9ef10e..535d1ff 100644
--- a/src/tint/resolver/array_accessor_test.cc
+++ b/src/tint/resolver/array_accessor_test.cc
@@ -225,13 +225,12 @@
     auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
     auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
     auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Decl(a),
              Decl(idx),
              Decl(f),
-         },
-         ast::AttributeList{});
+         });
 
     EXPECT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "");
@@ -242,12 +241,11 @@
     // var f : f32 = a[2.0f];
     auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
     auto* f = Var("a_2", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, 2_f)));
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Decl(a),
              Decl(f),
-         },
-         ast::AttributeList{});
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
 }
@@ -257,12 +255,11 @@
     // var f : f32 = a[2i];
     auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
     auto* f = Var("a_2", ty.f32(), IndexAccessor("a", 2_i));
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Decl(a),
              Decl(f),
-         },
-         ast::AttributeList{});
+         });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 3280ebf..29ad152 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -128,7 +128,9 @@
     auto& params = GetParam();
 
     Func("main",
-         ast::VariableList{Param("a", ty.vec4<f32>(), createAttributes({}, *this, params.kind))},
+         {
+             Param("a", ty.vec4<f32>(), createAttributes({}, *this, params.kind)),
+         },
          ty.void_(), {});
 
     if (params.should_pass) {
@@ -161,8 +163,11 @@
 TEST_P(FunctionReturnTypeAttributeTest, IsValid) {
     auto& params = GetParam();
 
-    Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1_f)}, {},
-         createAttributes({}, *this, params.kind));
+    Func("main", {}, ty.f32(),
+         {
+             Return(1_f),
+         },
+         {}, createAttributes({}, *this, params.kind));
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -195,9 +200,15 @@
 using ComputeShaderParameterAttributeTest = TestWithParams;
 TEST_P(ComputeShaderParameterAttributeTest, IsValid) {
     auto& params = GetParam();
-    auto* p = Param("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind));
-    Func("main", ast::VariableList{p}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    Func("main",
+         {
+             Param("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind)),
+         },
+         ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -242,7 +253,10 @@
         attrs.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
     }
     auto* p = Param("a", ty.vec4<f32>(), attrs);
-    Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func("frag_main", {p}, ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -276,8 +290,16 @@
         attrs.push_back(Location(Source{{34, 56}}, 2));
     }
     auto* p = Param("a", ty.vec4<f32>(), attrs);
-    Func("vertex_main", ast::VariableList{p}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::Builtin::kPosition)});
+    Func("vertex_main", {p}, ty.vec4<f32>(),
+         {
+             Return(Construct(ty.vec4<f32>())),
+         },
+         {
+             Stage(ast::PipelineStage::kVertex),
+         },
+         {
+             Builtin(ast::Builtin::kPosition),
+         });
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -316,8 +338,14 @@
 using ComputeShaderReturnTypeAttributeTest = TestWithParams;
 TEST_P(ComputeShaderReturnTypeAttributeTest, IsValid) {
     auto& params = GetParam();
-    Func("main", ast::VariableList{}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>(), 1_f))},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)},
+    Func("main", {}, ty.vec4<f32>(),
+         {
+             Return(Construct(ty.vec4<f32>(), 1_f)),
+         },
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         },
          createAttributes(Source{{12, 34}}, *this, params.kind));
 
     if (params.should_pass) {
@@ -363,7 +391,10 @@
     auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
     attrs.push_back(Location(Source{{34, 56}}, 2));
     Func("frag_main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {Stage(ast::PipelineStage::kFragment)}, attrs);
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         attrs);
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -413,8 +444,14 @@
     if (params.kind != AttributeKind::kBuiltin) {
         attrs.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
     }
-    Func("vertex_main", ast::VariableList{}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {Stage(ast::PipelineStage::kVertex)}, attrs);
+    Func("vertex_main", {}, ty.vec4<f32>(),
+         {
+             Return(Construct(ty.vec4<f32>())),
+         },
+         {
+             Stage(ast::PipelineStage::kVertex),
+         },
+         attrs);
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -450,8 +487,13 @@
 
 using EntryPointParameterAttributeTest = TestWithParams;
 TEST_F(EntryPointParameterAttributeTest, DuplicateAttribute) {
-    Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)},
+    Func("main", {}, ty.f32(),
+         {
+             Return(1_f),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
          {
              Location(Source{{12, 34}}, 2),
              Location(Source{{56, 78}}, 3),
@@ -471,15 +513,23 @@
                         Disable(ast::DisabledValidation::kBindingPointCollision),
                         Disable(ast::DisabledValidation::kEntryPointParameter),
                     });
-    Func("f", {s}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func("f", {s}, ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 using EntryPointReturnTypeAttributeTest = ResolverTest;
 TEST_F(EntryPointReturnTypeAttributeTest, DuplicateAttribute) {
-    Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1_f)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
+    Func("main", {}, ty.f32(),
+         {
+             Return(1_f),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
          ast::AttributeList{
              Location(Source{{12, 34}}, 2),
              Location(Source{{56, 78}}, 3),
@@ -492,7 +542,10 @@
 }
 
 TEST_F(EntryPointReturnTypeAttributeTest, DuplicateInternalAttribute) {
-    Func("f", {}, ty.i32(), {Return(1_i)}, {Stage(ast::PipelineStage::kFragment)},
+    Func("f", {}, ty.i32(), {Return(1_i)},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
          ast::AttributeList{
              Disable(ast::DisabledValidation::kBindingPointCollision),
              Disable(ast::DisabledValidation::kEntryPointParameter),
@@ -972,7 +1025,9 @@
              Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
                       Call("textureLoad", "B", vec2<i32>(1_i, 2_i), 0_i))),
          },
-         {Stage(ast::PipelineStage::kFragment)});
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -1000,13 +1055,17 @@
              Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
                       Call("textureLoad", "A", vec2<i32>(1_i, 2_i), 0_i))),
          },
-         {Stage(ast::PipelineStage::kFragment)});
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
     Func("F_B", {}, ty.void_(),
          {
              Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
                       Call("textureLoad", "B", vec2<i32>(1_i, 2_i), 0_i))),
          },
-         {Stage(ast::PipelineStage::kFragment)});
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -1033,10 +1092,14 @@
     auto* param =
         Param("p", ty.vec4<f32>(),
               {Invariant(Source{{12, 34}}), Builtin(Source{{56, 78}}, ast::Builtin::kPosition)});
-    Func("main", ast::VariableList{param}, ty.vec4<f32>(),
-         ast::StatementList{Return(Construct(ty.vec4<f32>()))},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
-         ast::AttributeList{
+    Func("main", {param}, ty.vec4<f32>(),
+         {
+             Return(Construct(ty.vec4<f32>())),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
              Location(0),
          });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1044,10 +1107,14 @@
 
 TEST_F(InvariantAttributeTests, InvariantWithoutPosition) {
     auto* param = Param("p", ty.vec4<f32>(), {Invariant(Source{{12, 34}}), Location(0)});
-    Func("main", ast::VariableList{param}, ty.vec4<f32>(),
-         ast::StatementList{Return(Construct(ty.vec4<f32>()))},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
-         ast::AttributeList{
+    Func("main", {param}, ty.vec4<f32>(),
+         {
+             Return(Construct(ty.vec4<f32>())),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
              Location(0),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -1135,10 +1202,17 @@
     auto& params = GetParam();
 
     Func("main",
-         ast::VariableList{
+         {
              Param("a", ty.f32(),
-                   {Location(0), Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
-         ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+                   {
+                       Location(0),
+                       Interpolate(Source{{12, 34}}, params.type, params.sampling),
+                   }),
+         },
+         ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1154,10 +1228,17 @@
     auto& params = GetParam();
 
     Func("main",
-         ast::VariableList{
+         {
              Param("a", ty.i32(),
-                   {Location(0), Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
-         ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+                   {
+                       Location(0),
+                       Interpolate(Source{{12, 34}}, params.type, params.sampling),
+                   }),
+         },
+         ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (params.type != ast::InterpolationType::kFlat) {
         EXPECT_FALSE(r()->Resolve());
@@ -1178,10 +1259,17 @@
     auto& params = GetParam();
 
     Func("main",
-         ast::VariableList{
+         {
              Param("a", ty.vec4<u32>(),
-                   {Location(0), Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
-         ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+                   {
+                       Location(0),
+                       Interpolate(Source{{12, 34}}, params.type, params.sampling),
+                   }),
+         },
+         ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (params.type != ast::InterpolationType::kFlat) {
         EXPECT_FALSE(r()->Resolve());
@@ -1217,8 +1305,10 @@
         Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kSample, false}));
 
 TEST_F(InterpolateTest, FragmentInput_Integer_MissingFlatInterpolation) {
-    Func("main", ast::VariableList{Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)})},
-         ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+    Func("main", {Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)})}, ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -1231,8 +1321,13 @@
                                  Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
                                  Member(Source{{12, 34}}, "u", ty.u32(), {Location(0)}),
                              });
-    Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
-         ast::AttributeList{Stage(ast::PipelineStage::kVertex)});
+    Func("main", {}, ty.Of(s),
+         {
+             Return(Construct(ty.Of(s))),
+         },
+         {
+             Stage(ast::PipelineStage::kVertex),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -1243,11 +1338,18 @@
 
 TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {
     Func("main",
-         ast::VariableList{Param("a", ty.vec4<f32>(),
-                                 {Builtin(ast::Builtin::kPosition),
-                                  Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
-                                              ast::InterpolationSampling::kNone)})},
-         ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+         {
+             Param("a", ty.vec4<f32>(),
+                   {
+                       Builtin(ast::Builtin::kPosition),
+                       Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+                                   ast::InterpolationSampling::kNone),
+                   }),
+         },
+         ty.void_(), {},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1255,11 +1357,18 @@
 }
 
 TEST_F(InterpolateTest, MissingLocationAttribute_ReturnType) {
-    Func("main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
-         {Builtin(ast::Builtin::kPosition),
-          Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
-                      ast::InterpolationSampling::kNone)});
+    Func("main", {}, ty.vec4<f32>(),
+         {
+             Return(Construct(ty.vec4<f32>())),
+         },
+         {
+             Stage(ast::PipelineStage::kVertex),
+         },
+         {
+             Builtin(ast::Builtin::kPosition),
+             Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+                         ast::InterpolationSampling::kNone),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index ab40296..d82c2f3 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -38,8 +38,14 @@
 
     auto* dpdx =
         create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"), ast::ExpressionList{Expr(1_f)});
-    Func(Source{{1, 2}}, "func", ast::VariableList{}, ty.void_(), {CallStmt(dpdx)},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    Func(Source{{1, 2}}, "func", {}, ty.void_(),
+         {
+             CallStmt(dpdx),
+         },
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "3:4 error: built-in cannot be used by compute pipeline stage");
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index 0c59485..6f0cf85 100644
--- a/src/tint/resolver/builtins_validation_test.cc
+++ b/src/tint/resolver/builtins_validation_test.cc
@@ -93,8 +93,7 @@
     const Params& params = GetParam();
 
     auto* p = Global("p", ty.vec4<f32>(), ast::StorageClass::kPrivate);
-    auto* input = Param("input", params.type(*this),
-                        ast::AttributeList{Builtin(Source{{12, 34}}, params.builtin)});
+    auto* input = Param("input", params.type(*this), {Builtin(Source{{12, 34}}, params.builtin)});
     switch (params.stage) {
         case ast::PipelineStage::kVertex:
             Func("main", {input}, ty.vec4<f32>(), {Return(p)}, {Stage(ast::PipelineStage::kVertex)},
@@ -105,7 +104,10 @@
             break;
         case ast::PipelineStage::kCompute:
             Func("main", {input}, ty.void_(), {},
-                 ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                 {
+                     Stage(ast::PipelineStage::kCompute),
+                     WorkgroupSize(1_i),
+                 });
             break;
         default:
             break;
@@ -130,10 +132,20 @@
     // fn fs_main(
     //   @builtin(frag_depth) fd: f32,
     // ) -> @location(0) f32 { return 1.0; }
-    auto* fd = Param("fd", ty.f32(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
-    Func("fs_main", ast::VariableList{fd}, ty.f32(), {Return(1_f)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    Func("fs_main",
+         {
+             Param("fd", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)}),
+         },
+         ty.f32(),
+         {
+             Return(1_f),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: builtin(frag_depth) cannot be used in input of "
@@ -149,11 +161,24 @@
 
     auto* s = Structure(
         "MyInputs",
-        {Member("frag_depth", ty.f32(),
-                ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)})});
+        {
+            Member("frag_depth", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)}),
+        });
 
-    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    Func("fragShader",
+         {
+             Param("arg", ty.Of(s)),
+         },
+         ty.f32(),
+         {
+             Return(1_f),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: builtin(frag_depth) cannot be used in input of "
@@ -184,11 +209,25 @@
     // @fragment
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* m = Member("position", ty.vec4<u32>(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
-    auto* s = Structure("MyInputs", {m});
-    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    auto* s =
+        Structure("MyInputs", {
+                                  Member("position", ty.vec4<u32>(),
+                                         {Builtin(Source{{12, 34}}, ast::Builtin::kPosition)}),
+                              });
+    Func("fragShader",
+         {
+             Param("arg", ty.Of(s)),
+         },
+         ty.f32(),
+         {
+             Return(1_f),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
@@ -211,11 +250,21 @@
     // @fragment
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* m = Member("frag_depth", ty.i32(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
-    auto* s = Structure("MyInputs", {m});
-    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    auto* s = Structure(
+        "MyInputs",
+        {
+            Member("frag_depth", ty.i32(), {Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)}),
+        });
+    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(),
+         {
+             Return(1_f),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
@@ -228,10 +277,11 @@
     // @fragment
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* s = Structure(
-        "MyInputs",
-        {Member("m", ty.f32(),
-                ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)})});
+    auto* s =
+        Structure("MyInputs",
+                  {
+                      Member("m", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)}),
+                  });
     Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
          {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
 
@@ -254,10 +304,20 @@
     // fn fs_main(
     //   @builtin(sample_mask) arg: bool
     // ) -> @location(0) f32 { return 1.0; }
-    auto* arg = Param("arg", ty.bool_(),
-                      ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
-    Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1_f)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    Func("fs_main",
+         {
+             Param("arg", ty.bool_(), {Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)}),
+         },
+         ty.f32(),
+         {
+             Return(1_f),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
 }
@@ -271,8 +331,9 @@
 
     auto* s = Structure(
         "MyInputs",
-        {Member("m", ty.f32(),
-                ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)})});
+        {
+            Member("m", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)}),
+        });
     Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
          {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
 
@@ -285,10 +346,17 @@
     // fn fs_main(
     //   @builtin(sample_index) arg: bool
     // ) -> @location(0) f32 { return 1.0; }
-    auto* arg = Param("arg", ty.bool_(),
-                      ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)});
-    Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1_f)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    Func("fs_main",
+         {
+             Param("arg", ty.bool_(), {Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)}),
+         },
+         ty.f32(), {Return(1_f)},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
 }
@@ -298,10 +366,17 @@
     // fn fs_main(
     //   @builtin(kPosition) p: vec3<f32>,
     // ) -> @location(0) f32 { return 1.0; }
-    auto* p = Param("p", ty.vec3<f32>(),
-                    ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
-    Func("fs_main", ast::VariableList{p}, ty.f32(), {Return(1_f)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    Func("fs_main",
+         {
+             Param("p", ty.vec3<f32>(), {Builtin(Source{{12, 34}}, ast::Builtin::kPosition)}),
+         },
+         ty.f32(), {Return(1_f)},
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(0),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
 }
@@ -310,9 +385,8 @@
     // @fragment
     // fn fs_main() -> @builtin(kFragDepth) f32 { var fd: i32; return fd; }
     auto* fd = Var("fd", ty.i32());
-    Func("fs_main", {}, ty.i32(), {Decl(fd), Return(fd)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
-         ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
+    Func("fs_main", {}, ty.i32(), {Decl(fd), Return(fd)}, {Stage(ast::PipelineStage::kFragment)},
+         {Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
 }
@@ -323,12 +397,10 @@
     //   @builtin(kVertexIndex) vi : f32,
     //   @builtin(kPosition) p :vec4<f32>
     // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
-    auto* p = Param("p", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)});
-    auto* vi = Param("vi", ty.f32(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
-    Func("main", ast::VariableList{vi, p}, ty.vec4<f32>(), {Return(Expr("p"))},
-         ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
-         ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+    auto* p = Param("p", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
+    auto* vi = Param("vi", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
+    Func("main", {vi, p}, ty.vec4<f32>(), {Return(Expr("p"))}, {Stage(ast::PipelineStage::kVertex)},
+         {Builtin(ast::Builtin::kPosition)});
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(vertex_index) must be 'u32'");
 }
@@ -339,12 +411,10 @@
     //   @builtin(kInstanceIndex) ii : f32,
     //   @builtin(kPosition) p :vec4<f32>
     // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
-    auto* p = Param("p", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)});
-    auto* ii = Param("ii", ty.f32(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)});
-    Func("main", ast::VariableList{ii, p}, ty.vec4<f32>(), {Return(Expr("p"))},
-         ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
-         ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+    auto* p = Param("p", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
+    auto* ii = Param("ii", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)});
+    Func("main", {ii, p}, ty.vec4<f32>(), {Return(Expr("p"))}, {Stage(ast::PipelineStage::kVertex)},
+         {Builtin(ast::Builtin::kPosition)});
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(instance_index) must be 'u32'");
 }
@@ -357,14 +427,13 @@
     //   @builtin(sample_index) si: u32,
     //   @builtin(sample_mask) sm : u32
     // ) -> @builtin(frag_depth) f32 { var fd: f32; return fd; }
-    auto* p = Param("p", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)});
-    auto* ff = Param("ff", ty.bool_(), ast::AttributeList{Builtin(ast::Builtin::kFrontFacing)});
-    auto* si = Param("si", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleIndex)});
-    auto* sm = Param("sm", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleMask)});
+    auto* p = Param("p", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
+    auto* ff = Param("ff", ty.bool_(), {Builtin(ast::Builtin::kFrontFacing)});
+    auto* si = Param("si", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
+    auto* sm = Param("sm", ty.u32(), {Builtin(ast::Builtin::kSampleMask)});
     auto* var_fd = Var("fd", ty.f32());
-    Func("fs_main", ast::VariableList{p, ff, si, sm}, ty.f32(), {Decl(var_fd), Return(var_fd)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
-         ast::AttributeList{Builtin(ast::Builtin::kFragDepth)});
+    Func("fs_main", {p, ff, si, sm}, ty.f32(), {Decl(var_fd), Return(var_fd)},
+         {Stage(ast::PipelineStage::kFragment)}, {Builtin(ast::Builtin::kFragDepth)});
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -374,19 +443,16 @@
     //   @builtin(vertex_index) vi : u32,
     //   @builtin(instance_index) ii : u32,
     // ) -> @builtin(position) vec4<f32> { var p :vec4<f32>; return p; }
-    auto* vi = Param("vi", ty.u32(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
+    auto* vi = Param("vi", ty.u32(), {Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
 
-    auto* ii = Param("ii", ty.u32(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)});
+    auto* ii = Param("ii", ty.u32(), {Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)});
     auto* p = Var("p", ty.vec4<f32>());
-    Func("main", ast::VariableList{vi, ii}, ty.vec4<f32>(),
+    Func("main", {vi, ii}, ty.vec4<f32>(),
          {
              Decl(p),
              Return(p),
          },
-         ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
-         ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+         {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::Builtin::kPosition)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -401,29 +467,24 @@
     //   @builtin(num_workgroups) nwgs: vec3<u32>,
     // ) {}
 
-    auto* li_id = Param("li_id", ty.vec3<u32>(),
-                        ast::AttributeList{Builtin(ast::Builtin::kLocalInvocationId)});
-    auto* li_index = Param("li_index", ty.u32(),
-                           ast::AttributeList{Builtin(ast::Builtin::kLocalInvocationIndex)});
-    auto* gi =
-        Param("gi", ty.vec3<u32>(), ast::AttributeList{Builtin(ast::Builtin::kGlobalInvocationId)});
-    auto* wi = Param("wi", ty.vec3<u32>(), ast::AttributeList{Builtin(ast::Builtin::kWorkgroupId)});
-    auto* nwgs =
-        Param("nwgs", ty.vec3<u32>(), ast::AttributeList{Builtin(ast::Builtin::kNumWorkgroups)});
+    auto* li_id = Param("li_id", ty.vec3<u32>(), {Builtin(ast::Builtin::kLocalInvocationId)});
+    auto* li_index = Param("li_index", ty.u32(), {Builtin(ast::Builtin::kLocalInvocationIndex)});
+    auto* gi = Param("gi", ty.vec3<u32>(), {Builtin(ast::Builtin::kGlobalInvocationId)});
+    auto* wi = Param("wi", ty.vec3<u32>(), {Builtin(ast::Builtin::kWorkgroupId)});
+    auto* nwgs = Param("nwgs", ty.vec3<u32>(), {Builtin(ast::Builtin::kNumWorkgroups)});
 
-    Func("main", ast::VariableList{li_id, li_index, gi, wi, nwgs}, ty.void_(), {},
-         ast::AttributeList{Stage(ast::PipelineStage::kCompute),
-                            WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    Func("main", {li_id, li_index, gi, wi, nwgs}, ty.void_(), {},
+         {Stage(ast::PipelineStage::kCompute),
+          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) {
-    auto* wi = Param("wi", ty.f32(),
-                     ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kWorkgroupId)});
-    Func("main", ast::VariableList{wi}, ty.void_(), {},
-         ast::AttributeList{Stage(ast::PipelineStage::kCompute),
-                            WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    auto* wi = Param("wi", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kWorkgroupId)});
+    Func("main", {wi}, ty.void_(), {},
+         {Stage(ast::PipelineStage::kCompute),
+          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -432,11 +493,10 @@
 }
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_NumWorkgroupsNotVec3U32) {
-    auto* nwgs = Param("nwgs", ty.f32(),
-                       ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kNumWorkgroups)});
-    Func("main", ast::VariableList{nwgs}, ty.void_(), {},
-         ast::AttributeList{Stage(ast::PipelineStage::kCompute),
-                            WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    auto* nwgs = Param("nwgs", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kNumWorkgroups)});
+    Func("main", {nwgs}, ty.void_(), {},
+         {Stage(ast::PipelineStage::kCompute),
+          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -446,11 +506,10 @@
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_GlobalInvocationNotVec3U32) {
     auto* gi =
-        Param("gi", ty.vec3<i32>(),
-              ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kGlobalInvocationId)});
-    Func("main", ast::VariableList{gi}, ty.void_(), {},
-         ast::AttributeList{Stage(ast::PipelineStage::kCompute),
-                            WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+        Param("gi", ty.vec3<i32>(), {Builtin(Source{{12, 34}}, ast::Builtin::kGlobalInvocationId)});
+    Func("main", {gi}, ty.void_(), {},
+         {Stage(ast::PipelineStage::kCompute),
+          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -459,12 +518,11 @@
 }
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationIndexNotU32) {
-    auto* li_index =
-        Param("li_index", ty.vec3<u32>(),
-              ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kLocalInvocationIndex)});
-    Func("main", ast::VariableList{li_index}, ty.void_(), {},
-         ast::AttributeList{Stage(ast::PipelineStage::kCompute),
-                            WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    auto* li_index = Param("li_index", ty.vec3<u32>(),
+                           {Builtin(Source{{12, 34}}, ast::Builtin::kLocalInvocationIndex)});
+    Func("main", {li_index}, ty.void_(), {},
+         {Stage(ast::PipelineStage::kCompute),
+          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -473,12 +531,11 @@
 }
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationNotVec3U32) {
-    auto* li_id =
-        Param("li_id", ty.vec2<u32>(),
-              ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kLocalInvocationId)});
-    Func("main", ast::VariableList{li_id}, ty.void_(), {},
-         ast::AttributeList{Stage(ast::PipelineStage::kCompute),
-                            WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    auto* li_id = Param("li_id", ty.vec2<u32>(),
+                        {Builtin(Source{{12, 34}}, ast::Builtin::kLocalInvocationId)});
+    Func("main", {li_id}, ty.void_(), {},
+         {Stage(ast::PipelineStage::kCompute),
+          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -496,13 +553,11 @@
     // @fragment
     // fn fragShader(arg: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* s = Structure(
-        "MyInputs",
-        {Member("position", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)}),
-         Member("front_facing", ty.bool_(),
-                ast::AttributeList{Builtin(ast::Builtin::kFrontFacing)}),
-         Member("sample_index", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleIndex)}),
-         Member("sample_mask", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleMask)})});
+    auto* s = Structure("MyInputs",
+                        {Member("position", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
+                         Member("front_facing", ty.bool_(), {Builtin(ast::Builtin::kFrontFacing)}),
+                         Member("sample_index", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)}),
+                         Member("sample_mask", ty.u32(), {Builtin(ast::Builtin::kSampleMask)})});
     Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
          {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -515,10 +570,9 @@
     // ) -> @location(0) f32 { return 1.0; }
 
     auto* is_front =
-        Param("is_front", ty.i32(),
-              ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFrontFacing)});
-    Func("fs_main", ast::VariableList{is_front}, ty.f32(), {Return(1_f)},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+        Param("is_front", ty.i32(), {Builtin(Source{{12, 34}}, ast::Builtin::kFrontFacing)});
+    Func("fs_main", {is_front}, ty.f32(), {Return(1_f)}, {Stage(ast::PipelineStage::kFragment)},
+         {Location(0)});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
@@ -533,8 +587,7 @@
 
     auto* s = Structure(
         "MyInputs",
-        {Member("pos", ty.f32(),
-                ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFrontFacing)})});
+        {Member("pos", ty.f32(), {Builtin(Source{{12, 34}}, ast::Builtin::kFrontFacing)})});
     Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1_f)},
          {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
 
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index 4aa6350..850e2c6 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -171,7 +171,7 @@
     Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(), {});
     Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(),
          ast::StatementList{CallStmt(Call("foo", Expr("p")))});
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(Var("v", ty.i32(), Expr(1_i))),
              CallStmt(Call("foo", AddressOf(Expr("v")))),
@@ -195,7 +195,7 @@
     auto* v = Var("v", ty.i32());
     auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kFunction), AddressOf(v));
     auto* c = Var("c", ty.i32(), ast::StorageClass::kNone, Call("x", Expr(Source{{12, 34}}, p)));
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(v),
              Decl(p),
@@ -222,7 +222,7 @@
     auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(v));
     auto* c = Var("c", ty.i32(), ast::StorageClass::kNone, Call("foo", Expr(Source{{12, 34}}, p)));
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(p),
              Decl(c),
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index cf8ddab..ac4e01f 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -394,7 +394,10 @@
     auto* bar = Param("bar", ty.f32());
     auto* baz = Var("baz", ty.f32(), Expr("bar"));
 
-    Func("foo", ast::VariableList{bar}, ty.void_(), {Decl(baz)});
+    Func("foo", {bar}, ty.void_(),
+         {
+             Decl(baz),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -407,7 +410,10 @@
     auto* bar = Param("bar", ty.f32());
     auto* baz = Let("baz", ty.f32(), Expr("bar"));
 
-    Func("foo", ast::VariableList{bar}, ty.void_(), {Decl(baz)});
+    Func("foo", {bar}, ty.void_(),
+         {
+             Decl(baz),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -709,7 +715,7 @@
     Structure("S", {Member("m", ty.atomic(ty.i32()))});
     auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
     auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
-    Func("f", ast::VariableList{bar}, ty.void_(), {});
+    Func("f", {bar}, ty.void_(), {});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -721,7 +727,7 @@
     Structure("S", {Member("m", ty.i32())});
     auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
     auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
-    Func("f", ast::VariableList{bar}, ty.void_(), {});
+    Func("f", {bar}, ty.void_(), {});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -779,7 +785,7 @@
     auto& param = GetParam();
     auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.storage_class);
     auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
-    Func("f", ast::VariableList{arg}, ty.void_(), {});
+    Func("f", {arg}, ty.void_(), {});
 
     if (param.should_pass) {
         ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 38f4d49..0fecf0c 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -270,8 +270,10 @@
 }
 
 TEST_F(ResolverTest, Stmt_Call) {
-    ast::VariableList params;
-    Func("my_func", params, ty.void_(), {Return()}, ast::AttributeList{});
+    Func("my_func", {}, ty.void_(),
+         {
+             Return(),
+         });
 
     auto* expr = Call("my_func");
 
@@ -333,8 +335,6 @@
     //   var bar : f32 = foo;
     // }
 
-    ast::VariableList params;
-
     // Declare i32 "foo" inside a block
     auto* foo_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
     auto* foo_i32_init = foo_i32->constructor;
@@ -357,7 +357,7 @@
     auto* bar_f32_init = bar_f32->constructor;
     auto* bar_f32_decl = Decl(bar_f32);
 
-    Func("func", params, ty.void_(), {inner, foo_f32_decl, bar_f32_decl}, ast::AttributeList{});
+    Func("func", {}, ty.void_(), {inner, foo_f32_decl, bar_f32_decl});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_NE(TypeOf(foo_i32_init), nullptr);
@@ -389,13 +389,11 @@
     //   var bar : f32 = foo;
     // }
 
-    ast::VariableList params;
-
     // Declare i32 "foo" inside a function
     auto* fn_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
     auto* fn_i32_init = fn_i32->constructor;
     auto* fn_i32_decl = Decl(fn_i32);
-    Func("func_i32", params, ty.void_(), {fn_i32_decl}, ast::AttributeList{});
+    Func("func_i32", {}, ty.void_(), {fn_i32_decl});
 
     // Declare f32 "foo" at module scope
     auto* mod_f32 = Var("foo", ty.f32(), ast::StorageClass::kPrivate, Expr(2_f));
@@ -406,7 +404,7 @@
     auto* fn_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
     auto* fn_f32_init = fn_f32->constructor;
     auto* fn_f32_decl = Decl(fn_f32);
-    Func("func_f32", params, ty.void_(), {fn_f32_decl}, ast::AttributeList{});
+    Func("func_f32", {}, ty.void_(), {fn_f32_decl});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_NE(TypeOf(mod_init), nullptr);
@@ -493,8 +491,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Call) {
-    ast::VariableList params;
-    Func("my_func", params, ty.f32(), {Return(0_f)}, ast::AttributeList{});
+    Func("my_func", {}, ty.f32(), {Return(0_f)});
 
     auto* call = Call("my_func");
     WrapInFunction(call);
@@ -506,8 +503,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Call_InBinaryOp) {
-    ast::VariableList params;
-    Func("func", params, ty.f32(), {Return(0_f)}, ast::AttributeList{});
+    Func("func", {}, ty.f32(), {Return(0_f)});
 
     auto* expr = Add(Call("func"), Call("func"));
     WrapInFunction(expr);
@@ -639,12 +635,11 @@
     auto* var = Let("my_var", ty.f32(), Construct(ty.f32()));
     auto* decl = Decl(Var("b", ty.f32(), ast::StorageClass::kNone, my_var_a));
 
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Decl(var),
              decl,
-         },
-         ast::AttributeList{});
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -663,13 +658,12 @@
     auto* a = Var("a", ty.array<bool, 10>(), array<bool, 10>());
     auto* idx = Var("idx", ty.f32(), Construct(ty.f32()));
     auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Decl(a),
              Decl(idx),
              Decl(f),
-         },
-         ast::AttributeList{});
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
@@ -682,12 +676,11 @@
 
     auto* var = Var("my_var", ty.f32());
 
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Decl(var),
              assign,
-         },
-         ast::AttributeList{});
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -712,13 +705,12 @@
     auto* v_decl = Decl(Var("v", ty.f32()));
     auto* p_decl = Decl(Let("p", ty.pointer<f32>(ast::StorageClass::kFunction), AddressOf(v)));
     auto* assign = Assign(Deref(p), 1.23_f);
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              v_decl,
              p_decl,
              assign,
-         },
-         ast::AttributeList{});
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -733,7 +725,10 @@
 }
 
 TEST_F(ResolverTest, Expr_Call_Function) {
-    Func("my_func", ast::VariableList{}, ty.f32(), {Return(0_f)}, ast::AttributeList{});
+    Func("my_func", {}, ty.f32(),
+         {
+             Return(0_f),
+         });
 
     auto* call = Call("my_func");
     WrapInFunction(call);
@@ -757,7 +752,7 @@
     auto* param_c = Param("c", ty.u32());
 
     auto* func = Func("my_func",
-                      ast::VariableList{
+                      {
                           param_a,
                           param_b,
                           param_c,
@@ -789,7 +784,7 @@
     auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
     auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
-    auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
+    auto* func = Func("my_func", {}, ty.void_(),
                       {
                           Assign("wg_var", "wg_var"),
                           Assign("sb_var", "sb_var"),
@@ -821,12 +816,11 @@
     auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
     auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
-    Func("my_func", ast::VariableList{}, ty.f32(),
+    Func("my_func", {}, ty.f32(),
          {Assign("wg_var", "wg_var"), Assign("sb_var", "sb_var"), Assign("priv_var", "priv_var"),
-          Return(0_f)},
-         ast::AttributeList{});
+          Return(0_f)});
 
-    auto* func2 = Func("func", ast::VariableList{}, ty.void_(),
+    auto* func2 = Func("func", {}, ty.void_(),
                        {
                            WrapInStatement(Call("my_func")),
                        },
@@ -846,7 +840,7 @@
 }
 
 TEST_F(ResolverTest, Function_NotRegisterFunctionVariable) {
-    auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
+    auto* func = Func("my_func", {}, ty.void_(),
                       {
                           Decl(Var("var", ty.f32())),
                           Assign("var", 1_f),
@@ -862,7 +856,7 @@
 }
 
 TEST_F(ResolverTest, Function_NotRegisterFunctionConstant) {
-    auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
+    auto* func = Func("my_func", {}, ty.void_(),
                       {
                           Decl(Let("var", ty.f32(), Construct(ty.f32()))),
                       });
@@ -877,7 +871,7 @@
 }
 
 TEST_F(ResolverTest, Function_NotRegisterFunctionParams) {
-    auto* func = Func("my_func", {Let("var", ty.f32(), Construct(ty.f32()))}, ty.void_(), {});
+    auto* func = Func("my_func", {Param("var", ty.f32())}, ty.void_(), {});
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* func_sem = Sem().Get(func);
@@ -888,11 +882,11 @@
 }
 
 TEST_F(ResolverTest, Function_CallSites) {
-    auto* foo = Func("foo", ast::VariableList{}, ty.void_(), {});
+    auto* foo = Func("foo", {}, ty.void_(), {});
 
     auto* call_1 = Call("foo");
     auto* call_2 = Call("foo");
-    auto* bar = Func("bar", ast::VariableList{}, ty.void_(),
+    auto* bar = Func("bar", {}, ty.void_(),
                      {
                          CallStmt(call_1),
                          CallStmt(call_2),
@@ -914,7 +908,7 @@
 TEST_F(ResolverTest, Function_WorkgroupSize_NotSet) {
     // @compute @workgroup_size(1)
     // fn main() {}
-    auto* func = Func("main", ast::VariableList{}, ty.void_(), {}, {});
+    auto* func = Func("main", {}, ty.void_(), {});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -932,8 +926,11 @@
 TEST_F(ResolverTest, Function_WorkgroupSize_Literals) {
     // @compute @workgroup_size(8, 2, 3)
     // fn main() {}
-    auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
-                      {Stage(ast::PipelineStage::kCompute), WorkgroupSize(8_i, 2_i, 3_i)});
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize(8_i, 2_i, 3_i),
+                      });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -957,9 +954,11 @@
     GlobalConst("width", ty.i32(), Expr(16_i));
     GlobalConst("height", ty.i32(), Expr(8_i));
     GlobalConst("depth", ty.i32(), Expr(2_i));
-    auto* func =
-        Func("main", ast::VariableList{}, ty.void_(), {},
-             {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height", "depth")});
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize("width", "height", "depth"),
+                      });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -983,8 +982,11 @@
                 Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 8_i))));
     GlobalConst("height", ty.i32(),
                 Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 4_i))));
-    auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
-                      {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height")});
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize("width", "height"),
+                      });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1008,9 +1010,11 @@
     auto* width = Override("width", ty.i32(), Expr(16_i), {Id(0)});
     auto* height = Override("height", ty.i32(), Expr(8_i), {Id(1)});
     auto* depth = Override("depth", ty.i32(), Expr(2_i), {Id(2)});
-    auto* func =
-        Func("main", ast::VariableList{}, ty.void_(), {},
-             {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height", "depth")});
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize("width", "height", "depth"),
+                      });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1034,9 +1038,11 @@
     auto* width = Override("width", ty.i32(), nullptr, {Id(0)});
     auto* height = Override("height", ty.i32(), nullptr, {Id(1)});
     auto* depth = Override("depth", ty.i32(), nullptr, {Id(2)});
-    auto* func =
-        Func("main", ast::VariableList{}, ty.void_(), {},
-             {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height", "depth")});
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize("width", "height", "depth"),
+                      });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1058,8 +1064,11 @@
     // fn main() {}
     auto* height = Override("height", ty.i32(), Expr(2_i), {Id(0)});
     GlobalConst("depth", ty.i32(), Expr(3_i));
-    auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
-                      {Stage(ast::PipelineStage::kCompute), WorkgroupSize(8_i, "height", "depth")});
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize(8_i, "height", "depth"),
+                      });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1709,7 +1718,7 @@
     auto* var = Var("var", ty.i32());
 
     auto* stmt = Decl(var);
-    Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+    Func("func", {}, ty.void_(), {stmt});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1745,7 +1754,7 @@
 TEST_F(ResolverTest, StorageClass_DoesNotSetOnConst) {
     auto* var = Let("var", ty.i32(), Construct(ty.i32()));
     auto* stmt = Decl(var);
-    Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+    Func("func", {}, ty.void_(), {stmt});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1802,26 +1811,40 @@
     Global("call_b", ty.f32(), ast::StorageClass::kPrivate);
     Global("call_c", ty.f32(), ast::StorageClass::kPrivate);
 
-    ast::VariableList params;
-    auto* func_b = Func("b", params, ty.f32(), {Return(0_f)}, ast::AttributeList{});
-    auto* func_c = Func("c", params, ty.f32(), {Assign("second", Call("b")), Return(0_f)},
-                        ast::AttributeList{});
+    auto* func_b = Func("b", {}, ty.f32(),
+                        {
+                            Return(0_f),
+                        });
+    auto* func_c = Func("c", {}, ty.f32(),
+                        {
+                            Assign("second", Call("b")),
+                            Return(0_f),
+                        });
 
-    auto* func_a = Func("a", params, ty.f32(), {Assign("first", Call("c")), Return(0_f)},
-                        ast::AttributeList{});
+    auto* func_a = Func("a", {}, ty.f32(),
+                        {
+                            Assign("first", Call("c")),
+                            Return(0_f),
+                        });
 
-    auto* ep_1 = Func("ep_1", params, ty.void_(),
+    auto* ep_1 = Func("ep_1", {}, ty.void_(),
                       {
                           Assign("call_a", Call("a")),
                           Assign("call_b", Call("b")),
                       },
-                      ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize(1_i),
+                      });
 
-    auto* ep_2 = Func("ep_2", params, ty.void_(),
+    auto* ep_2 = Func("ep_2", {}, ty.void_(),
                       {
                           Assign("call_c", Call("c")),
                       },
-                      ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+                      {
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize(1_i),
+                      });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1875,8 +1898,8 @@
     auto fn_a = [](int level) { return "l" + std::to_string(level + 1) + "a"; };
     auto fn_b = [](int level) { return "l" + std::to_string(level + 1) + "b"; };
 
-    Func(fn_a(levels), {}, ty.void_(), {}, {});
-    Func(fn_b(levels), {}, ty.void_(), {}, {});
+    Func(fn_a(levels), {}, ty.void_(), {});
+    Func(fn_b(levels), {}, ty.void_(), {});
 
     for (int i = levels - 1; i >= 0; i--) {
         Func(fn_a(i), {}, ty.void_(),
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index a5e68ce..119c310 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -121,9 +121,10 @@
     // }
     // var a: f32 = 2.1;
 
-    auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2_f));
-
-    Func("my_func", ast::VariableList{}, ty.void_(), {Decl(var)});
+    Func("my_func", {}, ty.void_(),
+         {
+             Decl(Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2_f))),
+         });
 
     Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
 
@@ -172,15 +173,14 @@
 
     auto* var1 = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(1_f));
 
-    Func("func0", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("func0", {}, ty.void_(),
+         {
              Decl(Source{{12, 34}}, var0),
              Return(),
-         },
-         ast::AttributeList{});
+         });
 
-    Func("func1", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("func1", {}, ty.void_(),
+         {
              Decl(Source{{13, 34}}, var1),
              Return(),
          });
@@ -374,11 +374,11 @@
 
     auto* var = Var(Source{{12, 34}}, "a", ty.array<i32>(), ast::StorageClass::kNone);
 
-    Func("func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("func", {}, ty.void_(),
+         {
              Decl(var),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kVertex),
          });
 
@@ -556,17 +556,16 @@
 
     auto* param = Param(Source{{12, 34}}, "a", ty.array<i32>());
 
-    Func("func", ast::VariableList{param}, ty.void_(),
-         ast::StatementList{
+    Func("func", {param}, ty.void_(),
+         {
              Return(),
-         },
-         ast::AttributeList{});
+         });
 
-    Func("main", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("main", {}, ty.void_(),
+         {
              Return(),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kVertex),
          });
 
@@ -582,11 +581,10 @@
     auto* param =
         Param(Source{{12, 34}}, "a", ty.pointer(ty.array<i32>(), ast::StorageClass::kWorkgroup));
 
-    Func("func", ast::VariableList{param}, ty.void_(),
-         ast::StatementList{
+    Func("func", {param}, ty.void_(),
+         {
              Return(),
-         },
-         ast::AttributeList{});
+         });
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 4fe5b2b..8cc3a8f 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -65,9 +65,17 @@
     Global("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
 
-    Func(Source{{9, 10}}, "f0", ast::VariableList{}, ty.vec4<f32>(), {stmt, Return(Expr("dst"))},
-         ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
-         ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+    Func(Source{{9, 10}}, "f0", {}, ty.vec4<f32>(),
+         {
+             stmt,
+             Return(Expr("dst")),
+         },
+         {
+             Stage(ast::PipelineStage::kVertex),
+         },
+         {
+             Builtin(ast::Builtin::kPosition),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -215,7 +223,7 @@
 
     Global("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
 
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Assign(Expr(Source{{12, 34}}, "global_var"), 3.14_f),
              Return(),
@@ -289,8 +297,10 @@
 TEST_F(ResolverValidationTest, StorageClass_FunctionVariableWorkgroupClass) {
     auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
 
-    auto* stmt = Decl(var);
-    Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+    Func("func", {}, ty.void_(),
+         {
+             Decl(var),
+         });
 
     EXPECT_FALSE(r()->Resolve());
 
@@ -300,8 +310,10 @@
 TEST_F(ResolverValidationTest, StorageClass_FunctionVariableI32) {
     auto* var = Var("s", ty.i32(), ast::StorageClass::kPrivate);
 
-    auto* stmt = Decl(var);
-    Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+    Func("func", {}, ty.void_(),
+         {
+             Decl(var),
+         });
 
     EXPECT_FALSE(r()->Resolve());
 
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index 2a9e20e..3e3974c 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -264,35 +264,38 @@
     /// Creates the gammaCorrection function if needed and returns a call
     /// expression to it.
     void createGammaCorrectionFn() {
-        ast::VariableList varList = {b.Param("v", b.ty.vec3<f32>()),
-                                     b.Param("params", b.ty.type_name(gamma_transfer_struct_sym))};
-
-        ast::StatementList statementList = {
-            // let cond = abs(v) < vec3(params.D);
-            b.Decl(b.Let(
-                "cond", nullptr,
-                b.LessThan(b.Call("abs", "v"), b.vec3<f32>(b.MemberAccessor("params", "D"))))),
-            // let t = sign(v) * ((params.C * abs(v)) + params.F);
-            b.Decl(b.Let("t", nullptr,
-                         b.Mul(b.Call("sign", "v"),
-                               b.Add(b.Mul(b.MemberAccessor("params", "C"), b.Call("abs", "v")),
-                                     b.MemberAccessor("params", "F"))))),
-            // let f = (sign(v) * pow(((params.A * abs(v)) + params.B),
-            // vec3(params.G))) + params.E;
-            b.Decl(b.Let(
-                "f", nullptr,
-                b.Mul(b.Call("sign", "v"),
-                      b.Add(b.Call("pow",
-                                   b.Add(b.Mul(b.MemberAccessor("params", "A"), b.Call("abs", "v")),
-                                         b.MemberAccessor("params", "B")),
-                                   b.vec3<f32>(b.MemberAccessor("params", "G"))),
-                            b.MemberAccessor("params", "E"))))),
-            // return select(f, t, cond);
-            b.Return(b.Call("select", "f", "t", "cond"))};
-
         gamma_correction_sym = b.Symbols().New("gammaCorrection");
 
-        b.Func(gamma_correction_sym, varList, b.ty.vec3<f32>(), statementList, {});
+        b.Func(
+            gamma_correction_sym,
+            {
+                b.Param("v", b.ty.vec3<f32>()),
+                b.Param("params", b.ty.type_name(gamma_transfer_struct_sym)),
+            },
+            b.ty.vec3<f32>(),
+            {
+                // let cond = abs(v) < vec3(params.D);
+                b.Decl(b.Let(
+                    "cond", nullptr,
+                    b.LessThan(b.Call("abs", "v"), b.vec3<f32>(b.MemberAccessor("params", "D"))))),
+                // let t = sign(v) * ((params.C * abs(v)) + params.F);
+                b.Decl(b.Let("t", nullptr,
+                             b.Mul(b.Call("sign", "v"),
+                                   b.Add(b.Mul(b.MemberAccessor("params", "C"), b.Call("abs", "v")),
+                                         b.MemberAccessor("params", "F"))))),
+                // let f = (sign(v) * pow(((params.A * abs(v)) + params.B),
+                // vec3(params.G))) + params.E;
+                b.Decl(b.Let("f", nullptr,
+                             b.Mul(b.Call("sign", "v"),
+                                   b.Add(b.Call("pow",
+                                                b.Add(b.Mul(b.MemberAccessor("params", "A"),
+                                                            b.Call("abs", "v")),
+                                                      b.MemberAccessor("params", "B")),
+                                                b.vec3<f32>(b.MemberAccessor("params", "G"))),
+                                         b.MemberAccessor("params", "E"))))),
+                // return select(f, t, cond);
+                b.Return(b.Call("select", "f", "t", "cond")),
+            });
     }
 
     /// Constructs a StatementList containing all the statements making up the
@@ -375,17 +378,19 @@
             texture_sample_external_sym = b.Symbols().New("textureSampleExternal");
 
             // Emit the textureSampleExternal function.
-            ast::VariableList varList = {
-                b.Param("plane0", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
-                b.Param("plane1", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
-                b.Param("smp", b.ty.sampler(ast::SamplerKind::kSampler)),
-                b.Param("coord", b.ty.vec2(b.ty.f32())),
-                b.Param("params", b.ty.type_name(params_struct_sym))};
-
-            ast::StatementList statementList =
-                createTexFnExtStatementList(sem::BuiltinType::kTextureSampleLevel);
-
-            b.Func(texture_sample_external_sym, varList, b.ty.vec4(b.ty.f32()), statementList, {});
+            b.Func(
+                texture_sample_external_sym,
+                {
+                    b.Param("plane0", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
+                    b.Param("plane1", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
+                    b.Param("smp", b.ty.sampler(ast::SamplerKind::kSampler)),
+                    b.Param("coord", b.ty.vec2(b.ty.f32())),
+                    b.Param("params", b.ty.type_name(params_struct_sym)),
+                },
+                b.ty.vec4(b.ty.f32()),
+                {
+                    createTexFnExtStatementList(sem::BuiltinType::kTextureSampleLevel),
+                });
         }
 
         const ast::IdentifierExpression* exp = b.Expr(texture_sample_external_sym);
@@ -421,22 +426,22 @@
             texture_load_external_sym = b.Symbols().New("textureLoadExternal");
 
             // Emit the textureLoadExternal function.
-            ast::VariableList var_list = {
-                b.Param("plane0", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
-                b.Param("plane1", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
-                b.Param("coord", b.ty.vec2(b.ty.i32())),
-                b.Param("params", b.ty.type_name(params_struct_sym))};
-
-            ast::StatementList statement_list =
-                createTexFnExtStatementList(sem::BuiltinType::kTextureLoad);
-
-            b.Func(texture_load_external_sym, var_list, b.ty.vec4(b.ty.f32()), statement_list, {});
+            b.Func(
+                texture_load_external_sym,
+                {
+                    b.Param("plane0", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
+                    b.Param("plane1", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32())),
+                    b.Param("coord", b.ty.vec2(b.ty.i32())),
+                    b.Param("params", b.ty.type_name(params_struct_sym)),
+                },
+                b.ty.vec4(b.ty.f32()),
+                {
+                    createTexFnExtStatementList(sem::BuiltinType::kTextureLoad),
+                });
         }
 
-        const ast::IdentifierExpression* exp = b.Expr(texture_load_external_sym);
-        params = {plane_0_binding_param, b.Expr(syms.plane_1), ctx.Clone(expr->args[1]),
-                  b.Expr(syms.params)};
-        return b.Call(exp, params);
+        return b.Call(texture_load_external_sym, plane_0_binding_param, syms.plane_1,
+                      ctx.Clone(expr->args[1]), syms.params);
     }
 };
 
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index c5bccf2..13e32fc 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -28,7 +28,7 @@
 using GlslGeneratorImplTest_Function = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Function) {
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Return(),
          });
@@ -48,7 +48,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Function_Name_Collision) {
-    Func("centroid", ast::VariableList{}, ty.void_(),
+    Func("centroid", {}, ty.void_(),
          {
              Return(),
          });
@@ -64,7 +64,12 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Function_WithParams) {
-    Func("my_func", ast::VariableList{Param("a", ty.f32()), Param("b", ty.i32())}, ty.void_(),
+    Func("my_func",
+         {
+             Param("a", ty.f32()),
+             Param("b", ty.i32()),
+         },
+         ty.void_(),
          {
              Return(),
          });
@@ -84,7 +89,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_NoReturn_Void) {
-    Func("func", ast::VariableList{}, ty.void_(), {/* no explicit return */},
+    Func("func", {}, ty.void_(), {/* no explicit return */},
          {
              Stage(ast::PipelineStage::kFragment),
          });
@@ -121,9 +126,20 @@
     // fn frag_main(@location(0) foo : f32) -> @location(1) f32 {
     //   return foo;
     // }
-    auto* foo_in = Param("foo", ty.f32(), {Location(0)});
-    Func("frag_main", ast::VariableList{foo_in}, ty.f32(), {Return("foo")},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(1)});
+    Func("frag_main",
+         {
+             Param("foo", ty.f32(), {Location(0)}),
+         },
+         ty.f32(),
+         {
+             Return("foo"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(1),
+         });
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -150,8 +166,20 @@
     //   return coord.x;
     // }
     auto* coord_in = Param("coord", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
-    Func("frag_main", ast::VariableList{coord_in}, ty.f32(), {Return(MemberAccessor("coord", "x"))},
-         {Stage(ast::PipelineStage::kFragment)}, {Builtin(ast::Builtin::kFragDepth)});
+    Func("frag_main",
+         {
+             coord_in,
+         },
+         ty.f32(),
+         {
+             Return(MemberAccessor("coord", "x")),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Builtin(ast::Builtin::kFragDepth),
+         });
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -384,7 +412,7 @@
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone,
                     MemberAccessor(MemberAccessor("uniforms", "coord"), "x"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -428,7 +456,7 @@
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -478,7 +506,7 @@
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -527,7 +555,7 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Assign(MemberAccessor("coord", "b"), Expr(2_f)),
              Return(),
@@ -575,7 +603,7 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Assign(MemberAccessor("coord", "b"), Expr(2_f)),
              Return(),
@@ -619,14 +647,14 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("sub_func", ast::VariableList{Param("param", ty.f32())}, ty.f32(),
+    Func("sub_func", {Param("param", ty.f32())}, ty.f32(),
          {
              Return(MemberAccessor("coord", "x")),
          });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, Call("sub_func", 1_f));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -668,14 +696,14 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("sub_func", ast::VariableList{Param("param", ty.f32())}, ty.f32(),
+    Func("sub_func", {Param("param", ty.f32())}, ty.f32(),
          {
              Return(MemberAccessor("coord", "x")),
          });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, Call("sub_func", 1_f));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -715,7 +743,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_WithNameCollision) {
-    Func("centroid", ast::VariableList{}, ty.void_(), {},
+    Func("centroid", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kFragment),
          });
@@ -737,11 +765,14 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute) {
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Return(),
          },
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+         {
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
     GeneratorImpl& gen = Build();
 
@@ -756,7 +787,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Literal) {
-    Func("main", ast::VariableList{}, ty.void_(), {},
+    Func("main", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(2_i, 4_i, 6_i),
@@ -778,7 +809,7 @@
     GlobalConst("width", ty.i32(), Construct(ty.i32(), 2_i));
     GlobalConst("height", ty.i32(), Construct(ty.i32(), 3_i));
     GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4_i));
-    Func("main", ast::VariableList{}, ty.void_(), {},
+    Func("main", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize("width", "height", "depth"),
@@ -804,7 +835,7 @@
     Override("width", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
     Override("height", ty.i32(), Construct(ty.i32(), 3_i), {Id(8u)});
     Override("depth", ty.i32(), Construct(ty.i32(), 4_i), {Id(9u)});
-    Func("main", ast::VariableList{}, ty.void_(), {},
+    Func("main", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize("width", "height", "depth"),
@@ -835,7 +866,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
-    Func("my_func", ast::VariableList{Param("a", ty.array<f32, 5>())}, ty.void_(),
+    Func("my_func", {Param("a", ty.array<f32, 5>())}, ty.void_(),
          {
              Return(),
          });
@@ -900,23 +931,29 @@
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("a", ast::VariableList{}, ty.void_(),
+        Func("a", {}, ty.void_(),
              {
                  Decl(var),
                  Return(),
              },
-             {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+             {
+                 Stage(ast::PipelineStage::kCompute),
+                 WorkgroupSize(1_i),
+             });
     }
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("b", ast::VariableList{}, ty.void_(),
+        Func("b", {}, ty.void_(),
              {
                  Decl(var),
                  Return(),
              },
-             {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+             {
+                 Stage(ast::PipelineStage::kCompute),
+                 WorkgroupSize(1_i),
+             });
     }
 
     GeneratorImpl& gen = SanitizeAndBuild();
diff --git a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
index 4f7fdb1..fcfa9df 100644
--- a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
@@ -100,8 +100,8 @@
 
     void SetupFunction(ast::StatementList statements) {
         ProgramBuilder& b = *this;
-        b.Func("main", ast::VariableList{}, b.ty.void_(), statements,
-               ast::AttributeList{
+        b.Func("main", {}, b.ty.void_(), statements,
+               {
                    b.Stage(ast::PipelineStage::kFragment),
                });
     }
diff --git a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
index 6575c56..f2b57c7 100644
--- a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
@@ -32,12 +32,12 @@
                create<ast::GroupAttribute>(2),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -75,12 +75,12 @@
                create<ast::GroupAttribute>(2),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -120,13 +120,13 @@
     auto* p = Let("p", nullptr, AddressOf("b"));
     auto* p2 = Let("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(p),
              Decl(p2),
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone, Call("arrayLength", p2))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -159,7 +159,7 @@
     auto* array_index = IndexAccessor(array_init, 3_i);
     auto* pos = Var("pos", ty.i32(), ast::StorageClass::kNone, array_index);
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(pos),
          },
@@ -198,7 +198,7 @@
     auto* struct_access = MemberAccessor(struct_init, "b");
     auto* pos = Var("pos", ty.vec3<f32>(), ast::StorageClass::kNone, struct_access);
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(pos),
          },
@@ -241,7 +241,7 @@
     auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), AddressOf(v));
     auto* x = Var("x", ty.i32(), ast::StorageClass::kNone, Deref(p));
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(v),
              Decl(p),
@@ -287,7 +287,7 @@
                    AddressOf(IndexAccessor(Deref(mp), 2_i)));
     auto* v = Var("v", ty.vec4<f32>(), ast::StorageClass::kNone, Deref(vp));
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(a),
              Decl(ap),
diff --git a/src/tint/writer/glsl/generator_impl_test.cc b/src/tint/writer/glsl/generator_impl_test.cc
index af2e205..70e5c0b 100644
--- a/src/tint/writer/glsl/generator_impl_test.cc
+++ b/src/tint/writer/glsl/generator_impl_test.cc
@@ -29,7 +29,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest, Generate) {
-    Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
+    Func("my_func", {}, ty.void_(), {});
 
     GeneratorImpl& gen = Build();
 
@@ -43,7 +43,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest, GenerateDesktop) {
-    Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
+    Func("my_func", {}, ty.void_(), {});
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
@@ -58,10 +58,15 @@
 
 TEST_F(GlslGeneratorImplTest, GenerateSampleIndexES) {
     Global("gl_SampleID", ty.i32(),
-           ast::AttributeList{Builtin(ast::Builtin::kSampleIndex),
-                              Disable(ast::DisabledValidation::kIgnoreStorageClass)},
+           ast::AttributeList{
+               Builtin(ast::Builtin::kSampleIndex),
+               Disable(ast::DisabledValidation::kIgnoreStorageClass),
+           },
            ast::StorageClass::kInput);
-    Func("my_func", {}, ty.i32(), ast::StatementList{Return(Expr("gl_SampleID"))});
+    Func("my_func", {}, ty.i32(),
+         {
+             Return(Expr("gl_SampleID")),
+         });
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kES, 3, 1));
 
@@ -78,10 +83,15 @@
 
 TEST_F(GlslGeneratorImplTest, GenerateSampleIndexDesktop) {
     Global("gl_SampleID", ty.i32(),
-           ast::AttributeList{Builtin(ast::Builtin::kSampleIndex),
-                              Disable(ast::DisabledValidation::kIgnoreStorageClass)},
+           ast::AttributeList{
+               Builtin(ast::Builtin::kSampleIndex),
+               Disable(ast::DisabledValidation::kIgnoreStorageClass),
+           },
            ast::StorageClass::kInput);
-    Func("my_func", {}, ty.i32(), ast::StatementList{Return(Expr("gl_SampleID"))});
+    Func("my_func", {}, ty.i32(),
+         {
+             Return(Expr("gl_SampleID")),
+         });
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
diff --git a/src/tint/writer/hlsl/generator_impl_function_test.cc b/src/tint/writer/hlsl/generator_impl_function_test.cc
index c994b35..dac42d0 100644
--- a/src/tint/writer/hlsl/generator_impl_function_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_function_test.cc
@@ -28,7 +28,7 @@
 using HlslGeneratorImplTest_Function = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function) {
-    Func("my_func", ast::VariableList{}, ty.void_(),
+    Func("my_func", {}, ty.void_(),
          {
              Return(),
          });
@@ -45,7 +45,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function_Name_Collision) {
-    Func("GeometryShader", ast::VariableList{}, ty.void_(),
+    Func("GeometryShader", {}, ty.void_(),
          {
              Return(),
          });
@@ -61,7 +61,12 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithParams) {
-    Func("my_func", ast::VariableList{Param("a", ty.f32()), Param("b", ty.i32())}, ty.void_(),
+    Func("my_func",
+         {
+             Param("a", ty.f32()),
+             Param("b", ty.i32()),
+         },
+         ty.void_(),
          {
              Return(),
          });
@@ -78,7 +83,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_NoReturn_Void) {
-    Func("main", ast::VariableList{}, ty.void_(), {/* no explicit return */},
+    Func("main", {}, ty.void_(), {/* no explicit return */},
          {
              Stage(ast::PipelineStage::kFragment),
          });
@@ -113,8 +118,16 @@
     //   return foo;
     // }
     auto* foo_in = Param("foo", ty.f32(), {Location(0)});
-    Func("frag_main", ast::VariableList{foo_in}, ty.f32(), {Return("foo")},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(1)});
+    Func("frag_main", {foo_in}, ty.f32(),
+         {
+             Return("foo"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(1),
+         });
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -144,8 +157,16 @@
     //   return coord.x;
     // }
     auto* coord_in = Param("coord", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
-    Func("frag_main", ast::VariableList{coord_in}, ty.f32(), {Return(MemberAccessor("coord", "x"))},
-         {Stage(ast::PipelineStage::kFragment)}, {Builtin(ast::Builtin::kFragDepth)});
+    Func("frag_main", {coord_in}, ty.f32(),
+         {
+             Return(MemberAccessor("coord", "x")),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Builtin(ast::Builtin::kFragDepth),
+         });
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -381,7 +402,7 @@
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone,
                     MemberAccessor(MemberAccessor("uniforms", "coord"), "x"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -418,7 +439,7 @@
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -454,7 +475,7 @@
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -488,7 +509,7 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Assign(MemberAccessor("coord", "b"), Expr(2_f)),
              Return(),
@@ -522,7 +543,7 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Assign(MemberAccessor("coord", "b"), Expr(2_f)),
              Return(),
@@ -552,14 +573,18 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("sub_func", ast::VariableList{Param("param", ty.f32())}, ty.f32(),
+    Func("sub_func",
+         {
+             Param("param", ty.f32()),
+         },
+         ty.f32(),
          {
              Return(MemberAccessor("coord", "x")),
          });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, Call("sub_func", 1_f));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -594,14 +619,18 @@
                create<ast::GroupAttribute>(1),
            });
 
-    Func("sub_func", ast::VariableList{Param("param", ty.f32())}, ty.f32(),
+    Func("sub_func",
+         {
+             Param("param", ty.f32()),
+         },
+         ty.f32(),
          {
              Return(MemberAccessor("coord", "x")),
          });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, Call("sub_func", 1_f));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
+    Func("frag_main", {}, ty.void_(),
          {
              Decl(var),
              Return(),
@@ -628,7 +657,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_WithNameCollision) {
-    Func("GeometryShader", ast::VariableList{}, ty.void_(), {},
+    Func("GeometryShader", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kFragment),
          });
@@ -643,7 +672,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute) {
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Return(),
          },
@@ -660,7 +689,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Literal) {
-    Func("main", ast::VariableList{}, ty.void_(), {},
+    Func("main", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(2_i, 4_i, 6_i),
@@ -680,7 +709,7 @@
     GlobalConst("width", ty.i32(), Construct(ty.i32(), 2_i));
     GlobalConst("height", ty.i32(), Construct(ty.i32(), 3_i));
     GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4_i));
-    Func("main", ast::VariableList{}, ty.void_(), {},
+    Func("main", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize("width", "height", "depth"),
@@ -705,7 +734,7 @@
     Override("width", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
     Override("height", ty.i32(), Construct(ty.i32(), 3_i), {Id(8u)});
     Override("depth", ty.i32(), Construct(ty.i32(), 4_i), {Id(9u)});
-    Func("main", ast::VariableList{}, ty.void_(), {},
+    Func("main", {}, ty.void_(), {},
          {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize("width", "height", "depth"),
@@ -735,7 +764,11 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
-    Func("my_func", ast::VariableList{Param("a", ty.array<f32, 5>())}, ty.void_(),
+    Func("my_func",
+         {
+             Param("a", ty.array<f32, 5>()),
+         },
+         ty.void_(),
          {
              Return(),
          });
@@ -839,23 +872,29 @@
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("a", ast::VariableList{}, ty.void_(),
+        Func("a", {}, ty.void_(),
              {
                  Decl(var),
                  Return(),
              },
-             {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+             {
+                 Stage(ast::PipelineStage::kCompute),
+                 WorkgroupSize(1_i),
+             });
     }
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("b", ast::VariableList{}, ty.void_(),
+        Func("b", {}, ty.void_(),
              {
                  Decl(var),
                  Return(),
              },
-             {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+             {
+                 Stage(ast::PipelineStage::kCompute),
+                 WorkgroupSize(1_i),
+             });
     }
 
     GeneratorImpl& gen = SanitizeAndBuild();
diff --git a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
index 10e9f10..ffa64fd 100644
--- a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -100,8 +100,8 @@
 
     void SetupFunction(ast::StatementList statements) {
         ProgramBuilder& b = *this;
-        b.Func("main", ast::VariableList{}, b.ty.void_(), statements,
-               ast::AttributeList{
+        b.Func("main", {}, b.ty.void_(), statements,
+               {
                    b.Stage(ast::PipelineStage::kFragment),
                });
     }
diff --git a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
index 66df535..e7b81e4 100644
--- a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
@@ -32,12 +32,12 @@
                create<ast::GroupAttribute>(2),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -70,12 +70,12 @@
                create<ast::GroupAttribute>(2),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -109,13 +109,13 @@
     auto* p = Let("p", nullptr, AddressOf("b"));
     auto* p2 = Let("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(p),
              Decl(p2),
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone, Call("arrayLength", p2))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -151,13 +151,13 @@
                create<ast::GroupAttribute>(2),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Add(Call("arrayLength", AddressOf(MemberAccessor("b", "a"))),
                           Call("arrayLength", AddressOf(MemberAccessor("c", "a")))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -191,7 +191,7 @@
     auto* array_index = IndexAccessor(array_init, 3_i);
     auto* pos = Var("pos", ty.i32(), ast::StorageClass::kNone, array_index);
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(pos),
          },
@@ -223,7 +223,7 @@
     auto* struct_access = MemberAccessor(struct_init, "b");
     auto* pos = Var("pos", ty.vec3<f32>(), ast::StorageClass::kNone, struct_access);
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(pos),
          },
@@ -259,7 +259,7 @@
     auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), AddressOf(v));
     auto* x = Var("x", ty.i32(), ast::StorageClass::kNone, Deref(p));
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(v),
              Decl(p),
@@ -298,7 +298,7 @@
                    AddressOf(IndexAccessor(Deref(mp), 2_i)));
     auto* v = Var("v", ty.vec4<f32>(), ast::StorageClass::kNone, Deref(vp));
 
-    Func("main", ast::VariableList{}, ty.void_(),
+    Func("main", {}, ty.void_(),
          {
              Decl(a),
              Decl(ap),
diff --git a/src/tint/writer/hlsl/generator_impl_test.cc b/src/tint/writer/hlsl/generator_impl_test.cc
index a1e0edb..997f51a 100644
--- a/src/tint/writer/hlsl/generator_impl_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_test.cc
@@ -29,7 +29,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest, Generate) {
-    Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
+    Func("my_func", {}, ty.void_(), {});
 
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/msl/generator_impl_function_test.cc b/src/tint/writer/msl/generator_impl_function_test.cc
index 3c78522..f7d2779 100644
--- a/src/tint/writer/msl/generator_impl_function_test.cc
+++ b/src/tint/writer/msl/generator_impl_function_test.cc
@@ -24,11 +24,10 @@
 using MslGeneratorImplTest = TestHelper;
 
 TEST_F(MslGeneratorImplTest, Emit_Function) {
-    Func("my_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("my_func", {}, ty.void_(),
+         {
              Return(),
-         },
-         {});
+         });
 
     GeneratorImpl& gen = Build();
 
@@ -46,15 +45,15 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_Function_WithParams) {
-    ast::VariableList params;
-    params.push_back(Param("a", ty.f32()));
-    params.push_back(Param("b", ty.i32()));
-
-    Func("my_func", params, ty.void_(),
-         ast::StatementList{
-             Return(),
+    Func("my_func",
+         {
+             Param("a", ty.f32()),
+             Param("b", ty.i32()),
          },
-         {});
+         ty.void_(),
+         {
+             Return(),
+         });
 
     GeneratorImpl& gen = Build();
 
@@ -72,7 +71,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_Attribute_EntryPoint_NoReturn_Void) {
-    Func("main", ast::VariableList{}, ty.void_(), ast::StatementList{/* no explicit return */},
+    Func("main", {}, ty.void_(), {/* no explicit return */},
          {Stage(ast::PipelineStage::kFragment)});
 
     GeneratorImpl& gen = Build();
@@ -93,8 +92,16 @@
     //   return foo;
     // }
     auto* foo_in = Param("foo", ty.f32(), {Location(0)});
-    Func("frag_main", ast::VariableList{foo_in}, ty.f32(), {Return("foo")},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(1)});
+    Func("frag_main", {foo_in}, ty.f32(),
+         {
+             Return("foo"),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Location(1),
+         });
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -129,8 +136,16 @@
     //   return coord.x;
     // }
     auto* coord_in = Param("coord", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)});
-    Func("frag_main", ast::VariableList{coord_in}, ty.f32(), {Return(MemberAccessor("coord", "x"))},
-         {Stage(ast::PipelineStage::kFragment)}, {Builtin(ast::Builtin::kFragDepth)});
+    Func("frag_main", {coord_in}, ty.f32(),
+         {
+             Return(MemberAccessor("coord", "x")),
+         },
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
+             Builtin(ast::Builtin::kFragDepth),
+         });
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -256,10 +271,10 @@
         "VertexOutput", {Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
 
     Func("foo", {Param("x", ty.f32())}, ty.Of(vertex_output_struct),
-         {Return(Construct(ty.Of(vertex_output_struct),
-                           Construct(ty.vec4<f32>(), "x", "x", "x", Expr(1_f))))},
-         {});
-
+         {
+             Return(Construct(ty.Of(vertex_output_struct),
+                              Construct(ty.vec4<f32>(), "x", "x", "x", Expr(1_f)))),
+         });
     Func("vert_main1", {}, ty.Of(vertex_output_struct), {Return(Expr(Call("foo", Expr(0.5_f))))},
          {Stage(ast::PipelineStage::kVertex)});
 
@@ -328,8 +343,8 @@
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("frag_main", {}, ty.void_(),
+         {
              Decl(var),
              Return(),
          },
@@ -370,8 +385,8 @@
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("frag_main", {}, ty.void_(),
+         {
              Decl(var),
              Return(),
          },
@@ -460,17 +475,19 @@
                create<ast::GroupAttribute>(0),
            });
 
-    ast::VariableList params;
-    params.push_back(Param("param", ty.f32()));
-
-    auto body = ast::StatementList{Return(MemberAccessor("coord", "b"))};
-
-    Func("sub_func", params, ty.f32(), body, {});
+    Func("sub_func",
+         {
+             Param("param", ty.f32()),
+         },
+         ty.f32(),
+         {
+             Return(MemberAccessor("coord", "b")),
+         });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, Call("sub_func", 1_f));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("frag_main", {}, ty.void_(),
+         {
              Decl(var),
              Return(),
          },
@@ -513,17 +530,19 @@
                create<ast::GroupAttribute>(0),
            });
 
-    ast::VariableList params;
-    params.push_back(Param("param", ty.f32()));
-
-    auto body = ast::StatementList{Return(MemberAccessor("coord", "b"))};
-
-    Func("sub_func", params, ty.f32(), body, {});
+    Func("sub_func",
+         {
+             Param("param", ty.f32()),
+         },
+         ty.f32(),
+         {
+             Return(MemberAccessor("coord", "b")),
+         });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, Call("sub_func", 1_f));
 
-    Func("frag_main", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("frag_main", {}, ty.void_(),
+         {
              Decl(var),
              Return(),
          },
@@ -555,10 +574,11 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_Function_WithArrayParams) {
-    ast::VariableList params;
-    params.push_back(Param("a", ty.array<f32, 5>()));
-
-    Func("my_func", params, ty.void_(),
+    Func("my_func",
+         {
+             Param("a", ty.array<f32, 5>()),
+         },
+         ty.void_(),
          {
              Return(),
          });
@@ -636,8 +656,8 @@
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("a", ast::VariableList{}, ty.void_(),
-             ast::StatementList{
+        Func("a", {}, ty.void_(),
+             {
                  Decl(var),
                  Return(),
              },
@@ -650,7 +670,7 @@
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("b", ast::VariableList{}, ty.void_(), ast::StatementList{Decl(var), Return()},
+        Func("b", {}, ty.void_(), {Decl(var), Return()},
              {
                  Stage(ast::PipelineStage::kCompute),
                  WorkgroupSize(1_i),
diff --git a/src/tint/writer/msl/generator_impl_sanitizer_test.cc b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
index 32ddb42..475025c 100644
--- a/src/tint/writer/msl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
@@ -33,12 +33,12 @@
                create<ast::GroupAttribute>(2),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -78,12 +78,12 @@
                create<ast::GroupAttribute>(2),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -125,13 +125,13 @@
     auto* p = Let("p", nullptr, AddressOf("b"));
     auto* p2 = Let("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(p),
              Decl(p2),
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone, Call("arrayLength", p2))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -174,13 +174,13 @@
                create<ast::GroupAttribute>(0),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Add(Call("arrayLength", AddressOf(MemberAccessor("b", "a"))),
                           Call("arrayLength", AddressOf(MemberAccessor("c", "a")))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -226,13 +226,13 @@
                create<ast::GroupAttribute>(0),
            });
 
-    Func("a_func", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("a_func", {}, ty.void_(),
+         {
              Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
                       Add(Call("arrayLength", AddressOf(MemberAccessor("b", "a"))),
                           Call("arrayLength", AddressOf(MemberAccessor("c", "a")))))),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
diff --git a/src/tint/writer/msl/generator_impl_test.cc b/src/tint/writer/msl/generator_impl_test.cc
index 7cef268..183fef5 100644
--- a/src/tint/writer/msl/generator_impl_test.cc
+++ b/src/tint/writer/msl/generator_impl_test.cc
@@ -32,8 +32,8 @@
 }
 
 TEST_F(MslGeneratorImplTest, Generate) {
-    Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{},
-         ast::AttributeList{
+    Func("my_func", {}, ty.void_(), {},
+         {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(1_i),
          });
@@ -88,7 +88,7 @@
 TEST_F(MslGeneratorImplTest, HasInvariantAttribute_True) {
     auto* out = Structure(
         "Out", {Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition), Invariant()})});
-    Func("vert_main", ast::VariableList{}, ty.Of(out), {Return(Construct(ty.Of(out)))},
+    Func("vert_main", {}, ty.Of(out), {Return(Construct(ty.Of(out)))},
          {Stage(ast::PipelineStage::kVertex)});
 
     GeneratorImpl& gen = Build();
@@ -119,7 +119,7 @@
 TEST_F(MslGeneratorImplTest, HasInvariantAttribute_False) {
     auto* out =
         Structure("Out", {Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
-    Func("vert_main", ast::VariableList{}, ty.Of(out), {Return(Construct(ty.Of(out)))},
+    Func("vert_main", {}, ty.Of(out), {Return(Construct(ty.Of(out)))},
          {Stage(ast::PipelineStage::kVertex)});
 
     GeneratorImpl& gen = Build();
@@ -142,7 +142,7 @@
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrix) {
     Global("m", ty.mat2x2<f32>(), ast::StorageClass::kWorkgroup);
-    Func("comp_main", ast::VariableList{}, ty.void_(), {Decl(Let("x", nullptr, Expr("m")))},
+    Func("comp_main", {}, ty.void_(), {Decl(Let("x", nullptr, Expr("m")))},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -179,7 +179,7 @@
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrixInArray) {
     Global("m", ty.array(ty.mat2x2<f32>(), 4_i), ast::StorageClass::kWorkgroup);
-    Func("comp_main", ast::VariableList{}, ty.void_(), {Decl(Let("x", nullptr, Expr("m")))},
+    Func("comp_main", {}, ty.void_(), {Decl(Let("x", nullptr, Expr("m")))},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -228,7 +228,7 @@
                         Member("s", ty.type_name("S1")),
                     });
     Global("s", ty.type_name("S2"), ast::StorageClass::kWorkgroup);
-    Func("comp_main", ast::VariableList{}, ty.void_(), {Decl(Let("x", nullptr, Expr("s")))},
+    Func("comp_main", {}, ty.void_(), {Decl(Let("x", nullptr, Expr("s")))},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -283,28 +283,28 @@
     Global("m7", ty.mat4x2<f32>(), ast::StorageClass::kWorkgroup);
     Global("m8", ty.mat4x3<f32>(), ast::StorageClass::kWorkgroup);
     Global("m9", ty.mat4x4<f32>(), ast::StorageClass::kWorkgroup);
-    Func("main1", ast::VariableList{}, ty.void_(),
+    Func("main1", {}, ty.void_(),
          {
              Decl(Let("a1", nullptr, Expr("m1"))),
              Decl(Let("a2", nullptr, Expr("m2"))),
              Decl(Let("a3", nullptr, Expr("m3"))),
          },
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
-    Func("main2", ast::VariableList{}, ty.void_(),
+    Func("main2", {}, ty.void_(),
          {
              Decl(Let("a1", nullptr, Expr("m4"))),
              Decl(Let("a2", nullptr, Expr("m5"))),
              Decl(Let("a3", nullptr, Expr("m6"))),
          },
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
-    Func("main3", ast::VariableList{}, ty.void_(),
+    Func("main3", {}, ty.void_(),
          {
              Decl(Let("a1", nullptr, Expr("m7"))),
              Decl(Let("a2", nullptr, Expr("m8"))),
              Decl(Let("a3", nullptr, Expr("m9"))),
          },
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
-    Func("main4_no_usages", ast::VariableList{}, ty.void_(), {},
+    Func("main4_no_usages", {}, ty.void_(), {},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
     GeneratorImpl& gen = SanitizeAndBuild();
diff --git a/src/tint/writer/spirv/builder_call_test.cc b/src/tint/writer/spirv/builder_call_test.cc
index b81ca96..73bc4fd 100644
--- a/src/tint/writer/spirv/builder_call_test.cc
+++ b/src/tint/writer/spirv/builder_call_test.cc
@@ -25,11 +25,12 @@
 using BuilderTest = TestHelper;
 
 TEST_F(BuilderTest, Expression_Call) {
-    ast::VariableList func_params;
-    func_params.push_back(Param("a", ty.f32()));
-    func_params.push_back(Param("b", ty.f32()));
-
-    auto* a_func = Func("a_func", func_params, ty.f32(), {Return(Add("a", "b"))});
+    auto* a_func = Func("a_func",
+                        {
+                            Param("a", ty.f32()),
+                            Param("b", ty.f32()),
+                        },
+                        ty.f32(), {Return(Add("a", "b"))});
     auto* func = Func("main", {}, ty.void_(), {Assign(Phony(), Call("a_func", 1_f, 1_f))});
 
     spirv::Builder& b = Build();
@@ -62,11 +63,12 @@
 }
 
 TEST_F(BuilderTest, Statement_Call) {
-    ast::VariableList func_params;
-    func_params.push_back(Param("a", ty.f32()));
-    func_params.push_back(Param("b", ty.f32()));
-
-    auto* a_func = Func("a_func", func_params, ty.f32(), {Return(Add("a", "b"))});
+    auto* a_func = Func("a_func",
+                        {
+                            Param("a", ty.f32()),
+                            Param("b", ty.f32()),
+                        },
+                        ty.f32(), {Return(Add("a", "b"))});
 
     auto* func = Func("main", {}, ty.void_(), {CallStmt(Call("a_func", 1_f, 1_f))});
 
diff --git a/src/tint/writer/spirv/builder_entry_point_test.cc b/src/tint/writer/spirv/builder_entry_point_test.cc
index a407aa7..8cbf2cc 100644
--- a/src/tint/writer/spirv/builder_entry_point_test.cc
+++ b/src/tint/writer/spirv/builder_entry_point_test.cc
@@ -46,9 +46,8 @@
     auto* loc1 = Param("loc1", ty.f32(), {Location(1u)});
     auto* mul = Mul(Expr(MemberAccessor("coord", "x")), Expr("loc1"));
     auto* col = Var("col", ty.f32(), ast::StorageClass::kNone, mul);
-    Func("frag_main", ast::VariableList{coord, loc1}, ty.void_(),
-         ast::StatementList{WrapInStatement(col)},
-         ast::AttributeList{
+    Func("frag_main", {coord, loc1}, ty.void_(), {WrapInStatement(col)},
+         {
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -116,15 +115,15 @@
     auto* loc_in = Param("loc_in", ty.u32(), {Location(0), Flat()});
     auto* cond =
         create<ast::BinaryExpression>(ast::BinaryOp::kGreaterThan, Expr("loc_in"), Expr(10_u));
-    Func("frag_main", ast::VariableList{loc_in}, ty.f32(),
-         ast::StatementList{
+    Func("frag_main", {loc_in}, ty.f32(),
+         {
              If(cond, Block(Return(0.5_f))),
              Return(1_f),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kFragment),
          },
-         ast::AttributeList{Location(0)});
+         {Location(0)});
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -198,20 +197,21 @@
     // }
 
     auto* interface = Structure(
-        "Interface",
-        {
-            Member("value", ty.f32(), ast::AttributeList{Location(1u)}),
-            Member("pos", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)}),
-        });
+        "Interface", {
+                         Member("value", ty.f32(), {Location(1u)}),
+                         Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
+                     });
 
     auto* vert_retval = Construct(ty.Of(interface), 42_f, Construct(ty.vec4<f32>()));
-    Func("vert_main", ast::VariableList{}, ty.Of(interface), {Return(vert_retval)},
+    Func("vert_main", {}, ty.Of(interface), {Return(vert_retval)},
          {Stage(ast::PipelineStage::kVertex)});
 
     auto* frag_inputs = Param("inputs", ty.Of(interface));
-    Func("frag_main", ast::VariableList{frag_inputs}, ty.f32(),
-         {Return(MemberAccessor(Expr("inputs"), "value"))}, {Stage(ast::PipelineStage::kFragment)},
-         {Builtin(ast::Builtin::kFragDepth)});
+    Func("frag_main", {frag_inputs}, ty.f32(),
+         {
+             Return(MemberAccessor(Expr("inputs"), "value")),
+         },
+         {Stage(ast::PipelineStage::kFragment)}, {Builtin(ast::Builtin::kFragDepth)});
 
     spirv::Builder& b = SanitizeAndBuild();
 
diff --git a/src/tint/writer/spirv/builder_function_attribute_test.cc b/src/tint/writer/spirv/builder_function_attribute_test.cc
index e5bb43d..c309813 100644
--- a/src/tint/writer/spirv/builder_function_attribute_test.cc
+++ b/src/tint/writer/spirv/builder_function_attribute_test.cc
@@ -25,8 +25,8 @@
 using BuilderTest = TestHelper;
 
 TEST_F(BuilderTest, Attribute_Stage) {
-    auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
                           Stage(ast::PipelineStage::kFragment),
                       });
 
@@ -91,8 +91,8 @@
                     FunctionStageData{ast::PipelineStage::kCompute, SpvExecutionModelGLCompute}));
 
 TEST_F(BuilderTest, Decoration_ExecutionMode_Fragment_OriginUpperLeft) {
-    auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
                           Stage(ast::PipelineStage::kFragment),
                       });
 
@@ -105,8 +105,8 @@
 }
 
 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Default) {
-    auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    auto* func =
+        Func("main", {}, ty.void_(), {}, {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
     spirv::Builder& b = Build();
 
@@ -117,8 +117,8 @@
 }
 
 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_Literals) {
-    auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
                           WorkgroupSize(2_i, 4_i, 6_i),
                           Stage(ast::PipelineStage::kCompute),
                       });
@@ -135,8 +135,8 @@
     GlobalConst("width", ty.i32(), Construct(ty.i32(), 2_i));
     GlobalConst("height", ty.i32(), Construct(ty.i32(), 3_i));
     GlobalConst("depth", ty.i32(), Construct(ty.i32(), 4_i));
-    auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
                           WorkgroupSize("width", "height", "depth"),
                           Stage(ast::PipelineStage::kCompute),
                       });
@@ -153,8 +153,8 @@
     Override("width", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
     Override("height", ty.i32(), Construct(ty.i32(), 3_i), {Id(8u)});
     Override("depth", ty.i32(), Construct(ty.i32(), 4_i), {Id(9u)});
-    auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
                           WorkgroupSize("width", "height", "depth"),
                           Stage(ast::PipelineStage::kCompute),
                       });
@@ -182,8 +182,8 @@
 TEST_F(BuilderTest, Decoration_ExecutionMode_WorkgroupSize_LiteralAndConst) {
     Override("height", ty.i32(), Construct(ty.i32(), 2_i), {Id(7u)});
     GlobalConst("depth", ty.i32(), Construct(ty.i32(), 3_i));
-    auto* func = Func("main", {}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{
+    auto* func = Func("main", {}, ty.void_(), {},
+                      {
                           WorkgroupSize(4_i, "height", "depth"),
                           Stage(ast::PipelineStage::kCompute),
                       });
@@ -207,13 +207,13 @@
 }
 
 TEST_F(BuilderTest, Decoration_ExecutionMode_MultipleFragment) {
-    auto* func1 = Func("main1", {}, ty.void_(), ast::StatementList{},
-                       ast::AttributeList{
+    auto* func1 = Func("main1", {}, ty.void_(), {},
+                       {
                            Stage(ast::PipelineStage::kFragment),
                        });
 
-    auto* func2 = Func("main2", {}, ty.void_(), ast::StatementList{},
-                       ast::AttributeList{
+    auto* func2 = Func("main2", {}, ty.void_(), {},
+                       {
                            Stage(ast::PipelineStage::kFragment),
                        });
 
@@ -242,12 +242,14 @@
 }
 
 TEST_F(BuilderTest, Decoration_ExecutionMode_FragDepth) {
-    Func("main", ast::VariableList{}, ty.f32(),
-         ast::StatementList{
+    Func("main", {}, ty.f32(),
+         {
              Return(Expr(1_f)),
          },
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
-         ast::AttributeList{
+         {
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {
              Builtin(ast::Builtin::kFragDepth),
          });
 
diff --git a/src/tint/writer/spirv/builder_function_test.cc b/src/tint/writer/spirv/builder_function_test.cc
index 2fd1c32..b2744ef 100644
--- a/src/tint/writer/spirv/builder_function_test.cc
+++ b/src/tint/writer/spirv/builder_function_test.cc
@@ -24,7 +24,7 @@
 using BuilderTest = TestHelper;
 
 TEST_F(BuilderTest, Function_Empty) {
-    Func("a_func", {}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
+    Func("a_func", {}, ty.void_(), {});
 
     spirv::Builder& b = Build();
 
@@ -42,10 +42,9 @@
 
 TEST_F(BuilderTest, Function_Terminator_Return) {
     Func("a_func", {}, ty.void_(),
-         ast::StatementList{
+         {
              Return(),
-         },
-         ast::AttributeList{});
+         });
 
     spirv::Builder& b = Build();
 
@@ -64,7 +63,7 @@
 TEST_F(BuilderTest, Function_Terminator_ReturnValue) {
     Global("a", ty.f32(), ast::StorageClass::kPrivate);
 
-    Func("a_func", {}, ty.f32(), ast::StatementList{Return("a")}, ast::AttributeList{});
+    Func("a_func", {}, ty.f32(), {Return("a")}, {});
 
     spirv::Builder& b = Build();
 
@@ -90,10 +89,9 @@
 
 TEST_F(BuilderTest, Function_Terminator_Discard) {
     Func("a_func", {}, ty.void_(),
-         ast::StatementList{
+         {
              create<ast::DiscardStatement>(),
-         },
-         ast::AttributeList{});
+         });
 
     spirv::Builder& b = Build();
 
@@ -110,9 +108,12 @@
 }
 
 TEST_F(BuilderTest, Function_WithParams) {
-    ast::VariableList params = {Param("a", ty.f32()), Param("b", ty.i32())};
-
-    Func("a_func", params, ty.f32(), ast::StatementList{Return("a")}, ast::AttributeList{});
+    Func("a_func",
+         {
+             Param("a", ty.f32()),
+             Param("b", ty.i32()),
+         },
+         ty.f32(), {Return("a")}, {});
 
     spirv::Builder& b = Build();
 
@@ -135,10 +136,9 @@
 
 TEST_F(BuilderTest, Function_WithBody) {
     Func("a_func", {}, ty.void_(),
-         ast::StatementList{
+         {
              Return(),
-         },
-         ast::AttributeList{});
+         });
 
     spirv::Builder& b = Build();
 
@@ -155,7 +155,7 @@
 }
 
 TEST_F(BuilderTest, FunctionType) {
-    Func("a_func", {}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
+    Func("a_func", {}, ty.void_(), {}, {});
 
     spirv::Builder& b = Build();
 
@@ -167,8 +167,8 @@
 }
 
 TEST_F(BuilderTest, FunctionType_DeDuplicate) {
-    auto* func1 = Func("a_func", {}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
-    auto* func2 = Func("b_func", {}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
+    auto* func1 = Func("a_func", {}, ty.void_(), {}, {});
+    auto* func2 = Func("b_func", {}, ty.void_(), {}, {});
 
     spirv::Builder& b = Build();
 
@@ -207,23 +207,23 @@
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("a", ast::VariableList{}, ty.void_(),
-             ast::StatementList{
+        Func("a", {}, ty.void_(),
+             {
                  Decl(var),
                  Return(),
              },
-             ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+             {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
     }
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("b", ast::VariableList{}, ty.void_(),
-             ast::StatementList{
+        Func("b", {}, ty.void_(),
+             {
                  Decl(var),
                  Return(),
              },
-             ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+             {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
     }
 
     spirv::Builder& b = SanitizeAndBuild();
diff --git a/src/tint/writer/wgsl/generator_impl_function_test.cc b/src/tint/writer/wgsl/generator_impl_function_test.cc
index e34292b..74f4a6e 100644
--- a/src/tint/writer/wgsl/generator_impl_function_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_function_test.cc
@@ -25,11 +25,10 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Function) {
-    auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
-                      ast::StatementList{
+    auto* func = Func("my_func", {}, ty.void_(),
+                      {
                           Return(),
-                      },
-                      ast::AttributeList{});
+                      });
 
     GeneratorImpl& gen = Build();
 
@@ -43,12 +42,10 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_WithParams) {
-    auto* func =
-        Func("my_func", ast::VariableList{Param("a", ty.f32()), Param("b", ty.i32())}, ty.void_(),
-             ast::StatementList{
-                 Return(),
-             },
-             ast::AttributeList{});
+    auto* func = Func("my_func", {Param("a", ty.f32()), Param("b", ty.i32())}, ty.void_(),
+                      {
+                          Return(),
+                      });
 
     GeneratorImpl& gen = Build();
 
@@ -62,8 +59,8 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_WithAttribute_WorkgroupSize) {
-    auto* func = Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{Return()},
-                      ast::AttributeList{
+    auto* func = Func("my_func", {}, ty.void_(), {Return()},
+                      {
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize(2_i, 4_i, 6_i),
                       });
@@ -82,8 +79,8 @@
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_WithAttribute_WorkgroupSize_WithIdent) {
     GlobalConst("height", ty.i32(), Expr(2_i));
-    auto* func = Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{Return()},
-                      ast::AttributeList{
+    auto* func = Func("my_func", {}, ty.void_(), {Return()},
+                      {
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize(2_i, "height"),
                       });
@@ -104,8 +101,8 @@
     auto* vec4 = ty.vec4<f32>();
     auto* coord = Param("coord", vec4, {Builtin(ast::Builtin::kPosition)});
     auto* loc1 = Param("loc1", ty.f32(), {Location(1u)});
-    auto* func = Func("frag_main", ast::VariableList{coord, loc1}, ty.void_(), ast::StatementList{},
-                      ast::AttributeList{
+    auto* func = Func("frag_main", {coord, loc1}, ty.void_(), {},
+                      {
                           Stage(ast::PipelineStage::kFragment),
                       });
 
@@ -121,14 +118,14 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_EntryPoint_ReturnValue) {
-    auto* func = Func("frag_main", ast::VariableList{}, ty.f32(),
-                      ast::StatementList{
+    auto* func = Func("frag_main", {}, ty.f32(),
+                      {
                           Return(1_f),
                       },
-                      ast::AttributeList{
+                      {
                           Stage(ast::PipelineStage::kFragment),
                       },
-                      ast::AttributeList{
+                      {
                           Location(1u),
                       });
 
@@ -172,12 +169,12 @@
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("a", ast::VariableList{}, ty.void_(),
-             ast::StatementList{
+        Func("a", {}, ty.void_(),
+             {
                  Decl(var),
                  Return(),
              },
-             ast::AttributeList{
+             {
                  Stage(ast::PipelineStage::kCompute),
                  WorkgroupSize(1_i),
              });
@@ -186,12 +183,12 @@
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
 
-        Func("b", ast::VariableList{}, ty.void_(),
-             ast::StatementList{
+        Func("b", {}, ty.void_(),
+             {
                  Decl(var),
                  Return(),
              },
-             ast::AttributeList{
+             {
                  Stage(ast::PipelineStage::kCompute),
                  WorkgroupSize(1_i),
              });
diff --git a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
index 48f1276..8f6d4f0 100644
--- a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
@@ -49,23 +49,23 @@
 
     auto* s0 = Structure("S0", {Member("a", ty.i32())});
 
-    Func("func", ast::VariableList{}, ty.f32(),
-         ast::StatementList{
+    Func("func", {}, ty.f32(),
+         {
              Return("a0"),
          },
-         ast::AttributeList{});
+         {});
 
     Global("a1", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* s1 = Structure("S1", {Member("a", ty.i32())});
 
-    Func("main", ast::VariableList{}, ty.void_(),
-         ast::StatementList{
+    Func("main", {}, ty.void_(),
+         {
              Decl(Var("s0", ty.Of(s0))),
              Decl(Var("s1", ty.Of(s1))),
              Assign("a1", Call("func")),
          },
-         ast::AttributeList{
+         {
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(1_i),
          });
diff --git a/src/tint/writer/wgsl/generator_impl_test.cc b/src/tint/writer/wgsl/generator_impl_test.cc
index f8972c7..4d98158 100644
--- a/src/tint/writer/wgsl/generator_impl_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_test.cc
@@ -21,7 +21,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Generate) {
-    Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{}, ast::AttributeList{});
+    Func("my_func", {}, ty.void_(), {}, {});
 
     GeneratorImpl& gen = Build();