mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 13:11:05 +01:00
This supports both struct type literals, `{.x: i32, .y: i32}`, and struct
value literals, `{.x = 3, .y = 4}`. The degenerate case of `{}` is
treated as a struct value literal, with the expectation that an empty
struct value has the same type/value duality as an empty tuple value.
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "toolchain/common/yaml_test_helpers.h"
|
||||
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
||||
#include "toolchain/diagnostics/mocks.h"
|
||||
#include "toolchain/lexer/tokenized_buffer.h"
|
||||
#include "toolchain/parser/parse_node_kind.h"
|
||||
#include "toolchain/parser/parse_test_helpers.h"
|
||||
@@ -19,11 +20,13 @@
|
||||
namespace Carbon {
|
||||
namespace {
|
||||
|
||||
using Carbon::Testing::DiagnosticMessage;
|
||||
using Carbon::Testing::ExpectedNode;
|
||||
using Carbon::Testing::MatchParseTreeNodes;
|
||||
using namespace Carbon::Testing::NodeMatchers;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Ne;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::StrEq;
|
||||
@@ -405,10 +408,11 @@ TEST_F(ParseTreeTest, InvalidDesignators) {
|
||||
MatchExpressionStatement(
|
||||
MatchDesignatorExpression(
|
||||
MatchNameReference("a"), ".", HasError),
|
||||
";"),
|
||||
HasError, ";"),
|
||||
MatchExpressionStatement(
|
||||
MatchDesignatorExpression(
|
||||
MatchNameReference("a"), ".", HasError),
|
||||
MatchNameReference("a"), ".",
|
||||
MatchDesignatedName("fn", HasError)),
|
||||
";"),
|
||||
MatchExpressionStatement(
|
||||
MatchDesignatorExpression(
|
||||
@@ -970,6 +974,110 @@ TEST_F(ParseTreeTest, Tuples) {
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, Structs) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(R"(
|
||||
var x: {.a: i32, .b: i32} = {.a = 1, .b = 2};
|
||||
var y: {} = {};
|
||||
var z: {.n: i32,} = {.n = 4,};
|
||||
)");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
MatchParseTreeNodes(
|
||||
{MatchVariableDeclaration(
|
||||
MatchPatternBinding(
|
||||
MatchDeclaredName("x"), ":",
|
||||
MatchStructTypeLiteral(
|
||||
MatchStructFieldType(MatchStructFieldDesignator(
|
||||
".", MatchDesignatedName("a")),
|
||||
":", MatchLiteral("i32")),
|
||||
MatchStructComma(),
|
||||
MatchStructFieldType(MatchStructFieldDesignator(
|
||||
".", MatchDesignatedName("b")),
|
||||
":", MatchLiteral("i32")),
|
||||
MatchStructEnd())),
|
||||
MatchVariableInitializer(MatchStructLiteral(
|
||||
MatchStructFieldValue(MatchStructFieldDesignator(
|
||||
".", MatchDesignatedName("a")),
|
||||
"=", MatchLiteral("1")),
|
||||
MatchStructComma(),
|
||||
MatchStructFieldValue(MatchStructFieldDesignator(
|
||||
".", MatchDesignatedName("b")),
|
||||
"=", MatchLiteral("2")),
|
||||
MatchStructEnd())),
|
||||
MatchDeclarationEnd()),
|
||||
MatchVariableDeclaration(
|
||||
MatchPatternBinding(MatchDeclaredName("y"), ":",
|
||||
MatchStructLiteral(MatchStructEnd())),
|
||||
MatchVariableInitializer(MatchStructLiteral(MatchStructEnd())),
|
||||
MatchDeclarationEnd()),
|
||||
MatchVariableDeclaration(
|
||||
MatchPatternBinding(
|
||||
MatchDeclaredName("z"), ":",
|
||||
MatchStructTypeLiteral(
|
||||
MatchStructFieldType(MatchStructFieldDesignator(
|
||||
".", MatchDesignatedName("n")),
|
||||
":", MatchLiteral("i32")),
|
||||
MatchStructComma(), MatchStructEnd())),
|
||||
MatchVariableInitializer(MatchStructLiteral(
|
||||
MatchStructFieldValue(MatchStructFieldDesignator(
|
||||
".", MatchDesignatedName("n")),
|
||||
"=", MatchLiteral("4")),
|
||||
MatchStructComma(), MatchStructEnd())),
|
||||
MatchDeclarationEnd()),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, StructErrors) {
|
||||
struct Testcase {
|
||||
llvm::StringLiteral input;
|
||||
::testing::Matcher<const Diagnostic&> diag_matcher;
|
||||
};
|
||||
Testcase testcases[] = {
|
||||
{"var x: {i32} = {};",
|
||||
DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
|
||||
{"var x: {a} = {};",
|
||||
DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
|
||||
{"var x: {a:} = {};",
|
||||
DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
|
||||
{"var x: {a=} = {};",
|
||||
DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
|
||||
{"var x: {.} = {};", DiagnosticMessage("Expected identifier after `.`.")},
|
||||
{"var x: {.\"hello\" = 0, .y = 4} = {};",
|
||||
DiagnosticMessage("Expected identifier after `.`.")},
|
||||
{"var x: {.\"hello\": i32, .y: i32} = {};",
|
||||
DiagnosticMessage("Expected identifier after `.`.")},
|
||||
{"var x: {.a} = {};",
|
||||
DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
|
||||
{"var x: {.a:} = {};", DiagnosticMessage("Expected expression.")},
|
||||
{"var x: {.a=} = {};", DiagnosticMessage("Expected expression.")},
|
||||
{"var x: {.a: i32, .b = 0} = {};",
|
||||
DiagnosticMessage("Expected `.field: type`.")},
|
||||
{"var x: {.a = 0, b: i32} = {};",
|
||||
DiagnosticMessage("Expected `.field = value`.")},
|
||||
{"var x: {,} = {};",
|
||||
DiagnosticMessage("Expected `.field: type` or `.field = value`.")},
|
||||
{"var x: {.a: i32,,} = {};",
|
||||
DiagnosticMessage("Expected `.field: type`.")},
|
||||
{"var x: {.a = 0,,} = {};",
|
||||
DiagnosticMessage("Expected `.field = value`.")},
|
||||
{"var x: {.a: i32 banana} = {.a = 0};",
|
||||
DiagnosticMessage("Expected `,` or `}`.")},
|
||||
{"var x: {.a: i32} = {.a = 0 banana};",
|
||||
DiagnosticMessage("Expected `,` or `}`.")},
|
||||
};
|
||||
|
||||
for (Testcase testcase : testcases) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(testcase.input);
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
EXPECT_CALL(consumer, HandleDiagnostic(testcase.diag_matcher));
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
EXPECT_TRUE(tree.HasErrors());
|
||||
}
|
||||
}
|
||||
|
||||
auto GetAndDropLine(llvm::StringRef& s) -> std::string {
|
||||
auto newline_offset = s.find_first_of('\n');
|
||||
llvm::StringRef line = s.slice(0, newline_offset);
|
||||
|
||||
Reference in New Issue
Block a user