This can be used immediately to simplify some of our options logic during DescriptorPool builds. PiperOrigin-RevId: 533265315pull/12837/head
parent
4ac36141db
commit
8ff658a09d
5 changed files with 426 additions and 186 deletions
@ -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…
Reference in new issue