in cdk/extra/protobuf/protobuf-3.19.6/src/google/protobuf/descriptor.cc [6133:6391]
void DescriptorBuilder::CrossLinkField(FieldDescriptor* field,
const FieldDescriptorProto& proto) {
if (field->options_ == nullptr) {
field->options_ = &FieldOptions::default_instance();
}
// Add the field to the lowercase-name and camelcase-name tables.
file_tables_->AddFieldByStylizedNames(field);
if (proto.has_extendee()) {
Symbol extendee =
LookupSymbol(proto.extendee(), field->full_name(),
DescriptorPool::PLACEHOLDER_EXTENDABLE_MESSAGE);
if (extendee.IsNull()) {
AddNotDefinedError(field->full_name(), proto,
DescriptorPool::ErrorCollector::EXTENDEE,
proto.extendee());
return;
} else if (extendee.type() != Symbol::MESSAGE) {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::EXTENDEE,
"\"" + proto.extendee() + "\" is not a message type.");
return;
}
field->containing_type_ = extendee.descriptor();
const Descriptor::ExtensionRange* extension_range =
field->containing_type()->FindExtensionRangeContainingNumber(
field->number());
if (extension_range == nullptr) {
// Set of valid extension numbers for MessageSet is different (< 2^32)
// from other extendees (< 2^29). If unknown deps are allowed, we may not
// have that information, and wrongly deem the extension as invalid.
auto skip_check = get_allow_unknown(pool_) &&
proto.extendee() == "google.protobuf.bridge.MessageSet";
if (!skip_check) {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::NUMBER,
strings::Substitute("\"$0\" does not declare $1 as an "
"extension number.",
field->containing_type()->full_name(),
field->number()));
}
}
}
if (field->containing_oneof() != nullptr) {
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
// we end up creating a placeholder.
bool expecting_enum = (proto.type() == FieldDescriptorProto::TYPE_ENUM) ||
proto.has_default_value();
// In case of weak fields we force building the dependency. We need to know
// if the type exist or not. If it doesn't exist we substitute Empty which
// should only be done if the type can't be found in the generated pool.
// TODO(gerbens) Ideally we should query the database directly to check
// if weak fields exist or not so that we don't need to force building
// weak dependencies. However the name lookup rules for symbols are
// somewhat complicated, so I defer it too another CL.
bool is_weak = !pool_->enforce_weak_ && proto.options().weak();
bool is_lazy = pool_->lazily_build_dependencies_ && !is_weak;
Symbol type =
LookupSymbol(proto.type_name(), field->full_name(),
expecting_enum ? DescriptorPool::PLACEHOLDER_ENUM
: DescriptorPool::PLACEHOLDER_MESSAGE,
LOOKUP_TYPES, !is_lazy);
if (type.IsNull()) {
if (is_lazy) {
// Save the symbol names for later for lookup, and allocate the once
// object needed for the accessors.
std::string name = proto.type_name();
field->type_once_ = tables_->Create<internal::once_flag>();
field->type_descriptor_.lazy_type_name = tables_->Strdup(name);
field->lazy_default_value_enum_name_ =
proto.has_default_value() ? tables_->Strdup(proto.default_value())
: nullptr;
// AddFieldByNumber and AddExtension are done later in this function,
// and can/must be done if the field type was not found. The related
// error checking is not necessary when in lazily_build_dependencies_
// mode, and can't be done without building the type's descriptor,
// which we don't want to do.
file_tables_->AddFieldByNumber(field);
if (field->is_extension()) {
tables_->AddExtension(field);
}
return;
} else {
// If the type is a weak type, we change the type to a google.protobuf.Empty
// field.
if (is_weak) {
type = FindSymbol(kNonLinkedWeakMessageReplacementName);
}
if (type.IsNull()) {
AddNotDefinedError(field->full_name(), proto,
DescriptorPool::ErrorCollector::TYPE,
proto.type_name());
return;
}
}
}
if (!proto.has_type()) {
// Choose field type based on symbol.
if (type.type() == Symbol::MESSAGE) {
field->type_ = FieldDescriptor::TYPE_MESSAGE;
} else if (type.type() == Symbol::ENUM) {
field->type_ = FieldDescriptor::TYPE_ENUM;
} else {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::TYPE,
"\"" + proto.type_name() + "\" is not a type.");
return;
}
}
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
field->type_descriptor_.message_type = type.descriptor();
if (field->type_descriptor_.message_type == nullptr) {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::TYPE,
"\"" + proto.type_name() + "\" is not a message type.");
return;
}
if (field->has_default_value()) {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::DEFAULT_VALUE,
"Messages can't have default values.");
}
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
field->type_descriptor_.enum_type = type.enum_descriptor();
if (field->type_descriptor_.enum_type == nullptr) {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::TYPE,
"\"" + proto.type_name() + "\" is not an enum type.");
return;
}
if (field->enum_type()->is_placeholder_) {
// We can't look up default values for placeholder types. We'll have
// to just drop them.
field->has_default_value_ = false;
}
if (field->has_default_value()) {
// 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,
"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.
const EnumValueDescriptor* default_value =
LookupSymbolNoPlaceholder(proto.default_value(),
field->enum_type()->full_name())
.enum_value_descriptor();
if (default_value != nullptr &&
default_value->type() == field->enum_type()) {
field->default_value_enum_ = default_value;
} 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
// an error elsewhere. We use the first defined value as the default
// if a default is not explicitly defined.
field->default_value_enum_ = field->enum_type()->value(0);
}
} else {
AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
"Field with primitive type has type_name.");
}
} else {
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE,
"Field with message or enum type missing type_name.");
}
}
// Add the field to the fields-by-number table.
// Note: We have to do this *after* cross-linking because extensions do not
// know their containing type until now. If we're in
// lazily_build_dependencies_ mode, we're guaranteed there's no errors, so no
// risk to calling containing_type() or other accessors that will build
// dependencies.
if (!file_tables_->AddFieldByNumber(field)) {
const FieldDescriptor* conflicting_field = file_tables_->FindFieldByNumber(
field->containing_type(), field->number());
std::string containing_type_name =
field->containing_type() == nullptr
? "unknown"
: field->containing_type()->full_name();
if (field->is_extension()) {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::NUMBER,
strings::Substitute("Extension number $0 has already been used "
"in \"$1\" by extension \"$2\".",
field->number(), containing_type_name,
conflicting_field->full_name()));
} else {
AddError(field->full_name(), proto,
DescriptorPool::ErrorCollector::NUMBER,
strings::Substitute("Field number $0 has already been used in "
"\"$1\" by field \"$2\".",
field->number(), containing_type_name,
conflicting_field->name()));
}
} else {
if (field->is_extension()) {
if (!tables_->AddExtension(field)) {
const FieldDescriptor* conflicting_field =
tables_->FindExtension(field->containing_type(), field->number());
std::string containing_type_name =
field->containing_type() == nullptr
? "unknown"
: field->containing_type()->full_name();
std::string error_msg = strings::Substitute(
"Extension number $0 has already been used in \"$1\" by extension "
"\"$2\" defined in $3.",
field->number(), containing_type_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);
}
}
}
}