[fidl][errors] Error support in fidlc
This adds support for the syntax introduced in FTP-014. If the error
syntax is used then structs and unions are generated to support them.
TEST=added compiler test and example
Change-Id: Iaf71052d2f3493a44a3917121ad3dfd21f61691e
diff --git a/system/host/fidl/examples/errors.fidl b/system/host/fidl/examples/errors.fidl
new file mode 100644
index 0000000..8eb9769
--- /dev/null
+++ b/system/host/fidl/examples/errors.fidl
@@ -0,0 +1,18 @@
+// Copyright 2019 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.
+
+library fidl.examples.errors;
+
+enum ErrorCode : int32 {
+ kBad = 1;
+ kReallyBad = 2;
+ kOMGSoTerrible = 3;
+};
+
+interface EpicFail {
+ 1: IntegerError(string x) -> (string y) error int32;
+ 2: UnsignedError(string x) -> (string y) error uint32;
+ 3: EnumError(string x) -> (string y) error ErrorCode;
+ 4: EmptyResult(string x) -> () error ErrorCode;
+};
diff --git a/system/host/fidl/include/fidl/flat_ast.h b/system/host/fidl/include/fidl/flat_ast.h
index d4bd4de..acedfa5 100644
--- a/system/host/fidl/include/fidl/flat_ast.h
+++ b/system/host/fidl/include/fidl/flat_ast.h
@@ -22,6 +22,7 @@
#include "error_reporter.h"
#include "raw_ast.h"
#include "type_shape.h"
+#include "virtual_source_file.h"
namespace fidl {
namespace flat {
@@ -1094,6 +1095,8 @@
const raw::AttributeList* attributes);
Name NextAnonymousName();
+ Name GeneratedName(const std::string& name);
+ Name DerivedName(const std::vector<StringView>& components);
bool CompileCompoundIdentifier(const raw::CompoundIdentifier* compound_identifier,
SourceLocation location, Name* out_name);
@@ -1111,8 +1114,9 @@
bool ConsumeEnumDeclaration(std::unique_ptr<raw::EnumDeclaration> enum_declaration);
bool
ConsumeInterfaceDeclaration(std::unique_ptr<raw::InterfaceDeclaration> interface_declaration);
- bool ConsumeParameterList(std::unique_ptr<raw::ParameterList> parameter_list,
- Struct** out_struct_decl);
+ bool ConsumeParameterList(Name name, std::unique_ptr<raw::ParameterList> parameter_list,
+ bool anonymous, Struct** out_struct_decl);
+ bool CreateMethodResult(const Name& interface_name, raw::InterfaceMethod* method, Struct* in_response, Struct** out_response);
bool ConsumeStructDeclaration(std::unique_ptr<raw::StructDeclaration> struct_declaration);
bool ConsumeTableDeclaration(std::unique_ptr<raw::TableDeclaration> table_declaration);
bool ConsumeUnionDeclaration(std::unique_ptr<raw::UnionDeclaration> union_declaration);
@@ -1121,6 +1125,7 @@
bool TypeCanBeConst(const Type* type);
const Type* TypeResolve(const Type* type);
bool TypeIsConvertibleTo(const Type* from_type, const Type* to_type);
+ std::unique_ptr<IdentifierType> IdentifierTypeForDecl(const Decl* decl, types::Nullability nullability);
// Given a const declaration of the form
// const type foo = name;
@@ -1224,6 +1229,8 @@
Typespace* typespace_;
uint32_t anon_counter_ = 0;
+
+ VirtualSourceFile generated_source_file_{"generated"};
};
} // namespace flat
diff --git a/system/host/fidl/include/fidl/raw_ast.h b/system/host/fidl/include/fidl/raw_ast.h
index 8ebe8ad..21e1e24 100644
--- a/system/host/fidl/include/fidl/raw_ast.h
+++ b/system/host/fidl/include/fidl/raw_ast.h
@@ -396,10 +396,12 @@
std::unique_ptr<Ordinal> ordinal,
std::unique_ptr<Identifier> identifier,
std::unique_ptr<ParameterList> maybe_request,
- std::unique_ptr<ParameterList> maybe_response)
+ std::unique_ptr<ParameterList> maybe_response,
+ std::unique_ptr<Type> maybe_error)
: SourceElement(element), attributes(std::move(attributes)),
ordinal(std::move(ordinal)), identifier(std::move(identifier)),
- maybe_request(std::move(maybe_request)), maybe_response(std::move(maybe_response)) {}
+ maybe_request(std::move(maybe_request)), maybe_response(std::move(maybe_response)),
+ maybe_error(std::move(maybe_error)) { }
void Accept(TreeVisitor& visitor);
@@ -408,6 +410,7 @@
std::unique_ptr<Identifier> identifier;
std::unique_ptr<ParameterList> maybe_request;
std::unique_ptr<ParameterList> maybe_response;
+ std::unique_ptr<Type> maybe_error;
};
class InterfaceDeclaration : public SourceElement {
diff --git a/system/host/fidl/include/fidl/source_file.h b/system/host/fidl/include/fidl/source_file.h
index 8b9f9d5..41798a6 100644
--- a/system/host/fidl/include/fidl/source_file.h
+++ b/system/host/fidl/include/fidl/source_file.h
@@ -16,7 +16,7 @@
class SourceFile {
public:
SourceFile(std::string filename, std::string data);
- ~SourceFile();
+ virtual ~SourceFile();
StringView filename() const { return filename_; }
StringView data() const { return data_; }
@@ -28,7 +28,7 @@
int column;
};
- StringView LineContaining(StringView view, Position* position_out) const;
+ virtual StringView LineContaining(StringView view, Position* position_out) const;
private:
std::string filename_;
diff --git a/system/host/fidl/include/fidl/token_definitions.inc b/system/host/fidl/include/fidl/token_definitions.inc
index a9cb6aa..6aa8881 100644
--- a/system/host/fidl/include/fidl/token_definitions.inc
+++ b/system/host/fidl/include/fidl/token_definitions.inc
@@ -63,6 +63,8 @@
KEYWORD(Union, "union")
KEYWORD(XUnion, "xunion")
+KEYWORD(Error, "error")
+
KEYWORD(True, "true")
KEYWORD(False, "false")
diff --git a/system/host/fidl/include/fidl/virtual_source_file.h b/system/host/fidl/include/fidl/virtual_source_file.h
new file mode 100644
index 0000000..a371df3
--- /dev/null
+++ b/system/host/fidl/include/fidl/virtual_source_file.h
@@ -0,0 +1,33 @@
+// Copyright 2019 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.
+
+#ifndef ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_VIRTUAL_SOURCE_FILE_H_
+#define ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_VIRTUAL_SOURCE_FILE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "source_file.h"
+#include "source_location.h"
+
+namespace fidl {
+
+class VirtualSourceFile : public SourceFile {
+public:
+ VirtualSourceFile(std::string filename) : SourceFile(filename, "") {}
+ virtual ~VirtualSourceFile() = default;
+
+ virtual StringView LineContaining(StringView view, Position* position_out) const;
+
+ SourceLocation AddLine(const std::string& line);
+
+private:
+ std::vector<std::unique_ptr<std::string>> virtual_lines_;
+};
+
+} // namespace fidl
+
+#endif // ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_VIRTUAL_SOURCE_FILE_H_
diff --git a/system/host/fidl/lib/flat_ast.cpp b/system/host/fidl/lib/flat_ast.cpp
index 7ef4253..5c2e6aa 100644
--- a/system/host/fidl/lib/flat_ast.cpp
+++ b/system/host/fidl/lib/flat_ast.cpp
@@ -681,6 +681,11 @@
"SocketControl",
"OvernetStream",
}));
+ AddAttributeSchema("Result", AttributeSchema({
+ AttributeSchema::Placement::kUnionDecl,
+ }, {
+ "",
+ }));
// clang-format on
}
@@ -857,7 +862,15 @@
std::ostringstream data;
data << "SomeLongAnonymousPrefix";
data << anon_counter_++;
- return Name(this, StringView(data.str()));
+ return GeneratedName(data.str());
+}
+
+Name Library::GeneratedName(const std::string& name) {
+ return Name(this, generated_source_file_.AddLine(name));
+}
+
+Name Library::DerivedName(const std::vector<StringView>& components) {
+ return GeneratedName(StringJoin(components, "_"));
}
bool Library::CompileCompoundIdentifier(const raw::CompoundIdentifier* compound_identifier,
@@ -1128,6 +1141,77 @@
return true;
}
+bool Library::CreateMethodResult(const Name& interface_name,
+ raw::InterfaceMethod* method,
+ Struct* in_response,
+ Struct** out_response) {
+ // Compile the error type.
+ auto error_location = method->maybe_error->location();
+ std::unique_ptr<Type> error_type;
+ if (!ConsumeType(std::move(method->maybe_error), error_location, &error_type))
+ return false;
+
+ const PrimitiveType* error_primitive = nullptr;
+ if (error_type->kind == Type::Kind::kPrimitive) {
+ error_primitive = static_cast<const PrimitiveType*>(error_type.get());
+ } else if (error_type->kind == Type::Kind::kIdentifier) {
+ auto identifier_type = static_cast<const IdentifierType*>(error_type.get());
+ Decl* decl = LookupDeclByName(identifier_type->name);
+ if (decl && decl->kind == Decl::Kind::kEnum) {
+ Enum* error_enum = static_cast<Enum*>(decl);
+ if (!error_enum->type) {
+ if (!CompileEnum(error_enum))
+ return false;
+ }
+ error_primitive = error_enum->type;
+ }
+ }
+ if (!error_primitive ||
+ (error_primitive->subtype != types::PrimitiveSubtype::kInt32 &&
+ error_primitive->subtype != types::PrimitiveSubtype::kUint32)) {
+ return Fail(error_location,
+ "invalid error type: must be int32, uint32 or an enum therof");
+ }
+
+ // Make the Result union containing the response struct and the
+ // error type.
+ Union::Member response_member{
+ IdentifierTypeForDecl(in_response, types::Nullability::kNonnullable),
+ GeneratedName("response").source_location(),
+ nullptr};
+ Union::Member error_member{
+ std::move(error_type),
+ GeneratedName("err").source_location(),
+ nullptr};
+ SourceLocation method_name = method->identifier->location();
+ Name result_name = DerivedName({interface_name.name_part(), method_name.data(), "Result"});
+ std::vector<Union::Member> result_members;
+ result_members.push_back(std::move(response_member));
+ result_members.push_back(std::move(error_member));
+ std::vector<std::unique_ptr<raw::Attribute>> result_attributes;
+ result_attributes.emplace_back(std::make_unique<raw::Attribute>(*method, "Result", ""));
+ auto result_attributelist = std::make_unique<raw::AttributeList>(*method,
+ std::move(result_attributes));
+ union_declarations_.push_back(std::make_unique<Union>(std::move(result_attributelist),
+ std::move(result_name),
+ std::move(result_members)));
+ auto result_decl = union_declarations_.back().get();
+ if (!RegisterDecl(result_decl))
+ return false;
+
+ // Make a new response struct for the method containing just the
+ // result union.
+ std::vector<Struct::Member> response_members;
+ response_members.push_back(Struct::Member(IdentifierTypeForDecl(result_decl, types::Nullability::kNonnullable),
+ GeneratedName("result").source_location(), nullptr, nullptr));
+ struct_declarations_.push_back(std::make_unique<Struct>(nullptr, NextAnonymousName(), std::move(response_members), true));
+ *out_response = struct_declarations_.back().get();
+ if (!RegisterDecl(*out_response))
+ return false;
+
+ return true;
+}
+
bool Library::ConsumeInterfaceDeclaration(
std::unique_ptr<raw::InterfaceDeclaration> interface_declaration) {
auto attributes = std::move(interface_declaration->attributes);
@@ -1153,16 +1237,25 @@
Struct* maybe_request = nullptr;
if (method->maybe_request != nullptr) {
- if (!ConsumeParameterList(std::move(method->maybe_request), &maybe_request))
+ Name request_name = NextAnonymousName();
+ if (!ConsumeParameterList(std::move(request_name), std::move(method->maybe_request), true, &maybe_request))
return false;
}
+ bool has_error = (method->maybe_error != nullptr);
+
Struct* maybe_response = nullptr;
if (method->maybe_response != nullptr) {
- if (!ConsumeParameterList(std::move(method->maybe_response), &maybe_response))
+ Name response_name = has_error ? DerivedName({name.name_part(), method_name.data(), "Response"}) : NextAnonymousName();
+ if (!ConsumeParameterList(std::move(response_name), std::move(method->maybe_response), !has_error, &maybe_response))
return false;
}
+ if (has_error) {
+ if (!CreateMethodResult(name, method.get(), maybe_response, &maybe_response))
+ return false;
+ }
+
assert(maybe_request != nullptr || maybe_response != nullptr);
methods.emplace_back(std::move(attributes),
std::move(ordinal_literal),
@@ -1177,8 +1270,12 @@
return RegisterDecl(interface_declarations_.back().get());
}
-bool Library::ConsumeParameterList(std::unique_ptr<raw::ParameterList> parameter_list,
- Struct** out_struct_decl) {
+std::unique_ptr<IdentifierType> Library::IdentifierTypeForDecl(const Decl* decl, types::Nullability nullability) {
+ return std::make_unique<IdentifierType>(Name(decl->name.library(), decl->name.name_part()), nullability);
+}
+
+bool Library::ConsumeParameterList(Name name, std::unique_ptr<raw::ParameterList> parameter_list,
+ bool anonymous, Struct** out_struct_decl) {
std::vector<Struct::Member> members;
for (auto& parameter : parameter_list->parameter_list) {
const SourceLocation name = parameter->identifier->location();
@@ -1191,10 +1288,9 @@
nullptr /* attributes */);
}
- auto name = NextAnonymousName();
struct_declarations_.push_back(
std::make_unique<Struct>(nullptr /* attributes */, std::move(name), std::move(members),
- true /* anonymous */));
+ anonymous));
auto struct_decl = struct_declarations_.back().get();
if (!RegisterDecl(struct_decl))
diff --git a/system/host/fidl/lib/parser.cpp b/system/host/fidl/lib/parser.cpp
index feb592a..6a19935 100644
--- a/system/host/fidl/lib/parser.cpp
+++ b/system/host/fidl/lib/parser.cpp
@@ -690,6 +690,7 @@
std::unique_ptr<raw::Identifier> method_name;
std::unique_ptr<raw::ParameterList> maybe_request;
std::unique_ptr<raw::ParameterList> maybe_response;
+ std::unique_ptr<raw::Type> maybe_error;
auto parse_params = [this](std::unique_ptr<raw::ParameterList>* params_out) {
ConsumeToken(OfKind(Token::Kind::kLeftParen));
@@ -722,6 +723,11 @@
return Fail();
if (!parse_params(&maybe_response))
return Fail();
+ if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kError))) {
+ maybe_error = ParseType();
+ if (!Ok())
+ return Fail();
+ }
}
}
@@ -733,7 +739,8 @@
std::move(ordinal),
std::move(method_name),
std::move(maybe_request),
- std::move(maybe_response));
+ std::move(maybe_response),
+ std::move(maybe_error));
}
std::unique_ptr<raw::InterfaceDeclaration>
diff --git a/system/host/fidl/lib/virtual_source_file.cpp b/system/host/fidl/lib/virtual_source_file.cpp
new file mode 100644
index 0000000..0bd6a8e
--- /dev/null
+++ b/system/host/fidl/lib/virtual_source_file.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 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/virtual_source_file.h"
+
+namespace fidl {
+
+SourceLocation VirtualSourceFile::AddLine(const std::string& line) {
+ return SourceLocation(*virtual_lines_.emplace_back(std::make_unique<std::string>(line)), *this);
+}
+
+StringView VirtualSourceFile::LineContaining(StringView view, Position* position_out) const {
+ for (int i = 0; i < virtual_lines_.size(); i++) {
+ const std::string& line = *virtual_lines_[i];
+ const char* line_begin = &*line.cbegin();
+ const char* line_end = &*line.cend();
+ if (view.data() < line_begin || view.data() + view.size() > line_end)
+ continue;
+ if (position_out != nullptr) {
+ auto column = view.data() - line_begin;
+ assert(column < std::numeric_limits<int>::max());
+ *position_out = {i + 1, (int)column};
+ }
+ return StringView(line);
+ }
+ return StringView();
+}
+
+} // namespace fidl
diff --git a/system/host/fidl/rules.mk b/system/host/fidl/rules.mk
index c649a59..0bcd772 100644
--- a/system/host/fidl/rules.mk
+++ b/system/host/fidl/rules.mk
@@ -30,6 +30,7 @@
$(LOCAL_DIR)/lib/source_manager.cpp \
$(LOCAL_DIR)/lib/tables_generator.cpp \
$(LOCAL_DIR)/lib/tree_visitor.cpp \
+ $(LOCAL_DIR)/lib/virtual_source_file.cpp \
$(BUILDGEN_DIR)/lib/json_schema.cpp \
$(BUILDGEN_DIR)/lib/json_schema.cpp: $(LOCAL_DIR)/schema.json
diff --git a/system/utest/fidl-compiler/errors_tests.cpp b/system/utest/fidl-compiler/errors_tests.cpp
new file mode 100644
index 0000000..50b59c0
--- /dev/null
+++ b/system/utest/fidl-compiler/errors_tests.cpp
@@ -0,0 +1,210 @@
+// Copyright 2018 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 <unittest/unittest.h>
+
+#include "test_library.h"
+
+namespace {
+
+bool GoodError() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+
+interface Example {
+ Method() -> (string foo) error int32;
+};
+
+)FIDL");
+
+ ASSERT_TRUE(library.Compile());
+
+ auto methods = &library.LookupInterface("Example")->methods;
+ ASSERT_EQ(methods->size(), 1);
+ auto method = &methods->at(0);
+ auto response = method->maybe_response;
+ ASSERT_NOT_NULL(response);
+ ASSERT_EQ(response->members.size(), 1);
+ auto response_member = &response->members.at(0);
+ ASSERT_EQ(response_member->type->kind, fidl::flat::Type::Kind::kIdentifier);
+ auto result_identifier = static_cast<fidl::flat::IdentifierType*>(response_member->type.get());
+ const fidl::flat::Union* result_union = library.LookupUnion(result_identifier->name.name_part());
+ ASSERT_NOT_NULL(result_union);
+ ASSERT_NOT_NULL(result_union->attributes);
+ ASSERT_TRUE(result_union->attributes->HasAttribute("Result"));
+ ASSERT_EQ(result_union->members.size(), 2);
+
+ ASSERT_STR_EQ("response", std::string(result_union->members.at(0).name.data()).c_str());
+
+ const fidl::flat::Union::Member& error = result_union->members.at(1);
+ ASSERT_STR_EQ("err", std::string(error.name.data()).c_str());
+
+ ASSERT_NOT_NULL(error.type);
+ ASSERT_EQ(error.type->kind, fidl::flat::Type::Kind::kPrimitive);
+ auto primitive_type = static_cast<fidl::flat::PrimitiveType*>(error.type.get());
+ ASSERT_EQ(primitive_type->subtype, fidl::types::PrimitiveSubtype::kInt32);
+
+ END_TEST;
+}
+
+bool GoodErrorUnsigned() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+
+interface Example {
+ Method() -> (string foo) error uint32;
+};
+
+)FIDL");
+
+ ASSERT_TRUE(library.Compile());
+ END_TEST;
+}
+
+bool GoodErrorEnum() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+
+enum ErrorType : int32 {
+ GOOD = 1;
+ BAD = 2;
+ UGLY = 3;
+};
+
+interface Example {
+ Method() -> (string foo) error ErrorType;
+};
+
+)FIDL");
+
+ ASSERT_TRUE(library.Compile());
+ END_TEST;
+}
+
+bool GoodErrorEnumAfter() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+
+interface Example {
+ Method() -> (string foo) error ErrorType;
+};
+
+enum ErrorType : int32 {
+ GOOD = 1;
+ BAD = 2;
+ UGLY = 3;
+};
+
+)FIDL");
+
+ ASSERT_TRUE(library.Compile());
+ END_TEST;
+}
+
+bool BadErrorUnknownIdentifier() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+
+interface Example {
+ Method() -> (string foo) error ErrorType;
+};
+)FIDL");
+
+ ASSERT_FALSE(library.Compile());
+ auto errors = library.errors();
+ ASSERT_EQ(errors.size(), 1);
+ ASSERT_STR_STR(errors[0].c_str(), "error: invalid error type");
+ END_TEST;
+}
+
+bool BadErrorWrongPrimitive() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+
+interface Example {
+ Method() -> (string foo) error float32;
+};
+)FIDL");
+
+ ASSERT_FALSE(library.Compile());
+ auto errors = library.errors();
+ ASSERT_EQ(errors.size(), 1);
+ ASSERT_STR_STR(errors[0].c_str(), "error: invalid error type");
+ END_TEST;
+}
+
+bool BadErrorMissingType() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+interface Example {
+ Method() -> (int32 flub) error;
+};
+)FIDL");
+ ASSERT_FALSE(library.Compile());
+ auto errors = library.errors();
+ ASSERT_EQ(errors.size(), 1);
+ ASSERT_STR_STR(errors[0].c_str(), "error: found unexpected token");
+ END_TEST;
+}
+
+bool BadErrorNotAType() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+interface Example {
+ Method() -> (int32 flub) error "hello";
+};
+)FIDL");
+ ASSERT_FALSE(library.Compile());
+ auto errors = library.errors();
+ ASSERT_EQ(errors.size(), 1);
+ ASSERT_STR_STR(errors[0].c_str(), "error: found unexpected token");
+ END_TEST;
+}
+
+bool BadErrorNoResponse() {
+ BEGIN_TEST;
+
+ TestLibrary library(R"FIDL(
+library example;
+interface Example {
+ Method() -> error int32;
+};
+)FIDL");
+ ASSERT_FALSE(library.Compile());
+ auto errors = library.errors();
+ ASSERT_EQ(errors.size(), 1);
+ ASSERT_STR_STR(errors[0].c_str(), "error: unexpected token \"error\"");
+ END_TEST;
+}
+} // namespace
+
+BEGIN_TEST_CASE(errors_tests);
+
+RUN_TEST(GoodError);
+RUN_TEST(GoodErrorUnsigned);
+RUN_TEST(GoodErrorEnum);
+RUN_TEST(GoodErrorEnumAfter);
+RUN_TEST(BadErrorUnknownIdentifier);
+RUN_TEST(BadErrorWrongPrimitive);
+RUN_TEST(BadErrorMissingType);
+RUN_TEST(BadErrorNotAType);
+RUN_TEST(BadErrorNoResponse);
+
+END_TEST_CASE(errors_tests);
diff --git a/system/utest/fidl-compiler/json_generator_tests.cpp b/system/utest/fidl-compiler/json_generator_tests.cpp
index 6143085..b23476f 100644
--- a/system/utest/fidl-compiler/json_generator_tests.cpp
+++ b/system/utest/fidl-compiler/json_generator_tests.cpp
@@ -835,6 +835,154 @@
END_TEST;
}
+
+bool json_generator_test_error() {
+ BEGIN_TEST;
+
+ for (int i = 0; i < kRepeatTestCount; i++) {
+ EXPECT_TRUE(checkJSONGenerator(R"FIDL(
+library fidl.test.json;
+
+interface Example {
+ foo(string s) -> (int64 y) error uint32;
+};
+
+)FIDL",
+ R"JSON({
+ "version": "0.0.1",
+ "name": "fidl.test.json",
+ "library_dependencies": [],
+ "const_declarations": [],
+ "enum_declarations": [],
+ "interface_declarations": [
+ {
+ "name": "fidl.test.json/Example",
+ "methods": [
+ {
+ "ordinal": 1369693400,
+ "generated_ordinal": 1369693400,
+ "name": "foo",
+ "has_request": true,
+ "maybe_request": [
+ {
+ "type": {
+ "kind": "string",
+ "nullable": false
+ },
+ "name": "s",
+ "size": 16,
+ "max_out_of_line": 4294967295,
+ "alignment": 8,
+ "offset": 16,
+ "max_handles": 0
+ }
+ ],
+ "maybe_request_size": 32,
+ "maybe_request_alignment": 8,
+ "has_response": true,
+ "maybe_response": [
+ {
+ "type": {
+ "kind": "identifier",
+ "identifier": "fidl.test.json/Example_foo_Result",
+ "nullable": false
+ },
+ "name": "result",
+ "size": 16,
+ "max_out_of_line": 0,
+ "alignment": 8,
+ "offset": 16,
+ "max_handles": 0
+ }
+ ],
+ "maybe_response_size": 32,
+ "maybe_response_alignment": 8
+ }
+ ]
+ }
+ ],
+ "struct_declarations": [
+ {
+ "name": "fidl.test.json/Example_foo_Response",
+ "anonymous": false,
+ "members": [
+ {
+ "type": {
+ "kind": "primitive",
+ "subtype": "int64"
+ },
+ "name": "y",
+ "size": 8,
+ "max_out_of_line": 0,
+ "alignment": 8,
+ "offset": 0,
+ "max_handles": 0
+ }
+ ],
+ "size": 8,
+ "max_out_of_line": 0,
+ "alignment": 8,
+ "max_handles": 0
+ }
+ ],
+ "table_declarations": [],
+ "union_declarations": [
+ {
+ "name": "fidl.test.json/Example_foo_Result",
+ "maybe_attributes": [
+ {
+ "name": "Result",
+ "value": ""
+ }
+ ],
+ "members": [
+ {
+ "type": {
+ "kind": "identifier",
+ "identifier": "fidl.test.json/Example_foo_Response",
+ "nullable": false
+ },
+ "name": "response",
+ "size": 8,
+ "max_out_of_line": 0,
+ "alignment": 8,
+ "offset": 8
+ },
+ {
+ "type": {
+ "kind": "primitive",
+ "subtype": "uint32"
+ },
+ "name": "err",
+ "size": 4,
+ "max_out_of_line": 0,
+ "alignment": 4,
+ "offset": 8
+ }
+ ],
+ "size": 16,
+ "max_out_of_line": 0,
+ "alignment": 8,
+ "max_handles": 0
+ }
+ ],
+ "xunion_declarations": [],
+ "declaration_order": [
+ "fidl.test.json/Example_foo_Response",
+ "fidl.test.json/Example_foo_Result",
+ "fidl.test.json/Example"
+ ],
+ "declarations": {
+ "fidl.test.json/Example": "interface",
+ "fidl.test.json/Example_foo_Response": "struct",
+ "fidl.test.json/Example_foo_Result": "union"
+ }
+})JSON"));
+ }
+
+ END_TEST;
+}
+
} // namespace
BEGIN_TEST_CASE(json_generator_tests);
@@ -845,4 +993,5 @@
RUN_TEST(json_generator_test_xunion);
RUN_TEST(json_generator_test_inheritance);
RUN_TEST(json_generator_test_inheritance_with_recursive_decl);
+RUN_TEST(json_generator_test_error);
END_TEST_CASE(json_generator_tests);
diff --git a/system/utest/fidl-compiler/rules.mk b/system/utest/fidl-compiler/rules.mk
index 1f2a4ca..dfbbb73 100644
--- a/system/utest/fidl-compiler/rules.mk
+++ b/system/utest/fidl-compiler/rules.mk
@@ -21,6 +21,7 @@
$(EXAMPLE_DIR)/empty.fidl \
$(EXAMPLE_DIR)/enums.fidl \
$(EXAMPLE_DIR)/events.fidl \
+ $(EXAMPLE_DIR)/errors.fidl \
$(EXAMPLE_DIR)/example-0.fidl \
$(EXAMPLE_DIR)/example-1.fidl \
$(EXAMPLE_DIR)/example-2.fidl \
@@ -66,6 +67,7 @@
$(LOCAL_DIR)/consts_tests.cpp \
$(LOCAL_DIR)/declaration_order_tests.cpp \
$(LOCAL_DIR)/enums_tests.cpp \
+ $(LOCAL_DIR)/errors_tests.cpp \
$(LOCAL_DIR)/flat_ast_tests.cpp \
$(LOCAL_DIR)/formatter_unittests.cpp \
$(LOCAL_DIR)/json_generator_tests.cpp \
@@ -80,6 +82,7 @@
$(LOCAL_DIR)/table_tests.cpp \
$(LOCAL_DIR)/types_tests.cpp \
$(LOCAL_DIR)/using_tests.cpp \
+ $(LOCAL_DIR)/virtual_source_tests.cpp \
$(LOCAL_DIR)/visitor_unittests.cpp \
$(LOCAL_DIR)/xunion_tests.cpp \
$(BUILDGEN_DIR)/examples.cpp \
diff --git a/system/utest/fidl-compiler/virtual_source_tests.cpp b/system/utest/fidl-compiler/virtual_source_tests.cpp
new file mode 100644
index 0000000..7131c31
--- /dev/null
+++ b/system/utest/fidl-compiler/virtual_source_tests.cpp
@@ -0,0 +1,53 @@
+// Copyright 2019 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 <unittest/unittest.h>
+
+#include "test_library.h"
+#include "fidl/source_location.h"
+
+namespace {
+
+bool AddLine() {
+ BEGIN_TEST;
+
+ fidl::VirtualSourceFile file("imaginary-test-file");
+
+ fidl::SourceLocation one = file.AddLine("one");
+ fidl::SourceLocation two = file.AddLine("two");
+ fidl::SourceLocation three = file.AddLine("three");
+
+ EXPECT_STR_EQ(std::string(one.data()).c_str(), "one");
+ EXPECT_STR_EQ(std::string(two.data()).c_str(), "two");
+ EXPECT_STR_EQ(std::string(three.data()).c_str(), "three");
+
+ END_TEST;
+}
+
+bool LineContaining() {
+ BEGIN_TEST;
+
+ fidl::VirtualSourceFile file("imaginary-test-file");
+
+ file.AddLine("one");
+ fidl::SourceLocation two = file.AddLine("two");
+ file.AddLine("three");
+
+ fidl::SourceFile::Position pos{};
+ file.LineContaining(two.data(), &pos);
+ EXPECT_EQ(pos.line, 2);
+ EXPECT_EQ(pos.column, 0);
+
+ END_TEST;
+}
+
+} // namespace
+
+BEGIN_TEST_CASE(virtual_source_tests);
+
+RUN_TEST(AddLine);
+RUN_TEST(LineContaining);
+
+END_TEST_CASE(virtual_source_tests);
+