1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <thrift/compiler/sema/standard_validator.h>
18 
19 #include <cmath>
20 #include <limits>
21 #include <memory>
22 
23 #include <folly/portability/GMock.h>
24 #include <folly/portability/GTest.h>
25 #include <thrift/compiler/ast/diagnostic.h>
26 #include <thrift/compiler/ast/diagnostic_context.h>
27 #include <thrift/compiler/ast/t_base_type.h>
28 #include <thrift/compiler/ast/t_enum.h>
29 #include <thrift/compiler/ast/t_enum_value.h>
30 #include <thrift/compiler/ast/t_exception.h>
31 #include <thrift/compiler/ast/t_field.h>
32 #include <thrift/compiler/ast/t_function.h>
33 #include <thrift/compiler/ast/t_interaction.h>
34 #include <thrift/compiler/ast/t_paramlist.h>
35 #include <thrift/compiler/ast/t_program.h>
36 #include <thrift/compiler/ast/t_service.h>
37 #include <thrift/compiler/ast/t_struct.h>
38 #include <thrift/compiler/ast/t_typedef.h>
39 
40 namespace apache::thrift::compiler {
41 namespace {
42 
43 using ::testing::UnorderedElementsAre;
44 
45 class StandardValidatorTest : public ::testing::Test {
46  protected:
validate(diagnostic_params params=diagnostic_params::keep_all ())47   std::vector<diagnostic> validate(
48       diagnostic_params params = diagnostic_params::keep_all()) {
49     diagnostic_results results;
50     diagnostic_context ctx{results, std::move(params)};
51     ctx.start_program(&program_);
52     standard_validator()(ctx, program_);
53     return std::move(results).diagnostics();
54   }
55 
inst(const t_struct * ttype,int lineno)56   std::unique_ptr<t_const> inst(const t_struct* ttype, int lineno) {
57     auto value = std::make_unique<t_const_value>();
58     value->set_map();
59     value->set_ttype(t_type_ref::from_ptr(ttype));
60     auto result =
61         std::make_unique<t_const>(&program_, ttype, "", std::move(value));
62     result->set_lineno(lineno);
63     return result;
64   }
65 
failure(int lineno,const std::string & msg)66   diagnostic failure(int lineno, const std::string& msg) {
67     return {diagnostic_level::failure, msg, "/path/to/file.thrift", lineno};
68   }
69 
warning(int lineno,const std::string & msg)70   diagnostic warning(int lineno, const std::string& msg) {
71     return {diagnostic_level::warning, msg, "/path/to/file.thrift", lineno};
72   }
73 
74   t_program program_{"/path/to/file.thrift"};
75 };
76 
TEST_F(StandardValidatorTest,InterfaceNamesUniqueNoError)77 TEST_F(StandardValidatorTest, InterfaceNamesUniqueNoError) {
78   // Create interfaces with non-overlapping functions.
79   {
80     auto service = std::make_unique<t_service>(&program_, "Service");
81     service->add_function(std::make_unique<t_function>(
82         &t_base_type::t_void(),
83         "bar",
84         std::make_unique<t_paramlist>(&program_)));
85     service->add_function(std::make_unique<t_function>(
86         &t_base_type::t_void(),
87         "baz",
88         std::make_unique<t_paramlist>(&program_)));
89     program_.add_service(std::move(service));
90   }
91 
92   {
93     auto interaction =
94         std::make_unique<t_interaction>(&program_, "Interaction");
95     interaction->add_function(std::make_unique<t_function>(
96         &t_base_type::t_void(),
97         "bar",
98         std::make_unique<t_paramlist>(&program_)));
99     interaction->add_function(std::make_unique<t_function>(
100         &t_base_type::t_void(),
101         "baz",
102         std::make_unique<t_paramlist>(&program_)));
103     program_.add_interaction(std::move(interaction));
104   }
105 
106   // No errors will be found
107   EXPECT_THAT(validate(), ::testing::IsEmpty());
108 }
109 
TEST_F(StandardValidatorTest,ReapeatedNamesInService)110 TEST_F(StandardValidatorTest, ReapeatedNamesInService) {
111   // Create interfaces with overlapping functions.
112   {
113     auto service = std::make_unique<t_service>(&program_, "Service");
114     auto fn1 = std::make_unique<t_function>(
115         &t_base_type::t_void(),
116         "foo",
117         std::make_unique<t_paramlist>(&program_));
118     fn1->set_lineno(1);
119     auto fn2 = std::make_unique<t_function>(
120         &t_base_type::t_void(),
121         "foo",
122         std::make_unique<t_paramlist>(&program_));
123     fn2->set_lineno(2);
124     service->add_function(std::move(fn1));
125     service->add_function(std::move(fn2));
126     service->add_function(std::make_unique<t_function>(
127         &t_base_type::t_void(),
128         "bar",
129         std::make_unique<t_paramlist>(&program_)));
130     program_.add_service(std::move(service));
131   }
132 
133   {
134     auto interaction =
135         std::make_unique<t_interaction>(&program_, "Interaction");
136     auto fn1 = std::make_unique<t_function>(
137         &t_base_type::t_void(),
138         "bar",
139         std::make_unique<t_paramlist>(&program_));
140     fn1->set_lineno(3);
141     auto fn2 = std::make_unique<t_function>(
142         &t_base_type::t_void(),
143         "bar",
144         std::make_unique<t_paramlist>(&program_));
145     fn2->set_lineno(4);
146 
147     interaction->add_function(std::make_unique<t_function>(
148         &t_base_type::t_void(),
149         "foo",
150         std::make_unique<t_paramlist>(&program_)));
151     interaction->add_function(std::move(fn1));
152     interaction->add_function(std::move(fn2));
153     program_.add_interaction(std::move(interaction));
154   }
155 
156   EXPECT_THAT(
157       validate(),
158       ::testing::UnorderedElementsAre(
159           failure(2, "Function `foo` is already defined for `Service`."),
160           failure(4, "Function `bar` is already defined for `Interaction`.")));
161 }
162 
TEST_F(StandardValidatorTest,RepeatedNameInExtendedService)163 TEST_F(StandardValidatorTest, RepeatedNameInExtendedService) {
164   // Create first service with non repeated functions
165   auto base = std::make_unique<t_service>(&program_, "Base");
166   base->add_function(std::make_unique<t_function>(
167       &t_base_type::t_void(), "bar", std::make_unique<t_paramlist>(&program_)));
168   base->add_function(std::make_unique<t_function>(
169       &t_base_type::t_void(), "baz", std::make_unique<t_paramlist>(&program_)));
170   auto derived = std::make_unique<t_service>(&program_, "Derived", base.get());
171   derived->add_function(std::make_unique<t_function>(
172       &t_base_type::t_void(), "foo", std::make_unique<t_paramlist>(&program_)));
173 
174   auto derived_ptr = derived.get();
175   program_.add_service(std::move(base));
176   program_.add_service(std::move(derived));
177 
178   EXPECT_THAT(validate(), ::testing::IsEmpty());
179 
180   // Add an overlapping function in the derived service.
181   auto dupe = std::make_unique<t_function>(
182       &t_base_type::t_void(), "baz", std::make_unique<t_paramlist>(&program_));
183   dupe->set_lineno(1);
184   derived_ptr->add_function(std::move(dupe));
185 
186   // An error will be found
187   EXPECT_THAT(
188       validate(),
189       ::testing::UnorderedElementsAre(failure(
190           1, "Function `Derived.baz` redefines `service file.Base.baz`.")));
191 }
192 
TEST_F(StandardValidatorTest,DuplicatedEnumValues)193 TEST_F(StandardValidatorTest, DuplicatedEnumValues) {
194   auto tenum = std::make_unique<t_enum>(&program_, "foo");
195   auto tenum_ptr = tenum.get();
196   program_.add_enum(std::move(tenum));
197 
198   tenum_ptr->append(std::make_unique<t_enum_value>("bar", 1));
199   tenum_ptr->append(std::make_unique<t_enum_value>("baz", 2));
200 
201   // No errors will be found.
202   EXPECT_THAT(validate(), ::testing::IsEmpty());
203 
204   // Add enum_value with repeated value.
205   auto enum_value_3 = std::make_unique<t_enum_value>("foo", 1);
206   enum_value_3->set_lineno(1);
207   tenum_ptr->append(std::move(enum_value_3));
208 
209   // An error will be found.
210   EXPECT_THAT(
211       validate(),
212       UnorderedElementsAre(failure(
213           1, "Duplicate value `foo=1` with value `bar` in enum `foo`.")));
214 }
215 
TEST_F(StandardValidatorTest,RepeatedNamesInEnumValues)216 TEST_F(StandardValidatorTest, RepeatedNamesInEnumValues) {
217   auto tenum = std::make_unique<t_enum>(&program_, "foo");
218   auto tenum_ptr = tenum.get();
219   program_.add_enum(std::move(tenum));
220 
221   tenum_ptr->append(std::make_unique<t_enum_value>("bar", 1));
222   tenum_ptr->append(std::make_unique<t_enum_value>("not_bar", 2));
223 
224   // No errors will be found.
225   EXPECT_THAT(validate(), ::testing::IsEmpty());
226 
227   // Add enum_value with repeated name.
228   auto enum_value_3 = std::make_unique<t_enum_value>("bar", 3);
229   enum_value_3->set_lineno(1);
230   tenum_ptr->append(std::move(enum_value_3));
231 
232   // An error will be found.
233   EXPECT_THAT(
234       validate(),
235       UnorderedElementsAre(
236           failure(1, "Enum value `bar` is already defined for `foo`.")));
237 }
238 
TEST_F(StandardValidatorTest,UnsetEnumValues)239 TEST_F(StandardValidatorTest, UnsetEnumValues) {
240   auto tenum = std::make_unique<t_enum>(&program_, "foo");
241   auto tenum_ptr = tenum.get();
242   program_.add_enum(std::move(tenum));
243 
244   auto enum_value_1 = std::make_unique<t_enum_value>("Foo", 1);
245   auto enum_value_2 = std::make_unique<t_enum_value>("Bar");
246   auto enum_value_3 = std::make_unique<t_enum_value>("Baz");
247   enum_value_1->set_lineno(1);
248   enum_value_2->set_lineno(2);
249   enum_value_3->set_lineno(3);
250   tenum_ptr->append(std::move(enum_value_1));
251   tenum_ptr->append(std::move(enum_value_2));
252   tenum_ptr->append(std::move(enum_value_3));
253 
254   // Bar and Baz will have errors.
255   EXPECT_THAT(
256       validate(),
257       UnorderedElementsAre(
258           failure(
259               2,
260               "The enum value, `Bar`, must have an explicitly assigned value."),
261           failure(
262               3,
263               "The enum value, `Baz`, must have an explicitly assigned value.")));
264 }
265 
TEST_F(StandardValidatorTest,UnionFieldAttributes)266 TEST_F(StandardValidatorTest, UnionFieldAttributes) {
267   auto tstruct = std::make_unique<t_struct>(&program_, "Struct");
268   auto tunion = std::make_unique<t_union>(&program_, "Union");
269   tunion->set_lineno(1);
270   {
271     auto field = std::make_unique<t_field>(&t_base_type::t_i64(), "req", 1);
272     field->set_lineno(2);
273     field->set_qualifier(t_field_qualifier::required);
274     tunion->append(std::move(field));
275   }
276   {
277     auto field = std::make_unique<t_field>(&t_base_type::t_i64(), "op", 2);
278     field->set_lineno(3);
279     field->set_qualifier(t_field_qualifier::optional);
280     tunion->append(std::move(field));
281   }
282   {
283     auto field = std::make_unique<t_field>(&t_base_type::t_i64(), "non", 3);
284     field->set_lineno(4);
285     tunion->append(std::move(field));
286   }
287   {
288     auto field = std::make_unique<t_field>(tstruct.get(), "mixin", 4);
289     field->set_lineno(5);
290     field->set_annotation("cpp.mixin");
291     tunion->append(std::move(field));
292   }
293   program_.add_struct(std::move(tstruct));
294   program_.add_struct(std::move(tunion));
295 
296   EXPECT_THAT(
297       validate(),
298       UnorderedElementsAre(
299           // Qualified fields will have errors.
300           failure(
301               2,
302               "Unions cannot contain qualified fields. Remove `required` qualifier from field `req`."),
303           failure(
304               3,
305               "Unions cannot contain qualified fields. Remove `optional` qualifier from field `op`."),
306           // Fields with cpp.mixing have errors.
307           failure(5, "Union `Union` cannot contain mixin field `mixin`.")));
308 }
309 
TEST_F(StandardValidatorTest,FieldId)310 TEST_F(StandardValidatorTest, FieldId) {
311   auto tstruct = std::make_unique<t_struct>(&program_, "Struct");
312   tstruct->append(
313       std::make_unique<t_field>(t_base_type::t_i64(), "explicit_id", 1));
314   tstruct->append(
315       std::make_unique<t_field>(t_base_type::t_i64(), "zero_id", 0));
316   tstruct->append(
317       std::make_unique<t_field>(t_base_type::t_i64(), "neg_id", -1));
318   tstruct->append(std::make_unique<t_field>(
319       t_base_type::t_i64(), "implicit_id", -2, false));
320 
321   program_.add_struct(std::move(tstruct));
322   EXPECT_THAT(
323       validate(),
324       UnorderedElementsAre(
325           failure(-1, "Zero value (0) not allowed as a field id for `zero_id`"),
326           warning(
327               -1,
328               "No field id specified for `implicit_id`, resulting protocol may have conflicts or not be backwards compatible!")));
329 }
330 
TEST_F(StandardValidatorTest,MixinFieldType)331 TEST_F(StandardValidatorTest, MixinFieldType) {
332   auto tstruct = std::make_unique<t_struct>(&program_, "Struct");
333   auto tunion = std::make_unique<t_union>(&program_, "Union");
334   auto texception = std::make_unique<t_exception>(&program_, "Exception");
335 
336   auto foo = std::make_unique<t_struct>(&program_, "Foo");
337   {
338     auto field = std::make_unique<t_field>(tstruct.get(), "struct_field", 1);
339     field->set_lineno(1);
340     field->set_annotation("cpp.mixin");
341     field->set_qualifier(t_field_qualifier::optional);
342     foo->append(std::move(field));
343   }
344   {
345     auto field = std::make_unique<t_field>(tunion.get(), "union_field", 2);
346     field->set_lineno(2);
347     field->set_annotation("cpp.mixin");
348     field->set_qualifier(t_field_qualifier::required);
349     foo->append(std::move(field));
350   }
351   {
352     auto field = std::make_unique<t_field>(texception.get(), "except_field", 3);
353     field->set_lineno(3);
354     field->set_annotation("cpp.mixin");
355     foo->append(std::move(field));
356   }
357   {
358     auto field =
359         std::make_unique<t_field>(&t_base_type::t_i32(), "other_field", 4);
360     field->set_lineno(4);
361     field->set_annotation("cpp.mixin");
362     foo->append(std::move(field));
363   }
364 
365   program_.add_struct(std::move(tstruct));
366   program_.add_struct(std::move(tunion));
367   program_.add_exception(std::move(texception));
368   program_.add_struct(std::move(foo));
369 
370   EXPECT_THAT(
371       validate(diagnostic_params::only_failures()),
372       UnorderedElementsAre(
373           failure(1, "Mixin field `struct_field` cannot be optional."),
374           failure(
375               3,
376               "Mixin field `except_field` type must be a struct or union. Found `Exception`."),
377           failure(
378               4,
379               "Mixin field `other_field` type must be a struct or union. Found `i32`.")));
380 }
381 
TEST_F(StandardValidatorTest,RepeatedStructuredAnnotation)382 TEST_F(StandardValidatorTest, RepeatedStructuredAnnotation) {
383   auto foo = std::make_unique<t_struct>(&program_, "Foo");
384   // A different program with the same name.
385   t_program other_program("/path/to/other/file.thrift");
386   // A different foo with the same file.name
387   auto other_foo = std::make_unique<t_struct>(&other_program, "Foo");
388   EXPECT_EQ(foo->get_full_name(), other_foo->get_full_name());
389 
390   t_scope scope;
391   auto bar = std::make_unique<t_typedef>(
392       &program_, &t_base_type::t_i32(), "Bar", &scope);
393   bar->add_structured_annotation(inst(other_foo.get(), 1));
394   bar->add_structured_annotation(inst(foo.get(), 2));
395   bar->add_structured_annotation(inst(foo.get(), 3));
396 
397   program_.add_struct(std::move(foo));
398   program_.add_typedef(std::move(bar));
399   other_program.add_struct(std::move(other_foo));
400 
401   // Only the third annotation is a duplicate.
402   EXPECT_THAT(
403       validate(diagnostic_params::only_failures()),
404       UnorderedElementsAre(failure(
405           3, "Structured annotation `Foo` is already defined for `Bar`.")));
406 }
407 
TEST_F(StandardValidatorTest,CustomDefaultValue)408 TEST_F(StandardValidatorTest, CustomDefaultValue) {
409   auto foo = std::make_unique<t_struct>(&program_, "Foo");
410 
411   auto custom_byte = std::make_unique<t_const_value>(
412       static_cast<int64_t>(std::numeric_limits<int8_t>::max()) + 1);
413   auto custom_short = std::make_unique<t_const_value>(
414       static_cast<int64_t>(std::numeric_limits<int16_t>::max()) + 1);
415   auto custom_integer = std::make_unique<t_const_value>(
416       static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1);
417   auto custom_float = std::make_unique<t_const_value>();
418   custom_float->set_double(std::nextafter(
419       static_cast<double>(std::numeric_limits<float>::max()),
420       std::numeric_limits<double>::max()));
421   auto custom_float_precision_loss =
422       std::make_unique<t_const_value>(std::numeric_limits<int32_t>::max());
423 
424   auto const_byte = std::make_unique<t_const>(
425       &program_, &t_base_type::t_byte(), "const_byte", std::move(custom_byte));
426   auto const_short = std::make_unique<t_const>(
427       &program_, &t_base_type::t_i16(), "const_short", std::move(custom_short));
428   auto const_integer = std::make_unique<t_const>(
429       &program_,
430       &t_base_type::t_i32(),
431       "const_integer",
432       std::move(custom_integer));
433   auto const_float = std::make_unique<t_const>(
434       &program_,
435       &t_base_type::t_float(),
436       "const_float",
437       std::move(custom_float));
438   auto const_float_precision_loss = std::make_unique<t_const>(
439       &program_,
440       &t_base_type::t_float(),
441       "const_float_precision_loss",
442       std::move(custom_float_precision_loss));
443 
444   program_.add_const(std::move(const_byte));
445   program_.add_const(std::move(const_short));
446   program_.add_const(std::move(const_integer));
447   program_.add_const(std::move(const_float));
448   program_.add_const(std::move(const_float_precision_loss));
449 
450   EXPECT_THAT(
451       validate(),
452       UnorderedElementsAre(
453           failure(
454               -1,
455               "value error: const `const_byte` has an invalid custom default value."),
456           failure(
457               -1,
458               "value error: const `const_short` has an invalid custom default value."),
459           failure(
460               -1,
461               "value error: const `const_integer` has an invalid custom default value."),
462           failure(
463               -1,
464               "value error: const `const_float` has an invalid custom default value."),
465           failure(
466               -1,
467               "value error: const `const_float_precision_loss` cannot be represented precisely as `float` or `double`.")));
468 }
469 
470 } // namespace
471 } // namespace apache::thrift::compiler
472