Annotate string and message field semantics.

PiperOrigin-RevId: 506731201
pull/11756/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 0e81135b0b
commit 9193c2f26c
  1. 11
      src/google/protobuf/compiler/annotation_test_util.cc
  2. 8
      src/google/protobuf/compiler/annotation_test_util.h
  3. 15
      src/google/protobuf/compiler/cpp/field_generators/message_field.cc
  4. 37
      src/google/protobuf/compiler/cpp/field_generators/string_field.cc
  5. 128
      src/google/protobuf/compiler/cpp/metadata_test.cc

@ -163,6 +163,17 @@ bool AnnotationMatchesSubstring(const std::string& file_content,
return AtLeastOneAnnotationMatchesSubstring(file_content, annotations,
expected_text);
}
absl::optional<absl::string_view> GetAnnotationSubstring(
absl::string_view file_content,
const GeneratedCodeInfo::Annotation& annotation) {
auto begin = annotation.begin();
auto end = annotation.end();
if (begin < 0 || end < begin || end > file_content.size()) {
return absl::nullopt;
}
return file_content.substr(begin, end - begin);
}
} // namespace annotation_test_util
} // namespace compiler
} // namespace protobuf

@ -34,6 +34,8 @@
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/testing/googletest.h"
#include <gtest/gtest.h>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
// Utilities that assist in writing tests for generator annotations.
// See java/internal/annotation_unittest.cc for an example.
@ -107,6 +109,12 @@ bool AnnotationMatchesSubstring(const std::string& file_content,
const GeneratedCodeInfo::Annotation* annotation,
const std::string& expected_text);
// Returns the text spanned by the annotation if the span is valid; otherwise
// returns nullopt.
absl::optional<absl::string_view> GetAnnotationSubstring(
absl::string_view file_content,
const GeneratedCodeInfo::Annotation& annotation);
} // namespace annotation_test_util
} // namespace compiler
} // namespace protobuf

