[fidl][errors] Parse method error syntax

This adds support for the syntax introduced in FTP-014. The error type
information is carried up through to the flat AST but is not used when
compiling types to IR.

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..6958d19
--- /dev/null
+++ b/system/host/fidl/examples/errors.fidl
@@ -0,0 +1,18 @@
+// 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.
+
+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;
+};
\ No newline at end of file
diff --git a/system/host/fidl/include/fidl/flat_ast.h b/system/host/fidl/include/fidl/flat_ast.h
index d33c990..c4740ab 100644
--- a/system/host/fidl/include/fidl/flat_ast.h
+++ b/system/host/fidl/include/fidl/flat_ast.h
@@ -773,10 +773,14 @@
         Method(std::unique_ptr<raw::AttributeList> attributes,
                std::unique_ptr<raw::Ordinal> ordinal, SourceLocation name,
                Struct* maybe_request,
-               Struct* maybe_response)
+               Struct* maybe_response,
+               std::unique_ptr<Type> maybe_error)
             : attributes(std::move(attributes)), ordinal(std::move(ordinal)), name(std::move(name)),
-              maybe_request(maybe_request), maybe_response(maybe_response) {
+              maybe_request(maybe_request), maybe_response(maybe_response), maybe_error(std::move(maybe_error)) {
             assert(this->maybe_request != nullptr || this->maybe_response != nullptr);
+            if (maybe_error) {
+                assert(this->maybe_request != nullptr && this->maybe_response != nullptr);
+            }
         }
 
         std::unique_ptr<raw::AttributeList> attributes;
@@ -784,6 +788,7 @@
         SourceLocation name;
         Struct* maybe_request;
         Struct* maybe_response;
+        std::unique_ptr<Type> maybe_error;
     };
 
     Interface(std::unique_ptr<raw::AttributeList> attributes, Name name,
diff --git a/system/host/fidl/include/fidl/raw_ast.h b/system/host/fidl/include/fidl/raw_ast.h
index b845646..4e74554 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/token_definitions.inc b/system/host/fidl/include/fidl/token_definitions.inc
index 910666f..5ac14c7 100644
--- a/system/host/fidl/include/fidl/token_definitions.inc
+++ b/system/host/fidl/include/fidl/token_definitions.inc
@@ -62,6 +62,8 @@
 KEYWORD(Table, "table")
 KEYWORD(Union, "union")
 
+KEYWORD(Error, "error")
+
 KEYWORD(True, "true")
 KEYWORD(False, "false")
 
diff --git a/system/host/fidl/lib/flat_ast.cpp b/system/host/fidl/lib/flat_ast.cpp
index 9a53aa7..6fca0aa 100644
--- a/system/host/fidl/lib/flat_ast.cpp
+++ b/system/host/fidl/lib/flat_ast.cpp
@@ -1123,11 +1123,18 @@
                 return false;
         }
 
+        std::unique_ptr<Type> maybe_error = nullptr;
+        if (method->maybe_error != nullptr) {
+            auto location = method->maybe_error->location();
+            if (!ConsumeType(std::move(method->maybe_error), location, &maybe_error))
+                return false;
+        }
+
         assert(maybe_request != nullptr || maybe_response != nullptr);
         methods.emplace_back(std::move(attributes),
                              std::move(ordinal_literal),
                              std::move(method_name), std::move(maybe_request),
-                             std::move(maybe_response));
+                             std::move(maybe_response), std::move(maybe_error));
     }
 
     interface_declarations_.push_back(
diff --git a/system/host/fidl/lib/parser.cpp b/system/host/fidl/lib/parser.cpp
index eabed11..533813a 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/utest/fidl-compiler/errors_tests.cpp b/system/utest/fidl-compiler/errors_tests.cpp
new file mode 100644
index 0000000..202a46f
--- /dev/null
+++ b/system/utest/fidl-compiler/errors_tests.cpp
@@ -0,0 +1,87 @@
+// 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() -> (int32 flub) error int32;
+};
+)FIDL");
+    ASSERT_TRUE(library.Compile());
+
+    auto methods = &library.LookupInterface("Example")->methods;
+    ASSERT_EQ(methods->size(), 1);
+    auto method = &methods->at(0);
+    auto error_type = method->maybe_error.get();
+    ASSERT_NE(error_type, nullptr);
+    ASSERT_EQ(error_type->kind, fidl::flat::Type::Kind::kPrimitive);
+    auto primitive_type = static_cast<fidl::flat::PrimitiveType*>(error_type);
+    ASSERT_EQ(primitive_type->subtype, fidl::types::PrimitiveSubtype::kInt32);
+
+    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);
+    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);
+    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);
+    END_TEST;
+}
+} // namespace
+
+BEGIN_TEST_CASE(errors_tests);
+
+RUN_TEST(GoodError);
+RUN_TEST(BadErrorMissingType);
+RUN_TEST(BadErrorNotAType);
+RUN_TEST(BadErrorNoResponse);
+
+END_TEST_CASE(errors_tests);
diff --git a/system/utest/fidl-compiler/rules.mk b/system/utest/fidl-compiler/rules.mk
index e8c92d3..2696fee 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 \
@@ -65,6 +66,7 @@
     $(LOCAL_DIR)/attributes_tests.cpp \
     $(LOCAL_DIR)/consts_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 \