• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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