Skip to content

Commit 9a8c096

Browse files
ahilgerfacebook-github-bot
authored andcommitted
warn on duplicate keys in field initializer
Summary: Expand validator coverage to include `t_const_value*` from field initializers. Reviewed By: createdbysk Differential Revision: D69219405 fbshipit-source-id: c1a865ee9f28e764bd7810f0a5b424af3d6c057d
1 parent 4da2793 commit 9a8c096

File tree

4 files changed

+53
-14
lines changed

4 files changed

+53
-14
lines changed

thrift/compiler/sema/check_map_keys.cc

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,27 +180,28 @@ std::vector<const t_const_value*> find_duplicate_keys(
180180
return duplicates;
181181
}
182182

183-
// If owner is null, it's a nested "anonymous" constant value.
184-
// If owner is non-null and doesn't match enclosing t_const, it's
185-
// defined elsewhere.
186-
bool is_named_const_value(const t_const_value* value, const t_const& const_) {
183+
// If node is a field, only have to check if owner is non-null.
184+
// If node is a const, also have to check if owner is not the encolsing const.
185+
bool is_named_const_value(const t_const_value* value, const t_node& node) {
187186
auto owner = value->get_owner();
188-
return owner != nullptr && owner != &const_;
187+
if (owner == nullptr) {
188+
return false;
189+
}
190+
// if node is a field, returns true b/c owner is non-null
191+
return owner != dynamic_cast<const t_const*>(&node);
189192
}
190193

191194
void check_key_value(
192-
diagnostics_engine& diags,
193-
const t_const& const_,
194-
const t_const_value* value) {
195+
diagnostics_engine& diags, const t_node& node, const t_const_value* value) {
195196
// recurse on elements
196197
if (value->kind() == t_const_value::CV_LIST) {
197198
for (const t_const_value* elem : value->get_list()) {
198-
check_key_value(diags, const_, elem);
199+
check_key_value(diags, node, elem);
199200
}
200201
}
201202
if (value->kind() == t_const_value::CV_MAP) {
202203
// Don't recurse or check constant defined elsewhere.
203-
if (is_named_const_value(value, const_)) {
204+
if (is_named_const_value(value, node)) {
204205
return;
205206
}
206207
auto duplicates = find_duplicate_keys(value);
@@ -209,16 +210,16 @@ void check_key_value(
209210
// fallback to the source range of the enclosing const.
210211
const source_range& src_range = duplicate->src_range()
211212
? *duplicate->src_range()
212-
: (value->src_range() ? *value->src_range() : const_.src_range());
213+
: (value->src_range() ? *value->src_range() : node.src_range());
213214
// TODO(T213710219): Enable this with error severity
214215
diags.warning(
215216
src_range.begin,
216217
"Duplicate key in map literal: `{}`",
217218
to_string(duplicate));
218219
}
219220
for (const auto& kv : value->get_map()) {
220-
check_key_value(diags, const_, kv.first);
221-
check_key_value(diags, const_, kv.second);
221+
check_key_value(diags, node, kv.first);
222+
check_key_value(diags, node, kv.second);
222223
}
223224
}
224225
}
@@ -230,5 +231,12 @@ void check_map_keys(diagnostics_engine& diags, const t_const& const_) {
230231
check_key_value(diags, const_, const_.value());
231232
}
232233

234+
void check_map_keys(diagnostics_engine& diags, const t_field& field_) {
235+
if (field_.default_value() == nullptr) {
236+
return;
237+
}
238+
check_key_value(diags, field_, field_.default_value());
239+
}
240+
233241
} // namespace detail
234242
} // namespace apache::thrift::compiler

thrift/compiler/sema/standard_validator.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ void validate_field_default_value(sema_context& ctx, const t_field& field) {
680680
// If initializer is not valid to begin with, stop checks and return error.
681681
return;
682682
}
683+
detail::check_map_keys(ctx, field);
683684

684685
const t_structured& parent_node =
685686
dynamic_cast<const t_structured&>(*ctx.parent());

thrift/compiler/sema/standard_validator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ bool is_initializer_default_value(
5252
* const.
5353
*/
5454
void check_map_keys(diagnostics_engine& diags, const t_const& const_);
55+
void check_map_keys(diagnostics_engine& diags, const t_field& field);
5556

5657
void validate_annotation_scopes(sema_context& ctx, const t_named& node);
5758

thrift/compiler/test/standard_validator_test.cc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ TEST(StandardValidatorTest, ValidatePy3EnableCppAdapter) {
122122
)");
123123
}
124124

125-
TEST(StandardValidatorTest, ConstMapKeyCollision) {
125+
TEST(StandardValidatorTest, ConstKeyCollision) {
126126
check_compile(R"(
127127
enum FooBar {
128128
Foo = 1,
@@ -199,3 +199,32 @@ TEST(StandardValidatorTest, ConstMapKeyCollision) {
199199
200200
)");
201201
}
202+
203+
TEST(StandardValidatorTest, FieldDefaultKeyCollision) {
204+
check_compile(R"(
205+
enum FooBar {
206+
Foo = 1,
207+
Bar = 2,
208+
}
209+
210+
const map<i64, string> INT_DUPE = {
211+
2: "Foo",
212+
4: "Bar",
213+
2: "Bar"
214+
# expected-warning@-1: Duplicate key in map literal: `2`
215+
}
216+
217+
struct S {
218+
1: map<FooBar, string> ok_init = {};
219+
2: map<i64, string> bad_init_no_err = INT_DUPE;
220+
3: map<FooBar, string> bad_init_should_err = {
221+
FooBar.Foo: "Foo", FooBar.Bar: "Bar", FooBar.Bar: "Bar"
222+
# expected-warning@-1: Duplicate key in map literal: `Bar`
223+
};
224+
4: map<list<i64>, i64> bad_init_list_key = {
225+
[1, 1, 2]: 1, [1, 1, 2]: 2
226+
# expected-warning@-1: Duplicate key in map literal: `[1, ..., 2]`
227+
};
228+
}
229+
)");
230+
}

0 commit comments

Comments
 (0)