Skip to content

Commit 56c0963

Browse files
hchokshifacebook-github-bot
authored andcommitted
Emit compiler warning on duplicate field name in RPC function throws clause
Summary: Emit a compiler warning when an RPC function `throws` declaration reuses field names. Example (reuse of field name `e`): ```lang=thrift exception Ex1 {} exception Ex2 {} service S { void foo() throws ( 1: Ex1 e, 2: Ex2 e, ); } ``` Reviewed By: iahs Differential Revision: D76552482 fbshipit-source-id: eacd74a4c06ea819141778c212fe46ffcc86380b
1 parent 06f5a36 commit 56c0963

File tree

2 files changed

+55
-13
lines changed

2 files changed

+55
-13
lines changed

third-party/thrift/src/thrift/compiler/sema/standard_validator.cc

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,25 +82,34 @@ bool has_lazy_field(const t_structured& node) {
8282
}
8383

8484
// Reports an existing name was redefined within the given parent node.
85-
void report_redef_error(
85+
void report_redef_diag(
8686
diagnostics_engine& diags,
87+
const diagnostic_level& level,
8788
const char* kind,
8889
std::string_view name,
8990
const t_named& parent,
9091
const t_node& child,
9192
const t_node& /*existing*/) {
9293
// TODO(afuller): Use `existing` to provide more detail in the
9394
// diagnostic.
94-
diags.error(
95-
child, "{} `{}` is already defined for `{}`.", kind, name, parent.name());
95+
diags.report(
96+
child,
97+
level,
98+
"{} `{}` is already defined for `{}`.",
99+
kind,
100+
name,
101+
parent.name());
96102
}
97103

98104
// Helper for checking for the redefinition of a name in the context of a node.
99105
class redef_checker {
100106
public:
101107
redef_checker(
102-
diagnostics_engine& diags, const char* kind, const t_named& parent)
103-
: diags_(diags), kind_(kind), parent_(parent) {}
108+
diagnostics_engine& diags,
109+
const char* kind,
110+
const t_named& parent,
111+
const diagnostic_level& level = diagnostic_level::error)
112+
: diags_(diags), kind_(kind), parent_(parent), level_(level) {}
104113

105114
// Checks if the given `name`, derived from `node` via `child`, has already
106115
// been defined.
@@ -116,10 +125,11 @@ class redef_checker {
116125
}
117126
if (&node == &parent_ && existing == &parent_) {
118127
// The degenerate case where parent_ is conflicting with itself.
119-
report_redef_error(diags_, kind_, name, parent_, child, *existing);
128+
report_redef_diag(diags_, level_, kind_, name, parent_, child, *existing);
120129
} else {
121-
diags_.error(
130+
diags_.report(
122131
child,
132+
level_,
123133
"{} `{}.{}` and `{}.{}` can not have same name in `{}`.",
124134
kind_,
125135
node.name(),
@@ -136,8 +146,8 @@ class redef_checker {
136146
// For example, all functions in an interface.
137147
void check(const t_named& child) {
138148
if (const t_named* existing = insert(child.name(), child)) {
139-
report_redef_error(
140-
diags_, kind_, child.name(), parent_, child, *existing);
149+
report_redef_diag(
150+
diags_, level_, kind_, child.name(), parent_, child, *existing);
141151
}
142152
}
143153

@@ -162,6 +172,7 @@ class redef_checker {
162172
diagnostics_engine& diags_;
163173
const char* kind_;
164174
const t_named& parent_;
175+
const diagnostic_level level_;
165176

166177
std::unordered_map<std::string_view, const t_named*> seen_;
167178
};
@@ -800,8 +811,9 @@ void validate_structured_annotation(sema_context& ctx, const t_named& node) {
800811
for (const t_const& annot : node.structured_annotations()) {
801812
auto [it, inserted] = seen.emplace(annot.type(), &annot);
802813
if (!inserted) {
803-
report_redef_error(
814+
report_redef_diag(
804815
ctx,
816+
diagnostic_level::error,
805817
"Structured annotation",
806818
it->first->name(),
807819
node,
@@ -823,8 +835,14 @@ void validate_uri_uniqueness(sema_context& ctx, const t_program& prog) {
823835
}
824836
auto result = uri_to_node.emplace(uri, &node);
825837
if (!result.second) {
826-
report_redef_error(
827-
ctx, "Thrift URI", uri, node, node, *result.first->second);
838+
report_redef_diag(
839+
ctx,
840+
diagnostic_level::error,
841+
"Thrift URI",
842+
uri,
843+
node,
844+
node,
845+
*result.first->second);
828846
}
829847
});
830848
for (const auto* p : prog.get_included_programs()) {
@@ -999,6 +1017,14 @@ void validate_function_priority_annotation(
9991017
}
10001018
}
10011019

1020+
void validate_function_exception_field_name_uniqueness(
1021+
sema_context& ctx, const t_function& node) {
1022+
if (node.exceptions() != nullptr) {
1023+
redef_checker(ctx, "Exception field", node, diagnostic_level::warning)
1024+
.check_all(node.exceptions()->fields());
1025+
}
1026+
}
1027+
10021028
void validate_exception_message_annotation(
10031029
sema_context& ctx, const t_exception& node) {
10041030
// Check that value of "message" annotation is
@@ -1679,6 +1705,8 @@ ast_validator standard_validator() {
16791705
validator.add_function_visitor(&validate_function_priority_annotation);
16801706
validator.add_function_visitor(ValidateAnnotationPositions{});
16811707
validator.add_function_visitor(&detail::validate_annotation_scopes<>);
1708+
validator.add_function_visitor(
1709+
&validate_function_exception_field_name_uniqueness);
16821710

16831711
validator.add_structured_definition_visitor(&validate_field_names_uniqueness);
16841712
validator.add_structured_definition_visitor(

third-party/thrift/src/thrift/compiler/test/compiler_test.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,20 @@ TEST(CompilerTest, mixin_field_name_uniqueness) {
680680
)");
681681
}
682682

683+
TEST(CompilerTest, function_exception_field_name_uniqueness) {
684+
check_compile(R"(
685+
exception Ex1 {}
686+
exception Ex2 {}
687+
688+
service S {
689+
void foo() throws (
690+
1: Ex1 e,
691+
2: Ex2 e, # expected-warning: Exception field `e` is already defined for `foo`.
692+
);
693+
}
694+
)");
695+
}
696+
683697
TEST(CompilerTest, annotation_positions) {
684698
check_compile(R"(
685699
struct Type {1: string name} (thrift.uri = "facebook.com/thrift/annotation/Type") # expected-warning: The annotation thrift.uri is deprecated. Please use @thrift.Uri instead.
@@ -2803,7 +2817,7 @@ TEST(CompilerTest, unresolved_struct_in_const) {
28032817
struct MyDeclaredStruct {
28042818
1: i64 some_field;
28052819
}
2806-
const MyDeclaredStruct X = MyUndeclaredStruct{};
2820+
const MyDeclaredStruct X = MyUndeclaredStruct{};
28072821
# expected-error-1: could not resolve type `MyUndeclaredStruct` (expected `main.MyDeclaredStruct`)
28082822
)";
28092823
check_compile(name_contents_map, "main.thrift");

0 commit comments

Comments
 (0)