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 #pragma once
18 
19 #include <cassert>
20 #include <exception>
21 #include <functional>
22 #include <type_traits>
23 #include <vector>
24 
25 #include <thrift/compiler/ast/detail/ast_visitor.h>
26 #include <thrift/compiler/ast/t_const.h>
27 #include <thrift/compiler/ast/t_enum.h>
28 #include <thrift/compiler/ast/t_enum_value.h>
29 #include <thrift/compiler/ast/t_exception.h>
30 #include <thrift/compiler/ast/t_field.h>
31 #include <thrift/compiler/ast/t_function.h>
32 #include <thrift/compiler/ast/t_interaction.h>
33 #include <thrift/compiler/ast/t_interface.h>
34 #include <thrift/compiler/ast/t_list.h>
35 #include <thrift/compiler/ast/t_map.h>
36 #include <thrift/compiler/ast/t_node.h>
37 #include <thrift/compiler/ast/t_program.h>
38 #include <thrift/compiler/ast/t_service.h>
39 #include <thrift/compiler/ast/t_set.h>
40 #include <thrift/compiler/ast/t_sink.h>
41 #include <thrift/compiler/ast/t_stream.h>
42 #include <thrift/compiler/ast/t_struct.h>
43 #include <thrift/compiler/ast/t_typedef.h>
44 #include <thrift/compiler/ast/t_union.h>
45 
46 namespace apache {
47 namespace thrift {
48 namespace compiler {
49 
50 // A list of visitor that accept the given arguments.
51 template <typename... Args>
52 using visitor_list = std::vector<std::function<void(Args...)>>;
53 
54 template <bool is_const, typename... Args>
55 class basic_ast_visitor;
56 
57 // A class that can traverse ast nodes, invoking registered visitors for each
58 // node visited.
59 //
60 // Visits AST nodes in 'preorder', visiting the parent node before children
61 // nodes.
62 //
63 // For each concrete node type, provides the following functions:
64 // - an operator() overload for visiting the node:
65 //     void operator()(args..., t_{name}&) const;
66 // - a function to add a node-specific visitor:
67 //     void add_{name}_visitor(std::function<void(args..., t_{name}&)>);
68 //
69 // Also provides helper functions for registering a visitor for multiple node
70 // types. For example: all interface, structured_declaration, and
71 // declaration visitors.
72 using ast_visitor = basic_ast_visitor<false>;
73 
74 // Same as ast_visitor, except traverse a const AST.
75 using const_ast_visitor = basic_ast_visitor<true>;
76 
77 // A class that can traverse an AST, calling registered visitors.
78 // See ast_visitor.
79 template <bool is_const, typename... Args>
80 class basic_ast_visitor {
81   template <typename N>
82   using node_type = ast_detail::node_type<is_const, N>;
83 
84  public:
85   // Adds visitor for all interface node types.
86   //
87   // For example: t_service and t_interaction.
88   template <typename V>
add_interface_visitor(V && visitor)89   void add_interface_visitor(V&& visitor) {
90     add_service_visitor(visitor);
91     add_interaction_visitor(std::forward<V>(visitor));
92   }
93 
94   // Adds a visitor for all structured IDL definition node types.
95   //
96   // For example: t_struct, t_union, and t_exception.
97   // Does not include other t_structured nodes like t_paramlist.
98   template <typename V>
add_structured_definition_visitor(V && visitor)99   void add_structured_definition_visitor(V&& visitor) {
100     add_struct_visitor(visitor);
101     add_union_visitor(visitor);
102     add_exception_visitor(std::forward<V>(visitor));
103   }
104 
105   // Adds a visitor for all IDL definition node types.
106   template <typename V>
add_definition_visitor(V && visitor)107   void add_definition_visitor(V&& visitor) {
108     add_interface_visitor(visitor);
109     add_function_visitor(visitor);
110 
111     add_structured_definition_visitor(visitor);
112     add_field_visitor(visitor);
113 
114     add_enum_visitor(visitor);
115     add_enum_value_visitor(visitor);
116     add_const_visitor(visitor);
117 
118     add_typedef_visitor(std::forward<V>(visitor));
119   }
120 
121   template <typename V>
add_container_visitor(V && visitor)122   void add_container_visitor(V&& visitor) {
123     add_set_visitor(visitor);
124     add_list_visitor(visitor);
125     add_map_visitor(std::forward<V>(visitor));
126   }
127 
128   template <typename V>
add_type_instantiation_visitor(V && visitor)129   void add_type_instantiation_visitor(V&& visitor) {
130     add_container_visitor(visitor);
131     add_sink_visitor(visitor);
132     add_stream_response_visitor(std::forward<V>(visitor));
133   }
134 
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(program)135   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(program) {
136     begin_visit(program_visitors_, node, args...);
137     visit_children_ptrs(node.services(), args...);
138     visit_children_ptrs(node.interactions(), args...);
139     // TODO(afuller): Split structs and unions in t_program accessors.
140     for (auto* struct_or_union : node.structs()) {
141       if (auto* tunion = ast_detail::as<t_union>(struct_or_union)) {
142         this->operator()(args..., *tunion);
143       } else {
144         this->operator()(args..., *struct_or_union);
145       }
146     }
147     visit_children_ptrs(node.exceptions(), args...);
148     visit_children_ptrs(node.typedefs(), args...);
149     visit_children_ptrs(node.enums(), args...);
150     visit_children_ptrs(node.consts(), args...);
151     for (auto& type_inst : node.type_instantiations()) {
152       if (auto* set_node = ast_detail::as<t_set>(&type_inst)) {
153         visit_child(*set_node, args...);
154       } else if (auto* list_node = ast_detail::as<t_list>(&type_inst)) {
155         visit_child(*list_node, args...);
156       } else if (auto* map_node = ast_detail::as<t_map>(&type_inst)) {
157         visit_child(*map_node, args...);
158       } else if (auto* sink_node = ast_detail::as<t_sink>(&type_inst)) {
159         visit_child(*sink_node, args...);
160       } else if (
161           auto* stream_node = ast_detail::as<t_stream_response>(&type_inst)) {
162         visit_child(*stream_node, args...);
163       } else {
164         std::terminate(); // Should be unreachable.
165       }
166     }
167     end_visit(node, args...);
168   }
169 
170   // Interfaces
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(service)171   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(service) {
172     assert(typeid(node) == typeid(service_type)); // Must actually be a service.
173     begin_visit(service_visitors_, node, args...);
174     visit_children(node.functions(), args...);
175     end_visit(node, args...);
176   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(interaction)177   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(interaction) {
178     begin_visit(interaction_visitors_, node, args...);
179     visit_children(node.functions(), args...);
180     end_visit(node, args...);
181   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(function)182   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(function) {
183     begin_visit(function_visitors_, node, args...);
184     if (node.exceptions() != nullptr) {
185       visit_child(*node.exceptions(), args...);
186     }
187     end_visit(node, args...);
188   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(throws)189   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(throws) {
190     begin_visit(throws_visitors_, node, args...);
191     end_visit(node, args...);
192   }
193 
194   // Types
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(struct)195   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(struct) {
196     assert(typeid(node) == typeid(struct_type)); // Must actually be a struct.
197     begin_visit(struct_visitors_, node, args...);
198     visit_children(node.fields(), args...);
199     end_visit(node, args...);
200   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(union)201   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(union) {
202     begin_visit(union_visitors_, node, args...);
203     visit_children(node.fields(), args...);
204     end_visit(node, args...);
205   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(exception)206   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(exception) {
207     begin_visit(exception_visitors_, node, args...);
208     visit_children(node.fields(), args...);
209     end_visit(node, args...);
210   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(field)211   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(field) {
212     begin_visit(field_visitors_, node, args...);
213     end_visit(node, args...);
214   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(enum)215   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(enum) {
216     begin_visit(enum_visitors_, node, args...);
217     visit_children(node.values(), args...);
218     end_visit(node, args...);
219   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(enum_value)220   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(enum_value) {
221     begin_visit(enum_value_visitors_, node, args...);
222     end_visit(node, args...);
223   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(const)224   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(const) {
225     begin_visit(const_visitors_, node, args...);
226     end_visit(node, args...);
227   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(typedef)228   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(typedef) {
229     begin_visit(typedef_visitors_, node, args...);
230     end_visit(node, args...);
231   }
232 
233   // Templated type instantiations.
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(set)234   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(set) {
235     begin_visit(set_visitors_, node, args...);
236     end_visit(node, args...);
237   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(list)238   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(list) {
239     begin_visit(list_visitors_, node, args...);
240     end_visit(node, args...);
241   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(map)242   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(map) {
243     begin_visit(map_visitors_, node, args...);
244     end_visit(node, args...);
245   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(sink)246   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(sink) {
247     begin_visit(sink_visitors_, node, args...);
248     if (node.sink_exceptions() != nullptr) {
249       visit_child(*node.sink_exceptions(), args...);
250     }
251     if (node.final_response_exceptions() != nullptr) {
252       visit_child(*node.final_response_exceptions(), args...);
253     }
254     end_visit(node, args...);
255   }
FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(stream_response)256   FBTHRIFT_AST_DETAIL_AST_VISITOR_NODE_T_(stream_response) {
257     begin_visit(stream_response_visitors_, node, args...);
258     if (node.exceptions() != nullptr) {
259       visit_child(*node.exceptions(), args...);
260     }
261     end_visit(node, args...);
262   }
263 
264  private:
265   template <typename N>
begin_visit(const visitor_list<Args...,N &> & visitors,N & node,Args...args)266   static void begin_visit(
267       const visitor_list<Args..., N&>& visitors, N& node, Args... args) {
268     // TODO(afuller): Replace with c++17 folding syntax when available.
269     using _ = int[];
270     void(_{0, (ast_detail::begin_visit(node, args), 0)...});
271 
272     for (auto&& visitor : visitors) {
273       visitor(args..., node);
274     }
275   }
276 
277   template <typename N>
end_visit(N & node,Args...args)278   static void end_visit(N& node, Args... args) {
279     // TODO(afuller): Replace with c++17 folding syntax when available.
280     auto cb = [&](auto& arg) {
281       return make_scope_guard([&] { ast_detail::end_visit(node, arg); });
282     };
283     using _ = int[];
284     void(_{0, (cb(args), 0)...});
285   }
286 
287   template <typename C>
visit_child(C & child,Args...args)288   void visit_child(C& child, Args... args) const {
289     operator()(args..., child);
290   }
291 
292   template <typename C>
visit_children(const C & children,Args...args)293   void visit_children(const C& children, Args... args) const {
294     for (auto&& child : children) {
295       operator()(args..., child);
296     }
297   }
298   template <typename C>
visit_children_ptrs(const C & children,Args...args)299   void visit_children_ptrs(const C& children, Args... args) const {
300     for (auto* child : children) {
301       operator()(args..., *child);
302     }
303   }
304 };
305 
306 template <bool is_const, typename N = t_node>
307 class basic_visitor_context {
308   using node_type = ast_detail::node_type<is_const, N>;
309 
310  public:
311   // The first node visited.
root()312   node_type* root() const noexcept {
313     assert(!context_.empty());
314     return context_.empty() ? nullptr : context_.front();
315   }
316 
317   // The node currently being visited, or nullptr.
current()318   node_type* current() const noexcept {
319     assert(!context_.empty());
320     return context_.empty() ? nullptr : context_.back();
321   }
322 
323   // The parent of the current node, or nullptr.
parent()324   node_type* parent() const noexcept {
325     return context_.size() < 2 ? nullptr : context_[context_.size() - 2];
326   }
327 
begin_visit(node_type & node)328   void begin_visit(node_type& node) { context_.emplace_back(&node); }
end_visit(node_type &)329   void end_visit(node_type&) { context_.pop_back(); }
330 
331  private:
332   std::vector<node_type*> context_;
333 };
334 
335 using visitor_context = basic_visitor_context<false>;
336 using const_visitor_context = basic_visitor_context<true>;
337 
338 } // namespace compiler
339 } // namespace thrift
340 } // namespace apache
341