[tint][resolver] Clean up attribute validation tests
Expand the test parameterizations to include the error message.
Reduces head-scratching when attempting to figure out what the actual
test error is expecting.
Fold TEST_F standalone tests into the parameterized cases where
possible.
Change-Id: I46ed4c162ce33a62063c6c9c1f52f47b0fb3d6f7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/159781
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/wgsl/resolver/attribute_validation_test.cc b/src/tint/lang/wgsl/resolver/attribute_validation_test.cc
index 64e2bc7..43ec16a 100644
--- a/src/tint/lang/wgsl/resolver/attribute_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/attribute_validation_test.cc
@@ -31,6 +31,8 @@
#include "src/tint/lang/wgsl/ast/transform/add_block_attribute.h"
#include "src/tint/lang/wgsl/resolver/resolver.h"
#include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/utils/containers/transform.h"
+#include "src/tint/utils/macros/compiler.h"
#include "src/tint/utils/text/string_stream.h"
#include "gmock/gmock.h"
@@ -57,7 +59,7 @@
enum class AttributeKind {
kAlign,
kBinding,
- kBuiltin,
+ kBuiltinPosition,
kDiagnostic,
kGroup,
kId,
@@ -68,18 +70,53 @@
kMustUse,
kOffset,
kSize,
- kStage,
+ kStageCompute,
kStride,
- kWorkgroup,
-
- kBindingAndGroup,
+ kWorkgroupSize,
};
+static std::ostream& operator<<(std::ostream& o, AttributeKind k) {
+ switch (k) {
+ case AttributeKind::kAlign:
+ return o << "@align";
+ case AttributeKind::kBinding:
+ return o << "@binding";
+ case AttributeKind::kBuiltinPosition:
+ return o << "@builtin(position)";
+ case AttributeKind::kDiagnostic:
+ return o << "@diagnostic";
+ case AttributeKind::kGroup:
+ return o << "@group";
+ case AttributeKind::kId:
+ return o << "@id";
+ case AttributeKind::kIndex:
+ return o << "@index";
+ case AttributeKind::kInterpolate:
+ return o << "@interpolate";
+ case AttributeKind::kInvariant:
+ return o << "@invariant";
+ case AttributeKind::kLocation:
+ return o << "@location";
+ case AttributeKind::kOffset:
+ return o << "@offset";
+ case AttributeKind::kMustUse:
+ return o << "@must_use";
+ case AttributeKind::kSize:
+ return o << "@size";
+ case AttributeKind::kStageCompute:
+ return o << "@stage(compute)";
+ case AttributeKind::kStride:
+ return o << "@stride";
+ case AttributeKind::kWorkgroupSize:
+ return o << "@workgroup_size";
+ }
+ TINT_UNREACHABLE();
+ return o << "<unknown>";
+}
static bool IsBindingAttribute(AttributeKind kind) {
switch (kind) {
case AttributeKind::kBinding:
case AttributeKind::kGroup:
- case AttributeKind::kBindingAndGroup:
return true;
default:
return false;
@@ -87,195 +124,499 @@
}
struct TestParams {
- AttributeKind kind;
- bool should_pass;
+ Vector<AttributeKind, 2> attributes;
+ std::string error; // empty string (Pass) is an expected pass
};
+
+static constexpr const char* Pass = "";
+
+static std::vector<TestParams> OnlyDiagnosticValidFor(std::string thing) {
+ return {TestParams{
+ {AttributeKind::kAlign},
+ "1:2 error: @align is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ "1:2 error: @binding is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ "1:2 error: @builtin is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ "1:2 error: @group is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kId},
+ "1:2 error: @id is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ "1:2 error: @index is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ "1:2 error: @interpolate is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ "1:2 error: @invariant is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ "1:2 error: @location is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ "1:2 error: @must_use is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ "1:2 error: @offset is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ "1:2 error: @size is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ "1:2 error: @stage is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ "1:2 error: @stride is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ "1:2 error: @workgroup_size is not valid for " + thing,
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ "1:2 error: @binding is not valid for " + thing,
+ }};
+}
+
+static std::ostream& operator<<(std::ostream& o, const TestParams& c) {
+ return o << "attributes: " << c.attributes << ", expect pass: " << c.error.empty();
+}
+
+const ast::Attribute* CreateAttribute(const Source& source,
+ ProgramBuilder& builder,
+ AttributeKind kind) {
+ switch (kind) {
+ case AttributeKind::kAlign:
+ return builder.MemberAlign(source, 4_i);
+ case AttributeKind::kBinding:
+ return builder.Binding(source, 1_a);
+ case AttributeKind::kBuiltinPosition:
+ return builder.Builtin(source, core::BuiltinValue::kPosition);
+ case AttributeKind::kDiagnostic:
+ return builder.DiagnosticAttribute(source, wgsl::DiagnosticSeverity::kInfo, "chromium",
+ "unreachable_code");
+ case AttributeKind::kGroup:
+ return builder.Group(source, 1_a);
+ case AttributeKind::kId:
+ return builder.Id(source, 0_a);
+ case AttributeKind::kIndex:
+ return builder.Index(source, 0_a);
+ case AttributeKind::kInterpolate:
+ return builder.Interpolate(source, core::InterpolationType::kLinear,
+ core::InterpolationSampling::kCenter);
+ case AttributeKind::kInvariant:
+ return builder.Invariant(source);
+ case AttributeKind::kLocation:
+ return builder.Location(source, 0_a);
+ case AttributeKind::kOffset:
+ return builder.MemberOffset(source, 4_a);
+ case AttributeKind::kMustUse:
+ return builder.MustUse(source);
+ case AttributeKind::kSize:
+ return builder.MemberSize(source, 16_a);
+ case AttributeKind::kStageCompute:
+ return builder.Stage(source, ast::PipelineStage::kCompute);
+ case AttributeKind::kStride:
+ return builder.create<ast::StrideAttribute>(source, 4u);
+ case AttributeKind::kWorkgroupSize:
+ return builder.create<ast::WorkgroupAttribute>(source, builder.Expr(1_i));
+ }
+ TINT_UNREACHABLE() << kind;
+ return nullptr;
+}
+
struct TestWithParams : ResolverTestWithParam<TestParams> {
- void EnableExtensionIfNecessary(AttributeKind attributeKind) {
- if (attributeKind == AttributeKind::kIndex) {
+ void EnableExtensionIfNecessary(AttributeKind attribute) {
+ if (attribute == AttributeKind::kIndex) {
Enable(wgsl::Extension::kChromiumInternalDualSourceBlending);
}
}
+ void EnableRequiredExtensions() {
+ for (auto attribute : GetParam().attributes) {
+ EnableExtensionIfNecessary(attribute);
+ }
+ }
+
+ Vector<const ast::Attribute*, 2> CreateAttributes(ProgramBuilder& builder,
+ VectorRef<AttributeKind> kinds) {
+ return Transform<2>(kinds, [&](AttributeKind kind, size_t index) {
+ return CreateAttribute(Source{{static_cast<uint32_t>(index) * 2 + 1,
+ static_cast<uint32_t>(index) * 2 + 2}},
+ builder, kind);
+ });
+ }
+
+ Vector<const ast::Attribute*, 2> CreateAttributes() {
+ return CreateAttributes(*this, GetParam().attributes);
+ }
};
-static Vector<const ast::Attribute*, 2> createAttributes(const Source& source,
- ProgramBuilder& builder,
- AttributeKind kind) {
- switch (kind) {
- case AttributeKind::kAlign:
- return {builder.MemberAlign(source, 4_i)};
- case AttributeKind::kBinding:
- return {builder.Binding(source, 1_a)};
- case AttributeKind::kBuiltin:
- return {builder.Builtin(source, core::BuiltinValue::kPosition)};
- case AttributeKind::kDiagnostic:
- return {builder.DiagnosticAttribute(source, wgsl::DiagnosticSeverity::kInfo, "chromium",
- "unreachable_code")};
- case AttributeKind::kGroup:
- return {builder.Group(source, 1_a)};
- case AttributeKind::kId:
- return {builder.Id(source, 0_a)};
- case AttributeKind::kIndex:
- return {builder.Index(source, 0_a)};
- case AttributeKind::kInterpolate:
- return {builder.Interpolate(source, core::InterpolationType::kLinear,
- core::InterpolationSampling::kCenter)};
- case AttributeKind::kInvariant:
- return {builder.Invariant(source)};
- case AttributeKind::kLocation:
- return {builder.Location(source, 1_a)};
- case AttributeKind::kOffset:
- return {builder.MemberOffset(source, 4_a)};
- case AttributeKind::kMustUse:
- return {builder.MustUse(source)};
- case AttributeKind::kSize:
- return {builder.MemberSize(source, 16_a)};
- case AttributeKind::kStage:
- return {builder.Stage(source, ast::PipelineStage::kCompute)};
- case AttributeKind::kStride:
- return {builder.create<ast::StrideAttribute>(source, 4u)};
- case AttributeKind::kWorkgroup:
- return {builder.create<ast::WorkgroupAttribute>(source, builder.Expr(1_i))};
- case AttributeKind::kBindingAndGroup:
- return {builder.Binding(source, 1_a), builder.Group(source, 1_a)};
- }
- return {};
-}
+#define CHECK() \
+ if (GetParam().error.empty()) { \
+ EXPECT_TRUE(r()->Resolve()) << r()->error(); \
+ } else { \
+ EXPECT_FALSE(r()->Resolve()); \
+ EXPECT_EQ(GetParam().error, r()->error()); \
+ } \
+ TINT_REQUIRE_SEMICOLON
-static std::string name(AttributeKind kind) {
- switch (kind) {
- case AttributeKind::kAlign:
- return "@align";
- case AttributeKind::kBinding:
- return "@binding";
- case AttributeKind::kBuiltin:
- return "@builtin";
- case AttributeKind::kDiagnostic:
- return "@diagnostic";
- case AttributeKind::kGroup:
- return "@group";
- case AttributeKind::kId:
- return "@id";
- case AttributeKind::kIndex:
- return "@index";
- case AttributeKind::kInterpolate:
- return "@interpolate";
- case AttributeKind::kInvariant:
- return "@invariant";
- case AttributeKind::kLocation:
- return "@location";
- case AttributeKind::kOffset:
- return "@offset";
- case AttributeKind::kMustUse:
- return "@must_use";
- case AttributeKind::kSize:
- return "@size";
- case AttributeKind::kStage:
- return "@stage";
- case AttributeKind::kStride:
- return "@stride";
- case AttributeKind::kWorkgroup:
- return "@workgroup_size";
- case AttributeKind::kBindingAndGroup:
- return "@binding";
- }
- return "<unknown>";
+namespace FunctionTests {
+using VoidFunctionAttributeTest = TestWithParams;
+TEST_P(VoidFunctionAttributeTest, IsValid) {
+ EnableRequiredExtensions();
+
+ Func(Source{{9, 9}}, "main", Empty, ty.void_(), Empty, CreateAttributes());
+
+ CHECK();
}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ VoidFunctionAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use can only be applied to functions that return a value)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(9:9 error: a compute shader must include 'workgroup_size' in its attributes)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute, AttributeKind::kWorkgroupSize},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is only valid for compute stages)",
+ }));
+
+using NonVoidFunctionAttributeTest = TestWithParams;
+TEST_P(NonVoidFunctionAttributeTest, IsValid) {
+ EnableRequiredExtensions();
+
+ Func(Source{{9, 9}}, "main", Empty, ty.i32(), Vector{Return(1_i)}, CreateAttributes());
+
+ CHECK();
+}
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ NonVoidFunctionAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(9:9 error: missing entry point IO attribute on return type)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute, AttributeKind::kWorkgroupSize},
+ R"(9:9 error: missing entry point IO attribute on return type)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for functions)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is only valid for compute stages)",
+ }));
+} // namespace FunctionTests
+
namespace FunctionInputAndOutputTests {
using FunctionParameterAttributeTest = TestWithParams;
TEST_P(FunctionParameterAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
Func("main",
Vector{
- Param("a", ty.vec4<f32>(), createAttributes({}, *this, params.kind)),
+ Param("a", ty.vec4<f32>(), CreateAttributes()),
},
ty.void_(), tint::Empty);
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else if (params.kind == AttributeKind::kLocation || params.kind == AttributeKind::kBuiltin ||
- params.kind == AttributeKind::kInvariant ||
- params.kind == AttributeKind::kInterpolate) {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: " + name(params.kind) +
- " is not valid for non-entry point function parameters");
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: " + name(params.kind) + " is not valid for function parameters");
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- FunctionParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ FunctionParameterAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for non-entry point function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for non-entry point function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for non-entry point function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for non-entry point function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for function parameters)",
+ }));
using FunctionReturnTypeAttributeTest = TestWithParams;
TEST_P(FunctionReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
Func("main", tint::Empty, ty.f32(),
Vector{
Return(1_f),
},
- tint::Empty, createAttributes({}, *this, params.kind));
+ tint::Empty, CreateAttributes());
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: " + name(params.kind) +
- " is not valid for non-entry point function "
- "return types");
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- FunctionReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ FunctionReturnTypeAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for non-entry point function return types)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for non-entry point function return types)",
+ }));
} // namespace FunctionInputAndOutputTests
namespace EntryPointInputAndOutputTests {
using ComputeShaderParameterAttributeTest = TestWithParams;
TEST_P(ComputeShaderParameterAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
Func("main",
Vector{
- Param("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind)),
+ Param("a", ty.vec4<f32>(), CreateAttributes()),
},
ty.void_(), tint::Empty,
Vector{
@@ -283,97 +624,174 @@
WorkgroupSize(1_i),
});
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: @builtin(position) cannot be used in input of compute pipeline stage)");
- } else if (params.kind == AttributeKind::kInterpolate ||
- params.kind == AttributeKind::kLocation ||
- params.kind == AttributeKind::kInvariant) {
- EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
- " is not valid for compute shader inputs");
- } else {
- EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
- " is not valid for function parameters");
- }
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- ComputeShaderParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ ComputeShaderParameterAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin(position) cannot be used in input of compute pipeline stage)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for compute shader inputs)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for compute shader inputs)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for compute shader inputs)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for function parameters)",
+ }));
using FragmentShaderParameterAttributeTest = TestWithParams;
TEST_P(FragmentShaderParameterAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- if (params.kind != AttributeKind::kBuiltin && params.kind != AttributeKind::kLocation) {
- attrs.Push(Builtin(Source{{34, 56}}, core::BuiltinValue::kPosition));
- }
- auto* p = Param("a", ty.vec4<f32>(), attrs);
+ EnableRequiredExtensions();
+ auto* p = Param(Source{{9, 9}}, "a", ty.vec4<f32>(), CreateAttributes());
Func("frag_main", Vector{p}, ty.void_(), tint::Empty,
Vector{
Stage(ast::PipelineStage::kFragment),
});
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for function parameters");
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- FragmentShaderParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, true},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- // kInterpolate tested separately (requires @location)
- TestParams{AttributeKind::kInvariant, true},
- TestParams{AttributeKind::kLocation, true},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ FragmentShaderParameterAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(9:9 error: missing entry point IO attribute on parameter)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate, AttributeKind::kBuiltinPosition},
+ R"(1:2 error: interpolate attribute must only be used with @location)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate, AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(9:9 error: missing entry point IO attribute on parameter)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant, AttributeKind::kBuiltinPosition},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for function parameters)",
+ }));
using VertexShaderParameterAttributeTest = TestWithParams;
TEST_P(VertexShaderParameterAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- if (params.kind != AttributeKind::kLocation) {
- attrs.Push(Location(Source{{34, 56}}, 2_a));
- }
- auto* p = Param("a", ty.vec4<f32>(), attrs);
+ auto* p = Param(Source{{9, 9}}, "a", ty.vec4<f32>(), CreateAttributes());
Func("vertex_main", Vector{p}, ty.vec4<f32>(),
Vector{
Return(Call<vec4<f32>>()),
@@ -385,48 +803,96 @@
Builtin(core::BuiltinValue::kPosition),
});
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: @builtin(position) cannot be used in input of vertex pipeline stage)");
- } else if (params.kind == AttributeKind::kInvariant) {
- EXPECT_EQ(r()->error(),
- "12:34 error: invariant attribute must only be applied to a "
- "position builtin");
- } else {
- EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
- " is not valid for function parameters");
- }
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- VertexShaderParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, true},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, true},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ VertexShaderParameterAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin(position) cannot be used in input of vertex pipeline stage)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(9:9 error: missing entry point IO attribute on parameter)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate, AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kInterpolate, AttributeKind::kBuiltinPosition},
+ R"(3:4 error: @builtin(position) cannot be used in input of vertex pipeline stage)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(9:9 error: missing entry point IO attribute on parameter)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant, AttributeKind::kLocation},
+ R"(1:2 error: invariant attribute must only be applied to a position builtin)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant, AttributeKind::kBuiltinPosition},
+ R"(3:4 error: @builtin(position) cannot be used in input of vertex pipeline stage)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for function parameters)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for function parameters)",
+ }));
using ComputeShaderReturnTypeAttributeTest = TestWithParams;
TEST_P(ComputeShaderReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
Func("main", tint::Empty, ty.vec4<f32>(),
Vector{
@@ -436,112 +902,188 @@
Stage(ast::PipelineStage::kCompute),
WorkgroupSize(1_i),
},
- createAttributes(Source{{12, 34}}, *this, params.kind));
+ CreateAttributes());
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: @builtin(position) cannot be used in output of compute pipeline stage)");
- } else if (params.kind == AttributeKind::kInterpolate ||
- params.kind == AttributeKind::kLocation ||
- params.kind == AttributeKind::kInvariant ||
- params.kind == AttributeKind::kIndex) {
- EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
- " is not valid for compute shader output");
- } else {
- EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
- " is not valid for entry point return "
- "types");
- }
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- ComputeShaderReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ ComputeShaderReturnTypeAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin(position) cannot be used in output of compute pipeline stage)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for compute shader output)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for compute shader output)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for compute shader output)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for compute shader output)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for entry point return types)",
+ }));
using FragmentShaderReturnTypeAttributeTest = TestWithParams;
TEST_P(FragmentShaderReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- attrs.Push(Location(Source{{34, 56}}, 2_a));
- Func("frag_main", tint::Empty, ty.vec4<f32>(), Vector{Return(Call<vec4<f32>>())},
+ Func(Source{{9, 9}}, "frag_main", tint::Empty, ty.vec4<f32>(),
+ Vector{Return(Call<vec4<f32>>())},
Vector{
Stage(ast::PipelineStage::kFragment),
},
- attrs);
+ CreateAttributes());
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: @builtin(position) cannot be used in output of fragment pipeline stage)");
- } else if (params.kind == AttributeKind::kInvariant) {
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: invariant attribute must only be applied to a position builtin)");
- } else if (params.kind == AttributeKind::kLocation) {
- EXPECT_EQ(r()->error(),
- R"(34:56 error: duplicate location attribute
-12:34 note: first attribute declared here)");
- } else {
- EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
- " is not valid for entry point return types");
- }
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- FragmentShaderReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, true},
- TestParams{AttributeKind::kInterpolate, true},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ FragmentShaderReturnTypeAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin(position) cannot be used in output of fragment pipeline stage)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kIndex, AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(9:9 error: missing entry point IO attribute on return type)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate, AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(9:9 error: missing entry point IO attribute on return type)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant, AttributeKind::kLocation},
+ R"(1:2 error: invariant attribute must only be applied to a position builtin)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ R"(1:2 error: @binding is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kIndex, AttributeKind::kLocation},
+ Pass,
+ }));
using VertexShaderReturnTypeAttributeTest = TestWithParams;
TEST_P(VertexShaderReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
+ EnableRequiredExtensions();
+ auto attrs = CreateAttributes();
// a vertex shader must include the 'position' builtin in its return type
- if (params.kind != AttributeKind::kBuiltin) {
- attrs.Push(Builtin(Source{{34, 56}}, core::BuiltinValue::kPosition));
+ if (!GetParam().attributes.Any([](auto b) { return b == AttributeKind::kBuiltinPosition; })) {
+ attrs.Push(Builtin(Source{{9, 9}}, core::BuiltinValue::kPosition));
}
Func("vertex_main", tint::Empty, ty.vec4<f32>(),
Vector{
@@ -550,64 +1092,90 @@
Vector{
Stage(ast::PipelineStage::kVertex),
},
- attrs);
+ std::move(attrs));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kLocation) {
- EXPECT_EQ(r()->error(),
- R"(34:56 error: multiple entry point IO attributes
-12:34 note: previously consumed @location)");
- } else if (params.kind == AttributeKind::kIndex) {
- EXPECT_EQ(r()->error(), R"(12:34 error: @index is not valid for vertex shader output)");
- } else {
- EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
- " is not valid for entry point return types");
- }
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- VertexShaderReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, true},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- // kInterpolate tested separately (requires @location)
- TestParams{AttributeKind::kInvariant, true},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ VertexShaderReturnTypeAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for vertex shader output)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: interpolate attribute must only be used with @location)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(9:9 error: multiple entry point IO attributes
+1:2 note: previously consumed @location)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ R"(1:2 error: @binding is not valid for entry point return types)",
+ },
+ TestParams{
+ {AttributeKind::kLocation, AttributeKind::kLocation},
+ R"(3:4 error: duplicate location attribute
+1:2 note: first attribute declared here)",
+ }));
using EntryPointParameterAttributeTest = TestWithParams;
-TEST_F(EntryPointParameterAttributeTest, DuplicateAttribute) {
- Func("main", tint::Empty, ty.f32(),
- Vector{
- Return(1_f),
- },
- Vector{
- Stage(ast::PipelineStage::kFragment),
- },
- Vector{
- Location(Source{{12, 34}}, 2_a),
- Location(Source{{56, 78}}, 3_a),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate location attribute
-12:34 note: first attribute declared here)");
-}
-
TEST_F(EntryPointParameterAttributeTest, DuplicateInternalAttribute) {
auto* s = Param("s", ty.sampler(core::type::SamplerKind::kSampler),
Vector{
@@ -625,25 +1193,6 @@
}
using EntryPointReturnTypeAttributeTest = ResolverTest;
-TEST_F(EntryPointReturnTypeAttributeTest, DuplicateAttribute) {
- Func("main", tint::Empty, ty.f32(),
- Vector{
- Return(1_f),
- },
- Vector{
- Stage(ast::PipelineStage::kFragment),
- },
- Vector{
- Location(Source{{12, 34}}, 2_a),
- Location(Source{{56, 78}}, 3_a),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate location attribute
-12:34 note: first attribute declared here)");
-}
-
TEST_F(EntryPointReturnTypeAttributeTest, DuplicateInternalAttribute) {
Func("f", tint::Empty, ty.i32(), Vector{Return(1_i)},
Vector{
@@ -662,114 +1211,177 @@
using StructAttributeTest = TestWithParams;
using SpirvBlockAttribute = ast::transform::AddBlockAttribute::BlockAttribute;
TEST_P(StructAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- Structure("mystruct", Vector{Member("a", ty.f32())},
- createAttributes(Source{{12, 34}}, *this, params.kind));
+ Structure("S", Vector{Member("a", ty.f32())}, CreateAttributes());
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for struct declarations");
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- StructAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ StructAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for struct declarations)",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ R"(1:2 error: @binding is not valid for struct declarations)",
+ }));
using StructMemberAttributeTest = TestWithParams;
TEST_P(StructMemberAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
- Vector<const ast::StructMember*, 1> members;
- if (params.kind == AttributeKind::kBuiltin) {
- members.Push(
- Member("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind)));
- } else {
- members.Push(Member("a", ty.f32(), createAttributes(Source{{12, 34}}, *this, params.kind)));
- }
- Structure("mystruct", members);
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for struct members");
- }
+ EnableRequiredExtensions();
+ Structure("S", Vector{Member("a", ty.vec4<f32>(), CreateAttributes())});
+
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- StructMemberAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, true},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, true},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- // kIndex tested separately (requires @location)
- // kInterpolate tested separately (requires @location)
- // kInvariant tested separately (requires position builtin)
- TestParams{AttributeKind::kLocation, true},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, true},
- TestParams{AttributeKind::kSize, true},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
-TEST_F(StructMemberAttributeTest, DuplicateAttribute) {
- Structure("mystruct", Vector{
- Member("a", ty.i32(),
- Vector{
- MemberAlign(Source{{12, 34}}, 4_i),
- MemberAlign(Source{{56, 78}}, 8_i),
- }),
- });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate align attribute
-12:34 note: first attribute declared here)");
-}
-TEST_F(StructMemberAttributeTest, InvariantAttributeWithPosition) {
- Structure("mystruct", Vector{
- Member("a", ty.vec4<f32>(),
- Vector{
- Invariant(),
- Builtin(core::BuiltinValue::kPosition),
- }),
- });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-TEST_F(StructMemberAttributeTest, InvariantAttributeWithoutPosition) {
- Structure("mystruct", Vector{
- Member("a", ty.vec4<f32>(),
- Vector{
- Invariant(Source{{12, 34}}),
- }),
- });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: invariant attribute must only be applied to a "
- "position builtin");
-}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ StructMemberAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: index attribute must only be used with @location)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: interpolate attribute must only be used with @location)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate, AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: invariant attribute must only be applied to a position builtin)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant, AttributeKind::kBuiltinPosition},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ R"(1:2 error: @binding is not valid for struct members)",
+ },
+ TestParams{
+ {AttributeKind::kAlign, AttributeKind::kAlign},
+ R"(3:4 error: duplicate align attribute
+1:2 note: first attribute declared here)",
+ }));
TEST_F(StructMemberAttributeTest, Align_Attribute_Const) {
GlobalConst("val", ty.i32(), Expr(1_i));
@@ -945,93 +1557,182 @@
using ArrayAttributeTest = TestWithParams;
TEST_P(ArrayAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- auto arr = ty.array(ty.f32(), createAttributes(Source{{12, 34}}, *this, params.kind));
- Structure("mystruct", Vector{
- Member("a", arr),
- });
+ auto arr = ty.array(ty.f32(), CreateAttributes());
+ Structure("S", Vector{
+ Member("a", arr),
+ });
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for array types");
- }
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
ArrayAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, true},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ R"(1:2 error: @binding is not valid for array types)",
+ },
+ TestParams{
+ {AttributeKind::kStride, AttributeKind::kStride},
+ R"(3:4 error: duplicate stride attribute
+1:2 note: first attribute declared here)",
+ }));
using VariableAttributeTest = TestWithParams;
TEST_P(VariableAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- if (IsBindingAttribute(params.kind)) {
- GlobalVar("a", ty.sampler(core::type::SamplerKind::kSampler), attrs);
+ if (GetParam().attributes.Any(IsBindingAttribute)) {
+ GlobalVar(Source{{9, 9}}, "a", ty.sampler(core::type::SamplerKind::kSampler),
+ CreateAttributes());
} else {
- GlobalVar("a", ty.f32(), core::AddressSpace::kPrivate, attrs);
+ GlobalVar(Source{{9, 9}}, "a", ty.f32(), core::AddressSpace::kPrivate, CreateAttributes());
}
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (!IsBindingAttribute(params.kind)) {
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for module-scope 'var'");
- }
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- VariableAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, true}));
-
-TEST_F(VariableAttributeTest, DuplicateAttribute) {
- GlobalVar("a", ty.sampler(core::type::SamplerKind::kSampler), Binding(Source{{12, 34}}, 2_a),
- Group(2_a), Binding(Source{{56, 78}}, 3_a));
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate binding attribute
-12:34 note: first attribute declared here)");
-}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ VariableAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(9:9 error: resource variables require @group and @binding attributes)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(9:9 error: resource variables require @group and @binding attributes)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for module-scope 'var')",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup, AttributeKind::kBinding},
+ R"(5:6 error: duplicate binding attribute
+1:2 note: first attribute declared here)",
+ }));
TEST_F(VariableAttributeTest, LocalVar) {
auto* v = Var("a", ty.f32(), Vector{Binding(Source{{12, 34}}, 2_a)});
@@ -1053,420 +1754,319 @@
using ConstantAttributeTest = TestWithParams;
TEST_P(ConstantAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- GlobalConst("a", ty.f32(), Expr(1.23_f),
- createAttributes(Source{{12, 34}}, *this, params.kind));
+ GlobalConst("a", ty.f32(), Expr(1.23_f), CreateAttributes());
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for 'const' declaration");
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- ConstantAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
-
-TEST_F(ConstantAttributeTest, InvalidAttribute) {
- GlobalConst("a", ty.f32(), Expr(1.23_f),
- Vector{
- Id(Source{{12, 34}}, 0_a),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @id is not valid for 'const' declaration");
-}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ ConstantAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ R"(1:2 error: @id is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for 'const' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ R"(1:2 error: @binding is not valid for 'const' declaration)",
+ }));
using OverrideAttributeTest = TestWithParams;
TEST_P(OverrideAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- Override("a", ty.f32(), Expr(1.23_f), createAttributes(Source{{12, 34}}, *this, params.kind));
+ Override("a", ty.f32(), Expr(1.23_f), CreateAttributes());
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for 'override' declaration");
- }
+ CHECK();
}
-INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
- OverrideAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kId, true},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
-
-TEST_F(OverrideAttributeTest, DuplicateAttribute) {
- Override("a", ty.f32(), Expr(1.23_f),
- Vector{
- Id(Source{{12, 34}}, 0_a),
- Id(Source{{56, 78}}, 1_a),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate id attribute
-12:34 note: first attribute declared here)");
-}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverAttributeValidationTest,
+ OverrideAttributeTest,
+ testing::Values(
+ TestParams{
+ {AttributeKind::kAlign},
+ R"(1:2 error: @align is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kBinding},
+ R"(1:2 error: @binding is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kBuiltinPosition},
+ R"(1:2 error: @builtin is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kDiagnostic},
+ R"(1:2 error: @diagnostic is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kGroup},
+ R"(1:2 error: @group is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kIndex},
+ R"(1:2 error: @index is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kId},
+ Pass,
+ },
+ TestParams{
+ {AttributeKind::kInterpolate},
+ R"(1:2 error: @interpolate is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kInvariant},
+ R"(1:2 error: @invariant is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kLocation},
+ R"(1:2 error: @location is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kMustUse},
+ R"(1:2 error: @must_use is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kOffset},
+ R"(1:2 error: @offset is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kSize},
+ R"(1:2 error: @size is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kStageCompute},
+ R"(1:2 error: @stage is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kStride},
+ R"(1:2 error: @stride is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kWorkgroupSize},
+ R"(1:2 error: @workgroup_size is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kBinding, AttributeKind::kGroup},
+ R"(1:2 error: @binding is not valid for 'override' declaration)",
+ },
+ TestParams{
+ {AttributeKind::kId, AttributeKind::kId},
+ R"(3:4 error: duplicate id attribute
+1:2 note: first attribute declared here)",
+ }));
using SwitchStatementAttributeTest = TestWithParams;
TEST_P(SwitchStatementAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- WrapInFunction(Switch(Expr(0_a), Vector{DefaultCase()},
- createAttributes(Source{{12, 34}}, *this, params.kind)));
+ WrapInFunction(Switch(Expr(0_a), Vector{DefaultCase()}, CreateAttributes()));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for switch statements");
- }
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
SwitchStatementAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+ testing::ValuesIn(OnlyDiagnosticValidFor("switch statements")));
using SwitchBodyAttributeTest = TestWithParams;
TEST_P(SwitchBodyAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- WrapInFunction(Switch(Expr(0_a), Vector{DefaultCase()}, tint::Empty,
- createAttributes(Source{{12, 34}}, *this, params.kind)));
+ WrapInFunction(Switch(Expr(0_a), Vector{DefaultCase()}, tint::Empty, CreateAttributes()));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for switch body");
- }
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
SwitchBodyAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+ testing::ValuesIn(OnlyDiagnosticValidFor("switch body")));
using IfStatementAttributeTest = TestWithParams;
TEST_P(IfStatementAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- WrapInFunction(If(Expr(true), Block(), ElseStmt(),
- createAttributes(Source{{12, 34}}, *this, params.kind)));
+ WrapInFunction(If(Expr(true), Block(), ElseStmt(), CreateAttributes()));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for if statements");
- }
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
IfStatementAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+ testing::ValuesIn(OnlyDiagnosticValidFor("if statements")));
using ForStatementAttributeTest = TestWithParams;
TEST_P(ForStatementAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- WrapInFunction(For(nullptr, Expr(false), nullptr, Block(),
- createAttributes(Source{{12, 34}}, *this, params.kind)));
+ WrapInFunction(For(nullptr, Expr(false), nullptr, Block(), CreateAttributes()));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for for statements");
- }
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
ForStatementAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+ testing::ValuesIn(OnlyDiagnosticValidFor("for statements")));
using LoopStatementAttributeTest = TestWithParams;
TEST_P(LoopStatementAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- WrapInFunction(
- Loop(Block(Return()), Block(), createAttributes(Source{{12, 34}}, *this, params.kind)));
+ WrapInFunction(Loop(Block(Return()), Block(), CreateAttributes()));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for loop statements");
- }
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
LoopStatementAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+ testing::ValuesIn(OnlyDiagnosticValidFor("loop statements")));
using WhileStatementAttributeTest = TestWithParams;
TEST_P(WhileStatementAttributeTest, IsValid) {
- auto& params = GetParam();
- EnableExtensionIfNecessary(params.kind);
+ EnableRequiredExtensions();
- WrapInFunction(
- While(Expr(false), Block(), createAttributes(Source{{12, 34}}, *this, params.kind)));
+ WrapInFunction(While(Expr(false), Block(), CreateAttributes()));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: " + name(params.kind) + " is not valid for while statements");
- }
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
WhileStatementAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+ testing::ValuesIn(OnlyDiagnosticValidFor("while statements")));
-namespace BlockStatementTests {
-class BlockStatementTest : public TestWithParams {
- protected:
- void Check() {
- if (GetParam().should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: " + name(GetParam().kind) + " is not valid for block statements");
- }
- }
-
- public:
- BlockStatementTest() { EnableExtensionIfNecessary(GetParam().kind); }
-};
+using BlockStatementTest = TestWithParams;
TEST_P(BlockStatementTest, CompoundStatement) {
Func("foo", tint::Empty, ty.void_(),
Vector{
- Block(Vector{Return()}, createAttributes({}, *this, GetParam().kind)),
+ Block(Vector{Return()}, CreateAttributes()),
});
- Check();
+
+ CHECK();
}
TEST_P(BlockStatementTest, FunctionBody) {
- Func("foo", tint::Empty, ty.void_(),
- Block(Vector{Return()}, createAttributes({}, *this, GetParam().kind)));
- Check();
+ Func("foo", tint::Empty, ty.void_(), Block(Vector{Return()}, CreateAttributes()));
+
+ CHECK();
}
TEST_P(BlockStatementTest, IfStatementBody) {
Func("foo", tint::Empty, ty.void_(),
Vector{
- If(Expr(true), Block(Vector{Return()}, createAttributes({}, *this, GetParam().kind))),
+ If(Expr(true), Block(Vector{Return()}, CreateAttributes())),
});
- Check();
+
+ CHECK();
}
TEST_P(BlockStatementTest, ElseStatementBody) {
Func("foo", tint::Empty, ty.void_(),
Vector{
If(Expr(true), Block(Vector{Return()}),
- Else(Block(Vector{Return()}, createAttributes({}, *this, GetParam().kind)))),
+ Else(Block(Vector{Return()}, CreateAttributes()))),
});
- Check();
+
+ CHECK();
}
TEST_P(BlockStatementTest, ForStatementBody) {
Func("foo", tint::Empty, ty.void_(),
Vector{
- For(nullptr, Expr(true), nullptr,
- Block(Vector{Break()}, createAttributes({}, *this, GetParam().kind))),
+ For(nullptr, Expr(true), nullptr, Block(Vector{Break()}, CreateAttributes())),
});
- Check();
+
+ CHECK();
}
TEST_P(BlockStatementTest, LoopStatementBody) {
Func("foo", tint::Empty, ty.void_(),
Vector{
- Loop(Block(Vector{Break()}, createAttributes({}, *this, GetParam().kind))),
+ Loop(Block(Vector{Break()}, CreateAttributes())),
});
- Check();
+
+ CHECK();
}
TEST_P(BlockStatementTest, WhileStatementBody) {
- Func(
- "foo", tint::Empty, ty.void_(),
- Vector{
- While(Expr(true), Block(Vector{Break()}, createAttributes({}, *this, GetParam().kind))),
- });
- Check();
+ Func("foo", tint::Empty, ty.void_(),
+ Vector{
+ While(Expr(true), Block(Vector{Break()}, CreateAttributes())),
+ });
+
+ CHECK();
}
TEST_P(BlockStatementTest, CaseStatementBody) {
Func("foo", tint::Empty, ty.void_(),
Vector{
- Switch(1_a,
- Case(CaseSelector(1_a),
- Block(Vector{Break()}, createAttributes({}, *this, GetParam().kind))),
+ Switch(1_a, Case(CaseSelector(1_a), Block(Vector{Break()}, CreateAttributes())),
DefaultCase(Block({}))),
});
- Check();
+
+ CHECK();
}
TEST_P(BlockStatementTest, DefaultStatementBody) {
Func("foo", tint::Empty, ty.void_(),
Vector{
- Switch(
- 1_a, Case(CaseSelector(1_a), Block()),
- DefaultCase(Block(Vector{Break()}, createAttributes({}, *this, GetParam().kind)))),
+ Switch(1_a, Case(CaseSelector(1_a), Block()),
+ DefaultCase(Block(Vector{Break()}, CreateAttributes()))),
});
- Check();
+
+ CHECK();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
BlockStatementTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kDiagnostic, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kIndex, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kMustUse, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
-
-} // namespace BlockStatementTests
+ testing::ValuesIn(OnlyDiagnosticValidFor("block statements")));
} // namespace
} // namespace AttributeTests
@@ -1509,9 +2109,9 @@
} else {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: arrays decorated with the stride attribute must "
- "have a stride that is at least the size of the element type, "
- "and be a multiple of the element type's alignment value");
+ "12:34 error: arrays decorated with the stride attribute must have a stride that "
+ "is at least the size of the element type, and be a multiple of the element "
+ "type's alignment value");
}
}
@@ -1720,96 +2320,10 @@
} // namespace
} // namespace ResourceTests
-namespace InvariantAttributeTests {
-namespace {
-using InvariantAttributeTests = ResolverTest;
-TEST_F(InvariantAttributeTests, InvariantWithPosition) {
- auto* param = Param("p", ty.vec4<f32>(),
- Vector{
- Invariant(Source{{12, 34}}),
- Builtin(Source{{56, 78}}, core::BuiltinValue::kPosition),
- });
- Func("main", Vector{param}, ty.vec4<f32>(),
- Vector{
- Return(Call<vec4<f32>>()),
- },
- Vector{
- Stage(ast::PipelineStage::kFragment),
- },
- Vector{
- Location(0_a),
- });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(InvariantAttributeTests, InvariantWithoutPosition) {
- auto* param = Param("p", ty.vec4<f32>(),
- Vector{
- Invariant(Source{{12, 34}}),
- Location(0_a),
- });
- Func("main", Vector{param}, ty.vec4<f32>(),
- Vector{
- Return(Call<vec4<f32>>()),
- },
- Vector{
- Stage(ast::PipelineStage::kFragment),
- },
- Vector{
- Location(0_a),
- });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: invariant attribute must only be applied to a "
- "position builtin");
-}
-} // namespace
-} // namespace InvariantAttributeTests
-
-namespace MustUseAttributeTests {
-namespace {
-
-using MustUseAttributeTests = ResolverTest;
-TEST_F(MustUseAttributeTests, MustUse) {
- Func("main", tint::Empty, ty.vec4<f32>(),
- Vector{
- Return(Call<vec4<f32>>()),
- },
- Vector{
- MustUse(Source{{12, 34}}),
- });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-} // namespace
-} // namespace MustUseAttributeTests
-
namespace WorkgroupAttributeTests {
namespace {
using WorkgroupAttribute = ResolverTest;
-TEST_F(WorkgroupAttribute, ComputeShaderPass) {
- Func("main", tint::Empty, ty.void_(), tint::Empty,
- Vector{
- Stage(ast::PipelineStage::kCompute),
- create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
- });
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(WorkgroupAttribute, Missing) {
- Func(Source{{12, 34}}, "main", tint::Empty, ty.void_(), tint::Empty,
- Vector{
- Stage(ast::PipelineStage::kCompute),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: a compute shader must include 'workgroup_size' in its "
- "attributes");
-}
-
TEST_F(WorkgroupAttribute, NotAnEntryPoint) {
Func("main", tint::Empty, ty.void_(), tint::Empty,
Vector{
@@ -1817,7 +2331,7 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @workgroup_size is only valid for compute stages");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @workgroup_size is only valid for compute stages)");
}
TEST_F(WorkgroupAttribute, NotAComputeShader) {
@@ -1828,7 +2342,7 @@
});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: @workgroup_size is only valid for compute stages");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @workgroup_size is only valid for compute stages)");
}
TEST_F(WorkgroupAttribute, DuplicateAttribute) {
@@ -1864,7 +2378,6 @@
using InterpolateParameterTest = TestWithParams;
TEST_P(InterpolateParameterTest, All) {
auto& params = GetParam();
-
Func("main",
Vector{
Param("a", ty.f32(),
@@ -1882,15 +2395,14 @@
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: flat interpolation attribute must not have a "
- "sampling parameter");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: flat interpolation attribute must not have a sampling parameter)");
}
}
TEST_P(InterpolateParameterTest, IntegerScalar) {
auto& params = GetParam();
-
Func("main",
Vector{
Param("a", ty.i32(),
@@ -1906,22 +2418,21 @@
if (params.type != core::InterpolationType::kFlat) {
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: interpolation type must be 'flat' for integral "
- "user-defined IO types");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: interpolation type must be 'flat' for integral user-defined IO types)");
} else if (params.should_pass) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: flat interpolation attribute must not have a "
- "sampling parameter");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: flat interpolation attribute must not have a sampling parameter)");
}
}
TEST_P(InterpolateParameterTest, IntegerVector) {
auto& params = GetParam();
-
Func("main",
Vector{
Param("a", ty.vec4<u32>(),
@@ -1937,16 +2448,16 @@
if (params.type != core::InterpolationType::kFlat) {
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: interpolation type must be 'flat' for integral "
- "user-defined IO types");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: interpolation type must be 'flat' for integral user-defined IO types)");
} else if (params.should_pass) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: flat interpolation attribute must not have a "
- "sampling parameter");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: flat interpolation attribute must not have a sampling parameter)");
}
}
@@ -2003,55 +2514,6 @@
note: while analyzing entry point 'main')");
}
-TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {
- Func("main",
- Vector{
- Param("a", ty.vec4<f32>(),
- Vector{
- Builtin(core::BuiltinValue::kPosition),
- Interpolate(Source{{12, 34}}, core::InterpolationType::kFlat),
- }),
- },
- ty.void_(), tint::Empty,
- Vector{
- Stage(ast::PipelineStage::kFragment),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: interpolate attribute must only be used with @location)");
-}
-
-TEST_F(InterpolateTest, MissingLocationAttribute_ReturnType) {
- Func("main", tint::Empty, ty.vec4<f32>(),
- Vector{
- Return(Call<vec4<f32>>()),
- },
- Vector{
- Stage(ast::PipelineStage::kVertex),
- },
- Vector{
- Builtin(core::BuiltinValue::kPosition),
- Interpolate(Source{{12, 34}}, core::InterpolationType::kFlat),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: interpolate attribute must only be used with @location)");
-}
-
-TEST_F(InterpolateTest, MissingLocationAttribute_Struct) {
- Structure("S",
- Vector{
- Member("a", ty.f32(),
- Vector{Interpolate(Source{{12, 34}}, core::InterpolationType::kFlat)}),
- });
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: interpolate attribute must only be used with @location)");
-}
-
using GroupAndBindingTest = ResolverTest;
TEST_F(GroupAndBindingTest, Const_I32) {
@@ -2287,21 +2749,6 @@
} // namespace
} // namespace InterpolateTests
-namespace MustUseTests {
-namespace {
-
-using MustUseAttributeTest = ResolverTest;
-TEST_F(MustUseAttributeTest, UsedOnFnWithNoReturnValue) {
- Func("fn_must_use", tint::Empty, ty.void_(), tint::Empty, Vector{MustUse(Source{{12, 34}})});
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: @must_use can only be applied to functions that return a value)");
-}
-
-} // namespace
-} // namespace MustUseTests
-
namespace InternalAttributeDeps {
namespace {