blob: a281fbe15e6bfa36c1722ce7da6f1463fff22551 [file] [log] [blame]
// 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