| Index: third_party/protobuf/src/google/protobuf/compiler/parser.cc
|
| diff --git a/third_party/protobuf/src/google/protobuf/compiler/parser.cc b/third_party/protobuf/src/google/protobuf/compiler/parser.cc
|
| index 23aa01ced01e7cdb210bf0d037e83a0048b8d1e8..ea792a9d8cba6150ddb8937dfd1920d6e333166a 100644
|
| --- a/third_party/protobuf/src/google/protobuf/compiler/parser.cc
|
| +++ b/third_party/protobuf/src/google/protobuf/compiler/parser.cc
|
| @@ -1,6 +1,6 @@
|
| // Protocol Buffers - Google's data interchange format
|
| // Copyright 2008 Google Inc. All rights reserved.
|
| -// http://code.google.com/p/protobuf/
|
| +// https://developers.google.com/protocol-buffers/
|
| //
|
| // Redistribution and use in source and binary forms, with or without
|
| // modification, are permitted provided that the following conditions are
|
| @@ -44,9 +44,10 @@
|
| #include <google/protobuf/descriptor.pb.h>
|
| #include <google/protobuf/wire_format.h>
|
| #include <google/protobuf/io/tokenizer.h>
|
| +#include <google/protobuf/stubs/logging.h>
|
| #include <google/protobuf/stubs/common.h>
|
| #include <google/protobuf/stubs/strutil.h>
|
| -#include <google/protobuf/stubs/map-util.h>
|
| +#include <google/protobuf/stubs/map_util.h>
|
|
|
| namespace google {
|
| namespace protobuf {
|
| @@ -84,6 +85,32 @@ TypeNameMap MakeTypeNameTable() {
|
|
|
| const TypeNameMap kTypeNames = MakeTypeNameTable();
|
|
|
| +// Camel-case the field name and append "Entry" for generated map entry name.
|
| +// e.g. map<KeyType, ValueType> foo_map => FooMapEntry
|
| +string MapEntryName(const string& field_name) {
|
| + string result;
|
| + static const char kSuffix[] = "Entry";
|
| + result.reserve(field_name.size() + sizeof(kSuffix));
|
| + bool cap_next = true;
|
| + for (int i = 0; i < field_name.size(); ++i) {
|
| + if (field_name[i] == '_') {
|
| + cap_next = true;
|
| + } else if (cap_next) {
|
| + // Note: Do not use ctype.h due to locales.
|
| + if ('a' <= field_name[i] && field_name[i] <= 'z') {
|
| + result.push_back(field_name[i] - 'a' + 'A');
|
| + } else {
|
| + result.push_back(field_name[i]);
|
| + }
|
| + cap_next = false;
|
| + } else {
|
| + result.push_back(field_name[i]);
|
| + }
|
| + }
|
| + result.append(kSuffix);
|
| + return result;
|
| +}
|
| +
|
| } // anonymous namespace
|
|
|
| // Makes code slightly more readable. The meaning of "DO(foo)" is
|
| @@ -251,27 +278,39 @@ bool Parser::ConsumeString(string* output, const char* error) {
|
| }
|
| }
|
|
|
| -bool Parser::TryConsumeEndOfDeclaration(const char* text,
|
| - const LocationRecorder* location) {
|
| +bool Parser::TryConsumeEndOfDeclaration(
|
| + const char* text, const LocationRecorder* location) {
|
| if (LookingAt(text)) {
|
| string leading, trailing;
|
| - input_->NextWithComments(&trailing, NULL, &leading);
|
| + vector<string> detached;
|
| + input_->NextWithComments(&trailing, &detached, &leading);
|
|
|
| // Save the leading comments for next time, and recall the leading comments
|
| // from last time.
|
| leading.swap(upcoming_doc_comments_);
|
|
|
| if (location != NULL) {
|
| - location->AttachComments(&leading, &trailing);
|
| + upcoming_detached_comments_.swap(detached);
|
| + location->AttachComments(&leading, &trailing, &detached);
|
| + } else if (strcmp(text, "}") == 0) {
|
| + // If the current location is null and we are finishing the current scope,
|
| + // drop pending upcoming detached comments.
|
| + upcoming_detached_comments_.swap(detached);
|
| + } else {
|
| + // Otherwise, append the new detached comments to the existing upcoming
|
| + // detached comments.
|
| + upcoming_detached_comments_.insert(upcoming_detached_comments_.end(),
|
| + detached.begin(), detached.end());
|
| }
|
| +
|
| return true;
|
| } else {
|
| return false;
|
| }
|
| }
|
|
|
| -bool Parser::ConsumeEndOfDeclaration(const char* text,
|
| - const LocationRecorder* location) {
|
| +bool Parser::ConsumeEndOfDeclaration(
|
| + const char* text, const LocationRecorder* location) {
|
| if (TryConsumeEndOfDeclaration(text, location)) {
|
| return true;
|
| } else {
|
| @@ -343,6 +382,11 @@ void Parser::LocationRecorder::StartAt(const io::Tokenizer::Token& token) {
|
| location_->set_span(1, token.column);
|
| }
|
|
|
| +void Parser::LocationRecorder::StartAt(const LocationRecorder& other) {
|
| + location_->set_span(0, other.location_->span(0));
|
| + location_->set_span(1, other.location_->span(1));
|
| +}
|
| +
|
| void Parser::LocationRecorder::EndAt(const io::Tokenizer::Token& token) {
|
| if (token.line != location_->span(0)) {
|
| location_->add_span(token.line);
|
| @@ -359,7 +403,8 @@ void Parser::LocationRecorder::RecordLegacyLocation(const Message* descriptor,
|
| }
|
|
|
| void Parser::LocationRecorder::AttachComments(
|
| - string* leading, string* trailing) const {
|
| + string* leading, string* trailing,
|
| + vector<string>* detached_comments) const {
|
| GOOGLE_CHECK(!location_->has_leading_comments());
|
| GOOGLE_CHECK(!location_->has_trailing_comments());
|
|
|
| @@ -369,6 +414,11 @@ void Parser::LocationRecorder::AttachComments(
|
| if (!trailing->empty()) {
|
| location_->mutable_trailing_comments()->swap(*trailing);
|
| }
|
| + for (int i = 0; i < detached_comments->size(); ++i) {
|
| + location_->add_leading_detached_comments()->swap(
|
| + (*detached_comments)[i]);
|
| + }
|
| + detached_comments->clear();
|
| }
|
|
|
| // -------------------------------------------------------------------
|
| @@ -408,6 +458,61 @@ void Parser::SkipRestOfBlock() {
|
|
|
| // ===================================================================
|
|
|
| +bool Parser::ValidateEnum(const EnumDescriptorProto* proto) {
|
| + bool has_allow_alias = false;
|
| + bool allow_alias = false;
|
| +
|
| + for (int i = 0; i < proto->options().uninterpreted_option_size(); i++) {
|
| + const UninterpretedOption option = proto->options().uninterpreted_option(i);
|
| + if (option.name_size() > 1) {
|
| + continue;
|
| + }
|
| + if (!option.name(0).is_extension() &&
|
| + option.name(0).name_part() == "allow_alias") {
|
| + has_allow_alias = true;
|
| + if (option.identifier_value() == "true") {
|
| + allow_alias = true;
|
| + }
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (has_allow_alias && !allow_alias) {
|
| + string error =
|
| + "\"" + proto->name() +
|
| + "\" declares 'option allow_alias = false;' which has no effect. "
|
| + "Please remove the declaration.";
|
| + // This needlessly clutters declarations with nops.
|
| + AddError(error);
|
| + return false;
|
| + }
|
| +
|
| + set<int> used_values;
|
| + bool has_duplicates = false;
|
| + for (int i = 0; i < proto->value_size(); ++i) {
|
| + const EnumValueDescriptorProto enum_value = proto->value(i);
|
| + if (used_values.find(enum_value.number()) != used_values.end()) {
|
| + has_duplicates = true;
|
| + break;
|
| + } else {
|
| + used_values.insert(enum_value.number());
|
| + }
|
| + }
|
| + if (allow_alias && !has_duplicates) {
|
| + string error =
|
| + "\"" + proto->name() +
|
| + "\" declares support for enum aliases but no enum values share field "
|
| + "numbers. Please remove the unnecessary 'option allow_alias = true;' "
|
| + "declaration.";
|
| + // Generate an error if an enum declares support for duplicate enum values
|
| + // and does not use it protect future authors.
|
| + AddError(error);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
|
| input_ = input;
|
| had_errors_ = false;
|
| @@ -420,21 +525,29 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
|
| SourceCodeInfo source_code_info;
|
| source_code_info_ = &source_code_info;
|
|
|
| + vector<string> top_doc_comments;
|
| if (LookingAtType(io::Tokenizer::TYPE_START)) {
|
| // Advance to first token.
|
| - input_->NextWithComments(NULL, NULL, &upcoming_doc_comments_);
|
| + input_->NextWithComments(NULL, &upcoming_detached_comments_,
|
| + &upcoming_doc_comments_);
|
| }
|
|
|
| {
|
| LocationRecorder root_location(this);
|
|
|
| if (require_syntax_identifier_ || LookingAt("syntax")) {
|
| - if (!ParseSyntaxIdentifier()) {
|
| + if (!ParseSyntaxIdentifier(root_location)) {
|
| // Don't attempt to parse the file if we didn't recognize the syntax
|
| // identifier.
|
| return false;
|
| }
|
| + // Store the syntax into the file.
|
| + if (file != NULL) file->set_syntax(syntax_identifier_);
|
| } else if (!stop_after_syntax_identifier_) {
|
| + GOOGLE_LOG(WARNING) << "No syntax specified for the proto file. "
|
| + << "Please use 'syntax = \"proto2\";' or "
|
| + << "'syntax = \"proto3\";' to specify a syntax "
|
| + << "version. (Defaulted to proto2 syntax.)";
|
| syntax_identifier_ = "proto2";
|
| }
|
|
|
| @@ -449,7 +562,8 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
|
|
|
| if (LookingAt("}")) {
|
| AddError("Unmatched \"}\".");
|
| - input_->NextWithComments(NULL, NULL, &upcoming_doc_comments_);
|
| + input_->NextWithComments(NULL, &upcoming_detached_comments_,
|
| + &upcoming_doc_comments_);
|
| }
|
| }
|
| }
|
| @@ -461,20 +575,25 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
|
| return !had_errors_;
|
| }
|
|
|
| -bool Parser::ParseSyntaxIdentifier() {
|
| - DO(Consume("syntax", "File must begin with 'syntax = \"proto2\";'."));
|
| +bool Parser::ParseSyntaxIdentifier(const LocationRecorder& parent) {
|
| + LocationRecorder syntax_location(parent,
|
| + FileDescriptorProto::kSyntaxFieldNumber);
|
| + DO(Consume(
|
| + "syntax",
|
| + "File must begin with a syntax statement, e.g. 'syntax = \"proto2\";'."));
|
| DO(Consume("="));
|
| io::Tokenizer::Token syntax_token = input_->current();
|
| string syntax;
|
| DO(ConsumeString(&syntax, "Expected syntax identifier."));
|
| - DO(ConsumeEndOfDeclaration(";", NULL));
|
| + DO(ConsumeEndOfDeclaration(";", &syntax_location));
|
|
|
| syntax_identifier_ = syntax;
|
|
|
| - if (syntax != "proto2" && !stop_after_syntax_identifier_) {
|
| + if (syntax != "proto2" && syntax != "proto3" &&
|
| + !stop_after_syntax_identifier_) {
|
| AddError(syntax_token.line, syntax_token.column,
|
| "Unrecognized syntax identifier \"" + syntax + "\". This parser "
|
| - "only recognizes \"proto2\".");
|
| + "only recognizes \"proto2\" and \"proto3\".");
|
| return false;
|
| }
|
|
|
| @@ -489,15 +608,15 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file,
|
| } else if (LookingAt("message")) {
|
| LocationRecorder location(root_location,
|
| FileDescriptorProto::kMessageTypeFieldNumber, file->message_type_size());
|
| - return ParseMessageDefinition(file->add_message_type(), location);
|
| + return ParseMessageDefinition(file->add_message_type(), location, file);
|
| } else if (LookingAt("enum")) {
|
| LocationRecorder location(root_location,
|
| FileDescriptorProto::kEnumTypeFieldNumber, file->enum_type_size());
|
| - return ParseEnumDefinition(file->add_enum_type(), location);
|
| + return ParseEnumDefinition(file->add_enum_type(), location, file);
|
| } else if (LookingAt("service")) {
|
| LocationRecorder location(root_location,
|
| FileDescriptorProto::kServiceFieldNumber, file->service_size());
|
| - return ParseServiceDefinition(file->add_service(), location);
|
| + return ParseServiceDefinition(file->add_service(), location, file);
|
| } else if (LookingAt("extend")) {
|
| LocationRecorder location(root_location,
|
| FileDescriptorProto::kExtensionFieldNumber);
|
| @@ -505,18 +624,19 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file,
|
| file->mutable_message_type(),
|
| root_location,
|
| FileDescriptorProto::kMessageTypeFieldNumber,
|
| - location);
|
| + location, file);
|
| } else if (LookingAt("import")) {
|
| return ParseImport(file->mutable_dependency(),
|
| file->mutable_public_dependency(),
|
| file->mutable_weak_dependency(),
|
| - root_location);
|
| + root_location, file);
|
| } else if (LookingAt("package")) {
|
| - return ParsePackage(file, root_location);
|
| + return ParsePackage(file, root_location, file);
|
| } else if (LookingAt("option")) {
|
| LocationRecorder location(root_location,
|
| FileDescriptorProto::kOptionsFieldNumber);
|
| - return ParseOption(file->mutable_options(), location, OPTION_STATEMENT);
|
| + return ParseOption(file->mutable_options(), location, file,
|
| + OPTION_STATEMENT);
|
| } else {
|
| AddError("Expected top-level statement (e.g. \"message\").");
|
| return false;
|
| @@ -526,8 +646,10 @@ bool Parser::ParseTopLevelStatement(FileDescriptorProto* file,
|
| // -------------------------------------------------------------------
|
| // Messages
|
|
|
| -bool Parser::ParseMessageDefinition(DescriptorProto* message,
|
| - const LocationRecorder& message_location) {
|
| +bool Parser::ParseMessageDefinition(
|
| + DescriptorProto* message,
|
| + const LocationRecorder& message_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(Consume("message"));
|
| {
|
| LocationRecorder location(message_location,
|
| @@ -536,7 +658,7 @@ bool Parser::ParseMessageDefinition(DescriptorProto* message,
|
| message, DescriptorPool::ErrorCollector::NAME);
|
| DO(ConsumeIdentifier(message->mutable_name(), "Expected message name."));
|
| }
|
| - DO(ParseMessageBlock(message, message_location));
|
| + DO(ParseMessageBlock(message, message_location, containing_file));
|
| return true;
|
| }
|
|
|
| @@ -575,7 +697,8 @@ void AdjustExtensionRangesWithMaxEndNumber(DescriptorProto* message) {
|
| } // namespace
|
|
|
| bool Parser::ParseMessageBlock(DescriptorProto* message,
|
| - const LocationRecorder& message_location) {
|
| + const LocationRecorder& message_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(ConsumeEndOfDeclaration("{", &message_location));
|
|
|
| while (!TryConsumeEndOfDeclaration("}", NULL)) {
|
| @@ -584,7 +707,7 @@ bool Parser::ParseMessageBlock(DescriptorProto* message,
|
| return false;
|
| }
|
|
|
| - if (!ParseMessageStatement(message, message_location)) {
|
| + if (!ParseMessageStatement(message, message_location, containing_file)) {
|
| // This statement failed to parse. Skip it, but keep looping to parse
|
| // other statements.
|
| SkipStatement();
|
| @@ -598,7 +721,8 @@ bool Parser::ParseMessageBlock(DescriptorProto* message,
|
| }
|
|
|
| bool Parser::ParseMessageStatement(DescriptorProto* message,
|
| - const LocationRecorder& message_location) {
|
| + const LocationRecorder& message_location,
|
| + const FileDescriptorProto* containing_file) {
|
| if (TryConsumeEndOfDeclaration(";", NULL)) {
|
| // empty statement; ignore
|
| return true;
|
| @@ -606,16 +730,20 @@ bool Parser::ParseMessageStatement(DescriptorProto* message,
|
| LocationRecorder location(message_location,
|
| DescriptorProto::kNestedTypeFieldNumber,
|
| message->nested_type_size());
|
| - return ParseMessageDefinition(message->add_nested_type(), location);
|
| + return ParseMessageDefinition(message->add_nested_type(), location,
|
| + containing_file);
|
| } else if (LookingAt("enum")) {
|
| LocationRecorder location(message_location,
|
| DescriptorProto::kEnumTypeFieldNumber,
|
| message->enum_type_size());
|
| - return ParseEnumDefinition(message->add_enum_type(), location);
|
| + return ParseEnumDefinition(message->add_enum_type(), location,
|
| + containing_file);
|
| } else if (LookingAt("extensions")) {
|
| LocationRecorder location(message_location,
|
| DescriptorProto::kExtensionRangeFieldNumber);
|
| - return ParseExtensions(message, location);
|
| + return ParseExtensions(message, location, containing_file);
|
| + } else if (LookingAt("reserved")) {
|
| + return ParseReserved(message, message_location);
|
| } else if (LookingAt("extend")) {
|
| LocationRecorder location(message_location,
|
| DescriptorProto::kExtensionFieldNumber);
|
| @@ -623,11 +751,21 @@ bool Parser::ParseMessageStatement(DescriptorProto* message,
|
| message->mutable_nested_type(),
|
| message_location,
|
| DescriptorProto::kNestedTypeFieldNumber,
|
| - location);
|
| + location, containing_file);
|
| } else if (LookingAt("option")) {
|
| LocationRecorder location(message_location,
|
| DescriptorProto::kOptionsFieldNumber);
|
| - return ParseOption(message->mutable_options(), location, OPTION_STATEMENT);
|
| + return ParseOption(message->mutable_options(), location,
|
| + containing_file, OPTION_STATEMENT);
|
| + } else if (LookingAt("oneof")) {
|
| + int oneof_index = message->oneof_decl_size();
|
| + LocationRecorder oneof_location(message_location,
|
| + DescriptorProto::kOneofDeclFieldNumber,
|
| + oneof_index);
|
| +
|
| + return ParseOneof(message->add_oneof_decl(), message,
|
| + oneof_index, oneof_location, message_location,
|
| + containing_file);
|
| } else {
|
| LocationRecorder location(message_location,
|
| DescriptorProto::kFieldFieldNumber,
|
| @@ -636,7 +774,8 @@ bool Parser::ParseMessageStatement(DescriptorProto* message,
|
| message->mutable_nested_type(),
|
| message_location,
|
| DescriptorProto::kNestedTypeFieldNumber,
|
| - location);
|
| + location,
|
| + containing_file);
|
| }
|
| }
|
|
|
| @@ -644,30 +783,106 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field,
|
| RepeatedPtrField<DescriptorProto>* messages,
|
| const LocationRecorder& parent_location,
|
| int location_field_number_for_nested_type,
|
| - const LocationRecorder& field_location) {
|
| - // Parse label and type.
|
| - io::Tokenizer::Token label_token = input_->current();
|
| + const LocationRecorder& field_location,
|
| + const FileDescriptorProto* containing_file) {
|
| {
|
| LocationRecorder location(field_location,
|
| FieldDescriptorProto::kLabelFieldNumber);
|
| FieldDescriptorProto::Label label;
|
| - DO(ParseLabel(&label));
|
| - field->set_label(label);
|
| + if (ParseLabel(&label, containing_file)) {
|
| + field->set_label(label);
|
| + if (label == FieldDescriptorProto::LABEL_OPTIONAL &&
|
| + syntax_identifier_ == "proto3") {
|
| + AddError(
|
| + "Explicit 'optional' labels are disallowed in the Proto3 syntax. "
|
| + "To define 'optional' fields in Proto3, simply remove the "
|
| + "'optional' label, as fields are 'optional' by default.");
|
| + }
|
| + }
|
| }
|
|
|
| + return ParseMessageFieldNoLabel(field, messages, parent_location,
|
| + location_field_number_for_nested_type,
|
| + field_location,
|
| + containing_file);
|
| +}
|
| +
|
| +bool Parser::ParseMessageFieldNoLabel(
|
| + FieldDescriptorProto* field,
|
| + RepeatedPtrField<DescriptorProto>* messages,
|
| + const LocationRecorder& parent_location,
|
| + int location_field_number_for_nested_type,
|
| + const LocationRecorder& field_location,
|
| + const FileDescriptorProto* containing_file) {
|
| + MapField map_field;
|
| + // Parse type.
|
| {
|
| LocationRecorder location(field_location); // add path later
|
| location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::TYPE);
|
|
|
| + bool type_parsed = false;
|
| FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32;
|
| string type_name;
|
| - DO(ParseType(&type, &type_name));
|
| - if (type_name.empty()) {
|
| - location.AddPath(FieldDescriptorProto::kTypeFieldNumber);
|
| - field->set_type(type);
|
| - } else {
|
| +
|
| + // Special case map field. We only treat the field as a map field if the
|
| + // field type name starts with the word "map" with a following "<".
|
| + if (TryConsume("map")) {
|
| + if (LookingAt("<")) {
|
| + map_field.is_map_field = true;
|
| + } else {
|
| + // False positive
|
| + type_parsed = true;
|
| + type_name = "map";
|
| + }
|
| + }
|
| + if (map_field.is_map_field) {
|
| + if (field->has_oneof_index()) {
|
| + AddError("Map fields are not allowed in oneofs.");
|
| + return false;
|
| + }
|
| + if (field->has_label()) {
|
| + AddError(
|
| + "Field labels (required/optional/repeated) are not allowed on "
|
| + "map fields.");
|
| + return false;
|
| + }
|
| + if (field->has_extendee()) {
|
| + AddError("Map fields are not allowed to be extensions.");
|
| + return false;
|
| + }
|
| + field->set_label(FieldDescriptorProto::LABEL_REPEATED);
|
| + DO(Consume("<"));
|
| + DO(ParseType(&map_field.key_type, &map_field.key_type_name));
|
| + DO(Consume(","));
|
| + DO(ParseType(&map_field.value_type, &map_field.value_type_name));
|
| + DO(Consume(">"));
|
| + // Defer setting of the type name of the map field until the
|
| + // field name is parsed. Add the source location though.
|
| location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber);
|
| - field->set_type_name(type_name);
|
| + } else {
|
| + // Handle the case where no explicit label is given for a non-map field.
|
| + if (!field->has_label() && DefaultToOptionalFields()) {
|
| + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
|
| + }
|
| + if (!field->has_label()) {
|
| + AddError("Expected \"required\", \"optional\", or \"repeated\".");
|
| + // We can actually reasonably recover here by just assuming the user
|
| + // forgot the label altogether.
|
| + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
|
| + }
|
| +
|
| + // Handle the case where the actual type is a message or enum named "map",
|
| + // which we already consumed in the code above.
|
| + if (!type_parsed) {
|
| + DO(ParseType(&type, &type_name));
|
| + }
|
| + if (type_name.empty()) {
|
| + location.AddPath(FieldDescriptorProto::kTypeFieldNumber);
|
| + field->set_type(type);
|
| + } else {
|
| + location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber);
|
| + field->set_type_name(type_name);
|
| + }
|
| }
|
| }
|
|
|
| @@ -693,14 +908,14 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field,
|
| }
|
|
|
| // Parse options.
|
| - DO(ParseFieldOptions(field, field_location));
|
| + DO(ParseFieldOptions(field, field_location, containing_file));
|
|
|
| // Deal with groups.
|
| if (field->has_type() && field->type() == FieldDescriptorProto::TYPE_GROUP) {
|
| // Awkward: Since a group declares both a message type and a field, we
|
| // have to create overlapping locations.
|
| LocationRecorder group_location(parent_location);
|
| - group_location.StartAt(label_token);
|
| + group_location.StartAt(field_location);
|
| group_location.AddPath(location_field_number_for_nested_type);
|
| group_location.AddPath(messages->size());
|
|
|
| @@ -736,7 +951,7 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field,
|
|
|
| field->set_type_name(group->name());
|
| if (LookingAt("{")) {
|
| - DO(ParseMessageBlock(group, group_location));
|
| + DO(ParseMessageBlock(group, group_location, containing_file));
|
| } else {
|
| AddError("Missing group body.");
|
| return false;
|
| @@ -745,11 +960,81 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field,
|
| DO(ConsumeEndOfDeclaration(";", &field_location));
|
| }
|
|
|
| + // Create a map entry type if this is a map field.
|
| + if (map_field.is_map_field) {
|
| + GenerateMapEntry(map_field, field, messages);
|
| + }
|
| +
|
| return true;
|
| }
|
|
|
| +void Parser::GenerateMapEntry(const MapField& map_field,
|
| + FieldDescriptorProto* field,
|
| + RepeatedPtrField<DescriptorProto>* messages) {
|
| + DescriptorProto* entry = messages->Add();
|
| + string entry_name = MapEntryName(field->name());
|
| + field->set_type_name(entry_name);
|
| + entry->set_name(entry_name);
|
| + entry->mutable_options()->set_map_entry(true);
|
| + FieldDescriptorProto* key_field = entry->add_field();
|
| + key_field->set_name("key");
|
| + key_field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
|
| + key_field->set_number(1);
|
| + if (map_field.key_type_name.empty()) {
|
| + key_field->set_type(map_field.key_type);
|
| + } else {
|
| + key_field->set_type_name(map_field.key_type_name);
|
| + }
|
| + FieldDescriptorProto* value_field = entry->add_field();
|
| + value_field->set_name("value");
|
| + value_field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
|
| + value_field->set_number(2);
|
| + if (map_field.value_type_name.empty()) {
|
| + value_field->set_type(map_field.value_type);
|
| + } else {
|
| + value_field->set_type_name(map_field.value_type_name);
|
| + }
|
| + // Propagate the "enforce_utf8" option to key and value fields if they
|
| + // are strings. This helps simplify the implementation of code generators
|
| + // and also reflection-based parsing code.
|
| + //
|
| + // The following definition:
|
| + // message Foo {
|
| + // map<string, string> value = 1 [enforce_utf8 = false];
|
| + // }
|
| + // will be interpreted as:
|
| + // message Foo {
|
| + // message ValueEntry {
|
| + // option map_entry = true;
|
| + // string key = 1 [enforce_utf8 = false];
|
| + // string value = 2 [enforce_utf8 = false];
|
| + // }
|
| + // repeated ValueEntry value = 1 [enforce_utf8 = false];
|
| + // }
|
| + //
|
| + // TODO(xiaofeng): Remove this when the "enforce_utf8" option is removed
|
| + // from protocol compiler.
|
| + for (int i = 0; i < field->options().uninterpreted_option_size(); ++i) {
|
| + const UninterpretedOption& option =
|
| + field->options().uninterpreted_option(i);
|
| + if (option.name_size() == 1 &&
|
| + option.name(0).name_part() == "enforce_utf8" &&
|
| + !option.name(0).is_extension()) {
|
| + if (key_field->type() == FieldDescriptorProto::TYPE_STRING) {
|
| + key_field->mutable_options()->add_uninterpreted_option()
|
| + ->CopyFrom(option);
|
| + }
|
| + if (value_field->type() == FieldDescriptorProto::TYPE_STRING) {
|
| + value_field->mutable_options()->add_uninterpreted_option()
|
| + ->CopyFrom(option);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| bool Parser::ParseFieldOptions(FieldDescriptorProto* field,
|
| - const LocationRecorder& field_location) {
|
| + const LocationRecorder& field_location,
|
| + const FileDescriptorProto* containing_file) {
|
| if (!LookingAt("[")) return true;
|
|
|
| LocationRecorder location(field_location,
|
| @@ -762,9 +1047,13 @@ bool Parser::ParseFieldOptions(FieldDescriptorProto* field,
|
| if (LookingAt("default")) {
|
| // We intentionally pass field_location rather than location here, since
|
| // the default value is not actually an option.
|
| - DO(ParseDefaultAssignment(field, field_location));
|
| + DO(ParseDefaultAssignment(field, field_location, containing_file));
|
| + } else if (LookingAt("json_name")) {
|
| + // Like default value, this "json_name" is not an actual option.
|
| + DO(ParseJsonName(field, field_location, containing_file));
|
| } else {
|
| - DO(ParseOption(field->mutable_options(), location, OPTION_ASSIGNMENT));
|
| + DO(ParseOption(field->mutable_options(), location,
|
| + containing_file, OPTION_ASSIGNMENT));
|
| }
|
| } while (TryConsume(","));
|
|
|
| @@ -772,8 +1061,10 @@ bool Parser::ParseFieldOptions(FieldDescriptorProto* field,
|
| return true;
|
| }
|
|
|
| -bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
|
| - const LocationRecorder& field_location) {
|
| +bool Parser::ParseDefaultAssignment(
|
| + FieldDescriptorProto* field,
|
| + const LocationRecorder& field_location,
|
| + const FileDescriptorProto* containing_file) {
|
| if (field->has_default_value()) {
|
| AddError("Already set option \"default\".");
|
| field->clear_default_value();
|
| @@ -790,8 +1081,16 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
|
|
|
| if (!field->has_type()) {
|
| // The field has a type name, but we don't know if it is a message or an
|
| - // enum yet. Assume an enum for now.
|
| - DO(ConsumeIdentifier(default_value, "Expected identifier."));
|
| + // enum yet. (If it were a primitive type, |field| would have a type set
|
| + // already.) In this case, simply take the current string as the default
|
| + // value; we will catch the error later if it is not a valid enum value.
|
| + // (N.B. that we do not check whether the current token is an identifier:
|
| + // doing so throws strange errors when the user mistypes a primitive
|
| + // typename and we assume it's an enum. E.g.: "optional int foo = 1 [default
|
| + // = 42]". In such a case the fundamental error is really that "int" is not
|
| + // a type, not that "42" is not an identifier. See b/12533582.)
|
| + *default_value = input_->current().text;
|
| + input_->Next();
|
| return true;
|
| }
|
|
|
| @@ -817,7 +1116,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
|
| }
|
| // Parse the integer to verify that it is not out-of-range.
|
| uint64 value;
|
| - DO(ConsumeInteger64(max_value, &value, "Expected integer."));
|
| + DO(ConsumeInteger64(max_value, &value,
|
| + "Expected integer for field default value."));
|
| // And stringify it again.
|
| default_value->append(SimpleItoa(value));
|
| break;
|
| @@ -839,7 +1139,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
|
| }
|
| // Parse the integer to verify that it is not out-of-range.
|
| uint64 value;
|
| - DO(ConsumeInteger64(max_value, &value, "Expected integer."));
|
| + DO(ConsumeInteger64(max_value, &value,
|
| + "Expected integer for field default value."));
|
| // And stringify it again.
|
| default_value->append(SimpleItoa(value));
|
| break;
|
| @@ -871,7 +1172,11 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
|
| break;
|
|
|
| case FieldDescriptorProto::TYPE_STRING:
|
| - DO(ConsumeString(default_value, "Expected string."));
|
| + // Note: When file opton java_string_check_utf8 is true, if a
|
| + // non-string representation (eg byte[]) is later supported, it must
|
| + // be checked for UTF-8-ness.
|
| + DO(ConsumeString(default_value, "Expected string for field default "
|
| + "value."));
|
| break;
|
|
|
| case FieldDescriptorProto::TYPE_BYTES:
|
| @@ -880,7 +1185,8 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
|
| break;
|
|
|
| case FieldDescriptorProto::TYPE_ENUM:
|
| - DO(ConsumeIdentifier(default_value, "Expected identifier."));
|
| + DO(ConsumeIdentifier(default_value, "Expected enum identifier for field "
|
| + "default value."));
|
| break;
|
|
|
| case FieldDescriptorProto::TYPE_MESSAGE:
|
| @@ -892,8 +1198,31 @@ bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
|
| return true;
|
| }
|
|
|
| +bool Parser::ParseJsonName(
|
| + FieldDescriptorProto* field,
|
| + const LocationRecorder& field_location,
|
| + const FileDescriptorProto* containing_file) {
|
| + if (field->has_json_name()) {
|
| + AddError("Already set option \"json_name\".");
|
| + field->clear_json_name();
|
| + }
|
| +
|
| + DO(Consume("json_name"));
|
| + DO(Consume("="));
|
| +
|
| + LocationRecorder location(field_location,
|
| + FieldDescriptorProto::kJsonNameFieldNumber);
|
| + location.RecordLegacyLocation(
|
| + field, DescriptorPool::ErrorCollector::OPTION_VALUE);
|
| + DO(ConsumeString(field->mutable_json_name(),
|
| + "Expected string for JSON name."));
|
| + return true;
|
| +}
|
| +
|
| +
|
| bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
|
| - const LocationRecorder& part_location) {
|
| + const LocationRecorder& part_location,
|
| + const FileDescriptorProto* containing_file) {
|
| UninterpretedOption::NamePart* name = uninterpreted_option->add_name();
|
| string identifier; // We parse identifiers into this string.
|
| if (LookingAt("(")) { // This is an extension.
|
| @@ -957,6 +1286,7 @@ bool Parser::ParseUninterpretedBlock(string* value) {
|
| // UninterpretedOption, to be interpreted later.
|
| bool Parser::ParseOption(Message* options,
|
| const LocationRecorder& options_location,
|
| + const FileDescriptorProto* containing_file,
|
| OptionStyle style) {
|
| // Create an entry in the uninterpreted_option field.
|
| const FieldDescriptor* uninterpreted_option_field = options->GetDescriptor()->
|
| @@ -988,14 +1318,16 @@ bool Parser::ParseOption(Message* options,
|
| {
|
| LocationRecorder part_location(name_location,
|
| uninterpreted_option->name_size());
|
| - DO(ParseOptionNamePart(uninterpreted_option, part_location));
|
| + DO(ParseOptionNamePart(uninterpreted_option, part_location,
|
| + containing_file));
|
| }
|
|
|
| while (LookingAt(".")) {
|
| DO(Consume("."));
|
| LocationRecorder part_location(name_location,
|
| uninterpreted_option->name_size());
|
| - DO(ParseOptionNamePart(uninterpreted_option, part_location));
|
| + DO(ParseOptionNamePart(uninterpreted_option, part_location,
|
| + containing_file));
|
| }
|
| }
|
|
|
| @@ -1092,7 +1424,8 @@ bool Parser::ParseOption(Message* options,
|
| }
|
|
|
| bool Parser::ParseExtensions(DescriptorProto* message,
|
| - const LocationRecorder& extensions_location) {
|
| + const LocationRecorder& extensions_location,
|
| + const FileDescriptorProto* containing_file) {
|
| // Parse the declaration.
|
| DO(Consume("extensions"));
|
|
|
| @@ -1146,11 +1479,83 @@ bool Parser::ParseExtensions(DescriptorProto* message,
|
| return true;
|
| }
|
|
|
| +// This is similar to extension range parsing, except that "max" is not
|
| +// supported, and accepts field name literals.
|
| +bool Parser::ParseReserved(DescriptorProto* message,
|
| + const LocationRecorder& message_location) {
|
| + // Parse the declaration.
|
| + DO(Consume("reserved"));
|
| + if (LookingAtType(io::Tokenizer::TYPE_STRING)) {
|
| + LocationRecorder location(message_location,
|
| + DescriptorProto::kReservedNameFieldNumber);
|
| + return ParseReservedNames(message, location);
|
| + } else {
|
| + LocationRecorder location(message_location,
|
| + DescriptorProto::kReservedRangeFieldNumber);
|
| + return ParseReservedNumbers(message, location);
|
| + }
|
| +}
|
| +
|
| +
|
| +bool Parser::ParseReservedNames(DescriptorProto* message,
|
| + const LocationRecorder& parent_location) {
|
| + do {
|
| + LocationRecorder location(parent_location, message->reserved_name_size());
|
| + DO(ConsumeString(message->add_reserved_name(), "Expected field name."));
|
| + } while (TryConsume(","));
|
| + DO(ConsumeEndOfDeclaration(";", &parent_location));
|
| + return true;
|
| +}
|
| +
|
| +bool Parser::ParseReservedNumbers(DescriptorProto* message,
|
| + const LocationRecorder& parent_location) {
|
| + bool first = true;
|
| + do {
|
| + LocationRecorder location(parent_location, message->reserved_range_size());
|
| +
|
| + DescriptorProto::ReservedRange* range = message->add_reserved_range();
|
| + int start, end;
|
| + io::Tokenizer::Token start_token;
|
| + {
|
| + LocationRecorder start_location(
|
| + location, DescriptorProto::ReservedRange::kStartFieldNumber);
|
| + start_token = input_->current();
|
| + DO(ConsumeInteger(&start, (first ?
|
| + "Expected field name or number range." :
|
| + "Expected field number range.")));
|
| + }
|
| +
|
| + if (TryConsume("to")) {
|
| + LocationRecorder end_location(
|
| + location, DescriptorProto::ReservedRange::kEndFieldNumber);
|
| + DO(ConsumeInteger(&end, "Expected integer."));
|
| + } else {
|
| + LocationRecorder end_location(
|
| + location, DescriptorProto::ReservedRange::kEndFieldNumber);
|
| + end_location.StartAt(start_token);
|
| + end_location.EndAt(start_token);
|
| + end = start;
|
| + }
|
| +
|
| + // Users like to specify inclusive ranges, but in code we like the end
|
| + // number to be exclusive.
|
| + ++end;
|
| +
|
| + range->set_start(start);
|
| + range->set_end(end);
|
| + first = false;
|
| + } while (TryConsume(","));
|
| +
|
| + DO(ConsumeEndOfDeclaration(";", &parent_location));
|
| + return true;
|
| +}
|
| +
|
| bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
|
| RepeatedPtrField<DescriptorProto>* messages,
|
| const LocationRecorder& parent_location,
|
| int location_field_number_for_nested_type,
|
| - const LocationRecorder& extend_location) {
|
| + const LocationRecorder& extend_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(Consume("extend"));
|
|
|
| // Parse the extendee type.
|
| @@ -1192,7 +1597,65 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
|
|
|
| if (!ParseMessageField(field, messages, parent_location,
|
| location_field_number_for_nested_type,
|
| - location)) {
|
| + location,
|
| + containing_file)) {
|
| + // This statement failed to parse. Skip it, but keep looping to parse
|
| + // other statements.
|
| + SkipStatement();
|
| + }
|
| + } while (!TryConsumeEndOfDeclaration("}", NULL));
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool Parser::ParseOneof(OneofDescriptorProto* oneof_decl,
|
| + DescriptorProto* containing_type,
|
| + int oneof_index,
|
| + const LocationRecorder& oneof_location,
|
| + const LocationRecorder& containing_type_location,
|
| + const FileDescriptorProto* containing_file) {
|
| + DO(Consume("oneof"));
|
| +
|
| + {
|
| + LocationRecorder name_location(oneof_location,
|
| + OneofDescriptorProto::kNameFieldNumber);
|
| + DO(ConsumeIdentifier(oneof_decl->mutable_name(), "Expected oneof name."));
|
| + }
|
| +
|
| + DO(ConsumeEndOfDeclaration("{", &oneof_location));
|
| +
|
| + do {
|
| + if (AtEnd()) {
|
| + AddError("Reached end of input in oneof definition (missing '}').");
|
| + return false;
|
| + }
|
| +
|
| + // Print a nice error if the user accidentally tries to place a label
|
| + // on an individual member of a oneof.
|
| + if (LookingAt("required") ||
|
| + LookingAt("optional") ||
|
| + LookingAt("repeated")) {
|
| + AddError("Fields in oneofs must not have labels (required / optional "
|
| + "/ repeated).");
|
| + // We can continue parsing here because we understand what the user
|
| + // meant. The error report will still make parsing fail overall.
|
| + input_->Next();
|
| + }
|
| +
|
| + LocationRecorder field_location(containing_type_location,
|
| + DescriptorProto::kFieldFieldNumber,
|
| + containing_type->field_size());
|
| +
|
| + FieldDescriptorProto* field = containing_type->add_field();
|
| + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
|
| + field->set_oneof_index(oneof_index);
|
| +
|
| + if (!ParseMessageFieldNoLabel(field,
|
| + containing_type->mutable_nested_type(),
|
| + containing_type_location,
|
| + DescriptorProto::kNestedTypeFieldNumber,
|
| + field_location,
|
| + containing_file)) {
|
| // This statement failed to parse. Skip it, but keep looping to parse
|
| // other statements.
|
| SkipStatement();
|
| @@ -1206,7 +1669,8 @@ bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
|
| // Enums
|
|
|
| bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type,
|
| - const LocationRecorder& enum_location) {
|
| + const LocationRecorder& enum_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(Consume("enum"));
|
|
|
| {
|
| @@ -1217,12 +1681,16 @@ bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type,
|
| DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name."));
|
| }
|
|
|
| - DO(ParseEnumBlock(enum_type, enum_location));
|
| + DO(ParseEnumBlock(enum_type, enum_location, containing_file));
|
| +
|
| + DO(ValidateEnum(enum_type));
|
| +
|
| return true;
|
| }
|
|
|
| bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type,
|
| - const LocationRecorder& enum_location) {
|
| + const LocationRecorder& enum_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(ConsumeEndOfDeclaration("{", &enum_location));
|
|
|
| while (!TryConsumeEndOfDeclaration("}", NULL)) {
|
| @@ -1231,7 +1699,7 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type,
|
| return false;
|
| }
|
|
|
| - if (!ParseEnumStatement(enum_type, enum_location)) {
|
| + if (!ParseEnumStatement(enum_type, enum_location, containing_file)) {
|
| // This statement failed to parse. Skip it, but keep looping to parse
|
| // other statements.
|
| SkipStatement();
|
| @@ -1242,7 +1710,8 @@ bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type,
|
| }
|
|
|
| bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type,
|
| - const LocationRecorder& enum_location) {
|
| + const LocationRecorder& enum_location,
|
| + const FileDescriptorProto* containing_file) {
|
| if (TryConsumeEndOfDeclaration(";", NULL)) {
|
| // empty statement; ignore
|
| return true;
|
| @@ -1250,16 +1719,17 @@ bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type,
|
| LocationRecorder location(enum_location,
|
| EnumDescriptorProto::kOptionsFieldNumber);
|
| return ParseOption(enum_type->mutable_options(), location,
|
| - OPTION_STATEMENT);
|
| + containing_file, OPTION_STATEMENT);
|
| } else {
|
| LocationRecorder location(enum_location,
|
| EnumDescriptorProto::kValueFieldNumber, enum_type->value_size());
|
| - return ParseEnumConstant(enum_type->add_value(), location);
|
| + return ParseEnumConstant(enum_type->add_value(), location, containing_file);
|
| }
|
| }
|
|
|
| bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value,
|
| - const LocationRecorder& enum_value_location) {
|
| + const LocationRecorder& enum_value_location,
|
| + const FileDescriptorProto* containing_file) {
|
| // Parse name.
|
| {
|
| LocationRecorder location(enum_value_location,
|
| @@ -1284,7 +1754,8 @@ bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value,
|
| enum_value->set_number(number);
|
| }
|
|
|
| - DO(ParseEnumConstantOptions(enum_value, enum_value_location));
|
| + DO(ParseEnumConstantOptions(enum_value, enum_value_location,
|
| + containing_file));
|
|
|
| DO(ConsumeEndOfDeclaration(";", &enum_value_location));
|
|
|
| @@ -1293,7 +1764,8 @@ bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value,
|
|
|
| bool Parser::ParseEnumConstantOptions(
|
| EnumValueDescriptorProto* value,
|
| - const LocationRecorder& enum_value_location) {
|
| + const LocationRecorder& enum_value_location,
|
| + const FileDescriptorProto* containing_file) {
|
| if (!LookingAt("[")) return true;
|
|
|
| LocationRecorder location(
|
| @@ -1302,7 +1774,8 @@ bool Parser::ParseEnumConstantOptions(
|
| DO(Consume("["));
|
|
|
| do {
|
| - DO(ParseOption(value->mutable_options(), location, OPTION_ASSIGNMENT));
|
| + DO(ParseOption(value->mutable_options(), location,
|
| + containing_file, OPTION_ASSIGNMENT));
|
| } while (TryConsume(","));
|
|
|
| DO(Consume("]"));
|
| @@ -1312,8 +1785,10 @@ bool Parser::ParseEnumConstantOptions(
|
| // -------------------------------------------------------------------
|
| // Services
|
|
|
| -bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service,
|
| - const LocationRecorder& service_location) {
|
| +bool Parser::ParseServiceDefinition(
|
| + ServiceDescriptorProto* service,
|
| + const LocationRecorder& service_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(Consume("service"));
|
|
|
| {
|
| @@ -1324,12 +1799,13 @@ bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service,
|
| DO(ConsumeIdentifier(service->mutable_name(), "Expected service name."));
|
| }
|
|
|
| - DO(ParseServiceBlock(service, service_location));
|
| + DO(ParseServiceBlock(service, service_location, containing_file));
|
| return true;
|
| }
|
|
|
| bool Parser::ParseServiceBlock(ServiceDescriptorProto* service,
|
| - const LocationRecorder& service_location) {
|
| + const LocationRecorder& service_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(ConsumeEndOfDeclaration("{", &service_location));
|
|
|
| while (!TryConsumeEndOfDeclaration("}", NULL)) {
|
| @@ -1338,7 +1814,7 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service,
|
| return false;
|
| }
|
|
|
| - if (!ParseServiceStatement(service, service_location)) {
|
| + if (!ParseServiceStatement(service, service_location, containing_file)) {
|
| // This statement failed to parse. Skip it, but keep looping to parse
|
| // other statements.
|
| SkipStatement();
|
| @@ -1349,23 +1825,26 @@ bool Parser::ParseServiceBlock(ServiceDescriptorProto* service,
|
| }
|
|
|
| bool Parser::ParseServiceStatement(ServiceDescriptorProto* service,
|
| - const LocationRecorder& service_location) {
|
| + const LocationRecorder& service_location,
|
| + const FileDescriptorProto* containing_file) {
|
| if (TryConsumeEndOfDeclaration(";", NULL)) {
|
| // empty statement; ignore
|
| return true;
|
| } else if (LookingAt("option")) {
|
| LocationRecorder location(
|
| service_location, ServiceDescriptorProto::kOptionsFieldNumber);
|
| - return ParseOption(service->mutable_options(), location, OPTION_STATEMENT);
|
| + return ParseOption(service->mutable_options(), location,
|
| + containing_file, OPTION_STATEMENT);
|
| } else {
|
| LocationRecorder location(service_location,
|
| ServiceDescriptorProto::kMethodFieldNumber, service->method_size());
|
| - return ParseServiceMethod(service->add_method(), location);
|
| + return ParseServiceMethod(service->add_method(), location, containing_file);
|
| }
|
| }
|
|
|
| bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
|
| - const LocationRecorder& method_location) {
|
| + const LocationRecorder& method_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(Consume("rpc"));
|
|
|
| {
|
| @@ -1379,6 +1858,15 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
|
| // Parse input type.
|
| DO(Consume("("));
|
| {
|
| + if (LookingAt("stream")) {
|
| + LocationRecorder location(
|
| + method_location, MethodDescriptorProto::kClientStreamingFieldNumber);
|
| + location.RecordLegacyLocation(
|
| + method, DescriptorPool::ErrorCollector::OTHER);
|
| + method->set_client_streaming(true);
|
| + DO(Consume("stream"));
|
| +
|
| + }
|
| LocationRecorder location(method_location,
|
| MethodDescriptorProto::kInputTypeFieldNumber);
|
| location.RecordLegacyLocation(
|
| @@ -1391,6 +1879,15 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
|
| DO(Consume("returns"));
|
| DO(Consume("("));
|
| {
|
| + if (LookingAt("stream")) {
|
| + LocationRecorder location(
|
| + method_location, MethodDescriptorProto::kServerStreamingFieldNumber);
|
| + location.RecordLegacyLocation(
|
| + method, DescriptorPool::ErrorCollector::OTHER);
|
| + DO(Consume("stream"));
|
| + method->set_server_streaming(true);
|
| +
|
| + }
|
| LocationRecorder location(method_location,
|
| MethodDescriptorProto::kOutputTypeFieldNumber);
|
| location.RecordLegacyLocation(
|
| @@ -1401,9 +1898,9 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
|
|
|
| if (LookingAt("{")) {
|
| // Options!
|
| - DO(ParseOptions(method_location,
|
| - MethodDescriptorProto::kOptionsFieldNumber,
|
| - method->mutable_options()));
|
| + DO(ParseMethodOptions(method_location, containing_file,
|
| + MethodDescriptorProto::kOptionsFieldNumber,
|
| + method->mutable_options()));
|
| } else {
|
| DO(ConsumeEndOfDeclaration(";", &method_location));
|
| }
|
| @@ -1412,9 +1909,10 @@ bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
|
| }
|
|
|
|
|
| -bool Parser::ParseOptions(const LocationRecorder& parent_location,
|
| - const int optionsFieldNumber,
|
| - Message* mutable_options) {
|
| +bool Parser::ParseMethodOptions(const LocationRecorder& parent_location,
|
| + const FileDescriptorProto* containing_file,
|
| + const int optionsFieldNumber,
|
| + Message* mutable_options) {
|
| // Options!
|
| ConsumeEndOfDeclaration("{", &parent_location);
|
| while (!TryConsumeEndOfDeclaration("}", NULL)) {
|
| @@ -1428,7 +1926,8 @@ bool Parser::ParseOptions(const LocationRecorder& parent_location,
|
| } else {
|
| LocationRecorder location(parent_location,
|
| optionsFieldNumber);
|
| - if (!ParseOption(mutable_options, location, OPTION_STATEMENT)) {
|
| + if (!ParseOption(mutable_options, location,
|
| + containing_file, OPTION_STATEMENT)) {
|
| // This statement failed to parse. Skip it, but keep looping to
|
| // parse other statements.
|
| SkipStatement();
|
| @@ -1441,7 +1940,8 @@ bool Parser::ParseOptions(const LocationRecorder& parent_location,
|
|
|
| // -------------------------------------------------------------------
|
|
|
| -bool Parser::ParseLabel(FieldDescriptorProto::Label* label) {
|
| +bool Parser::ParseLabel(FieldDescriptorProto::Label* label,
|
| + const FileDescriptorProto* containing_file) {
|
| if (TryConsume("optional")) {
|
| *label = FieldDescriptorProto::LABEL_OPTIONAL;
|
| return true;
|
| @@ -1451,13 +1951,8 @@ bool Parser::ParseLabel(FieldDescriptorProto::Label* label) {
|
| } else if (TryConsume("required")) {
|
| *label = FieldDescriptorProto::LABEL_REQUIRED;
|
| return true;
|
| - } else {
|
| - AddError("Expected \"required\", \"optional\", or \"repeated\".");
|
| - // We can actually reasonably recover here by just assuming the user
|
| - // forgot the label altogether.
|
| - *label = FieldDescriptorProto::LABEL_OPTIONAL;
|
| - return true;
|
| }
|
| + return false;
|
| }
|
|
|
| bool Parser::ParseType(FieldDescriptorProto::Type* type,
|
| @@ -1510,7 +2005,8 @@ bool Parser::ParseUserDefinedType(string* type_name) {
|
| // ===================================================================
|
|
|
| bool Parser::ParsePackage(FileDescriptorProto* file,
|
| - const LocationRecorder& root_location) {
|
| + const LocationRecorder& root_location,
|
| + const FileDescriptorProto* containing_file) {
|
| if (file->has_package()) {
|
| AddError("Multiple package definitions.");
|
| // Don't append the new package to the old one. Just replace it. Not
|
| @@ -1544,7 +2040,8 @@ bool Parser::ParsePackage(FileDescriptorProto* file,
|
| bool Parser::ParseImport(RepeatedPtrField<string>* dependency,
|
| RepeatedField<int32>* public_dependency,
|
| RepeatedField<int32>* weak_dependency,
|
| - const LocationRecorder& root_location) {
|
| + const LocationRecorder& root_location,
|
| + const FileDescriptorProto* containing_file) {
|
| DO(Consume("import"));
|
| if (LookingAt("public")) {
|
| LocationRecorder location(
|
| @@ -1583,7 +2080,7 @@ bool SourceLocationTable::Find(
|
| DescriptorPool::ErrorCollector::ErrorLocation location,
|
| int* line, int* column) const {
|
| const pair<int, int>* result =
|
| - FindOrNull(location_map_, make_pair(descriptor, location));
|
| + FindOrNull(location_map_, std::make_pair(descriptor, location));
|
| if (result == NULL) {
|
| *line = -1;
|
| *column = 0;
|
| @@ -1599,7 +2096,8 @@ void SourceLocationTable::Add(
|
| const Message* descriptor,
|
| DescriptorPool::ErrorCollector::ErrorLocation location,
|
| int line, int column) {
|
| - location_map_[make_pair(descriptor, location)] = make_pair(line, column);
|
| + location_map_[std::make_pair(descriptor, location)] =
|
| + std::make_pair(line, column);
|
| }
|
|
|
| void SourceLocationTable::Clear() {
|
|
|