@ -34,6 +34,7 @@
#include <memory>
#include <string>
#include <tuple>
#include "absl/container/flat_hash_map.h"
#include "absl/log/absl_check.h"
@ -202,8 +203,11 @@ void MessageFieldGenerator::GenerateAccessorDeclarations(
format(
"$deprecated_attr$const $type$& ${1$$name$$}$() const;\n"
"PROTOBUF_NODISCARD $deprecated_attr$$type$* "
"${1$$release_name$$}$();\n"
"$deprecated_attr$$type$* ${1$mutable_$name$$}$();\n"
"${1$$release_name$$}$();\n",
descriptor_);
format("$deprecated_attr$$type$* ${1$mutable_$name$$}$();\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS));
format(
"$deprecated_attr$void ${1$set_allocated_$name$$}$"
"($type$* $name$);\n"
"private:\n"
@ -793,10 +797,13 @@ void RepeatedMessageFieldGenerator::GeneratePrivateMembers(
void RepeatedMessageFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$deprecated_attr$$type$* ${1$mutable_$name$$}$(int index);\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS));
format(
"$deprecated_attr$$type$* ${1$mutable_$name$$}$(int index);\n"
"$deprecated_attr$::$proto_ns$::RepeatedPtrField< $type$ >*\n"
" ${1$mutable_$name$$}$();\n"
" ${1$mutable_$name$$}$();\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS));
format(
"private:\n"
"const $type$& ${1$_internal_$name$$}$(int index) const;\n"
"$type$* ${1$_internal_add_$name$$}$();\n"

@ -34,6 +34,7 @@
#include <memory>
#include <string>
#include <tuple>
#include "absl/container/flat_hash_map.h"
#include "absl/log/absl_check.h"
@ -232,11 +233,14 @@ void StringFieldGenerator::GenerateAccessorDeclarations(
format(
"$deprecated_attr$const std::string& ${1$$name$$}$() const;\n"
"template <typename ArgT0 = const std::string&, typename... ArgT>\n"
"$deprecated_attr$void ${1$set_$name$$}$(ArgT0&& arg0, ArgT... args);\n",
"template <typename ArgT0 = const std::string&, typename... ArgT>\n",
descriptor_);
format(
"$deprecated_attr$std::string* ${1$mutable_$name$$}$();\n"
"$deprecated_attr$void ${1$set_$name$$}$(ArgT0&& arg0, ArgT... args);\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
format("$deprecated_attr$std::string* ${1$mutable_$name$$}$();\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS));
format(
"PROTOBUF_NODISCARD $deprecated_attr$std::string* "
"${1$$release_name$$}$();\n"
"$deprecated_attr$void ${1$set_allocated_$name$$}$(std::string* "
@ -767,19 +771,22 @@ void RepeatedStringFieldGenerator::GenerateAccessorDeclarations(
}
format(
"$deprecated_attr$const std::string& ${1$$name$$}$(int index) const;\n"
"$deprecated_attr$std::string* ${1$mutable_$name$$}$(int index);\n"
"$deprecated_attr$const std::string& ${1$$name$$}$(int index) const;\n",
descriptor_);
format("$deprecated_attr$std::string* ${1$mutable_$name$$}$(int index);\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS));
format(
"$deprecated_attr$void ${1$set_$name$$}$(int index, const "
"std::string& value);\n"
"$deprecated_attr$void ${1$set_$name$$}$(int index, std::string&& "
"value);\n"
"$deprecated_attr$void ${1$set_$name$$}$(int index, const "
"char* value);\n",
descriptor_);
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
format(
"$deprecated_attr$void ${1$set_$name$$}$(int index, "
"absl::string_view value);\n",
descriptor_);
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
format(
"$deprecated_attr$void ${1$set_$name$$}$("
"int index, const $pointer_type$* value, ::size_t size);\n"
@ -787,19 +794,25 @@ void RepeatedStringFieldGenerator::GenerateAccessorDeclarations(
"$deprecated_attr$void ${1$add_$name$$}$(const std::string& value);\n"
"$deprecated_attr$void ${1$add_$name$$}$(std::string&& value);\n"
"$deprecated_attr$void ${1$add_$name$$}$(const char* value);\n",
descriptor_);
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
format("$deprecated_attr$void ${1$add_$name$$}$(absl::string_view value);\n",
descriptor_);
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
format(
"$deprecated_attr$void ${1$add_$name$$}$(const $pointer_type$* "
"value, ::size_t size)"
";\n"
";\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
format(
"$deprecated_attr$const ::$proto_ns$::RepeatedPtrField<std::string>& "
"${1$$name$$}$() "
"const;\n"
"const;\n",
descriptor_);
format(
"$deprecated_attr$::$proto_ns$::RepeatedPtrField<std::string>* "
"${1$mutable_$name$$}$()"
";\n"
";\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS));
format(
"private:\n"
"const std::string& ${1$_internal_$name$$}$(int index) const;\n"
"std::string* _internal_add_$name$();\n"

@ -158,6 +158,134 @@ TEST_F(CppMetadataTest, CapturesMessageNames) {
atu::AnnotationMatchesSubstring(pb_h, message_annotation, "Message"));
}
TEST_F(CppMetadataTest, RangeChecksWork) {
absl::string_view test = "test";
GeneratedCodeInfo::Annotation annotation;
annotation.set_begin(-1);
annotation.set_end(0);
EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value());
annotation.set_begin(1);
EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value());
annotation.set_begin(0);
annotation.set_end(1);
EXPECT_TRUE(atu::GetAnnotationSubstring(test, annotation).has_value());
annotation.set_begin(4);
annotation.set_end(4);
ASSERT_TRUE(atu::GetAnnotationSubstring(test, annotation).has_value());
EXPECT_EQ("", *atu::GetAnnotationSubstring(test, annotation));
annotation.set_end(5);
EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value());
}
constexpr absl::string_view kStringFieldTestFile = R"(
syntax = "proto2";
package foo;
message Message {
optional string sfield = 1;
repeated string rsfield = 2;
}
)";
TEST_F(CppMetadataTest, AnnotatesStringSemantics) {
FileDescriptorProto file;
GeneratedCodeInfo info;
std::string pb_h;
atu::AddFile("test.proto", kStringFieldTestFile);
EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
nullptr, nullptr));
EXPECT_EQ("Message", file.message_type(0).name());
std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
DescriptorProto::kFieldFieldNumber, 0};
std::vector<const GeneratedCodeInfo::Annotation*> annotations;
atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
EXPECT_TRUE(!annotations.empty());
for (const auto* annotation : annotations) {
auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
ASSERT_TRUE(substring.has_value());
if (*substring == "sfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
annotation->semantic());
} else if (*substring == "set_sfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
annotation->semantic());
} else if (*substring == "mutable_sfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
annotation->semantic());
}
}
field_path.back() = 1;
annotations.clear();
atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
EXPECT_TRUE(!annotations.empty());
for (const auto* annotation : annotations) {
auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
ASSERT_TRUE(substring.has_value());
if (*substring == "rsfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
annotation->semantic());
} else if (*substring == "set_rsfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
annotation->semantic());
} else if (*substring == "mutable_rsfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
annotation->semantic());
}
}
}
constexpr absl::string_view kMessageFieldTestFile = R"(
syntax = "proto2";
package foo;
message SMessage { }
message Message {
optional SMessage mfield = 1;
repeated SMessage rmfield = 2;
}
)";
TEST_F(CppMetadataTest, AnnotatesMessageSemantics) {
FileDescriptorProto file;
GeneratedCodeInfo info;
std::string pb_h;
atu::AddFile("test.proto", kMessageFieldTestFile);
EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
nullptr, nullptr));
EXPECT_EQ("Message", file.message_type(1).name());
std::vector<int> field_path;
field_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
field_path.push_back(1);
field_path.push_back(DescriptorProto::kFieldFieldNumber);
field_path.push_back(0);
std::vector<const GeneratedCodeInfo::Annotation*> annotations;
atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
EXPECT_TRUE(!annotations.empty());
for (const auto* annotation : annotations) {
auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
ASSERT_TRUE(substring.has_value());
if (*substring == "mfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
annotation->semantic());
} else if (*substring == "mutable_mfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
annotation->semantic());
}
}
field_path.back() = 1;
annotations.clear();
atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
EXPECT_TRUE(!annotations.empty());
for (const auto* annotation : annotations) {
auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
ASSERT_TRUE(substring.has_value());
if (substring == "rmfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
annotation->semantic());
} else if (substring == "mutable_rmfield") {
EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
annotation->semantic());
}
}
}
} // namespace
} // namespace cpp
} // namespace compiler

Loading…
Cancel
Save