Expose a common helper for visiting descriptors.

This can be used immediately to simplify some of our options logic during DescriptorPool builds.

PiperOrigin-RevId: 533265315
pull/12837/head
Mike Kruskal 2 years ago committed by Copybara-Service
parent 4ac36141db
commit 8ff658a09d
  1. 26
      src/google/protobuf/BUILD.bazel
  2. 165
      src/google/protobuf/compiler/command_line_interface.cc
  3. 93
      src/google/protobuf/descriptor.cc
  4. 180
      src/google/protobuf/descriptor_visitor.h
  5. 148
      src/google/protobuf/descriptor_visitor_test.cc

@ -457,6 +457,7 @@ cc_library(
"descriptor.pb.h",
"descriptor_database.h",
"descriptor_legacy.h",
"descriptor_visitor.h",
"dynamic_message.h",
"field_access_listener.h",
"generated_enum_reflection.h",
@ -550,6 +551,19 @@ cc_library(
],
)
cc_library(
name = "descriptor_visitor",
hdrs = ["descriptor_visitor.h"],
copts = COPTS,
include_prefix = "google/protobuf",
linkopts = LINK_OPTS,
visibility = ["//:__subpackages__"],
deps = [
":port_def",
":protobuf_nowkt",
],
)
filegroup(
name = "well_known_type_protos",
srcs = [
@ -1435,6 +1449,18 @@ cc_test(
],
)
cc_test(
name = "descriptor_visitor_test",
srcs = ["descriptor_visitor_test.cc"],
deps = [
":protobuf",
":cc_test_protos",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
################################################################################
# Helper targets for Kotlin tests
################################################################################

@ -40,6 +40,7 @@
#include "absl/types/span.h"
#include "google/protobuf/compiler/allowlists/allowlists.h"
#include "google/protobuf/descriptor_legacy.h"
#include "google/protobuf/descriptor_visitor.h"
#include "google/protobuf/stubs/platform_macros.h"
@ -1025,100 +1026,6 @@ bool ContainsProto3Optional(const FileDescriptor* file) {
return false;
}
template <typename Visitor>
struct VisitImpl {
Visitor visitor;
void Visit(const FieldDescriptor* descriptor) { visitor(descriptor); }
void Visit(const EnumValueDescriptor* descriptor) { visitor(descriptor); }
void Visit(const EnumDescriptor* descriptor) {
visitor(descriptor);
for (int i = 0; i < descriptor->value_count(); i++) {
Visit(descriptor->value(i));
}
}
void Visit(const Descriptor::ExtensionRange* descriptor) {
visitor(descriptor);
}
void Visit(const OneofDescriptor* descriptor) { visitor(descriptor); }
void Visit(const Descriptor* descriptor) {
visitor(descriptor);
for (int i = 0; i < descriptor->enum_type_count(); i++) {
Visit(descriptor->enum_type(i));
}
for (int i = 0; i < descriptor->field_count(); i++) {
Visit(descriptor->field(i));
}
for (int i = 0; i < descriptor->nested_type_count(); i++) {
Visit(descriptor->nested_type(i));
}
for (int i = 0; i < descriptor->extension_count(); i++) {
Visit(descriptor->extension(i));
}
for (int i = 0; i < descriptor->extension_range_count(); i++) {
Visit(descriptor->extension_range(i));
}
for (int i = 0; i < descriptor->oneof_decl_count(); i++) {
Visit(descriptor->oneof_decl(i));
}
}
void Visit(const MethodDescriptor* method) { visitor(method); }
void Visit(const ServiceDescriptor* descriptor) {
visitor(descriptor);
for (int i = 0; i < descriptor->method_count(); i++) {
Visit(descriptor->method(i));
}
}
void Visit(absl::Span<const FileDescriptor*> descriptors) {
for (const FileDescriptor* descriptor : descriptors) {
visitor(descriptor);
for (int i = 0; i < descriptor->message_type_count(); i++) {
Visit(descriptor->message_type(i));
}
for (int i = 0; i < descriptor->enum_type_count(); i++) {
Visit(descriptor->enum_type(i));
}
for (int i = 0; i < descriptor->extension_count(); i++) {
Visit(descriptor->extension(i));
}
for (int i = 0; i < descriptor->service_count(); i++) {
Visit(descriptor->service(i));
}
}
}
};
// Visit every node in the descriptors calling `visitor(node)`.
// The visitor does not need to handle all possible node types. Types that are
// not visitable via `visitor` will be ignored.
template <typename Visitor>
void VisitDescriptors(absl::Span<const FileDescriptor*> descriptors,
Visitor visitor) {
// Provide a fallback to ignore all the nodes that are not interesting to the
// input visitor.
struct VisitorImpl : Visitor {
explicit VisitorImpl(Visitor visitor) : Visitor(visitor) {}
using Visitor::operator();
// Honeypot to ignore all inputs that Visitor does not take.
void operator()(const void*) const {}
};
VisitImpl<VisitorImpl>{VisitorImpl(visitor)}.Visit(descriptors);
}
bool HasReservedFieldNumber(const FieldDescriptor* field) {
if (field->number() >= FieldDescriptor::kFirstReservedNumber &&
field->number() <= FieldDescriptor::kLastReservedNumber) {
@ -1365,49 +1272,47 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
bool validation_error = false; // Defer exiting so we log more warnings.
VisitDescriptors(
absl::Span<const FileDescriptor*>(parsed_files.data(),
parsed_files.size()),
[&](const FieldDescriptor* field) {
if (HasReservedFieldNumber(field)) {
const char* error_link = nullptr;
validation_error = true;
std::string error;
if (field->number() >= FieldDescriptor::kFirstReservedNumber &&
field->number() <= FieldDescriptor::kLastReservedNumber) {
error = absl::Substitute(
"Field numbers $0 through $1 are reserved "
"for the protocol buffer library implementation.",
FieldDescriptor::kFirstReservedNumber,
FieldDescriptor::kLastReservedNumber);
} else {
error = absl::Substitute(
"Field number $0 is reserved for specific purposes.",
field->number());
}
if (error_link) {
absl::StrAppend(&error, "(See ", error_link, ")");
for (auto& file : parsed_files) {
google::protobuf::internal::VisitDescriptors(
*file, [&](const FieldDescriptor& field) {
if (HasReservedFieldNumber(&field)) {
const char* error_link = nullptr;
validation_error = true;
std::string error;
if (field.number() >= FieldDescriptor::kFirstReservedNumber &&
field.number() <= FieldDescriptor::kLastReservedNumber) {
error = absl::Substitute(
"Field numbers $0 through $1 are reserved "
"for the protocol buffer library implementation.",
FieldDescriptor::kFirstReservedNumber,
FieldDescriptor::kLastReservedNumber);
} else {
error = absl::Substitute(
"Field number $0 is reserved for specific purposes.",
field.number());
}
if (error_link) {
absl::StrAppend(&error, "(See ", error_link, ")");
}
static_cast<DescriptorPool::ErrorCollector*>(error_collector.get())
->RecordError(field.file()->name(), field.full_name(), nullptr,
DescriptorPool::ErrorCollector::NUMBER, error);
}
static_cast<DescriptorPool::ErrorCollector*>(error_collector.get())
->RecordError(field->file()->name(), field->full_name(), nullptr,
DescriptorPool::ErrorCollector::NUMBER, error);
}
});
});
}
// We visit one file at a time because we need to provide the file name for
// error messages. Usually we can get the file name from any descriptor with
// something like descriptor->file()->name(), but ExtensionRange does not
// support this.
for (const google::protobuf::FileDescriptor* file : parsed_files) {
VisitDescriptors(
absl::Span<const FileDescriptor*>(&file, 1),
[&](const auto* descriptor) {
if (!ValidateTargetConstraints(
descriptor->options(), *descriptor_pool, *error_collector,
file->name(), GetTargetType(descriptor))) {
validation_error = true;
}
});
google::protobuf::internal::VisitDescriptors(*file, [&](const auto& descriptor) {
if (!ValidateTargetConstraints(descriptor.options(), *descriptor_pool,
*error_collector, file->name(),
GetTargetType(&descriptor))) {
validation_error = true;
}
});
}

@ -74,6 +74,7 @@
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/descriptor_database.h"
#include "google/protobuf/descriptor_legacy.h"
#include "google/protobuf/descriptor_visitor.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/generated_message_util.h"
#include "google/protobuf/io/strtod.h"
@ -4207,20 +4208,21 @@ class DescriptorBuilder {
// descriptors, which are the ones that have been interpreted. The const
// proto references are passed in only so they can be provided to calls to
// AddError(). Do not look at their options, which have not been interpreted.
void ValidateFileOptions(const FileDescriptor* file,
const FileDescriptorProto& proto);
void ValidateMessageOptions(const Descriptor* message,
const DescriptorProto& proto);
void ValidateOneofOptions(const OneofDescriptor* oneof,
const OneofDescriptorProto& proto);
void ValidateFieldOptions(const FieldDescriptor* field,
const FieldDescriptorProto& proto);
void ValidateOptions(const FileDescriptor* file,
const FileDescriptorProto& proto);
void ValidateOptions(const Descriptor* message, const DescriptorProto& proto);
void ValidateOptions(const OneofDescriptor* oneof,
const OneofDescriptorProto& proto);
void ValidateOptions(const FieldDescriptor* field,
const FieldDescriptorProto& proto);
void ValidateFieldFeatures(const FieldDescriptor* field,
const FieldDescriptorProto& proto);
void ValidateEnumOptions(const EnumDescriptor* enm,
const EnumDescriptorProto& proto);
void ValidateEnumValueOptions(const EnumValueDescriptor* enum_value,
const EnumValueDescriptorProto& proto);
void ValidateOptions(const EnumDescriptor* enm,
const EnumDescriptorProto& proto);
void ValidateOptions(const EnumValueDescriptor* enum_value,
const EnumValueDescriptorProto& proto);
void ValidateOptions(const Descriptor::ExtensionRange* range,
const DescriptorProto::ExtensionRange& proto) {}
void ValidateExtensionRangeOptions(const DescriptorProto& proto,
const Descriptor& message);
void ValidateExtensionDeclaration(
@ -4228,10 +4230,10 @@ class DescriptorBuilder {
const RepeatedPtrField<ExtensionRangeOptions_Declaration>& declarations,
const DescriptorProto_ExtensionRange& proto,
absl::flat_hash_set<absl::string_view>& full_name_set);
void ValidateServiceOptions(const ServiceDescriptor* service,
const ServiceDescriptorProto& proto);
void ValidateMethodOptions(const MethodDescriptor* method,
const MethodDescriptorProto& proto);
void ValidateOptions(const ServiceDescriptor* service,
const ServiceDescriptorProto& proto);
void ValidateOptions(const MethodDescriptor* method,
const MethodDescriptorProto& proto);
void ValidateProto3(const FileDescriptor* file,
const FileDescriptorProto& proto);
void ValidateProto3Message(const Descriptor* message,
@ -5478,7 +5480,10 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl(
// Validate options. See comments at InternalSetLazilyBuildDependencies about
// error checking and lazy import building.
if (!had_errors_ && !pool_->lazily_build_dependencies_) {
ValidateFileOptions(result, proto);
internal::VisitDescriptors(*result, proto,
[&](const auto& descriptor, const auto& proto) {
ValidateOptions(&descriptor, proto);
});
}
// Additional naming conflict check for map entry types. Only need to check
@ -7103,12 +7108,6 @@ void DescriptorBuilder::SuggestFieldNumbers(FileDescriptor* file,
// -------------------------------------------------------------------
#define VALIDATE_OPTIONS_FROM_ARRAY(descriptor, array_name, type) \
for (int i = 0; i < descriptor->array_name##_count(); ++i) { \
Validate##type##Options(descriptor->array_name##s_ + i, \
proto.array_name(i)); \
}
// Determine if the file uses optimize_for = LITE_RUNTIME, being careful to
// avoid problems that exist at init time.
static bool IsLite(const FileDescriptor* file) {
@ -7119,13 +7118,8 @@ static bool IsLite(const FileDescriptor* file) {
file->options().optimize_for() == FileOptions::LITE_RUNTIME;
}
void DescriptorBuilder::ValidateFileOptions(const FileDescriptor* file,
const FileDescriptorProto& proto) {
VALIDATE_OPTIONS_FROM_ARRAY(file, message_type, Message);
VALIDATE_OPTIONS_FROM_ARRAY(file, enum_type, Enum);
VALIDATE_OPTIONS_FROM_ARRAY(file, service, Service);
VALIDATE_OPTIONS_FROM_ARRAY(file, extension, Field);
void DescriptorBuilder::ValidateOptions(const FileDescriptor* file,
const FileDescriptorProto& proto) {
// Lite files can only be imported by other Lite files.
if (!IsLite(file)) {
for (int i = 0; i < file->dependency_count(); i++) {
@ -7235,26 +7229,19 @@ void DescriptorBuilder::ValidateProto3Enum(const EnumDescriptor* enm,
}
}
void DescriptorBuilder::ValidateMessageOptions(const Descriptor* message,
const DescriptorProto& proto) {
VALIDATE_OPTIONS_FROM_ARRAY(message, field, Field);
VALIDATE_OPTIONS_FROM_ARRAY(message, nested_type, Message);
VALIDATE_OPTIONS_FROM_ARRAY(message, enum_type, Enum);
VALIDATE_OPTIONS_FROM_ARRAY(message, extension, Field);
void DescriptorBuilder::ValidateOptions(const Descriptor* message,
const DescriptorProto& proto) {
CheckFieldJsonNameUniqueness(proto, message);
ValidateExtensionRangeOptions(proto, *message);
for (int i = 0; i < message->real_oneof_decl_count(); ++i) {
ValidateOneofOptions(message->oneof_decl(i), proto.oneof_decl(i));
}
}
void DescriptorBuilder::ValidateOneofOptions(
const OneofDescriptor* /*oneof*/, const OneofDescriptorProto& /*proto*/) {}
void DescriptorBuilder::ValidateOptions(const OneofDescriptor* /*oneof*/,
const OneofDescriptorProto& /*proto*/) {
}
void DescriptorBuilder::ValidateFieldOptions(
const FieldDescriptor* field, const FieldDescriptorProto& proto) {
void DescriptorBuilder::ValidateOptions(const FieldDescriptor* field,
const FieldDescriptorProto& proto) {
if (pool_->lazily_build_dependencies_ && (!field || !field->message_type())) {
return;
}
@ -7345,10 +7332,8 @@ void DescriptorBuilder::ValidateFieldFeatures(
const FieldDescriptor* field, const FieldDescriptorProto& proto) {
}
void DescriptorBuilder::ValidateEnumOptions(const EnumDescriptor* enm,
const EnumDescriptorProto& proto) {
VALIDATE_OPTIONS_FROM_ARRAY(enm, value, EnumValue);
void DescriptorBuilder::ValidateOptions(const EnumDescriptor* enm,
const EnumDescriptorProto& proto) {
CheckEnumValueUniqueness(proto, enm);
if (!enm->options().has_allow_alias() || !enm->options().allow_alias()) {
@ -7390,7 +7375,7 @@ void DescriptorBuilder::ValidateEnumOptions(const EnumDescriptor* enm,
}
}
void DescriptorBuilder::ValidateEnumValueOptions(
void DescriptorBuilder::ValidateOptions(
const EnumValueDescriptor* /* enum_value */,
const EnumValueDescriptorProto& /* proto */) {
// Nothing to do so far.
@ -7531,8 +7516,8 @@ void DescriptorBuilder::ValidateExtensionRangeOptions(
}
}
void DescriptorBuilder::ValidateServiceOptions(
const ServiceDescriptor* service, const ServiceDescriptorProto& proto) {
void DescriptorBuilder::ValidateOptions(const ServiceDescriptor* service,
const ServiceDescriptorProto& proto) {
if (IsLite(service->file()) &&
(service->file()->options().cc_generic_services() ||
service->file()->options().java_generic_services())) {
@ -7541,11 +7526,9 @@ void DescriptorBuilder::ValidateServiceOptions(
"unless you set both options cc_generic_services and "
"java_generic_services to false.");
}
VALIDATE_OPTIONS_FROM_ARRAY(service, method, Method);
}
void DescriptorBuilder::ValidateMethodOptions(
void DescriptorBuilder::ValidateOptions(
const MethodDescriptor* /* method */,
const MethodDescriptorProto& /* proto */) {
// Nothing to do so far.
@ -7725,8 +7708,6 @@ void DescriptorBuilder::ValidateJSType(const FieldDescriptor* field,
}
}
#undef VALIDATE_OPTIONS_FROM_ARRAY
// -------------------------------------------------------------------
DescriptorBuilder::OptionInterpreter::OptionInterpreter(

@ -0,0 +1,180 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
// 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
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_VISITOR_H__
#define GOOGLE_PROTOBUF_DESCRIPTOR_VISITOR_H__
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/generated_message_reflection.h"
namespace google {
namespace protobuf {
namespace internal {
// Visit every node in the descriptors calling `visitor(node, proto)`.
// The visitor does not need to handle all possible node types. Types that are
// not visitable via `visitor` will be ignored.
template <typename Visitor>
void VisitDescriptors(const FileDescriptor& file,
const FileDescriptorProto& proto, Visitor visitor);
// Visit just the descriptors, without a corresponding proto tree.
template <typename Visitor>
void VisitDescriptors(const FileDescriptor& file, Visitor visitor);
template <typename Visitor>
struct VisitImpl {
Visitor visitor;
template <typename... Proto>
void Visit(const FieldDescriptor& descriptor, const Proto&... proto) {
visitor(descriptor, proto...);
}
template <typename... Proto>
void Visit(const EnumValueDescriptor& descriptor, const Proto&... proto) {
visitor(descriptor, proto...);
}
template <typename... Proto>
void Visit(const EnumDescriptor& descriptor, const Proto&... proto) {
visitor(descriptor, proto...);
for (int i = 0; i < descriptor.value_count(); i++) {
Visit(*descriptor.value(i), proto.value(i)...);
}
}
template <typename... Proto>
void Visit(const Descriptor::ExtensionRange& descriptor,
const Proto&... proto) {
visitor(descriptor, proto...);
}
template <typename... Proto>
void Visit(const OneofDescriptor& descriptor, const Proto&... proto) {
visitor(descriptor, proto...);
}
template <typename... Proto>
void Visit(const Descriptor& descriptor, const Proto&... proto) {
visitor(descriptor, proto...);
for (int i = 0; i < descriptor.enum_type_count(); i++) {
Visit(*descriptor.enum_type(i), proto.enum_type(i)...);
}
for (int i = 0; i < descriptor.oneof_decl_count(); i++) {
Visit(*descriptor.oneof_decl(i), proto.oneof_decl(i)...);
}
for (int i = 0; i < descriptor.field_count(); i++) {
Visit(*descriptor.field(i), proto.field(i)...);
}
for (int i = 0; i < descriptor.nested_type_count(); i++) {
Visit(*descriptor.nested_type(i), proto.nested_type(i)...);
}
for (int i = 0; i < descriptor.extension_count(); i++) {
Visit(*descriptor.extension(i), proto.extension(i)...);
}
for (int i = 0; i < descriptor.extension_range_count(); i++) {
Visit(*descriptor.extension_range(i), proto.extension_range(i)...);
}
}
template <typename... Proto>
void Visit(const MethodDescriptor& method, const Proto&... proto) {
visitor(method, proto...);
}
template <typename... Proto>
void Visit(const ServiceDescriptor& descriptor, const Proto&... proto) {
visitor(descriptor, proto...);
for (int i = 0; i < descriptor.method_count(); i++) {
Visit(*descriptor.method(i), proto.method(i)...);
}
}
template <typename... Proto>
void Visit(const FileDescriptor& descriptor, const Proto&... proto) {
visitor(descriptor, proto...);
for (int i = 0; i < descriptor.message_type_count(); i++) {
Visit(*descriptor.message_type(i), proto.message_type(i)...);
}
for (int i = 0; i < descriptor.enum_type_count(); i++) {
Visit(*descriptor.enum_type(i), proto.enum_type(i)...);
}
for (int i = 0; i < descriptor.extension_count(); i++) {
Visit(*descriptor.extension(i), proto.extension(i)...);
}
for (int i = 0; i < descriptor.service_count(); i++) {
Visit(*descriptor.service(i), proto.service(i)...);
}
}
};
// Provide a fallback to ignore all the nodes that are not interesting to the
// input visitor.
template <typename Visitor>
struct VisitorImpl : Visitor {
explicit VisitorImpl(Visitor visitor) : Visitor(visitor) {}
// Pull in all of the supplied callbacks.
using Visitor::operator();
// Honeypots to ignore all inputs that Visitor does not take.
struct DescriptorEater {
template <typename T>
DescriptorEater(T&&) {} // NOLINT
};
void operator()(DescriptorEater, DescriptorEater) const {}
void operator()(DescriptorEater) const {}
};
template <typename Visitor>
void VisitDescriptors(const FileDescriptor& file,
const FileDescriptorProto& proto, Visitor visitor) {
using VisitorImpl = internal::VisitorImpl<Visitor>;
internal::VisitImpl<VisitorImpl>{VisitorImpl(visitor)}.Visit(file, proto);
}
template <typename Visitor>
void VisitDescriptors(const FileDescriptor& file, Visitor visitor) {
using VisitorImpl = internal::VisitorImpl<Visitor>;
internal::VisitImpl<VisitorImpl>{VisitorImpl(visitor)}.Visit(file);
}
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_DESCRIPTOR_VISITOR_H__

@ -0,0 +1,148 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
// 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
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/descriptor_visitor.h"
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/strings/string_view.h"
#include "google/protobuf/unittest.pb.h"
namespace google {
namespace protobuf {
namespace {
using ::testing::Contains;
using ::testing::IsSupersetOf;
using ::testing::Not;
constexpr absl::string_view kUnittestProtoFile =
"google/protobuf/unittest.proto";
TEST(VisitDescriptorsTest, SingleTypeNoProto) {
const FileDescriptor& file =
*protobuf_unittest::TestAllTypes::GetDescriptor()->file();
std::vector<std::string> descriptors;
VisitDescriptors(file, [&](const Descriptor& descriptor) {
descriptors.push_back(descriptor.full_name());
});
EXPECT_THAT(descriptors,
IsSupersetOf({"protobuf_unittest.TestAllTypes",
"protobuf_unittest.TestAllTypes.NestedMessage"}));
}
TEST(VisitDescriptorsTest, SingleTypeWithProto) {
const FileDescriptor& file =
*protobuf_unittest::TestAllTypes::GetDescriptor()->file();
FileDescriptorProto proto;
file.CopyTo(&proto);
std::vector<std::string> descriptors;
VisitDescriptors(
file, proto,
[&](const Descriptor& descriptor, const DescriptorProto& proto) {
descriptors.push_back(descriptor.full_name());
EXPECT_EQ(descriptor.name(), proto.name());
});
EXPECT_THAT(descriptors,
IsSupersetOf({"protobuf_unittest.TestAllTypes",
"protobuf_unittest.TestAllTypes.NestedMessage"}));
}
TEST(VisitDescriptorsTest, AllTypesDeduce) {
const FileDescriptor& file =
*protobuf_unittest::TestAllTypes::GetDescriptor()->file();
std::vector<std::string> descriptors;
VisitDescriptors(file, [&](const auto& descriptor) {
descriptors.push_back(descriptor.name());
});
EXPECT_THAT(descriptors, Contains(kUnittestProtoFile));
EXPECT_THAT(descriptors, IsSupersetOf({"TestAllTypes", "TestSparseEnum",
"SPARSE_C", "optional_int32",
"oneof_nested_message", "oneof_field",
"optional_nested_message_extension"}));
}
TEST(VisitDescriptorsTest, AllTypesDeduceSelective) {
const FileDescriptor& file =
*protobuf_unittest::TestAllTypes::GetDescriptor()->file();
std::vector<std::string> descriptors;
VisitDescriptors(
file,
// Only select on descriptors with a full_name method.
[&](const auto& descriptor)
-> std::enable_if_t<
!std::is_void<decltype(descriptor.full_name())>::value> {
descriptors.push_back(descriptor.full_name());
});
// FileDescriptor doesn't have a full_name method.
EXPECT_THAT(descriptors, Not(Contains(kUnittestProtoFile)));
EXPECT_THAT(descriptors,
IsSupersetOf(
{"protobuf_unittest.TestAllTypes",
"protobuf_unittest.TestSparseEnum", "protobuf_unittest.SPARSE_C",
"protobuf_unittest.TestAllTypes.optional_int32",
"protobuf_unittest.TestAllTypes.oneof_nested_message",
"protobuf_unittest.TestAllTypes.oneof_field",
"protobuf_unittest.optional_nested_message_extension"}));
}
void TestHandle(const Descriptor& message, const DescriptorProto& proto,
std::vector<std::string>* result) {
if (result != nullptr) result->push_back(message.full_name());
EXPECT_EQ(message.name(), proto.name());
}
void TestHandle(const EnumDescriptor& enm, const EnumDescriptorProto& proto,
std::vector<std::string>* result) {
if (result != nullptr) result->push_back(enm.full_name());
EXPECT_EQ(enm.name(), proto.name());
}
TEST(VisitDescriptorsTest, AllTypesDeduceDelegate) {
const FileDescriptor& file =
*protobuf_unittest::TestAllTypes::GetDescriptor()->file();
FileDescriptorProto proto;
file.CopyTo(&proto);
std::vector<std::string> descriptors;
VisitDescriptors(file, proto,
[&](const auto& descriptor, const auto& proto)
-> decltype(TestHandle(descriptor, proto, nullptr)) {
TestHandle(descriptor, proto, &descriptors);
});
EXPECT_THAT(descriptors, IsSupersetOf({"protobuf_unittest.TestAllTypes",
"protobuf_unittest.TestSparseEnum"}));
}
} // namespace
} // namespace protobuf
} // namespace google
Loading…
Cancel
Save