1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include <memory>
9
10 #include "google/protobuf/testing/file.h"
11 #include "google/protobuf/testing/file.h"
12 #include "google/protobuf/compiler/cpp/generator.h"
13 #include "google/protobuf/compiler/command_line_interface.h"
14 #include "google/protobuf/descriptor.pb.h"
15 #include <gtest/gtest.h>
16 #include "absl/log/absl_check.h"
17 #include "google/protobuf/compiler/annotation_test_util.h"
18 #include "google/protobuf/compiler/cpp/helpers.h"
19
20 namespace google {
21 namespace protobuf {
22 namespace compiler {
23 namespace cpp {
24
25 namespace atu = annotation_test_util;
26
27 namespace {
28
29 class CppMetadataTest : public ::testing::Test {
30 public:
31 // Tries to capture a FileDescriptorProto, GeneratedCodeInfo, and output
32 // code from the previously added file with name `filename`. Returns true on
33 // success. If pb_h is non-null, expects a .pb.h and a .pb.h.meta (copied to
34 // pb_h and pb_h_info respecfively); similarly for proto_h and proto_h_info.
CaptureMetadata(const std::string & filename,FileDescriptorProto * file,std::string * pb_h,GeneratedCodeInfo * pb_h_info,std::string * proto_h,GeneratedCodeInfo * proto_h_info,std::string * pb_cc)35 bool CaptureMetadata(const std::string& filename, FileDescriptorProto* file,
36 std::string* pb_h, GeneratedCodeInfo* pb_h_info,
37 std::string* proto_h, GeneratedCodeInfo* proto_h_info,
38 std::string* pb_cc) {
39 CommandLineInterface cli;
40 CppGenerator cpp_generator;
41 cli.RegisterGenerator("--cpp_out", &cpp_generator, "");
42 std::string cpp_out = absl::StrCat(
43 "--cpp_out=annotate_headers=true,"
44 "annotation_pragma_name=pragma_name,"
45 "annotation_guard_name=guard_name:",
46 ::testing::TempDir());
47
48 const bool result = atu::RunProtoCompiler(filename, cpp_out, &cli, file);
49
50 if (!result) {
51 return result;
52 }
53
54 std::string output_base =
55 absl::StrCat(::testing::TempDir(), "/", StripProto(filename));
56
57 if (pb_cc != nullptr) {
58 ABSL_CHECK_OK(File::GetContents(absl::StrCat(output_base, ".pb.cc"),
59 pb_cc, true));
60 }
61
62 if (pb_h != nullptr && pb_h_info != nullptr) {
63 ABSL_CHECK_OK(File::GetContents(absl::StrCat(output_base, ".pb.h"), pb_h,
64 true));
65 if (!atu::DecodeMetadata(absl::StrCat(output_base, ".pb.h.meta"),
66 pb_h_info)) {
67 return false;
68 }
69 }
70
71 if (proto_h != nullptr && proto_h_info != nullptr) {
72 ABSL_CHECK_OK(File::GetContents(absl::StrCat(output_base, ".proto.h"),
73 proto_h, true));
74 if (!atu::DecodeMetadata(absl::StrCat(output_base, ".proto.h.meta"),
75 proto_h_info)) {
76 return false;
77 }
78 }
79
80 return true;
81 }
82 };
83
84 constexpr absl::string_view kSmallTestFile =
85 "syntax = \"proto2\";\n"
86 "package foo;\n"
87 "enum Enum { VALUE = 0; }\n"
88 "message Message { }\n";
89
TEST_F(CppMetadataTest,CapturesEnumNames)90 TEST_F(CppMetadataTest, CapturesEnumNames) {
91 FileDescriptorProto file;
92 GeneratedCodeInfo info;
93 std::string pb_h;
94 atu::AddFile("test.proto", kSmallTestFile);
95 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
96 nullptr, nullptr));
97 EXPECT_EQ("Enum", file.enum_type(0).name());
98 std::vector<int> enum_path;
99 enum_path.push_back(FileDescriptorProto::kEnumTypeFieldNumber);
100 enum_path.push_back(0);
101 const GeneratedCodeInfo::Annotation* enum_annotation =
102 atu::FindAnnotationOnPath(info, "test.proto", enum_path);
103 EXPECT_TRUE(nullptr != enum_annotation);
104 EXPECT_TRUE(atu::AnnotationMatchesSubstring(pb_h, enum_annotation, "Enum"));
105 }
106
TEST_F(CppMetadataTest,AddsPragma)107 TEST_F(CppMetadataTest, AddsPragma) {
108 FileDescriptorProto file;
109 GeneratedCodeInfo info;
110 std::string pb_h;
111 atu::AddFile("test.proto", kSmallTestFile);
112 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
113 nullptr, nullptr));
114 EXPECT_TRUE(pb_h.find("#ifdef guard_name") != std::string::npos);
115 EXPECT_TRUE(pb_h.find("#pragma pragma_name \"test.pb.h.meta\"") !=
116 std::string::npos);
117 }
118
TEST_F(CppMetadataTest,CapturesMessageNames)119 TEST_F(CppMetadataTest, CapturesMessageNames) {
120 FileDescriptorProto file;
121 GeneratedCodeInfo info;
122 std::string pb_h;
123 atu::AddFile("test.proto", kSmallTestFile);
124 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
125 nullptr, nullptr));
126 EXPECT_EQ("Message", file.message_type(0).name());
127 std::vector<int> message_path;
128 message_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
129 message_path.push_back(0);
130 const GeneratedCodeInfo::Annotation* message_annotation =
131 atu::FindAnnotationOnPath(info, "test.proto", message_path);
132 EXPECT_TRUE(nullptr != message_annotation);
133 EXPECT_TRUE(
134 atu::AnnotationMatchesSubstring(pb_h, message_annotation, "Message"));
135 }
136
TEST_F(CppMetadataTest,RangeChecksWork)137 TEST_F(CppMetadataTest, RangeChecksWork) {
138 absl::string_view test = "test";
139 GeneratedCodeInfo::Annotation annotation;
140 annotation.set_begin(-1);
141 annotation.set_end(0);
142 EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value());
143 annotation.set_begin(1);
144 EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value());
145 annotation.set_begin(0);
146 annotation.set_end(1);
147 EXPECT_TRUE(atu::GetAnnotationSubstring(test, annotation).has_value());
148 annotation.set_begin(4);
149 annotation.set_end(4);
150 ASSERT_TRUE(atu::GetAnnotationSubstring(test, annotation).has_value());
151 EXPECT_EQ("", *atu::GetAnnotationSubstring(test, annotation));
152 annotation.set_end(5);
153 EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value());
154 }
155
156 constexpr absl::string_view kEnumFieldTestFile = R"(
157 syntax = "proto2";
158 package foo;
159 enum Enum { VALUE = 0; }
160 message Message {
161 optional Enum efield = 1;
162 repeated Enum refield = 2;
163 }
164 )";
165
TEST_F(CppMetadataTest,AnnotatesEnumSemantics)166 TEST_F(CppMetadataTest, AnnotatesEnumSemantics) {
167 FileDescriptorProto file;
168 GeneratedCodeInfo info;
169 std::string pb_h;
170 atu::AddFile("test.proto", kEnumFieldTestFile);
171 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
172 nullptr, nullptr));
173 EXPECT_EQ("Message", file.message_type(0).name());
174 std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
175 DescriptorProto::kFieldFieldNumber, 0};
176 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
177 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
178 EXPECT_TRUE(!annotations.empty());
179 for (const auto* annotation : annotations) {
180 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
181 ASSERT_TRUE(substring.has_value());
182 if (*substring == "efield") {
183 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
184 annotation->semantic());
185 } else if (*substring == "set_efield" || *substring == "clear_efield") {
186 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
187 annotation->semantic());
188 }
189 }
190 field_path.back() = 1;
191 annotations.clear();
192 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
193 EXPECT_TRUE(!annotations.empty());
194 for (const auto* annotation : annotations) {
195 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
196 ASSERT_TRUE(substring.has_value());
197 if (*substring == "refield") {
198 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
199 annotation->semantic());
200 } else if (*substring == "set_refield" || *substring == "clear_refield" ||
201 *substring == "add_refield") {
202 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
203 annotation->semantic());
204 } else if (*substring == "mutable_refield") {
205 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
206 annotation->semantic());
207 }
208 }
209 }
210
211 constexpr absl::string_view kMapFieldTestFile = R"(
212 syntax = "proto2";
213 package foo;
214 message Message {
215 map<string, int32> mfield = 1;
216 }
217 )";
218
TEST_F(CppMetadataTest,AnnotatesMapSemantics)219 TEST_F(CppMetadataTest, AnnotatesMapSemantics) {
220 FileDescriptorProto file;
221 GeneratedCodeInfo info;
222 std::string pb_h;
223 atu::AddFile("test.proto", kMapFieldTestFile);
224 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
225 nullptr, nullptr));
226 EXPECT_EQ("Message", file.message_type(0).name());
227 std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
228 DescriptorProto::kFieldFieldNumber, 0};
229 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
230 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
231 EXPECT_TRUE(!annotations.empty());
232 for (const auto* annotation : annotations) {
233 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
234 ASSERT_TRUE(substring.has_value());
235 if (*substring == "mfield") {
236 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
237 annotation->semantic());
238 } else if (*substring == "clear_mfield") {
239 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
240 annotation->semantic());
241 } else if (*substring == "mutable_mfield") {
242 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
243 annotation->semantic());
244 }
245 }
246 }
247
248 constexpr absl::string_view kPrimFieldTestFile = R"(
249 syntax = "proto2";
250 package foo;
251 message Message {
252 optional int32 ifield = 1;
253 repeated int32 rifield = 2;
254 }
255 )";
256
TEST_F(CppMetadataTest,AnnotatesPrimSemantics)257 TEST_F(CppMetadataTest, AnnotatesPrimSemantics) {
258 FileDescriptorProto file;
259 GeneratedCodeInfo info;
260 std::string pb_h;
261 atu::AddFile("test.proto", kPrimFieldTestFile);
262 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
263 nullptr, nullptr));
264 EXPECT_EQ("Message", file.message_type(0).name());
265 std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
266 DescriptorProto::kFieldFieldNumber, 0};
267 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
268 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
269 EXPECT_TRUE(!annotations.empty());
270 for (const auto* annotation : annotations) {
271 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
272 ASSERT_TRUE(substring.has_value());
273 if (*substring == "ifield") {
274 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
275 annotation->semantic());
276 } else if (*substring == "set_ifield" || *substring == "clear_ifield") {
277 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
278 annotation->semantic());
279 }
280 }
281 field_path.back() = 1;
282 annotations.clear();
283 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
284 EXPECT_TRUE(!annotations.empty());
285 for (const auto* annotation : annotations) {
286 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
287 ASSERT_TRUE(substring.has_value());
288 if (*substring == "rifield") {
289 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
290 annotation->semantic());
291 } else if (*substring == "set_rifield" || *substring == "clear_rifield" ||
292 *substring == "add_rifield") {
293 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
294 annotation->semantic());
295 } else if (*substring == "mutable_rifield") {
296 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
297 annotation->semantic());
298 }
299 }
300 }
301
302 constexpr absl::string_view kCordFieldTestFile = R"(
303 syntax = "proto2";
304 package foo;
305 message Message {
306 optional string sfield = 1 [ctype = CORD];
307 repeated string rsfield = 2 [ctype = CORD];
308 }
309 )";
310
TEST_F(CppMetadataTest,AnnotatesCordSemantics)311 TEST_F(CppMetadataTest, AnnotatesCordSemantics) {
312 FileDescriptorProto file;
313 GeneratedCodeInfo info;
314 std::string pb_h;
315 atu::AddFile("test.proto", kCordFieldTestFile);
316 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
317 nullptr, nullptr));
318 EXPECT_EQ("Message", file.message_type(0).name());
319 std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
320 DescriptorProto::kFieldFieldNumber, 0};
321 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
322 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
323 EXPECT_TRUE(!annotations.empty());
324 for (const auto* annotation : annotations) {
325 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
326 ASSERT_TRUE(substring.has_value());
327 if (*substring == "sfield") {
328 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
329 annotation->semantic());
330 } else if (*substring == "set_sfield" || *substring == "clear_sfield") {
331 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
332 annotation->semantic());
333 } else if (*substring == "mutable_sfield") {
334 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
335 annotation->semantic());
336 }
337 }
338 field_path.back() = 1;
339 annotations.clear();
340 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
341 EXPECT_TRUE(!annotations.empty());
342 for (const auto* annotation : annotations) {
343 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
344 ASSERT_TRUE(substring.has_value());
345 if (*substring == "rsfield") {
346 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
347 annotation->semantic());
348 } else if (*substring == "set_rsfield" || *substring == "clear_rsfield" ||
349 *substring == "add_rsfield") {
350 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
351 annotation->semantic());
352 } else if (*substring == "mutable_rsfield") {
353 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
354 annotation->semantic());
355 }
356 }
357 }
358
359 constexpr absl::string_view kStringPieceFieldTestFile = R"(
360 syntax = "proto2";
361 package foo;
362 message Message {
363 optional string sfield = 1 [ctype = STRING_PIECE];
364 repeated string rsfield = 2 [ctype = STRING_PIECE];
365 }
366 )";
367
TEST_F(CppMetadataTest,AnnotatesStringPieceSemantics)368 TEST_F(CppMetadataTest, AnnotatesStringPieceSemantics) {
369 FileDescriptorProto file;
370 GeneratedCodeInfo info;
371 std::string pb_h;
372 atu::AddFile("test.proto", kStringPieceFieldTestFile);
373 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
374 nullptr, nullptr));
375 EXPECT_EQ("Message", file.message_type(0).name());
376 std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
377 DescriptorProto::kFieldFieldNumber, 0};
378 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
379 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
380 EXPECT_TRUE(!annotations.empty());
381 for (const auto* annotation : annotations) {
382 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
383 ASSERT_TRUE(substring.has_value());
384 if (*substring == "sfield") {
385 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
386 annotation->semantic());
387 } else if (*substring == "set_sfield" || *substring == "set_alias_sfield" ||
388 *substring == "clear_sfield") {
389 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
390 annotation->semantic());
391 } else if (*substring == "mutable_sfield") {
392 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
393 annotation->semantic());
394 }
395 }
396 field_path.back() = 1;
397 annotations.clear();
398 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
399 EXPECT_TRUE(!annotations.empty());
400 for (const auto* annotation : annotations) {
401 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
402 ASSERT_TRUE(substring.has_value());
403 if (*substring == "rsfield") {
404 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
405 annotation->semantic());
406 } else if (*substring == "set_rsfield" ||
407 *substring == "set_alias_rsfield" ||
408 *substring == "clear_rsfield" ||
409 *substring == "add_alias_rsfield" ||
410 *substring == "add_rsfield") {
411 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
412 annotation->semantic());
413 } else if (*substring == "mutable_rsfield") {
414 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
415 annotation->semantic());
416 }
417 }
418 }
419
420 constexpr absl::string_view kStringFieldTestFile = R"(
421 syntax = "proto2";
422 package foo;
423 message Message {
424 optional string sfield = 1;
425 repeated string rsfield = 2;
426 }
427 )";
428
TEST_F(CppMetadataTest,AnnotatesStringSemantics)429 TEST_F(CppMetadataTest, AnnotatesStringSemantics) {
430 FileDescriptorProto file;
431 GeneratedCodeInfo info;
432 std::string pb_h;
433 atu::AddFile("test.proto", kStringFieldTestFile);
434 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
435 nullptr, nullptr));
436 EXPECT_EQ("Message", file.message_type(0).name());
437 std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
438 DescriptorProto::kFieldFieldNumber, 0};
439 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
440 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
441 EXPECT_TRUE(!annotations.empty());
442 for (const auto* annotation : annotations) {
443 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
444 ASSERT_TRUE(substring.has_value());
445 if (*substring == "sfield") {
446 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
447 annotation->semantic());
448 } else if (*substring == "set_sfield" || *substring == "clear_sfield") {
449 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
450 annotation->semantic());
451 } else if (*substring == "mutable_sfield") {
452 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
453 annotation->semantic());
454 }
455 }
456 field_path.back() = 1;
457 annotations.clear();
458 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
459 EXPECT_TRUE(!annotations.empty());
460 for (const auto* annotation : annotations) {
461 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
462 ASSERT_TRUE(substring.has_value());
463 if (*substring == "rsfield") {
464 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
465 annotation->semantic());
466 } else if (*substring == "set_rsfield" || *substring == "clear_rsfield" ||
467 *substring == "add_rsfield") {
468 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
469 annotation->semantic());
470 } else if (*substring == "mutable_rsfield") {
471 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
472 annotation->semantic());
473 }
474 }
475 }
476
477 constexpr absl::string_view kMessageFieldTestFile = R"(
478 syntax = "proto2";
479 package foo;
480 message SMessage { }
481 message Message {
482 optional SMessage mfield = 1;
483 repeated SMessage rmfield = 2;
484 oneof ofield {
485 int32 oint = 3;
486 }
487 }
488 )";
489
TEST_F(CppMetadataTest,AnnotatesMessageSemantics)490 TEST_F(CppMetadataTest, AnnotatesMessageSemantics) {
491 FileDescriptorProto file;
492 GeneratedCodeInfo info;
493 std::string pb_h;
494 atu::AddFile("test.proto", kMessageFieldTestFile);
495 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
496 nullptr, nullptr));
497 EXPECT_EQ("Message", file.message_type(1).name());
498 std::vector<int> field_path;
499 field_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
500 field_path.push_back(1);
501 field_path.push_back(DescriptorProto::kFieldFieldNumber);
502 field_path.push_back(0);
503 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
504 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
505 EXPECT_TRUE(!annotations.empty());
506 for (const auto* annotation : annotations) {
507 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
508 ASSERT_TRUE(substring.has_value());
509 if (*substring == "mfield") {
510 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
511 annotation->semantic());
512 } else if (*substring == "clear_mfield") {
513 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
514 annotation->semantic());
515 } else if (*substring == "mutable_mfield") {
516 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
517 annotation->semantic());
518 }
519 }
520 field_path.back() = 1;
521 annotations.clear();
522 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
523 EXPECT_TRUE(!annotations.empty());
524 for (const auto* annotation : annotations) {
525 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
526 ASSERT_TRUE(substring.has_value());
527 if (*substring == "rmfield") {
528 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
529 annotation->semantic());
530 } else if (*substring == "add_rmfield" || *substring == "clear_rmfield") {
531 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
532 annotation->semantic());
533 } else if (*substring == "mutable_rmfield") {
534 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
535 annotation->semantic());
536 }
537 }
538 field_path.clear();
539 field_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
540 field_path.push_back(1);
541 field_path.push_back(DescriptorProto::kOneofDeclFieldNumber);
542 field_path.push_back(0);
543 annotations.clear();
544 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
545 EXPECT_TRUE(!annotations.empty());
546 for (const auto* annotation : annotations) {
547 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
548 ASSERT_TRUE(substring.has_value());
549 if (*substring == "ofield_case") {
550 EXPECT_EQ(GeneratedCodeInfo::Annotation::NONE, annotation->semantic());
551 } else if (*substring == "clear_ofield") {
552 EXPECT_EQ(GeneratedCodeInfo::Annotation::SET, annotation->semantic());
553 }
554 }
555 }
556
557 constexpr absl::string_view kLazyMessageFieldTestFile = R"(
558 syntax = "proto2";
559 package foo;
560 message SMessage { }
561 message Message {
562 optional SMessage mfield = 1 [lazy=true];
563 }
564 )";
565
TEST_F(CppMetadataTest,AnnotatesLazyMessageSemantics)566 TEST_F(CppMetadataTest, AnnotatesLazyMessageSemantics) {
567 FileDescriptorProto file;
568 GeneratedCodeInfo info;
569 std::string pb_h;
570 atu::AddFile("test.proto", kLazyMessageFieldTestFile);
571 EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
572 nullptr, nullptr));
573 EXPECT_EQ("Message", file.message_type(1).name());
574 std::vector<int> field_path;
575 field_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
576 field_path.push_back(1);
577 field_path.push_back(DescriptorProto::kFieldFieldNumber);
578 field_path.push_back(0);
579 std::vector<const GeneratedCodeInfo::Annotation*> annotations;
580 atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
581 EXPECT_TRUE(!annotations.empty());
582 for (const auto* annotation : annotations) {
583 auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
584 ASSERT_TRUE(substring.has_value());
585 if (*substring == "mfield") {
586 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
587 annotation->semantic());
588 } else if (*substring == "mutable_mfield") {
589 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
590 annotation->semantic());
591 } else if (*substring == "set_encoded_mfield" ||
592 *substring == "clear_mfield") {
593 EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
594 annotation->semantic());
595 }
596 }
597 }
598 } // namespace
599 } // namespace cpp
600 } // namespace compiler
601 } // namespace protobuf
602 } // namespace google
603