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() { |