1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 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 "google/protobuf/compiler/cpp/generator.h"
9
10 #include <memory>
11
12 #include "google/protobuf/descriptor.pb.h"
13 #include <gtest/gtest.h>
14 #include "google/protobuf/compiler/command_line_interface_tester.h"
15 #include "google/protobuf/cpp_features.pb.h"
16
17 namespace google {
18 namespace protobuf {
19 namespace compiler {
20 namespace cpp {
21 namespace {
22
23 class CppGeneratorTest : public CommandLineInterfaceTester {
24 protected:
CppGeneratorTest()25 CppGeneratorTest() {
26 RegisterGenerator("--cpp_out", "--cpp_opt",
27 std::make_unique<CppGenerator>(), "C++ test generator");
28
29 // Generate built-in protos.
30 CreateTempFile(
31 "google/protobuf/descriptor.proto",
32 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
33 CreateTempFile("google/protobuf/cpp_features.proto",
34 pb::CppFeatures::descriptor()->file()->DebugString());
35 }
36 };
37
TEST_F(CppGeneratorTest,Basic)38 TEST_F(CppGeneratorTest, Basic) {
39 CreateTempFile("foo.proto",
40 R"schema(
41 syntax = "proto2";
42 message Foo {
43 optional int32 bar = 1;
44 })schema");
45
46 RunProtoc(
47 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
48
49 ExpectNoErrors();
50 }
51
TEST_F(CppGeneratorTest,BasicError)52 TEST_F(CppGeneratorTest, BasicError) {
53 CreateTempFile("foo.proto",
54 R"schema(
55 syntax = "proto2";
56 message Foo {
57 int32 bar = 1;
58 })schema");
59
60 RunProtoc(
61 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
62
63 ExpectErrorSubstring(
64 "foo.proto:4:7: Expected \"required\", \"optional\", or \"repeated\"");
65 }
66
TEST_F(CppGeneratorTest,LegacyClosedEnumOnNonEnumField)67 TEST_F(CppGeneratorTest, LegacyClosedEnumOnNonEnumField) {
68 CreateTempFile("foo.proto",
69 R"schema(
70 edition = "2023";
71 import "google/protobuf/cpp_features.proto";
72
73 message Foo {
74 int32 bar = 1 [features.(pb.cpp).legacy_closed_enum = true];
75 })schema");
76
77 RunProtoc(
78 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
79
80 ExpectErrorSubstring(
81 "Field Foo.bar specifies the legacy_closed_enum feature but has non-enum "
82 "type.");
83 }
84
TEST_F(CppGeneratorTest,LegacyClosedEnum)85 TEST_F(CppGeneratorTest, LegacyClosedEnum) {
86 CreateTempFile("foo.proto",
87 R"schema(
88 edition = "2023";
89 import "google/protobuf/cpp_features.proto";
90
91 enum TestEnum {
92 TEST_ENUM_UNKNOWN = 0;
93 }
94 message Foo {
95 TestEnum bar = 1 [features.(pb.cpp).legacy_closed_enum = true];
96 })schema");
97
98 RunProtoc(
99 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
100
101 ExpectWarningSubstring(
102 "foo.proto:9:16: warning: Feature pb.CppFeatures.legacy_closed_enum has "
103 "been deprecated in edition 2023");
104 }
105
TEST_F(CppGeneratorTest,LegacyClosedEnumInherited)106 TEST_F(CppGeneratorTest, LegacyClosedEnumInherited) {
107 CreateTempFile("foo.proto",
108 R"schema(
109 edition = "2023";
110 import "google/protobuf/cpp_features.proto";
111 option features.(pb.cpp).legacy_closed_enum = true;
112
113 enum TestEnum {
114 TEST_ENUM_UNKNOWN = 0;
115 }
116 message Foo {
117 TestEnum bar = 1;
118 int32 baz = 2;
119 })schema");
120
121 RunProtoc(
122 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
123
124 ExpectWarningSubstring(
125 "foo.proto: warning: Feature pb.CppFeatures.legacy_closed_enum has "
126 "been deprecated in edition 2023");
127 }
128
TEST_F(CppGeneratorTest,LegacyClosedEnumImplicit)129 TEST_F(CppGeneratorTest, LegacyClosedEnumImplicit) {
130 CreateTempFile("foo.proto", R"schema(
131 edition = "2023";
132 import "google/protobuf/cpp_features.proto";
133 option features.(pb.cpp).legacy_closed_enum = true;
134
135 enum TestEnum {
136 TEST_ENUM_UNKNOWN = 0;
137 }
138 message Foo {
139 TestEnum bar = 1 [features.field_presence = IMPLICIT];
140 int32 baz = 2;
141 }
142 )schema");
143
144 RunProtoc(
145 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
146
147 ExpectErrorSubstring(
148 "Field Foo.bar has a closed enum type with implicit presence.");
149 }
150
TEST_F(CppGeneratorTest,AllowStringTypeForEdition2023)151 TEST_F(CppGeneratorTest, AllowStringTypeForEdition2023) {
152 CreateTempFile("foo.proto", R"schema(
153 edition = "2023";
154 import "google/protobuf/cpp_features.proto";
155
156 message Foo {
157 int32 bar = 1;
158 bytes baz = 2 [features.(pb.cpp).string_type = CORD];
159 }
160 )schema");
161
162 RunProtoc(
163 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
164
165 ExpectNoErrors();
166 }
167
TEST_F(CppGeneratorTest,ErrorsOnBothStringTypeAndCtype)168 TEST_F(CppGeneratorTest, ErrorsOnBothStringTypeAndCtype) {
169 CreateTempFile("foo.proto", R"schema(
170 edition = "2023";
171 import "google/protobuf/cpp_features.proto";
172
173 message Foo {
174 int32 bar = 1;
175 bytes baz = 2 [ctype = CORD, features.(pb.cpp).string_type = VIEW];
176 }
177 )schema");
178
179 RunProtoc(
180 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
181
182 ExpectErrorSubstring(
183 "Foo.baz specifies both string_type and ctype which is not supported.");
184 }
185
TEST_F(CppGeneratorTest,StringTypeForCord)186 TEST_F(CppGeneratorTest, StringTypeForCord) {
187 CreateTempFile("foo.proto", R"schema(
188 edition = "2024";
189 import "google/protobuf/cpp_features.proto";
190
191 message Foo {
192 int32 bar = 1;
193 bytes baz = 2 [features.(pb.cpp).string_type = CORD];
194 }
195 )schema");
196
197 RunProtoc(
198 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir "
199 "--experimental_editions foo.proto");
200
201 ExpectNoErrors();
202 }
203
TEST_F(CppGeneratorTest,CtypeForCord)204 TEST_F(CppGeneratorTest, CtypeForCord) {
205 CreateTempFile("foo.proto", R"schema(
206 edition = "2023";
207
208 message Foo {
209 int32 bar = 1;
210 bytes baz = 2 [ctype = CORD];
211 }
212 )schema");
213
214 RunProtoc(
215 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
216
217 ExpectNoErrors();
218 }
219
TEST_F(CppGeneratorTest,StringTypeForStringFieldsOnly)220 TEST_F(CppGeneratorTest, StringTypeForStringFieldsOnly) {
221 CreateTempFile("foo.proto", R"schema(
222 edition = "2024";
223 import "google/protobuf/cpp_features.proto";
224
225 message Foo {
226 int32 bar = 1;
227 int32 baz = 2 [features.(pb.cpp).string_type = CORD];
228 }
229 )schema");
230
231 RunProtoc(
232 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir "
233 "--experimental_editions foo.proto");
234
235 ExpectErrorSubstring(
236 "Field Foo.baz specifies string_type, but is not a string nor bytes "
237 "field.");
238 }
239
TEST_F(CppGeneratorTest,StringTypeCordNotForExtension)240 TEST_F(CppGeneratorTest, StringTypeCordNotForExtension) {
241 CreateTempFile("foo.proto", R"schema(
242 edition = "2024";
243 import "google/protobuf/cpp_features.proto";
244
245 message Foo {
246 extensions 1 to max;
247 }
248 extend Foo {
249 bytes bar = 1 [features.(pb.cpp).string_type = CORD];
250 }
251 )schema");
252
253 RunProtoc(
254 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir "
255 "--experimental_editions foo.proto");
256
257 ExpectErrorSubstring(
258 "Extension bar specifies Cord type which is not supported for "
259 "extensions.");
260 }
261
TEST_F(CppGeneratorTest,InheritedStringTypeCordNotForExtension)262 TEST_F(CppGeneratorTest, InheritedStringTypeCordNotForExtension) {
263 CreateTempFile("foo.proto", R"schema(
264 edition = "2024";
265 import "google/protobuf/cpp_features.proto";
266 option features.(pb.cpp).string_type = CORD;
267
268 message Foo {
269 extensions 1 to max;
270 }
271 extend Foo {
272 bytes bar = 1;
273 }
274 )schema");
275
276 RunProtoc(
277 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir "
278 "--experimental_editions foo.proto");
279
280 ExpectNoErrors();
281 }
282
TEST_F(CppGeneratorTest,CtypeOnNoneStringFieldTest)283 TEST_F(CppGeneratorTest, CtypeOnNoneStringFieldTest) {
284 CreateTempFile("foo.proto",
285 R"schema(
286 edition = "2023";
287 message Foo {
288 int32 bar = 1 [ctype=STRING];
289 })schema");
290 RunProtoc(
291 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
292 ExpectErrorSubstring(
293 "Field Foo.bar specifies ctype, but is not "
294 "a string nor bytes field.");
295 }
296
TEST_F(CppGeneratorTest,CtypeOnExtensionTest)297 TEST_F(CppGeneratorTest, CtypeOnExtensionTest) {
298 CreateTempFile("foo.proto",
299 R"schema(
300 edition = "2023";
301 message Foo {
302 extensions 1 to max;
303 }
304 extend Foo {
305 bytes bar = 1 [ctype=CORD];
306 })schema");
307 RunProtoc(
308 "protocol_compiler --proto_path=$tmpdir --cpp_out=$tmpdir foo.proto");
309 ExpectErrorSubstring(
310 "Extension bar specifies Cord type which is "
311 "not supported for extensions.");
312 }
313 } // namespace
314 } // namespace cpp
315 } // namespace compiler
316 } // namespace protobuf
317 } // namespace google
318