| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fidl/flat_ast.h> |
| #include <fidl/lexer.h> |
| #include <fidl/parser.h> |
| #include <fidl/source_file.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "error_test.h" |
| #include "fidl/diagnostics.h" |
| #include "test_library.h" |
| |
| namespace { |
| |
| void invalid_resource_modifier(const std::string& type, const std::string& definition) { |
| std::string fidl_library = "library example;\n\n" + definition + "\n"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1); |
| ASSERT_ERR(errors[0], fidl::ErrCannotSpecifyModifier); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "resource"); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), type); |
| } |
| |
| TEST(ResourcenessTests, bad_bits_resourceness) { |
| invalid_resource_modifier("bits", R"FIDL( |
| resource bits Foo { |
| BAR = 0x1; |
| }; |
| )FIDL"); |
| } |
| |
| TEST(ResourcenessTests, bad_enum_resourceness) { |
| invalid_resource_modifier("enum", R"FIDL( |
| resource enum Foo { |
| BAR = 1; |
| }; |
| )FIDL"); |
| } |
| |
| TEST(ResourcenessTests, bad_const_resourceness) { |
| invalid_resource_modifier("const", R"FIDL( |
| resource const uint32 BAR = 1; |
| )FIDL"); |
| } |
| |
| TEST(ResourcenessTests, bad_protocol_resourceness) { |
| invalid_resource_modifier("protocol", R"FIDL( |
| resource protocol Foo {}; |
| )FIDL"); |
| } |
| |
| TEST(ResourcenessTests, bad_using_resourceness) { |
| invalid_resource_modifier("using", R"FIDL( |
| resource using B = bool; |
| )FIDL"); |
| } |
| |
| TEST(ResourcenessTests, bad_duplicate_modifier) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| resource struct One {}; |
| resource resource struct Two {}; // line 5 |
| resource resource resource struct Three {}; // line 6 |
| )FIDL"); |
| ASSERT_FALSE(library.Compile()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 3); |
| ASSERT_ERR(errors[0], fidl::ErrDuplicateModifier); |
| EXPECT_EQ(errors[0]->span->position().line, 5); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "resource"); |
| ASSERT_ERR(errors[1], fidl::ErrDuplicateModifier); |
| EXPECT_EQ(errors[1]->span->position().line, 6); |
| ASSERT_SUBSTR(errors[1]->msg.c_str(), "resource"); |
| ASSERT_ERR(errors[2], fidl::ErrDuplicateModifier); |
| EXPECT_EQ(errors[2]->span->position().line, 6); |
| ASSERT_SUBSTR(errors[2]->msg.c_str(), "resource"); |
| } |
| |
| TEST(ResourcenessTests, good_resource_struct) { |
| for (const std::string& definition : { |
| "resource struct Foo {};", |
| "resource struct Foo { bool b; };", |
| "resource struct Foo { handle h; };", |
| "resource struct Foo { array<handle>:1 a; };", |
| "resource struct Foo { vector<handle> v; };", |
| }) { |
| std::string fidl_library = "library example;\n\n" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_TRUE(library.Compile(), "%s", fidl_library.c_str()); |
| EXPECT_EQ(library.LookupStruct("Foo")->resourceness, fidl::types::Resourceness::kResource, "%s", |
| fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, good_resource_table) { |
| for (const std::string& definition : { |
| "resource table Foo {};", |
| "resource table Foo { 1: bool b; };", |
| "resource table Foo { 1: handle h; };", |
| "resource table Foo { 1: array<handle>:1 a; };", |
| "resource table Foo { 1: vector<handle> v; };", |
| }) { |
| std::string fidl_library = "library example;\n\n" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_TRUE(library.Compile(), "%s", fidl_library.c_str()); |
| EXPECT_EQ(library.LookupTable("Foo")->resourceness, fidl::types::Resourceness::kResource, "%s", |
| fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, good_resource_union) { |
| for (const std::string& definition : { |
| "resource union Foo { 1: bool b; };", |
| "resource union Foo { 1: handle h; };", |
| "resource union Foo { 1: array<handle>:1 a; };", |
| "resource union Foo { 1: vector<handle> v; };", |
| }) { |
| std::string fidl_library = "library example;\n\n" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_TRUE(library.Compile(), "%s", fidl_library.c_str()); |
| EXPECT_EQ(library.LookupUnion("Foo")->resourceness, fidl::types::Resourceness::kResource, "%s", |
| fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_handles_in_value_struct) { |
| for (const std::string& definition : { |
| "struct Foo { handle bad_member; };", |
| "struct Foo { handle? bad_member; };", |
| "struct Foo { array<handle>:1 bad_member; };", |
| "struct Foo { vector<handle> bad_member; };", |
| "struct Foo { vector<handle>:0 bad_member; };", |
| }) { |
| std::string fidl_library = "library example;\n\n" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile(), "%s", fidl_library.c_str()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1, "%s", fidl_library.c_str()); |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource, "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo", "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member", "%s", fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_handles_in_value_table) { |
| for (const std::string& definition : { |
| "table Foo { 1: handle bad_member; };", |
| "table Foo { 1: array<handle>:1 bad_member; };", |
| "table Foo { 1: vector<handle> bad_member; };", |
| "table Foo { 1: vector<handle>:0 bad_member; };", |
| }) { |
| std::string fidl_library = "library example;\n\n" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile(), "%s", fidl_library.c_str()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1, "%s", fidl_library.c_str()); |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource, "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo", "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member", "%s", fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_handles_in_value_union) { |
| for (const std::string& definition : { |
| "union Foo { 1: handle bad_member; };", |
| "union Foo { 1: array<handle>:1 bad_member; };", |
| "union Foo { 1: vector<handle> bad_member; };", |
| "union Foo { 1: vector<handle>:0 bad_member; };", |
| }) { |
| std::string fidl_library = "library example;\n\n" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile(), "%s", fidl_library.c_str()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1, "%s", fidl_library.c_str()); |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource, "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo", "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member", "%s", fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_protocols_in_value_type) { |
| for (const std::string& definition : { |
| "struct Foo { Protocol bad_member; };", |
| "struct Foo { Protocol? bad_member; };", |
| "struct Foo { request<Protocol> bad_member; };", |
| "struct Foo { request<Protocol>? bad_member; };", |
| }) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| protocol Protocol {}; |
| |
| )FIDL" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile(), "%s", fidl_library.c_str()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1, "%s", fidl_library.c_str()); |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource, "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo", "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member", "%s", fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_resource_types_in_value_type) { |
| for (const std::string& definition : { |
| "struct Foo { ResourceStruct bad_member; };", |
| "struct Foo { ResourceStruct? bad_member; };", |
| "struct Foo { ResourceTable bad_member; };", |
| "struct Foo { ResourceUnion bad_member; };", |
| "struct Foo { ResourceUnion? bad_member; };", |
| }) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| resource struct ResourceStruct {}; |
| resource table ResourceTable {}; |
| resource union ResourceUnion { 1: bool b; }; |
| |
| )FIDL" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile(), "%s", fidl_library.c_str()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1, "%s", fidl_library.c_str()); |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource, "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo", "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member", "%s", fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_resource_aliases_in_value_type) { |
| for (const std::string& definition : { |
| "struct Foo { HandleAlias bad_member; };", |
| "struct Foo { ProtocolAlias bad_member; };", |
| "struct Foo { ResourceStructAlias bad_member; };", |
| "struct Foo { ResourceTableAlias bad_member; };", |
| "struct Foo { ResourceUnionAlias bad_member; };", |
| }) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| using HandleAlias = handle; |
| using ProtocolAlias = Protocol; |
| using ResourceStructAlias = ResourceStruct; |
| using ResourceTableAlias = ResourceStruct; |
| using ResourceUnionAlias = ResourceStruct; |
| |
| protocol Protocol {}; |
| resource struct ResourceStruct {}; |
| resource table ResourceTable {}; |
| resource union ResourceUnion { 1: bool b; }; |
| |
| )FIDL" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile(), "%s", fidl_library.c_str()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1, "%s", fidl_library.c_str()); |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource, "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo", "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member", "%s", fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_resources_in_nested_containers) { |
| for (const std::string& definition : { |
| "struct Foo { vector<vector<handle>> bad_member; };", |
| "struct Foo { vector<vector<handle?>> bad_member; };", |
| "struct Foo { vector<vector<Protocol>> bad_member; };", |
| "struct Foo { vector<vector<ResourceStruct>> bad_member; };", |
| "struct Foo { vector<vector<ResourceTable>> bad_member; };", |
| "struct Foo { vector<vector<ResourceUnion>> bad_member; };", |
| "struct Foo { vector<array<vector<ResourceStruct>?>:2>? bad_member; };", |
| }) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| protocol Protocol {}; |
| resource struct ResourceStruct {}; |
| resource table ResourceTable {}; |
| resource union ResourceUnion { 1: bool b; }; |
| |
| )FIDL" + definition + "\n"; |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile(), "%s", fidl_library.c_str()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1, "%s", fidl_library.c_str()); |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource, "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo", "%s", fidl_library.c_str()); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member", "%s", fidl_library.c_str()); |
| } |
| } |
| |
| TEST(ResourcenessTests, bad_multiple_resource_types_in_value_type) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| struct Foo { |
| handle first; |
| handle? second; |
| ResourceStruct third; |
| }; |
| |
| resource struct ResourceStruct {}; |
| )FIDL"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 3); |
| |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Foo"); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "first"); |
| |
| ASSERT_ERR(errors[1], fidl::ErrTypeMustBeResource); |
| ASSERT_SUBSTR(errors[1]->msg.c_str(), "Foo"); |
| ASSERT_SUBSTR(errors[1]->msg.c_str(), "second"); |
| |
| ASSERT_ERR(errors[2], fidl::ErrTypeMustBeResource); |
| ASSERT_SUBSTR(errors[2]->msg.c_str(), "Foo"); |
| ASSERT_SUBSTR(errors[2]->msg.c_str(), "third"); |
| } |
| |
| TEST(ResourcenessTests, good_transitive_resource_member) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| resource struct Top { |
| Middle middle; |
| }; |
| resource struct Middle { |
| Bottom bottom; |
| }; |
| resource struct Bottom {}; |
| )FIDL"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_TRUE(library.Compile()); |
| EXPECT_EQ(library.LookupStruct("Top")->resourceness, fidl::types::Resourceness::kResource); |
| } |
| |
| TEST(ResourcenessTests, bad_transitive_resource_member) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| struct Top { |
| Middle middle; |
| }; |
| struct Middle { |
| Bottom bottom; |
| }; |
| resource struct Bottom {}; |
| )FIDL"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile()); |
| |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 2); |
| |
| // `Middle` must be a resource because it includes `bottom`, a *nominal* resource. |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Middle"); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bottom"); |
| |
| // `Top` must be a resource because it includes `middle`, an *effective* resource. |
| ASSERT_ERR(errors[1], fidl::ErrTypeMustBeResource); |
| ASSERT_SUBSTR(errors[1]->msg.c_str(), "Top"); |
| ASSERT_SUBSTR(errors[1]->msg.c_str(), "middle"); |
| } |
| |
| TEST(ResourcenessTests, good_recursive_value_types) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| struct Ouro { |
| Boros? b; |
| }; |
| |
| struct Boros { |
| Ouro? o; |
| }; |
| )FIDL"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_TRUE(library.Compile()); |
| } |
| |
| TEST(ResourcenessTests, good_recursive_resource_types) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| resource struct Ouro { |
| Boros? b; |
| }; |
| |
| resource struct Boros { |
| Ouro? o; |
| }; |
| )FIDL"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_TRUE(library.Compile()); |
| } |
| |
| TEST(ResourcenessTests, bad_recursive_resource_types) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| resource struct Ouro { |
| Boros? b; |
| }; |
| |
| struct Boros { |
| Ouro? bad_member; |
| }; |
| )FIDL"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_FALSE(library.Compile()); |
| const auto& errors = library.errors(); |
| ASSERT_EQ(errors.size(), 1); |
| |
| ASSERT_ERR(errors[0], fidl::ErrTypeMustBeResource); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "Boros"); |
| ASSERT_SUBSTR(errors[0]->msg.c_str(), "bad_member"); |
| } |
| |
| TEST(ResourcenessTests, strict_resource_order_independent) { |
| std::string fidl_library = R"FIDL( |
| library example; |
| |
| strict resource union SR { 1: bool b; }; |
| resource strict union RS { 1: bool b; }; |
| )FIDL"; |
| |
| TestLibrary library(fidl_library); |
| ASSERT_TRUE(library.Compile()); |
| |
| const auto strict_resource = library.LookupUnion("SR"); |
| EXPECT_EQ(strict_resource->strictness, fidl::types::Strictness::kStrict); |
| EXPECT_EQ(strict_resource->resourceness, fidl::types::Resourceness::kResource); |
| |
| const auto resource_strict = library.LookupUnion("RS"); |
| EXPECT_EQ(resource_strict->strictness, fidl::types::Strictness::kStrict); |
| EXPECT_EQ(resource_strict->resourceness, fidl::types::Resourceness::kResource); |
| } |
| |
| } // namespace |