tint: Rework errors around variable declarations
Standarize the messages, using 'let', 'override' or 'var'. Module-scope 'let' needs to be replaced with 'const', but baby steps. Bug: tint:1582 Change-Id: I290aede118a30ab0f4294c89ec43005371c87b45 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/93446 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
418e873ad2
commit
2a8861d20e
|
@ -433,7 +433,7 @@ Maybe<bool> ParserImpl::global_decl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gc.matched) {
|
if (gc.matched) {
|
||||||
if (!expect("let declaration", Token::Type::kSemicolon)) {
|
if (!expect("'let' declaration", Token::Type::kSemicolon)) {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +562,7 @@ Maybe<const ast::Variable*> ParserImpl::global_constant_decl(ast::AttributeList&
|
||||||
bool is_overridable = false;
|
bool is_overridable = false;
|
||||||
const char* use = nullptr;
|
const char* use = nullptr;
|
||||||
if (match(Token::Type::kLet)) {
|
if (match(Token::Type::kLet)) {
|
||||||
use = "let declaration";
|
use = "'let' declaration";
|
||||||
} else if (match(Token::Type::kOverride)) {
|
} else if (match(Token::Type::kOverride)) {
|
||||||
use = "override declaration";
|
use = "override declaration";
|
||||||
is_overridable = true;
|
is_overridable = true;
|
||||||
|
@ -575,8 +575,18 @@ Maybe<const ast::Variable*> ParserImpl::global_constant_decl(ast::AttributeList&
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_initializer = false;
|
||||||
|
if (is_overridable) {
|
||||||
|
has_initializer = match(Token::Type::kEqual);
|
||||||
|
} else {
|
||||||
|
if (!expect(use, Token::Type::kEqual)) {
|
||||||
|
return Failure::kErrored;
|
||||||
|
}
|
||||||
|
has_initializer = true;
|
||||||
|
}
|
||||||
|
|
||||||
const ast::Expression* initializer = nullptr;
|
const ast::Expression* initializer = nullptr;
|
||||||
if (match(Token::Type::kEqual)) {
|
if (has_initializer) {
|
||||||
auto init = expect_const_expr();
|
auto init = expect_const_expr();
|
||||||
if (init.errored) {
|
if (init.errored) {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
|
@ -1757,13 +1767,13 @@ Maybe<const ast::ReturnStatement*> ParserImpl::return_stmt() {
|
||||||
// | CONST variable_ident_decl EQUAL logical_or_expression
|
// | CONST variable_ident_decl EQUAL logical_or_expression
|
||||||
Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
|
Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
|
||||||
if (match(Token::Type::kLet)) {
|
if (match(Token::Type::kLet)) {
|
||||||
auto decl = expect_variable_ident_decl("let declaration",
|
auto decl = expect_variable_ident_decl("'let' declaration",
|
||||||
/*allow_inferred = */ true);
|
/*allow_inferred = */ true);
|
||||||
if (decl.errored) {
|
if (decl.errored) {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!expect("let declaration", Token::Type::kEqual)) {
|
if (!expect("'let' declaration", Token::Type::kEqual)) {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1772,7 +1782,7 @@ Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
if (!constructor.matched) {
|
if (!constructor.matched) {
|
||||||
return add_error(peek(), "missing constructor for let declaration");
|
return add_error(peek(), "missing constructor for 'let' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* var = create<ast::Variable>(decl->source, // source
|
auto* var = create<ast::Variable>(decl->source, // source
|
||||||
|
@ -1803,7 +1813,7 @@ Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
}
|
}
|
||||||
if (!constructor_expr.matched) {
|
if (!constructor_expr.matched) {
|
||||||
return add_error(peek(), "missing constructor for variable declaration");
|
return add_error(peek(), "missing constructor for 'var' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor = constructor_expr.value;
|
constructor = constructor_expr.value;
|
||||||
|
|
|
@ -204,7 +204,7 @@ fn f() { x = vec2<u32>(1,2; }
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, ConstVarStmtInvalid) {
|
TEST_F(ParserImplErrorTest, ConstVarStmtInvalid) {
|
||||||
EXPECT("fn f() { let >; }",
|
EXPECT("fn f() { let >; }",
|
||||||
R"(test.wgsl:1:14 error: expected identifier for let declaration
|
R"(test.wgsl:1:14 error: expected identifier for 'let' declaration
|
||||||
fn f() { let >; }
|
fn f() { let >; }
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
@ -212,7 +212,7 @@ fn f() { let >; }
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, ConstVarStmtMissingAssignment) {
|
TEST_F(ParserImplErrorTest, ConstVarStmtMissingAssignment) {
|
||||||
EXPECT("fn f() { let a : i32; }",
|
EXPECT("fn f() { let a : i32; }",
|
||||||
R"(test.wgsl:1:21 error: expected '=' for let declaration
|
R"(test.wgsl:1:21 error: expected '=' for 'let' declaration
|
||||||
fn f() { let a : i32; }
|
fn f() { let a : i32; }
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
@ -220,7 +220,7 @@ fn f() { let a : i32; }
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, ConstVarStmtMissingConstructor) {
|
TEST_F(ParserImplErrorTest, ConstVarStmtMissingConstructor) {
|
||||||
EXPECT("fn f() { let a : i32 = >; }",
|
EXPECT("fn f() { let a : i32 = >; }",
|
||||||
R"(test.wgsl:1:24 error: missing constructor for let declaration
|
R"(test.wgsl:1:24 error: missing constructor for 'let' declaration
|
||||||
fn f() { let a : i32 = >; }
|
fn f() { let a : i32 = >; }
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
@ -472,7 +472,7 @@ test.wgsl:3:3 error: statement found outside of function body
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, GlobalDeclConstInvalidIdentifier) {
|
TEST_F(ParserImplErrorTest, GlobalDeclConstInvalidIdentifier) {
|
||||||
EXPECT("let ^ : i32 = 1;",
|
EXPECT("let ^ : i32 = 1;",
|
||||||
R"(test.wgsl:1:5 error: expected identifier for let declaration
|
R"(test.wgsl:1:5 error: expected identifier for 'let' declaration
|
||||||
let ^ : i32 = 1;
|
let ^ : i32 = 1;
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
@ -480,7 +480,7 @@ let ^ : i32 = 1;
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, GlobalDeclConstMissingSemicolon) {
|
TEST_F(ParserImplErrorTest, GlobalDeclConstMissingSemicolon) {
|
||||||
EXPECT("let i : i32 = 1",
|
EXPECT("let i : i32 = 1",
|
||||||
R"(test.wgsl:1:16 error: expected ';' for let declaration
|
R"(test.wgsl:1:16 error: expected ';' for 'let' declaration
|
||||||
let i : i32 = 1
|
let i : i32 = 1
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
@ -512,7 +512,7 @@ let i : vec2<i32> = vec2<i32>(!);
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteralSpaceLessThan) {
|
TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteralSpaceLessThan) {
|
||||||
EXPECT("let i = 1 < 2;",
|
EXPECT("let i = 1 < 2;",
|
||||||
R"(test.wgsl:1:11 error: expected ';' for let declaration
|
R"(test.wgsl:1:11 error: expected ';' for 'let' declaration
|
||||||
let i = 1 < 2;
|
let i = 1 < 2;
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
@ -1215,7 +1215,7 @@ fn f() { var a : u32 }
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, VarStmtInvalidAssignment) {
|
TEST_F(ParserImplErrorTest, VarStmtInvalidAssignment) {
|
||||||
EXPECT("fn f() { var a : u32 = >; }",
|
EXPECT("fn f() { var a : u32 = >; }",
|
||||||
R"(test.wgsl:1:24 error: missing constructor for variable declaration
|
R"(test.wgsl:1:24 error: missing constructor for 'var' declaration
|
||||||
fn f() { var a : u32 = >; }
|
fn f() { var a : u32 = >; }
|
||||||
^
|
^
|
||||||
)");
|
)");
|
||||||
|
|
|
@ -253,7 +253,7 @@ TEST_F(ForStmtErrorTest, MissingRightBrace) {
|
||||||
// Test a for loop with an invalid initializer statement.
|
// Test a for loop with an invalid initializer statement.
|
||||||
TEST_F(ForStmtErrorTest, InvalidInitializerAsConstDecl) {
|
TEST_F(ForStmtErrorTest, InvalidInitializerAsConstDecl) {
|
||||||
std::string for_str = "for (let x: i32;;) { }";
|
std::string for_str = "for (let x: i32;;) { }";
|
||||||
std::string error_str = "1:16: expected '=' for let declaration";
|
std::string error_str = "1:16: expected '=' for 'let' declaration";
|
||||||
|
|
||||||
TestForWithError(for_str, error_str);
|
TestForWithError(for_str, error_str);
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,7 @@ TEST_F(ForStmtErrorTest, InvalidContinuingMatch) {
|
||||||
// Test a for loop with an invalid body.
|
// Test a for loop with an invalid body.
|
||||||
TEST_F(ForStmtErrorTest, InvalidBody) {
|
TEST_F(ForStmtErrorTest, InvalidBody) {
|
||||||
std::string for_str = "for (;;) { let x: i32; }";
|
std::string for_str = "for (;;) { let x: i32; }";
|
||||||
std::string error_str = "1:22: expected '=' for let declaration";
|
std::string error_str = "1:22: expected '=' for 'let' declaration";
|
||||||
|
|
||||||
TestForWithError(for_str, error_str);
|
TestForWithError(for_str, error_str);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,18 +61,25 @@ TEST_F(ParserImplTest, GlobalDecl_GlobalConstant) {
|
||||||
EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
|
EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingInitializer) {
|
||||||
|
auto p = parser("let a : vec2<i32>;");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), "1:18: expected '=' for 'let' declaration");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_Invalid) {
|
TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_Invalid) {
|
||||||
auto p = parser("let a : vec2<i32> 1.0;");
|
auto p = parser("let a : vec2<i32> 1.0;");
|
||||||
p->global_decl();
|
p->global_decl();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:19: expected ';' for let declaration");
|
EXPECT_EQ(p->error(), "1:19: expected '=' for 'let' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingSemicolon) {
|
TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingSemicolon) {
|
||||||
auto p = parser("let a : vec2<i32> = vec2<i32>(1, 2)");
|
auto p = parser("let a : vec2<i32> = vec2<i32>(1, 2)");
|
||||||
p->global_decl();
|
p->global_decl();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:36: expected ';' for let declaration");
|
EXPECT_EQ(p->error(), "1:36: expected ';' for 'let' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
|
TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
|
||||||
|
|
|
@ -114,7 +114,7 @@ TEST_F(ParserImplTest, Statement_Variable_Invalid) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_FALSE(e.matched);
|
EXPECT_FALSE(e.matched);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:14: missing constructor for variable declaration");
|
EXPECT_EQ(p->error(), "1:14: missing constructor for 'var' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, Statement_Variable_MissingSemicolon) {
|
TEST_F(ParserImplTest, Statement_Variable_MissingSemicolon) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ TEST_F(ParserImplTest, VariableStmt_VariableDecl_ConstructorInvalid) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_TRUE(p->has_error());
|
EXPECT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:15: missing constructor for variable declaration");
|
EXPECT_EQ(p->error(), "1:15: missing constructor for 'var' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, VariableStmt_VariableDecl_ArrayInit) {
|
TEST_F(ParserImplTest, VariableStmt_VariableDecl_ArrayInit) {
|
||||||
|
@ -160,7 +160,7 @@ TEST_F(ParserImplTest, VariableStmt_Let_MissingEqual) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_TRUE(p->has_error());
|
EXPECT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:13: expected '=' for let declaration");
|
EXPECT_EQ(p->error(), "1:13: expected '=' for 'let' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, VariableStmt_Let_MissingConstructor) {
|
TEST_F(ParserImplTest, VariableStmt_Let_MissingConstructor) {
|
||||||
|
@ -170,7 +170,7 @@ TEST_F(ParserImplTest, VariableStmt_Let_MissingConstructor) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_TRUE(p->has_error());
|
EXPECT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:14: missing constructor for let declaration");
|
EXPECT_EQ(p->error(), "1:14: missing constructor for 'let' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, VariableStmt_Let_InvalidConstructor) {
|
TEST_F(ParserImplTest, VariableStmt_Let_InvalidConstructor) {
|
||||||
|
@ -180,7 +180,7 @@ TEST_F(ParserImplTest, VariableStmt_Let_InvalidConstructor) {
|
||||||
EXPECT_TRUE(e.errored);
|
EXPECT_TRUE(e.errored);
|
||||||
EXPECT_EQ(e.value, nullptr);
|
EXPECT_EQ(e.value, nullptr);
|
||||||
EXPECT_TRUE(p->has_error());
|
EXPECT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:15: missing constructor for let declaration");
|
EXPECT_EQ(p->error(), "1:15: missing constructor for 'let' declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -222,7 +222,7 @@ TEST_F(ResolverAssignmentValidationTest, AssignToLet_Fail) {
|
||||||
WrapInFunction(var, Assign(Expr(Source{{12, 34}}, "a"), 2_i));
|
WrapInFunction(var, Assign(Expr(Source{{12, 34}}, "a"), 2_i));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: cannot assign to const\nnote: 'a' is declared here:");
|
EXPECT_EQ(r()->error(), "12:34 error: cannot assign to 'let'\nnote: 'a' is declared here:");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_Handle) {
|
TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_Handle) {
|
||||||
|
|
|
@ -721,7 +721,7 @@ TEST_P(VariableAttributeTest, IsValid) {
|
||||||
} else {
|
} else {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
if (!IsBindingAttribute(params.kind)) {
|
if (!IsBindingAttribute(params.kind)) {
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for variables");
|
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for module-scope 'var'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -783,11 +783,59 @@ TEST_P(ConstantAttributeTest, IsValid) {
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
} else {
|
} else {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for constants");
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: attribute is not valid for module-scope 'let' declaration");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||||
ConstantAttributeTest,
|
ConstantAttributeTest,
|
||||||
|
testing::Values(TestParams{AttributeKind::kAlign, false},
|
||||||
|
TestParams{AttributeKind::kBinding, false},
|
||||||
|
TestParams{AttributeKind::kBuiltin, false},
|
||||||
|
TestParams{AttributeKind::kGroup, false},
|
||||||
|
TestParams{AttributeKind::kId, false},
|
||||||
|
TestParams{AttributeKind::kInterpolate, false},
|
||||||
|
TestParams{AttributeKind::kInvariant, false},
|
||||||
|
TestParams{AttributeKind::kLocation, 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, DuplicateAttribute) {
|
||||||
|
GlobalConst("a", ty.f32(), Expr(1.23_f),
|
||||||
|
ast::AttributeList{
|
||||||
|
create<ast::IdAttribute>(Source{{12, 34}}, 0),
|
||||||
|
create<ast::IdAttribute>(Source{{56, 78}}, 1),
|
||||||
|
});
|
||||||
|
|
||||||
|
WrapInFunction();
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
R"(56:78 error: duplicate id attribute
|
||||||
|
12:34 note: first attribute declared here)");
|
||||||
|
}
|
||||||
|
|
||||||
|
using OverrideAttributeTest = TestWithParams;
|
||||||
|
TEST_P(OverrideAttributeTest, IsValid) {
|
||||||
|
auto& params = GetParam();
|
||||||
|
|
||||||
|
Override("a", ty.f32(), Expr(1.23_f), createAttributes(Source{{12, 34}}, *this, params.kind));
|
||||||
|
|
||||||
|
WrapInFunction();
|
||||||
|
|
||||||
|
if (params.should_pass) {
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for 'override' declaration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||||
|
OverrideAttributeTest,
|
||||||
testing::Values(TestParams{AttributeKind::kAlign, false},
|
testing::Values(TestParams{AttributeKind::kAlign, false},
|
||||||
TestParams{AttributeKind::kBinding, false},
|
TestParams{AttributeKind::kBinding, false},
|
||||||
TestParams{AttributeKind::kBuiltin, false},
|
TestParams{AttributeKind::kBuiltin, false},
|
||||||
|
@ -803,7 +851,7 @@ INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||||
TestParams{AttributeKind::kWorkgroup, false},
|
TestParams{AttributeKind::kWorkgroup, false},
|
||||||
TestParams{AttributeKind::kBindingAndGroup, false}));
|
TestParams{AttributeKind::kBindingAndGroup, false}));
|
||||||
|
|
||||||
TEST_F(ConstantAttributeTest, DuplicateAttribute) {
|
TEST_F(OverrideAttributeTest, DuplicateAttribute) {
|
||||||
GlobalConst("a", ty.f32(), Expr(1.23_f),
|
GlobalConst("a", ty.f32(), Expr(1.23_f),
|
||||||
ast::AttributeList{
|
ast::AttributeList{
|
||||||
create<ast::IdAttribute>(Source{{12, 34}}, 0),
|
create<ast::IdAttribute>(Source{{12, 34}}, 0),
|
||||||
|
|
|
@ -249,7 +249,7 @@ TEST_F(ResolverCompoundAssignmentValidationTest, LhsConstant) {
|
||||||
WrapInFunction(a, CompoundAssign(Expr(Source{{56, 78}}, "a"), 1_i, ast::BinaryOp::kAdd));
|
WrapInFunction(a, CompoundAssign(Expr(Source{{56, 78}}, "a"), 1_i, ast::BinaryOp::kAdd));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(56:78 error: cannot assign to const
|
EXPECT_EQ(r()->error(), R"(56:78 error: cannot assign to 'let'
|
||||||
12:34 note: 'a' is declared here:)");
|
12:34 note: 'a' is declared here:)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ TEST_F(ResolverIncrementDecrementValidationTest, Constant) {
|
||||||
WrapInFunction(a, Increment(Expr(Source{{56, 78}}, "a")));
|
WrapInFunction(a, Increment(Expr(Source{{56, 78}}, "a")));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify constant value
|
EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify 'let'
|
||||||
12:34 note: 'a' is declared here:)");
|
12:34 note: 'a' is declared here:)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ TEST_P(ResolverInferredTypeParamTest, GlobalVar_Fail) {
|
||||||
WrapInFunction();
|
WrapInFunction();
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: global var declaration must specify a type");
|
EXPECT_EQ(r()->error(), "12:34 error: module-scope 'var' declaration must specify a type");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ResolverInferredTypeParamTest, LocalLet_Pass) {
|
TEST_P(ResolverInferredTypeParamTest, LocalLet_Pass) {
|
||||||
|
|
|
@ -326,19 +326,19 @@ sem::Variable* Resolver::Variable(const ast::Variable* var,
|
||||||
// If the variable has no declared type, infer it from the RHS
|
// If the variable has no declared type, infer it from the RHS
|
||||||
if (!storage_ty) {
|
if (!storage_ty) {
|
||||||
if (!var->is_const && kind == VariableKind::kGlobal) {
|
if (!var->is_const && kind == VariableKind::kGlobal) {
|
||||||
AddError("global var declaration must specify a type", var->source);
|
AddError("module-scope 'var' declaration must specify a type", var->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
storage_ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
|
storage_ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
|
||||||
}
|
}
|
||||||
} else if (var->is_const && !var->is_overridable && kind != VariableKind::kParameter) {
|
} else if (var->is_const && !var->is_overridable && kind != VariableKind::kParameter) {
|
||||||
AddError("let declaration must have an initializer", var->source);
|
AddError("'let' declaration must have an initializer", var->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else if (!var->type) {
|
} else if (!var->type) {
|
||||||
AddError((kind == VariableKind::kGlobal)
|
AddError((kind == VariableKind::kGlobal)
|
||||||
? "module scope var declaration requires a type and initializer"
|
? "module-scope 'var' declaration requires a type or initializer"
|
||||||
: "function scope var declaration requires a type or initializer",
|
: "function-scope 'var' declaration requires a type or initializer",
|
||||||
var->source);
|
var->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ sem::Variable* Resolver::Variable(const ast::Variable* var,
|
||||||
storage_class != ast::StorageClass::kFunction &&
|
storage_class != ast::StorageClass::kFunction &&
|
||||||
validator_.IsValidationEnabled(var->attributes,
|
validator_.IsValidationEnabled(var->attributes,
|
||||||
ast::DisabledValidation::kIgnoreStorageClass)) {
|
ast::DisabledValidation::kIgnoreStorageClass)) {
|
||||||
AddError("function variable has a non-function storage class", var->source);
|
AddError("function-scope 'var' declaration must use 'function' storage class", var->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,11 +519,13 @@ sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* var) {
|
||||||
|
|
||||||
auto storage_class = sem->StorageClass();
|
auto storage_class = sem->StorageClass();
|
||||||
if (!var->is_const && storage_class == ast::StorageClass::kNone) {
|
if (!var->is_const && storage_class == ast::StorageClass::kNone) {
|
||||||
AddError("global variables must have a storage class", var->source);
|
AddError("module-scope 'var' declaration must have a storage class", var->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (var->is_const && storage_class != ast::StorageClass::kNone) {
|
if (var->is_const && storage_class != ast::StorageClass::kNone) {
|
||||||
AddError("global constants shouldn't have a storage class", var->source);
|
AddError(var->is_overridable ? "'override' declaration must not have a storage class"
|
||||||
|
: "'let' declaration must not have a storage class",
|
||||||
|
var->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2069,20 +2071,15 @@ sem::Array* Resolver::Array(const ast::Array* arr) {
|
||||||
if (auto* ident = count_expr->As<ast::IdentifierExpression>()) {
|
if (auto* ident = count_expr->As<ast::IdentifierExpression>()) {
|
||||||
// Make sure the identifier is a non-overridable module-scope constant.
|
// Make sure the identifier is a non-overridable module-scope constant.
|
||||||
auto* var = sem_.ResolvedSymbol<sem::GlobalVariable>(ident);
|
auto* var = sem_.ResolvedSymbol<sem::GlobalVariable>(ident);
|
||||||
if (!var || !var->Declaration()->is_const) {
|
if (!var || !var->Declaration()->is_const || var->IsOverridable()) {
|
||||||
AddError("array size identifier must be a module-scope constant", size_source);
|
AddError("array size identifier must be a literal or a module-scope 'let'",
|
||||||
return nullptr;
|
size_source);
|
||||||
}
|
|
||||||
if (var->IsOverridable()) {
|
|
||||||
AddError("array size expression must not be pipeline-overridable", size_source);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
count_expr = var->Declaration()->constructor;
|
count_expr = var->Declaration()->constructor;
|
||||||
} else if (!count_expr->Is<ast::LiteralExpression>()) {
|
} else if (!count_expr->Is<ast::LiteralExpression>()) {
|
||||||
AddError(
|
AddError("array size identifier must be a literal or a module-scope 'let'",
|
||||||
"array size expression must be either a literal or a module-scope "
|
|
||||||
"constant",
|
|
||||||
size_source);
|
size_source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
|
||||||
Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kNone);
|
Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kNone);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: global variables must have a storage class");
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: module-scope 'var' declaration must have a storage class");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverStorageClassValidationTest, GlobalVariableFunctionStorageClass_Fail) {
|
TEST_F(ResolverStorageClassValidationTest, GlobalVariableFunctionStorageClass_Fail) {
|
||||||
|
@ -39,8 +40,7 @@ TEST_F(ResolverStorageClassValidationTest, GlobalVariableFunctionStorageClass_Fa
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: variables declared at module scope must not be in "
|
"12:34 error: module-scope 'var' must not use storage class 'function'");
|
||||||
"the function storage class");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArray) {
|
TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArray) {
|
||||||
|
|
|
@ -90,11 +90,21 @@ TEST_F(ResolverTypeValidationTest, GlobalVariableWithStorageClass_Pass) {
|
||||||
TEST_F(ResolverTypeValidationTest, GlobalLetWithStorageClass_Fail) {
|
TEST_F(ResolverTypeValidationTest, GlobalLetWithStorageClass_Fail) {
|
||||||
// let<private> global_var: f32;
|
// let<private> global_var: f32;
|
||||||
AST().AddGlobalVariable(create<ast::Variable>(
|
AST().AddGlobalVariable(create<ast::Variable>(
|
||||||
Source{{12, 34}}, Symbols().Register("global_var"), ast::StorageClass::kPrivate,
|
Source{{12, 34}}, Symbols().Register("global_let"), ast::StorageClass::kPrivate,
|
||||||
ast::Access::kUndefined, ty.f32(), true, false, Expr(1.23_f), ast::AttributeList{}));
|
ast::Access::kUndefined, ty.f32(), true, false, Expr(1.23_f), ast::AttributeList{}));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: global constants shouldn't have a storage class");
|
EXPECT_EQ(r()->error(), "12:34 error: 'let' declaration must not have a storage class");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTypeValidationTest, OverrideWithStorageClass_Fail) {
|
||||||
|
// let<private> global_var: f32;
|
||||||
|
AST().AddGlobalVariable(create<ast::Variable>(
|
||||||
|
Source{{12, 34}}, Symbols().Register("global_override"), ast::StorageClass::kPrivate,
|
||||||
|
ast::Access::kUndefined, ty.f32(), true, true, Expr(1.23_f), ast::AttributeList{}));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), "12:34 error: 'override' declaration must not have a storage class");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTypeValidationTest, GlobalConstNoStorageClass_Pass) {
|
TEST_F(ResolverTypeValidationTest, GlobalConstNoStorageClass_Pass) {
|
||||||
|
@ -334,7 +344,8 @@ TEST_F(ResolverTypeValidationTest, ArraySize_Overridable) {
|
||||||
Override("size", nullptr, Expr(10_i));
|
Override("size", nullptr, Expr(10_i));
|
||||||
Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
|
Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: array size expression must not be pipeline-overridable");
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: array size identifier must be a literal or a module-scope 'let'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTypeValidationTest, ArraySize_ModuleVar) {
|
TEST_F(ResolverTypeValidationTest, ArraySize_ModuleVar) {
|
||||||
|
@ -343,7 +354,8 @@ TEST_F(ResolverTypeValidationTest, ArraySize_ModuleVar) {
|
||||||
Global("size", ty.i32(), Expr(10_i), ast::StorageClass::kPrivate);
|
Global("size", ty.i32(), Expr(10_i), ast::StorageClass::kPrivate);
|
||||||
Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
|
Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: array size identifier must be a module-scope constant");
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: array size identifier must be a literal or a module-scope 'let'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTypeValidationTest, ArraySize_FunctionLet) {
|
TEST_F(ResolverTypeValidationTest, ArraySize_FunctionLet) {
|
||||||
|
@ -355,7 +367,8 @@ TEST_F(ResolverTypeValidationTest, ArraySize_FunctionLet) {
|
||||||
auto* a = Var("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")));
|
auto* a = Var("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")));
|
||||||
WrapInFunction(Block(Decl(size), Decl(a)));
|
WrapInFunction(Block(Decl(size), Decl(a)));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: array size identifier must be a module-scope constant");
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: array size identifier must be a literal or a module-scope 'let'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTypeValidationTest, ArraySize_InvalidExpr) {
|
TEST_F(ResolverTypeValidationTest, ArraySize_InvalidExpr) {
|
||||||
|
@ -364,8 +377,7 @@ TEST_F(ResolverTypeValidationTest, ArraySize_InvalidExpr) {
|
||||||
WrapInFunction(Block(Decl(a)));
|
WrapInFunction(Block(Decl(a)));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: array size expression must be either a literal or a "
|
"12:34 error: array size identifier must be a literal or a module-scope 'let'");
|
||||||
"module-scope constant");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTypeValidationTest, RuntimeArrayInFunction_Fail) {
|
TEST_F(ResolverTypeValidationTest, RuntimeArrayInFunction_Fail) {
|
||||||
|
|
|
@ -304,7 +304,8 @@ TEST_F(ResolverValidationTest, StorageClass_FunctionVariableWorkgroupClass) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
|
||||||
EXPECT_EQ(r()->error(), "error: function variable has a non-function storage class");
|
EXPECT_EQ(r()->error(),
|
||||||
|
"error: function-scope 'var' declaration must use 'function' storage class");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest, StorageClass_FunctionVariableI32) {
|
TEST_F(ResolverValidationTest, StorageClass_FunctionVariableI32) {
|
||||||
|
@ -317,7 +318,8 @@ TEST_F(ResolverValidationTest, StorageClass_FunctionVariableI32) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
|
||||||
EXPECT_EQ(r()->error(), "error: function variable has a non-function storage class");
|
EXPECT_EQ(r()->error(),
|
||||||
|
"error: function-scope 'var' declaration must use 'function' storage class");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverValidationTest, StorageClass_SamplerExplicitStorageClass) {
|
TEST_F(ResolverValidationTest, StorageClass_SamplerExplicitStorageClass) {
|
||||||
|
|
|
@ -506,16 +506,16 @@ bool Validator::GlobalVariable(
|
||||||
|
|
||||||
for (auto* attr : decl->attributes) {
|
for (auto* attr : decl->attributes) {
|
||||||
if (decl->is_const) {
|
if (decl->is_const) {
|
||||||
|
if (decl->is_overridable) {
|
||||||
if (auto* id_attr = attr->As<ast::IdAttribute>()) {
|
if (auto* id_attr = attr->As<ast::IdAttribute>()) {
|
||||||
uint32_t id = id_attr->value;
|
uint32_t id = id_attr->value;
|
||||||
auto it = constant_ids.find(id);
|
auto it = constant_ids.find(id);
|
||||||
if (it != constant_ids.end() && it->second != var) {
|
if (it != constant_ids.end() && it->second != var) {
|
||||||
AddError("pipeline constant IDs must be unique", attr->source);
|
AddError("pipeline constant IDs must be unique", attr->source);
|
||||||
AddNote(
|
AddNote("a pipeline constant with an ID of " + std::to_string(id) +
|
||||||
"a pipeline constant with an ID of " + std::to_string(id) +
|
" was previously declared here:",
|
||||||
" was previously declared "
|
ast::GetAttribute<ast::IdAttribute>(
|
||||||
"here:",
|
it->second->Declaration()->attributes)
|
||||||
ast::GetAttribute<ast::IdAttribute>(it->second->Declaration()->attributes)
|
|
||||||
->source);
|
->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -524,7 +524,11 @@ bool Validator::GlobalVariable(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AddError("attribute is not valid for constants", attr->source);
|
AddError("attribute is not valid for 'override' declaration", attr->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AddError("attribute is not valid for module-scope 'let' declaration", attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -536,17 +540,14 @@ bool Validator::GlobalVariable(
|
||||||
if (!(attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
|
if (!(attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
|
||||||
ast::InternalAttribute>()) &&
|
ast::InternalAttribute>()) &&
|
||||||
(!is_shader_io_attribute || !has_io_storage_class)) {
|
(!is_shader_io_attribute || !has_io_storage_class)) {
|
||||||
AddError("attribute is not valid for variables", attr->source);
|
AddError("attribute is not valid for module-scope 'var'", attr->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (var->StorageClass() == ast::StorageClass::kFunction) {
|
if (var->StorageClass() == ast::StorageClass::kFunction) {
|
||||||
AddError(
|
AddError("module-scope 'var' must not use storage class 'function'", decl->source);
|
||||||
"variables declared at module scope must not be in the function "
|
|
||||||
"storage class",
|
|
||||||
decl->source);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,10 +560,7 @@ bool Validator::GlobalVariable(
|
||||||
// Each resource variable must be declared with both group and binding
|
// Each resource variable must be declared with both group and binding
|
||||||
// attributes.
|
// attributes.
|
||||||
if (!binding_point) {
|
if (!binding_point) {
|
||||||
AddError(
|
AddError("resource variables require @group and @binding attributes", decl->source);
|
||||||
"resource variables require @group and @binding "
|
|
||||||
"attributes",
|
|
||||||
decl->source);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -571,9 +569,7 @@ bool Validator::GlobalVariable(
|
||||||
if (binding_point.binding || binding_point.group) {
|
if (binding_point.binding || binding_point.group) {
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#attribute-binding
|
// https://gpuweb.github.io/gpuweb/wgsl/#attribute-binding
|
||||||
// Must only be applied to a resource variable
|
// Must only be applied to a resource variable
|
||||||
AddError(
|
AddError("non-resource variables must not have @group or @binding attributes",
|
||||||
"non-resource variables must not have @group or @binding "
|
|
||||||
"attributes",
|
|
||||||
decl->source);
|
decl->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2162,7 +2158,9 @@ bool Validator::Assignment(const ast::Statement* a, const sem::Type* rhs_ty) con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (decl->is_const) {
|
if (decl->is_const) {
|
||||||
AddError("cannot assign to const", lhs->source);
|
AddError(
|
||||||
|
decl->is_overridable ? "cannot assign to 'override'" : "cannot assign to 'let'",
|
||||||
|
lhs->source);
|
||||||
AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
|
AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2210,7 +2208,8 @@ bool Validator::IncrementDecrementStatement(const ast::IncrementDecrementStateme
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (decl->is_const) {
|
if (decl->is_const) {
|
||||||
AddError("cannot modify constant value", lhs->source);
|
AddError(decl->is_overridable ? "cannot modify 'override'" : "cannot modify 'let'",
|
||||||
|
lhs->source);
|
||||||
AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
|
AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ TEST_F(ResolverVarLetValidationTest, LetNoInitializer) {
|
||||||
WrapInFunction(Let(Source{{12, 34}}, "a", ty.i32(), nullptr));
|
WrapInFunction(Let(Source{{12, 34}}, "a", ty.i32(), nullptr));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: let declaration must have an initializer");
|
EXPECT_EQ(r()->error(), "12:34 error: 'let' declaration must have an initializer");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverVarLetValidationTest, GlobalLetNoInitializer) {
|
TEST_F(ResolverVarLetValidationTest, GlobalLetNoInitializer) {
|
||||||
|
@ -37,7 +37,7 @@ TEST_F(ResolverVarLetValidationTest, GlobalLetNoInitializer) {
|
||||||
GlobalConst(Source{{12, 34}}, "a", ty.i32(), nullptr);
|
GlobalConst(Source{{12, 34}}, "a", ty.i32(), nullptr);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), "12:34 error: let declaration must have an initializer");
|
EXPECT_EQ(r()->error(), "12:34 error: 'let' declaration must have an initializer");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverVarLetValidationTest, VarNoInitializerNoType) {
|
TEST_F(ResolverVarLetValidationTest, VarNoInitializerNoType) {
|
||||||
|
@ -46,8 +46,7 @@ TEST_F(ResolverVarLetValidationTest, VarNoInitializerNoType) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: function scope var declaration requires a type or "
|
"12:34 error: function-scope 'var' declaration requires a type or initializer");
|
||||||
"initializer");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverVarLetValidationTest, GlobalVarNoInitializerNoType) {
|
TEST_F(ResolverVarLetValidationTest, GlobalVarNoInitializerNoType) {
|
||||||
|
@ -56,8 +55,7 @@ TEST_F(ResolverVarLetValidationTest, GlobalVarNoInitializerNoType) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: module scope var declaration requires a type and "
|
"12:34 error: module-scope 'var' declaration requires a type or initializer");
|
||||||
"initializer");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverVarLetValidationTest, VarTypeNotStorable) {
|
TEST_F(ResolverVarLetValidationTest, VarTypeNotStorable) {
|
||||||
|
|
Loading…
Reference in New Issue