diff options
author | jieluo@google.com <jieluo@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2014-07-18 00:47:59 +0000 |
---|---|---|
committer | jieluo@google.com <jieluo@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2014-07-18 00:47:59 +0000 |
commit | 4de8f55113007fdc8e34107950e605fc0209d465 (patch) | |
tree | 92b7da8757a7740d9e1f2d3ead233542947d8c8c /src/google/protobuf/descriptor.cc | |
parent | c5553a3d18f80132b9079c5504bc0aa1f7f950a0 (diff) |
down integrate to svn
Diffstat (limited to 'src/google/protobuf/descriptor.cc')
-rw-r--r-- | src/google/protobuf/descriptor.cc | 737 |
1 files changed, 601 insertions, 136 deletions
diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 3f0d15f7..abd13a0b 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -35,6 +35,7 @@ #include <google/protobuf/stubs/hash.h> #include <map> #include <set> +#include <string> #include <vector> #include <algorithm> #include <limits> @@ -43,9 +44,11 @@ #include <google/protobuf/descriptor_database.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/dynamic_message.h> +#include <google/protobuf/generated_message_util.h> #include <google/protobuf/text_format.h> #include <google/protobuf/unknown_field_set.h> #include <google/protobuf/wire_format.h> +#include <google/protobuf/io/strtod.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/tokenizer.h> #include <google/protobuf/io/zero_copy_stream_impl.h> @@ -53,7 +56,7 @@ #include <google/protobuf/stubs/once.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> -#include <google/protobuf/stubs/map-util.h> +#include <google/protobuf/stubs/map_util.h> #include <google/protobuf/stubs/stl_util.h> #undef PACKAGE // autoheader #defines this. :( @@ -131,6 +134,8 @@ const char * const FieldDescriptor::kLabelToName[MAX_LABEL + 1] = { "repeated", // LABEL_REPEATED }; +static const char * const kNonLinkedWeakMessageReplacementName = "google.protobuf.Empty"; + #ifndef _MSC_VER // MSVC doesn't need these and won't even accept them. const int FieldDescriptor::kMaxNumber; const int FieldDescriptor::kFirstReservedNumber; @@ -234,13 +239,14 @@ struct PointerStringPairHash { struct Symbol { enum Type { - NULL_SYMBOL, MESSAGE, FIELD, ENUM, ENUM_VALUE, SERVICE, METHOD, + NULL_SYMBOL, MESSAGE, FIELD, ONEOF, ENUM, ENUM_VALUE, SERVICE, METHOD, PACKAGE }; Type type; union { const Descriptor* descriptor; const FieldDescriptor* field_descriptor; + const OneofDescriptor* oneof_descriptor; const EnumDescriptor* enum_descriptor; const EnumValueDescriptor* enum_value_descriptor; const ServiceDescriptor* service_descriptor; @@ -266,6 +272,7 @@ struct Symbol { CONSTRUCTOR(Descriptor , MESSAGE , descriptor ) CONSTRUCTOR(FieldDescriptor , FIELD , field_descriptor ) + CONSTRUCTOR(OneofDescriptor , ONEOF , oneof_descriptor ) CONSTRUCTOR(EnumDescriptor , ENUM , enum_descriptor ) CONSTRUCTOR(EnumValueDescriptor, ENUM_VALUE, enum_value_descriptor ) CONSTRUCTOR(ServiceDescriptor , SERVICE , service_descriptor ) @@ -278,6 +285,7 @@ struct Symbol { case NULL_SYMBOL: return NULL; case MESSAGE : return descriptor ->file(); case FIELD : return field_descriptor ->file(); + case ONEOF : return oneof_descriptor ->containing_type()->file(); case ENUM : return enum_descriptor ->file(); case ENUM_VALUE : return enum_value_descriptor->type()->file(); case SERVICE : return service_descriptor ->file(); @@ -314,7 +322,7 @@ typedef hash_map<EnumIntPair, const EnumValueDescriptor*, // for that. typedef map<DescriptorIntPair, const FieldDescriptor*> ExtensionsGroupedByDescriptorMap; - +typedef hash_map<string, const SourceCodeInfo_Location*> LocationsByPathMap; } // anonymous namespace // =================================================================== @@ -368,10 +376,18 @@ class DescriptorPool::Tables { vector<string> pending_files_; // A set of files which we have tried to load from the fallback database - // and encountered errors. We will not attempt to load them again. + // and encountered errors. We will not attempt to load them again during + // execution of the current public API call, but for compatibility with + // legacy clients, this is cleared at the beginning of each public API call. // Not used when fallback_database_ == NULL. hash_set<string> known_bad_files_; + // A set of symbols which we have tried to load from the fallback database + // and encountered errors. We will not attempt to load them again during + // execution of the current public API call, but for compatibility with + // legacy clients, this is cleared at the beginning of each public API call. + hash_set<string> known_bad_symbols_; + // The set of descriptors for which we've already loaded the full // set of extensions numbers from fallback_database_. hash_set<const Descriptor*> extensions_loaded_from_db_; @@ -388,7 +404,7 @@ class DescriptorPool::Tables { // declaring Symbol in descriptor.h, which would drag all kinds of other // stuff into the header. Yay C++. Symbol FindByNameHelper( - const DescriptorPool* pool, const string& name) const; + const DescriptorPool* pool, const string& name); // These return NULL if not found. inline const FileDescriptor* FindFile(const string& key) const; @@ -529,17 +545,34 @@ class FileDescriptorTables { // fails because we allow duplicates; the first field by the name wins. void AddFieldByStylizedNames(const FieldDescriptor* field); + // Populates p->first->locations_by_path_ from p->second. + // Unusual signature dictated by GoogleOnceDynamic. + static void BuildLocationsByPath( + pair<const FileDescriptorTables*, const SourceCodeInfo*>* p); + + // Returns the location denoted by the specified path through info, + // or NULL if not found. + // The value of info must be that of the corresponding FileDescriptor. + // (Conceptually a pure function, but stateful as an optimisation.) + const SourceCodeInfo_Location* GetSourceLocation( + const vector<int>& path, const SourceCodeInfo* info) const; + private: SymbolsByParentMap symbols_by_parent_; FieldsByNameMap fields_by_lowercase_name_; FieldsByNameMap fields_by_camelcase_name_; FieldsByNumberMap fields_by_number_; // Not including extensions. EnumValuesByNumberMap enum_values_by_number_; + + // Populated on first request to save space, hence constness games. + mutable GoogleOnceDynamic locations_by_path_once_; + mutable LocationsByPathMap locations_by_path_; }; DescriptorPool::Tables::Tables() // Start some hash_map and hash_set objects with a small # of buckets : known_bad_files_(3), + known_bad_symbols_(3), extensions_loaded_from_db_(3), symbols_by_name_(3), files_by_name_(3) {} @@ -663,8 +696,10 @@ inline Symbol FileDescriptorTables::FindNestedSymbolOfType( } Symbol DescriptorPool::Tables::FindByNameHelper( - const DescriptorPool* pool, const string& name) const { + const DescriptorPool* pool, const string& name) { MutexLockMaybe lock(pool->mutex_); + known_bad_symbols_.clear(); + known_bad_files_.clear(); Symbol result = FindSymbol(name); if (result.IsNull() && pool->underlay_ != NULL) { @@ -835,6 +870,22 @@ void* DescriptorPool::Tables::AllocateBytes(int size) { return result; } +void FileDescriptorTables::BuildLocationsByPath( + pair<const FileDescriptorTables*, const SourceCodeInfo*>* p) { + for (int i = 0, len = p->second->location_size(); i < len; ++i) { + const SourceCodeInfo_Location* loc = &p->second->location().Get(i); + p->first->locations_by_path_[Join(loc->path(), ",")] = loc; + } +} + +const SourceCodeInfo_Location* FileDescriptorTables::GetSourceLocation( + const vector<int>& path, const SourceCodeInfo* info) const { + pair<const FileDescriptorTables*, const SourceCodeInfo*> p( + make_pair(this, info)); + locations_by_path_once_.Init(&FileDescriptorTables::BuildLocationsByPath, &p); + return FindPtrOrNull(locations_by_path_, Join(path, ",")); +} + // =================================================================== // DescriptorPool @@ -847,7 +898,8 @@ DescriptorPool::DescriptorPool() underlay_(NULL), tables_(new Tables), enforce_dependencies_(true), - allow_unknown_(false) {} + allow_unknown_(false), + enforce_weak_(false) {} DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, ErrorCollector* error_collector) @@ -857,7 +909,8 @@ DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, underlay_(NULL), tables_(new Tables), enforce_dependencies_(true), - allow_unknown_(false) { + allow_unknown_(false), + enforce_weak_(false) { } DescriptorPool::DescriptorPool(const DescriptorPool* underlay) @@ -867,7 +920,8 @@ DescriptorPool::DescriptorPool(const DescriptorPool* underlay) underlay_(underlay), tables_(new Tables), enforce_dependencies_(true), - allow_unknown_(false) {} + allow_unknown_(false), + enforce_weak_(false) {} DescriptorPool::~DescriptorPool() { if (mutex_ != NULL) delete mutex_; @@ -880,6 +934,14 @@ void DescriptorPool::InternalDontEnforceDependencies() { enforce_dependencies_ = false; } +void DescriptorPool::AddUnusedImportTrackFile(const string& file_name) { + unused_import_track_files_.insert(file_name); +} + +void DescriptorPool::ClearUnusedImportTrackFiles() { + unused_import_track_files_.clear(); +} + bool DescriptorPool::InternalIsFileLoaded(const string& filename) const { MutexLockMaybe lock(mutex_); return tables_->FindFile(filename) != NULL; @@ -961,6 +1023,8 @@ void DescriptorPool::InternalAddGeneratedFile( const FileDescriptor* DescriptorPool::FindFileByName(const string& name) const { MutexLockMaybe lock(mutex_); + tables_->known_bad_symbols_.clear(); + tables_->known_bad_files_.clear(); const FileDescriptor* result = tables_->FindFile(name); if (result != NULL) return result; if (underlay_ != NULL) { @@ -977,6 +1041,8 @@ const FileDescriptor* DescriptorPool::FindFileByName(const string& name) const { const FileDescriptor* DescriptorPool::FindFileContainingSymbol( const string& symbol_name) const { MutexLockMaybe lock(mutex_); + tables_->known_bad_symbols_.clear(); + tables_->known_bad_files_.clear(); Symbol result = tables_->FindSymbol(symbol_name); if (!result.IsNull()) return result.GetFile(); if (underlay_ != NULL) { @@ -1019,6 +1085,12 @@ const FieldDescriptor* DescriptorPool::FindExtensionByName( } } +const OneofDescriptor* DescriptorPool::FindOneofByName( + const string& name) const { + Symbol result = tables_->FindByNameHelper(this, name); + return (result.type == Symbol::ONEOF) ? result.oneof_descriptor : NULL; +} + const EnumDescriptor* DescriptorPool::FindEnumTypeByName( const string& name) const { Symbol result = tables_->FindByNameHelper(this, name); @@ -1047,6 +1119,8 @@ const MethodDescriptor* DescriptorPool::FindMethodByName( const FieldDescriptor* DescriptorPool::FindExtensionByNumber( const Descriptor* extendee, int number) const { MutexLockMaybe lock(mutex_); + tables_->known_bad_symbols_.clear(); + tables_->known_bad_files_.clear(); const FieldDescriptor* result = tables_->FindExtension(extendee, number); if (result != NULL) { return result; @@ -1067,6 +1141,8 @@ const FieldDescriptor* DescriptorPool::FindExtensionByNumber( void DescriptorPool::FindAllExtensions( const Descriptor* extendee, vector<const FieldDescriptor*>* out) const { MutexLockMaybe lock(mutex_); + tables_->known_bad_symbols_.clear(); + tables_->known_bad_files_.clear(); // Initialize tables_->extensions_ from the fallback database first // (but do this only once per descriptor). @@ -1091,6 +1167,7 @@ void DescriptorPool::FindAllExtensions( } } + // ------------------------------------------------------------------- const FieldDescriptor* @@ -1137,6 +1214,17 @@ Descriptor::FindFieldByName(const string& key) const { } } +const OneofDescriptor* +Descriptor::FindOneofByName(const string& key) const { + Symbol result = + file()->tables_->FindNestedSymbolOfType(this, key, Symbol::ONEOF); + if (!result.IsNull()) { + return result.oneof_descriptor; + } else { + return NULL; + } +} + const FieldDescriptor* Descriptor::FindExtensionByName(const string& key) const { Symbol result = @@ -1301,16 +1389,17 @@ FileDescriptor::FindExtensionByCamelcaseName(const string& key) const { } } -bool Descriptor::IsExtensionNumber(int number) const { +const Descriptor::ExtensionRange* +Descriptor::FindExtensionRangeContainingNumber(int number) const { // Linear search should be fine because we don't expect a message to have // more than a couple extension ranges. for (int i = 0; i < extension_range_count(); i++) { if (number >= extension_range(i)->start && number < extension_range(i)->end) { - return true; + return extension_range(i); } } - return false; + return NULL; } // ------------------------------------------------------------------- @@ -1326,7 +1415,6 @@ bool DescriptorPool::TryFindFileInFallbackDatabase(const string& name) const { tables_->known_bad_files_.insert(name); return false; } - return true; } @@ -1355,36 +1443,38 @@ bool DescriptorPool::IsSubSymbolOfBuiltType(const string& name) const { bool DescriptorPool::TryFindSymbolInFallbackDatabase(const string& name) const { if (fallback_database_ == NULL) return false; - // We skip looking in the fallback database if the name is a sub-symbol of - // any descriptor that already exists in the descriptor pool (except for - // package descriptors). This is valid because all symbols except for - // packages are defined in a single file, so if the symbol exists then we - // should already have its definition. - // - // The other reason to do this is to support "overriding" type definitions - // by merging two databases that define the same type. (Yes, people do - // this.) The main difficulty with making this work is that - // FindFileContainingSymbol() is allowed to return both false positives - // (e.g., SimpleDescriptorDatabase, UpgradedDescriptorDatabase) and false - // negatives (e.g. ProtoFileParser, SourceTreeDescriptorDatabase). When two - // such databases are merged, looking up a non-existent sub-symbol of a type - // that already exists in the descriptor pool can result in an attempt to - // load multiple definitions of the same type. The check below avoids this. - if (IsSubSymbolOfBuiltType(name)) return false; + if (tables_->known_bad_symbols_.count(name) > 0) return false; FileDescriptorProto file_proto; - if (!fallback_database_->FindFileContainingSymbol(name, &file_proto)) { - return false; - } - - if (tables_->FindFile(file_proto.name()) != NULL) { - // We've already loaded this file, and it apparently doesn't contain the - // symbol we're looking for. Some DescriptorDatabases return false - // positives. - return false; - } - - if (BuildFileFromDatabase(file_proto) == NULL) { + if (// We skip looking in the fallback database if the name is a sub-symbol + // of any descriptor that already exists in the descriptor pool (except + // for package descriptors). This is valid because all symbols except + // for packages are defined in a single file, so if the symbol exists + // then we should already have its definition. + // + // The other reason to do this is to support "overriding" type + // definitions by merging two databases that define the same type. (Yes, + // people do this.) The main difficulty with making this work is that + // FindFileContainingSymbol() is allowed to return both false positives + // (e.g., SimpleDescriptorDatabase, UpgradedDescriptorDatabase) and false + // negatives (e.g. ProtoFileParser, SourceTreeDescriptorDatabase). + // When two such databases are merged, looking up a non-existent + // sub-symbol of a type that already exists in the descriptor pool can + // result in an attempt to load multiple definitions of the same type. + // The check below avoids this. + IsSubSymbolOfBuiltType(name) + + // Look up file containing this symbol in fallback database. + || !fallback_database_->FindFileContainingSymbol(name, &file_proto) + + // Check if we've already built this file. If so, it apparently doesn't + // contain the symbol we're looking for. Some DescriptorDatabases + // return false positives. + || tables_->FindFile(file_proto.name()) != NULL + + // Build the file. + || BuildFileFromDatabase(file_proto) == NULL) { + tables_->known_bad_symbols_.insert(name); return false; } @@ -1511,6 +1601,9 @@ void Descriptor::CopyTo(DescriptorProto* proto) const { for (int i = 0; i < field_count(); i++) { field(i)->CopyTo(proto->add_field()); } + for (int i = 0; i < oneof_decl_count(); i++) { + oneof_decl(i)->CopyTo(proto->add_oneof_decl()); + } for (int i = 0; i < nested_type_count(); i++) { nested_type(i)->CopyTo(proto->add_nested_type()); } @@ -1571,11 +1664,19 @@ void FieldDescriptor::CopyTo(FieldDescriptorProto* proto) const { proto->set_default_value(DefaultValueAsString(false)); } + if (containing_oneof() != NULL && !is_extension()) { + proto->set_oneof_index(containing_oneof()->index()); + } + if (&options() != &FieldOptions::default_instance()) { proto->mutable_options()->CopyFrom(options()); } } +void OneofDescriptor::CopyTo(OneofDescriptorProto* proto) const { + proto->set_name(name()); +} + void EnumDescriptor::CopyTo(EnumDescriptorProto* proto) const { proto->set_name(name()); @@ -1679,7 +1780,7 @@ bool RetrieveOptions(int depth, bool FormatBracketedOptions(int depth, const Message &options, string *output) { vector<string> all_options; if (RetrieveOptions(depth, options, &all_options)) { - output->append(JoinStrings(all_options, ", ")); + output->append(Join(all_options, ", ")); } return !all_options.empty(); } @@ -1766,7 +1867,7 @@ string FileDescriptor::DebugString() const { strings::SubstituteAndAppend(&contents, "extend .$0 {\n", containing_type->full_name()); } - extension(i)->DebugString(1, &contents); + extension(i)->DebugString(1, FieldDescriptor::PRINT_LABEL, &contents); } if (extension_count() > 0) contents.append("}\n\n"); @@ -1813,7 +1914,12 @@ void Descriptor::DebugString(int depth, string *contents) const { enum_type(i)->DebugString(depth, contents); } for (int i = 0; i < field_count(); i++) { - field(i)->DebugString(depth, contents); + if (field(i)->containing_oneof() == NULL) { + field(i)->DebugString(depth, FieldDescriptor::PRINT_LABEL, contents); + } else if (field(i)->containing_oneof()->field(0) == field(i)) { + // This is the first field in this oneof, so print the whole oneof. + field(i)->containing_oneof()->DebugString(depth, contents); + } } for (int i = 0; i < extension_range_count(); i++) { @@ -1832,7 +1938,8 @@ void Descriptor::DebugString(int depth, string *contents) const { strings::SubstituteAndAppend(contents, "$0 extend .$1 {\n", prefix, containing_type->full_name()); } - extension(i)->DebugString(depth + 1, contents); + extension(i)->DebugString( + depth + 1, FieldDescriptor::PRINT_LABEL, contents); } if (extension_count() > 0) strings::SubstituteAndAppend(contents, "$0 }\n", prefix); @@ -1848,14 +1955,16 @@ string FieldDescriptor::DebugString() const { containing_type()->full_name()); depth = 1; } - DebugString(depth, &contents); + DebugString(depth, PRINT_LABEL, &contents); if (is_extension()) { - contents.append("}\n"); + contents.append("}\n"); } return contents; } -void FieldDescriptor::DebugString(int depth, string *contents) const { +void FieldDescriptor::DebugString(int depth, + PrintLabelFlag print_label_flag, + string *contents) const { string prefix(depth * 2, ' '); string field_type; switch (type()) { @@ -1869,9 +1978,15 @@ void FieldDescriptor::DebugString(int depth, string *contents) const { field_type = kTypeToName[type()]; } - strings::SubstituteAndAppend(contents, "$0$1 $2 $3 = $4", + string label; + if (print_label_flag == PRINT_LABEL) { + label = kLabelToName[this->label()]; + label.push_back(' '); + } + + strings::SubstituteAndAppend(contents, "$0$1$2 $3 = $4", prefix, - kLabelToName[label()], + label, field_type, type() == TYPE_GROUP ? message_type()->name() : name(), @@ -1902,6 +2017,23 @@ void FieldDescriptor::DebugString(int depth, string *contents) const { } } +string OneofDescriptor::DebugString() const { + string contents; + DebugString(0, &contents); + return contents; +} + +void OneofDescriptor::DebugString(int depth, string* contents) const { + string prefix(depth * 2, ' '); + ++depth; + strings::SubstituteAndAppend( + contents, "$0 oneof $1 {\n", prefix, name()); + for (int i = 0; i < field_count(); i++) { + field(i)->DebugString(depth, FieldDescriptor::OMIT_LABEL, contents); + } + strings::SubstituteAndAppend(contents, "$0}\n", prefix); +} + string EnumDescriptor::DebugString() const { string contents; DebugString(0, &contents); @@ -1984,29 +2116,21 @@ void MethodDescriptor::DebugString(int depth, string *contents) const { // Location methods =============================================== -static bool PathsEqual(const vector<int>& x, const RepeatedField<int32>& y) { - if (x.size() != y.size()) return false; - for (int i = 0; i < x.size(); ++i) { - if (x[i] != y.Get(i)) return false; - } - return true; -} - bool FileDescriptor::GetSourceLocation(const vector<int>& path, SourceLocation* out_location) const { GOOGLE_CHECK_NOTNULL(out_location); - const SourceCodeInfo* info = source_code_info_; - for (int i = 0; info && i < info->location_size(); ++i) { - if (PathsEqual(path, info->location(i).path())) { - const RepeatedField<int32>& span = info->location(i).span(); + if (source_code_info_) { + if (const SourceCodeInfo_Location* loc = + tables_->GetSourceLocation(path, source_code_info_)) { + const RepeatedField<int32>& span = loc->span(); if (span.size() == 3 || span.size() == 4) { out_location->start_line = span.Get(0); out_location->start_column = span.Get(1); out_location->end_line = span.Get(span.size() == 3 ? 0 : 2); out_location->end_column = span.Get(span.size() - 1); - out_location->leading_comments = info->location(i).leading_comments(); - out_location->trailing_comments = info->location(i).trailing_comments(); + out_location->leading_comments = loc->leading_comments(); + out_location->trailing_comments = loc->trailing_comments(); return true; } } @@ -2030,6 +2154,12 @@ bool FieldDescriptor::GetSourceLocation(SourceLocation* out_location) const { return file()->GetSourceLocation(path, out_location); } +bool OneofDescriptor::GetSourceLocation(SourceLocation* out_location) const { + vector<int> path; + GetLocationPath(&path); + return containing_type()->file()->GetSourceLocation(path, out_location); +} + bool EnumDescriptor::GetSourceLocation(SourceLocation* out_location) const { vector<int> path; GetLocationPath(&path); @@ -2067,8 +2197,25 @@ void Descriptor::GetLocationPath(vector<int>* output) const { } void FieldDescriptor::GetLocationPath(vector<int>* output) const { + if (is_extension()) { + if (extension_scope() == NULL) { + output->push_back(FileDescriptorProto::kExtensionFieldNumber); + output->push_back(index()); + } else { + extension_scope()->GetLocationPath(output); + output->push_back(DescriptorProto::kExtensionFieldNumber); + output->push_back(index()); + } + } else { + containing_type()->GetLocationPath(output); + output->push_back(DescriptorProto::kFieldFieldNumber); + output->push_back(index()); + } +} + +void OneofDescriptor::GetLocationPath(vector<int>* output) const { containing_type()->GetLocationPath(output); - output->push_back(DescriptorProto::kFieldFieldNumber); + output->push_back(DescriptorProto::kOneofDeclFieldNumber); output->push_back(index()); } @@ -2154,6 +2301,10 @@ class DescriptorBuilder { FileDescriptorTables* file_tables_; set<const FileDescriptor*> dependencies_; + // unused_dependency_ is used to record the unused imported files. + // Note: public import is not considered. + set<const FileDescriptor*> unused_dependency_; + // If LookupSymbol() finds a symbol that is in a file which is not a declared // dependency of this file, it will fail, but will set // possible_undeclared_dependency_ to point at that file. This is only used @@ -2164,10 +2315,22 @@ class DescriptorBuilder { const FileDescriptor* possible_undeclared_dependency_; string possible_undeclared_dependency_name_; + // If LookupSymbol() could resolve a symbol which is not defined, + // record the resolved name. This is only used by AddNotDefinedError() + // to report a more useful error message. + string undefine_resolved_name_; + void AddError(const string& element_name, const Message& descriptor, DescriptorPool::ErrorCollector::ErrorLocation location, const string& error); + void AddError(const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const char* error); + void AddRecursiveImportError(const FileDescriptorProto& proto, int from_here); + void AddTwiceListedError(const FileDescriptorProto& proto, int index); + void AddImportError(const FileDescriptorProto& proto, int index); // Adds an error indicating that undefined_symbol was not defined. Must // only be called after LookupSymbol() fails. @@ -2177,6 +2340,10 @@ class DescriptorBuilder { DescriptorPool::ErrorCollector::ErrorLocation location, const string& undefined_symbol); + void AddWarning(const string& element_name, const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& error); + // Silly helper which determines if the given file is in the given package. // I.e., either file->package() == package_name or file->package() is a // nested package within package_name. @@ -2311,6 +2478,9 @@ class DescriptorBuilder { void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto, const Descriptor* parent, Descriptor::ExtensionRange* result); + void BuildOneof(const OneofDescriptorProto& proto, + Descriptor* parent, + OneofDescriptor* result); void BuildEnum(const EnumDescriptorProto& proto, const Descriptor* parent, EnumDescriptor* result); @@ -2324,6 +2494,8 @@ class DescriptorBuilder { const ServiceDescriptor* parent, MethodDescriptor* result); + void LogUnusedDependency(const FileDescriptor* result); + // Must be run only after building. // // NOTE: Options will not be available during cross-linking, as they @@ -2459,6 +2631,9 @@ class DescriptorBuilder { static inline bool get_allow_unknown(const DescriptorPool* pool) { return pool->allow_unknown_; } + static inline bool get_enforce_weak(const DescriptorPool* pool) { + return pool->enforce_weak_; + } static inline bool get_is_placeholder(const Descriptor* descriptor) { return descriptor->is_placeholder_; } @@ -2501,6 +2676,8 @@ const FileDescriptor* DescriptorPool::BuildFile( "DescriptorDatabase. You must instead find a way to get your file " "into the underlying database."; GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. + tables_->known_bad_symbols_.clear(); + tables_->known_bad_files_.clear(); return DescriptorBuilder(this, tables_.get(), NULL).BuildFile(proto); } @@ -2512,6 +2689,8 @@ const FileDescriptor* DescriptorPool::BuildFileCollectingErrors( "DescriptorDatabase. You must instead find a way to get your file " "into the underlying database."; GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. + tables_->known_bad_symbols_.clear(); + tables_->known_bad_files_.clear(); return DescriptorBuilder(this, tables_.get(), error_collector).BuildFile(proto); } @@ -2519,8 +2698,16 @@ const FileDescriptor* DescriptorPool::BuildFileCollectingErrors( const FileDescriptor* DescriptorPool::BuildFileFromDatabase( const FileDescriptorProto& proto) const { mutex_->AssertHeld(); - return DescriptorBuilder(this, tables_.get(), - default_error_collector_).BuildFile(proto); + if (tables_->known_bad_files_.count(proto.name()) > 0) { + return NULL; + } + const FileDescriptor* result = + DescriptorBuilder(this, tables_.get(), + default_error_collector_).BuildFile(proto); + if (result == NULL) { + tables_->known_bad_files_.insert(proto.name()); + } + return result; } DescriptorBuilder::DescriptorBuilder( @@ -2531,7 +2718,8 @@ DescriptorBuilder::DescriptorBuilder( tables_(tables), error_collector_(error_collector), had_errors_(false), - possible_undeclared_dependency_(NULL) {} + possible_undeclared_dependency_(NULL), + undefine_resolved_name_("") {} DescriptorBuilder::~DescriptorBuilder() {} @@ -2553,21 +2741,53 @@ void DescriptorBuilder::AddError( had_errors_ = true; } +void DescriptorBuilder::AddError( + const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const char* error) { + AddError(element_name, descriptor, location, string(error)); +} + void DescriptorBuilder::AddNotDefinedError( const string& element_name, const Message& descriptor, DescriptorPool::ErrorCollector::ErrorLocation location, const string& undefined_symbol) { - if (possible_undeclared_dependency_ == NULL) { + if (possible_undeclared_dependency_ == NULL && + undefine_resolved_name_.empty()) { AddError(element_name, descriptor, location, "\"" + undefined_symbol + "\" is not defined."); } else { - AddError(element_name, descriptor, location, - "\"" + possible_undeclared_dependency_name_ + - "\" seems to be defined in \"" + - possible_undeclared_dependency_->name() + "\", which is not " - "imported by \"" + filename_ + "\". To use it here, please " - "add the necessary import."); + if (possible_undeclared_dependency_ != NULL) { + AddError(element_name, descriptor, location, + "\"" + possible_undeclared_dependency_name_ + + "\" seems to be defined in \"" + + possible_undeclared_dependency_->name() + "\", which is not " + "imported by \"" + filename_ + "\". To use it here, please " + "add the necessary import."); + } + if (!undefine_resolved_name_.empty()) { + AddError(element_name, descriptor, location, + "\"" + undefined_symbol + "\" is resolved to \"" + + undefine_resolved_name_ + "\", which is not defined. " + "The innermost scope is searched first in name resolution. " + "Consider using a leading '.'(i.e., \"." + + undefined_symbol + + "\") to start from the outermost scope."); + } + } +} + +void DescriptorBuilder::AddWarning( + const string& element_name, const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& error) { + if (error_collector_ == NULL) { + GOOGLE_LOG(WARNING) << filename_ << " " << element_name << ": " << error; + } else { + error_collector_->AddWarning(filename_, element_name, &descriptor, location, + error); } } @@ -2628,7 +2848,10 @@ Symbol DescriptorBuilder::FindSymbol(const string& name) { // Only find symbols which were defined in this file or one of its // dependencies. const FileDescriptor* file = result.GetFile(); - if (file == file_ || dependencies_.count(file) > 0) return result; + if (file == file_ || dependencies_.count(file) > 0) { + unused_dependency_.erase(file); + return result; + } if (result.type == Symbol::PACKAGE) { // Arg, this is overcomplicated. The symbol is a package name. It could @@ -2654,6 +2877,7 @@ Symbol DescriptorBuilder::FindSymbol(const string& name) { Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( const string& name, const string& relative_to, ResolveMode resolve_mode) { possible_undeclared_dependency_ = NULL; + undefine_resolved_name_.clear(); if (name.size() > 0 && name[0] == '.') { // Fully-qualified name. @@ -2702,7 +2926,11 @@ Symbol DescriptorBuilder::LookupSymbolNoPlaceholder( if (result.IsAggregate()) { scope_to_try.append(name, first_part_of_name.size(), name.size() - first_part_of_name.size()); - return FindSymbol(scope_to_try); + result = FindSymbol(scope_to_try); + if (result.IsNull()) { + undefine_resolved_name_ = scope_to_try; + } + return result; } else { // We found a symbol but it's not an aggregate. Continue the loop. } @@ -2755,7 +2983,7 @@ Symbol DescriptorBuilder::NewPlaceholder(const string& name, placeholder_name = tables_->AllocateString( placeholder_full_name->substr(dotpos + 1)); } else { - placeholder_package = &::google::protobuf::internal::GetEmptyString(); + placeholder_package = &internal::GetEmptyString(); placeholder_name = placeholder_full_name; } @@ -2771,6 +2999,7 @@ Symbol DescriptorBuilder::NewPlaceholder(const string& name, placeholder_file->pool_ = pool_; placeholder_file->options_ = &FileOptions::default_instance(); placeholder_file->tables_ = &FileDescriptorTables::kEmpty; + placeholder_file->is_placeholder_ = true; // All other fields are zero or NULL. if (placeholder_type == PLACEHOLDER_ENUM) { @@ -2841,10 +3070,11 @@ const FileDescriptor* DescriptorBuilder::NewPlaceholderFile( memset(placeholder, 0, sizeof(*placeholder)); placeholder->name_ = tables_->AllocateString(name); - placeholder->package_ = &::google::protobuf::internal::GetEmptyString(); + placeholder->package_ = &internal::GetEmptyString(); placeholder->pool_ = pool_; placeholder->options_ = &FileOptions::default_instance(); placeholder->tables_ = &FileDescriptorTables::kEmpty; + placeholder->is_placeholder_ = true; // All other fields are zero or NULL. return placeholder; @@ -3015,6 +3245,45 @@ template<class DescriptorT> void DescriptorBuilder::AllocateOptionsImpl( METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i); \ } +void DescriptorBuilder::AddRecursiveImportError( + const FileDescriptorProto& proto, int from_here) { + string error_message("File recursively imports itself: "); + for (int i = from_here; i < tables_->pending_files_.size(); i++) { + error_message.append(tables_->pending_files_[i]); + error_message.append(" -> "); + } + error_message.append(proto.name()); + + AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, + error_message); +} + +void DescriptorBuilder::AddTwiceListedError(const FileDescriptorProto& proto, + int index) { + AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, + "Import \"" + proto.dependency(index) + "\" was listed twice."); +} + +void DescriptorBuilder::AddImportError(const FileDescriptorProto& proto, + int index) { + string message; + if (pool_->fallback_database_ == NULL) { + message = "Import \"" + proto.dependency(index) + + "\" has not been loaded."; + } else { + message = "Import \"" + proto.dependency(index) + + "\" was not found or had errors."; + } + AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, message); +} + +static bool ExistingFileMatchesProto(const FileDescriptor* existing_file, + const FileDescriptorProto& proto) { + FileDescriptorProto existing_proto; + existing_file->CopyTo(&existing_proto); + return existing_proto.SerializeAsString() == proto.SerializeAsString(); +} + const FileDescriptor* DescriptorBuilder::BuildFile( const FileDescriptorProto& proto) { filename_ = proto.name(); @@ -3027,9 +3296,7 @@ const FileDescriptor* DescriptorBuilder::BuildFile( const FileDescriptor* existing_file = tables_->FindFile(filename_); if (existing_file != NULL) { // File already in pool. Compare the existing one to the input. - FileDescriptorProto existing_proto; - existing_file->CopyTo(&existing_proto); - if (existing_proto.SerializeAsString() == proto.SerializeAsString()) { + if (ExistingFileMatchesProto(existing_file, proto)) { // They're identical. Return the existing descriptor. return existing_file; } @@ -3048,15 +3315,7 @@ const FileDescriptor* DescriptorBuilder::BuildFile( // at all. for (int i = 0; i < tables_->pending_files_.size(); i++) { if (tables_->pending_files_[i] == proto.name()) { - string error_message("File recursively imports itself: "); - for (; i < tables_->pending_files_.size(); i++) { - error_message.append(tables_->pending_files_[i]); - error_message.append(" -> "); - } - error_message.append(proto.name()); - - AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, - error_message); + AddRecursiveImportError(proto, i); return NULL; } } @@ -3083,6 +3342,7 @@ const FileDescriptor* DescriptorBuilder::BuildFile( FileDescriptor* result = tables_->Allocate<FileDescriptor>(); file_ = result; + result->is_placeholder_ = false; if (proto.has_source_code_info()) { SourceCodeInfo *info = tables_->AllocateMessage<SourceCodeInfo>(); info->CopyFrom(proto.source_code_info()); @@ -3129,11 +3389,14 @@ const FileDescriptor* DescriptorBuilder::BuildFile( result->dependency_count_ = proto.dependency_size(); result->dependencies_ = tables_->AllocateArray<const FileDescriptor*>(proto.dependency_size()); + unused_dependency_.clear(); + set<int> weak_deps; + for (int i = 0; i < proto.weak_dependency_size(); ++i) { + weak_deps.insert(proto.weak_dependency(i)); + } for (int i = 0; i < proto.dependency_size(); i++) { if (!seen_dependencies.insert(proto.dependency(i)).second) { - AddError(proto.name(), proto, - DescriptorPool::ErrorCollector::OTHER, - "Import \"" + proto.dependency(i) + "\" was listed twice."); + AddTwiceListedError(proto, i); } const FileDescriptor* dependency = tables_->FindFile(proto.dependency(i)); @@ -3142,20 +3405,20 @@ const FileDescriptor* DescriptorBuilder::BuildFile( } if (dependency == NULL) { - if (pool_->allow_unknown_) { + if (pool_->allow_unknown_ || + (!pool_->enforce_weak_ && weak_deps.find(i) != weak_deps.end())) { dependency = NewPlaceholderFile(proto.dependency(i)); } else { - string message; - if (pool_->fallback_database_ == NULL) { - message = "Import \"" + proto.dependency(i) + - "\" has not been loaded."; - } else { - message = "Import \"" + proto.dependency(i) + - "\" was not found or had errors."; - } - AddError(proto.name(), proto, - DescriptorPool::ErrorCollector::OTHER, - message); + AddImportError(proto, i); + } + } else { + // Add to unused_dependency_ to track unused imported files. + // Note: do not track unused imported files for public import. + if (pool_->enforce_dependencies_ && + (pool_->unused_import_track_files_.find(proto.name()) != + pool_->unused_import_track_files_.end()) && + (dependency->public_dependency_count() == 0)) { + unused_dependency_.insert(dependency); } } @@ -3171,6 +3434,8 @@ const FileDescriptor* DescriptorBuilder::BuildFile( int index = proto.public_dependency(i); if (index >= 0 && index < proto.dependency_size()) { result->public_dependencies_[public_dependency_count++] = index; + // Do not track unused imported files for public import. + unused_dependency_.erase(result->dependency(index)); } else { AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, @@ -3237,6 +3502,11 @@ const FileDescriptor* DescriptorBuilder::BuildFile( ValidateFileOptions(result, proto); } + + if (!unused_dependency_.empty()) { + LogUnusedDependency(result); + } + if (had_errors_) { tables_->RollbackToLastCheckpoint(); return NULL; @@ -3264,6 +3534,8 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, result->is_placeholder_ = false; result->is_unqualified_placeholder_ = false; + // Build oneofs first so that fields and extension ranges can refer to them. + BUILD_ARRAY(proto, result, oneof_decl , BuildOneof , result); BUILD_ARRAY(proto, result, field , BuildField , result); BUILD_ARRAY(proto, result, nested_type , BuildMessage , result); BUILD_ARRAY(proto, result, enum_type , BuildEnum , result); @@ -3354,6 +3626,19 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, result->label_ = static_cast<FieldDescriptor::Label>( implicit_cast<int>(proto.label())); + // An extension cannot have a required field (b/13365836). + if (result->is_extension_ && + result->label_ == FieldDescriptor::LABEL_REQUIRED) { + AddError(result->full_name(), proto, + // Error location `TYPE`: we would really like to indicate + // `LABEL`, but the `ErrorLocation` enum has no entry for this, and + // we don't necessarily know about all implementations of the + // `ErrorCollector` interface to extend them to handle the new + // error location type properly. + DescriptorPool::ErrorCollector::TYPE, + "Message extensions cannot have required fields."); + } + // Some of these may be filled in when cross-linking. result->containing_type_ = NULL; result->extension_scope_ = NULL; @@ -3397,7 +3682,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, result->default_value_float_ = numeric_limits<float>::quiet_NaN(); } else { result->default_value_float_ = - NoLocaleStrtod(proto.default_value().c_str(), &end_pos); + io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos); } break; case FieldDescriptor::CPPTYPE_DOUBLE: @@ -3409,7 +3694,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, result->default_value_double_ = numeric_limits<double>::quiet_NaN(); } else { result->default_value_double_ = - NoLocaleStrtod(proto.default_value().c_str(), &end_pos); + io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos); } break; case FieldDescriptor::CPPTYPE_BOOL: @@ -3451,7 +3736,8 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, if (proto.default_value().empty() || *end_pos != '\0') { AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::DEFAULT_VALUE, - "Couldn't parse default value."); + "Couldn't parse default value \"" + proto.default_value() + + "\"."); } } } else { @@ -3483,7 +3769,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, result->default_value_enum_ = NULL; break; case FieldDescriptor::CPPTYPE_STRING: - result->default_value_string_ = &::google::protobuf::internal::GetEmptyString(); + result->default_value_string_ = &internal::GetEmptyString(); break; case FieldDescriptor::CPPTYPE_MESSAGE: break; @@ -3524,6 +3810,16 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, } result->extension_scope_ = parent; + + if (proto.has_oneof_index()) { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::OTHER, + "FieldDescriptorProto.oneof_index should not be set for " + "extensions."); + } + + // Fill in later (maybe). + result->containing_oneof_ = NULL; } else { if (proto.has_extendee()) { AddError(result->full_name(), proto, @@ -3532,6 +3828,23 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, } result->containing_type_ = parent; + + if (proto.has_oneof_index()) { + if (proto.oneof_index() < 0 || + proto.oneof_index() >= parent->oneof_decl_count()) { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::OTHER, + strings::Substitute("FieldDescriptorProto.oneof_index $0 is " + "out of range for type \"$1\".", + proto.oneof_index(), + parent->name())); + result->containing_oneof_ = NULL; + } else { + result->containing_oneof_ = parent->oneof_decl(proto.oneof_index()); + } + } else { + result->containing_oneof_ = NULL; + } } // Copy options. @@ -3569,6 +3882,28 @@ void DescriptorBuilder::BuildExtensionRange( } } +void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto, + Descriptor* parent, + OneofDescriptor* result) { + string* full_name = tables_->AllocateString(parent->full_name()); + full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + + result->containing_type_ = parent; + + // We need to fill these in later. + result->field_count_ = 0; + result->fields_ = NULL; + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); +} + void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto, const Descriptor* parent, EnumDescriptor* result) { @@ -3778,6 +4113,46 @@ void DescriptorBuilder::CrossLinkMessage( for (int i = 0; i < message->extension_count(); i++) { CrossLinkField(&message->extensions_[i], proto.extension(i)); } + + // Set up field array for each oneof. + + // First count the number of fields per oneof. + for (int i = 0; i < message->field_count(); i++) { + const OneofDescriptor* oneof_decl = message->field(i)->containing_oneof(); + if (oneof_decl != NULL) { + // Must go through oneof_decls_ array to get a non-const version of the + // OneofDescriptor. + ++message->oneof_decls_[oneof_decl->index()].field_count_; + } + } + + // Then allocate the arrays. + for (int i = 0; i < message->oneof_decl_count(); i++) { + OneofDescriptor* oneof_decl = &message->oneof_decls_[i]; + + if (oneof_decl->field_count() == 0) { + AddError(message->full_name() + "." + oneof_decl->name(), + proto.oneof_decl(i), + DescriptorPool::ErrorCollector::NAME, + "Oneof must have at least one field."); + } + + oneof_decl->fields_ = + tables_->AllocateArray<const FieldDescriptor*>(oneof_decl->field_count_); + oneof_decl->field_count_ = 0; + } + + // Then fill them in. + for (int i = 0; i < message->field_count(); i++) { + const OneofDescriptor* oneof_decl = message->field(i)->containing_oneof(); + if (oneof_decl != NULL) { + OneofDescriptor* mutable_oneof_decl = + &message->oneof_decls_[oneof_decl->index()]; + message->fields_[i].index_in_oneof_ = mutable_oneof_decl->field_count_; + mutable_oneof_decl->fields_[mutable_oneof_decl->field_count_++] = + message->field(i); + } + } } void DescriptorBuilder::CrossLinkField( @@ -3802,7 +4177,10 @@ void DescriptorBuilder::CrossLinkField( } field->containing_type_ = extendee.descriptor; - if (!field->containing_type()->IsExtensionNumber(field->number())) { + const Descriptor::ExtensionRange* extension_range = field->containing_type() + ->FindExtensionRangeContainingNumber(field->number()); + + if (extension_range == NULL) { AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER, strings::Substitute("\"$0\" does not declare $1 as an " @@ -3812,6 +4190,17 @@ void DescriptorBuilder::CrossLinkField( } } + if (field->containing_oneof() != NULL) { + if (field->label() != FieldDescriptor::LABEL_OPTIONAL) { + // Note that this error will never happen when parsing .proto files. + // It can only happen if you manually construct a FileDescriptorProto + // that is incorrect. + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NAME, + "Fields of oneofs must themselves have label LABEL_OPTIONAL."); + } + } + if (proto.has_type_name()) { // Assume we are expecting a message type unless the proto contains some // evidence that it expects an enum type. This only makes a difference if @@ -3824,6 +4213,11 @@ void DescriptorBuilder::CrossLinkField( expecting_enum ? PLACEHOLDER_ENUM : PLACEHOLDER_MESSAGE, LOOKUP_TYPES); + // If the type is a weak type, we change the type to a google.protobuf.Empty field. + if (type.IsNull() && !pool_->enforce_weak_ && proto.options().weak()) { + type = FindSymbol(kNonLinkedWeakMessageReplacementName); + } + if (type.IsNull()) { AddNotDefinedError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, @@ -3875,21 +4269,34 @@ void DescriptorBuilder::CrossLinkField( } if (field->has_default_value()) { - // We can't just use field->enum_type()->FindValueByName() here - // because that locks the pool's mutex, which we have already locked - // at this point. - Symbol default_value = - LookupSymbolNoPlaceholder(proto.default_value(), - field->enum_type()->full_name()); - - if (default_value.type == Symbol::ENUM_VALUE && - default_value.enum_value_descriptor->type() == field->enum_type()) { - field->default_value_enum_ = default_value.enum_value_descriptor; - } else { + // Ensure that the default value is an identifier. Parser cannot always + // verify this because it does not have complete type information. + // N.B. that this check yields better error messages but is not + // necessary for correctness (an enum symbol must be a valid identifier + // anyway), only for better errors. + if (!io::Tokenizer::IsIdentifier(proto.default_value())) { AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::DEFAULT_VALUE, - "Enum type \"" + field->enum_type()->full_name() + - "\" has no value named \"" + proto.default_value() + "\"."); + "Default value for an enum field must be an identifier."); + } else { + // We can't just use field->enum_type()->FindValueByName() here + // because that locks the pool's mutex, which we have already locked + // at this point. + Symbol default_value = + LookupSymbolNoPlaceholder(proto.default_value(), + field->enum_type()->full_name()); + + if (default_value.type == Symbol::ENUM_VALUE && + default_value.enum_value_descriptor->type() == + field->enum_type()) { + field->default_value_enum_ = default_value.enum_value_descriptor; + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Enum type \"" + field->enum_type()->full_name() + + "\" has no value named \"" + proto.default_value() + + "\"."); + } } } else if (field->enum_type()->value_count() > 0) { // All enums must have at least one value, or we would have reported @@ -3933,12 +4340,26 @@ void DescriptorBuilder::CrossLinkField( field->containing_type()->full_name(), conflicting_field->name())); } - } - - if (field->is_extension()) { - // No need for error checking: if the extension number collided, - // we've already been informed of it by the if() above. - tables_->AddExtension(field); + } else { + if (field->is_extension()) { + if (!tables_->AddExtension(field)) { + const FieldDescriptor* conflicting_field = + tables_->FindExtension(field->containing_type(), field->number()); + string error_msg = strings::Substitute( + "Extension number $0 has already been used in \"$1\" by extension " + "\"$2\" defined in $3.", + field->number(), + field->containing_type()->full_name(), + conflicting_field->full_name(), + conflicting_field->file()->name()); + // Conflicting extension numbers should be an error. However, before + // turning this into an error we need to fix all existing broken + // protos first. + // TODO(xiaofeng): Change this to an error. + AddWarning(field->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, error_msg); + } + } } // Add the field to the lowercase-name and camelcase-name tables. @@ -4022,7 +4443,6 @@ static bool IsLite(const FileDescriptor* file) { // TODO(kenton): I don't even remember how many of these conditions are // actually possible. I'm just being super-safe. return file != NULL && - &file->options() != NULL && &file->options() != &FileOptions::default_instance() && file->options().optimize_for() == FileOptions::LITE_RUNTIME; } @@ -4050,6 +4470,7 @@ void DescriptorBuilder::ValidateFileOptions(FileDescriptor* file, } } + void DescriptorBuilder::ValidateMessageOptions(Descriptor* message, const DescriptorProto& proto) { VALIDATE_OPTIONS_FROM_ARRAY(message, field, Field); @@ -4388,6 +4809,15 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( // so we will just leave it as uninterpreted. AddWithoutInterpreting(*uninterpreted_option_, options); return true; + } else if (!(builder_->undefine_resolved_name_).empty()) { + // Option is resolved to a name which is not defined. + return AddNameError( + "Option \"" + debug_msg_name + "\" is resolved to \"(" + + builder_->undefine_resolved_name_ + + ")\", which is not defined. The innermost scope is searched first " + "in name resolution. Consider using a leading '.'(i.e., \"(." + + debug_msg_name.substr(1) + + "\") to start from the outermost scope."); } else { return AddNameError("Option \"" + debug_msg_name + "\" unknown."); } @@ -4408,14 +4838,15 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( "\" is not a field or extension of message \"" + descriptor->name() + "\"."); } - } else if (field->is_repeated()) { - return AddNameError("Option field \"" + debug_msg_name + - "\" is repeated. Repeated options are not " - "supported."); } else if (i < uninterpreted_option_->name_size() - 1) { if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { return AddNameError("Option \"" + debug_msg_name + "\" is an atomic type, not a message."); + } else if (field->is_repeated()) { + return AddNameError("Option field \"" + debug_msg_name + + "\" is a repeated message. Repeated message " + "options must be initialized using an " + "aggregate value."); } else { // Drill down into the submessage. intermediate_fields.push_back(field); @@ -4432,7 +4863,7 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption( // known will populate them correctly. // First see if the option is already set. - if (!ExamineIfOptionIsSet( + if (!field->is_repeated() && !ExamineIfOptionIsSet( intermediate_fields.begin(), intermediate_fields.end(), field, debug_msg_name, @@ -4947,5 +5378,39 @@ void DescriptorBuilder::OptionInterpreter::SetUInt64(int number, uint64 value, } } +void DescriptorBuilder::LogUnusedDependency(const FileDescriptor* result) { + + if (!unused_dependency_.empty()) { + std::set<string> annotation_extensions; + annotation_extensions.insert("google.protobuf.MessageOptions"); + annotation_extensions.insert("google.protobuf.FileOptions"); + annotation_extensions.insert("google.protobuf.FieldOptions"); + annotation_extensions.insert("google.protobuf.EnumOptions"); + annotation_extensions.insert("google.protobuf.EnumValueOptions"); + annotation_extensions.insert("google.protobuf.ServiceOptions"); + annotation_extensions.insert("google.protobuf.MethodOptions"); + annotation_extensions.insert("google.protobuf.StreamOptions"); + for (set<const FileDescriptor*>::const_iterator + it = unused_dependency_.begin(); + it != unused_dependency_.end(); ++it) { + // Do not log warnings for proto files which extend annotations. + int i; + for (i = 0 ; i < (*it)->extension_count(); ++i) { + if (annotation_extensions.find( + (*it)->extension(i)->containing_type()->full_name()) + != annotation_extensions.end()) { + break; + } + } + // Log warnings for unused imported files. + if (i == (*it)->extension_count()) { + GOOGLE_LOG(WARNING) << "Warning: Unused import: \"" << result->name() + << "\" imports \"" << (*it)->name() + << "\" which is not used."; + } + } + } +} + } // namespace protobuf } // namespace google |