0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
translation_unit_visitor.h
Go to the documentation of this file.
1/**
2 * @file src/sequence_diagram/visitor/translation_unit_visitor.h
3 *
4 * Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18#pragma once
19
23#include "config/config.h"
25
26#include <clang/AST/Expr.h>
27#include <clang/AST/RecursiveASTVisitor.h>
28#include <clang/Basic/SourceManager.h>
29
30#include <stack>
31
33
35using common::model::template_parameter;
36
37std::string to_string(const clang::FunctionTemplateDecl *decl);
38
42
43/**
44 * @brief Sequence diagram translation unit visitor
45 *
46 * This class implements the `clang::RecursiveASTVisitor` interface
47 * for selected visitors relevant to generating sequence diagrams.
48 */
50 : public clang::RecursiveASTVisitor<translation_unit_visitor>,
52public:
55
58
59 /**
60 * @brief Constructor.
61 *
62 * @param sm Current source manager reference
63 * @param diagram Diagram model
64 * @param config Diagram configuration
65 */
66 translation_unit_visitor(clang::SourceManager &sm,
69
70 ~translation_unit_visitor() override = default;
71
72 /**
73 * \defgroup Implementation of ResursiveASTVisitor methods
74 * @{
75 */
77
78 bool VisitCallExpr(clang::CallExpr *expr);
79
80 bool TraverseVarDecl(clang::VarDecl *VD);
81
82 bool TraverseCallExpr(clang::CallExpr *expr);
83
84 bool TraverseCUDAKernelCallExpr(clang::CUDAKernelCallExpr *expr);
85
86 bool TraverseCXXMemberCallExpr(clang::CXXMemberCallExpr *expr);
87
88 bool TraverseCXXOperatorCallExpr(clang::CXXOperatorCallExpr *expr);
89
90 bool VisitCXXConstructExpr(clang::CXXConstructExpr *expr);
91
92 bool TraverseCXXConstructExpr(clang::CXXConstructExpr *expr);
93
94 bool TraverseCXXTemporaryObjectExpr(clang::CXXTemporaryObjectExpr *expr);
95
96 bool VisitLambdaExpr(clang::LambdaExpr *expr);
97
98 bool TraverseLambdaExpr(clang::LambdaExpr *expr);
99
100 bool TraverseCXXMethodDecl(clang::CXXMethodDecl *declaration);
101
102 bool VisitCXXMethodDecl(clang::CXXMethodDecl *declaration);
103
104 bool VisitCXXRecordDecl(clang::CXXRecordDecl *declaration);
105
106 bool VisitClassTemplateDecl(clang::ClassTemplateDecl *declaration);
107
109 clang::ClassTemplateSpecializationDecl *declaration);
110
111 bool TraverseFunctionDecl(clang::FunctionDecl *declaration);
112
113 bool VisitFunctionDecl(clang::FunctionDecl *declaration);
114
116 clang::FunctionTemplateDecl *function_declaration);
117
118 bool TraverseCompoundStmt(clang::CompoundStmt *stmt);
119
120 bool TraverseIfStmt(clang::IfStmt *stmt);
121
122 bool TraverseWhileStmt(clang::WhileStmt *stmt);
123
124 bool TraverseDoStmt(clang::DoStmt *stmt);
125
126 bool TraverseForStmt(clang::ForStmt *stmt);
127
128 bool TraverseCXXForRangeStmt(clang::CXXForRangeStmt *stmt);
129
130 bool TraverseCXXTryStmt(clang::CXXTryStmt *stmt);
131
132 bool TraverseCXXCatchStmt(clang::CXXCatchStmt *stmt);
133
134 bool TraverseSwitchStmt(clang::SwitchStmt *stmt);
135
136 bool TraverseCaseStmt(clang::CaseStmt *stmt);
137
138 bool TraverseDefaultStmt(clang::DefaultStmt *stmt);
139
140 bool TraverseConditionalOperator(clang::ConditionalOperator *stmt);
141 /** @} */
142
143 /**
144 * @brief Get current call expression context reference
145 *
146 * @return Reference to the current call expression context
147 */
149
150 /**
151 * @brief Get current call expression context reference
152 *
153 * @return Reference to the current call expression context
154 */
155 const call_expression_context &context() const;
156
157 /**
158 * @brief Get participant by declaration
159 *
160 * @tparam T Participant type
161 * @param decl Clang entity declaration
162 * @return Optional reference to participant diagram element
163 */
164 template <typename T = model::participant>
166 {
167 assert(decl != nullptr);
168
169 auto unique_participant_id = get_unique_id(eid_t{decl->getID()});
170 if (!unique_participant_id.has_value())
171 return {};
172
173 return get_participant<T>(unique_participant_id.value());
174 }
175
176 /**
177 * @brief Get participant by declaration
178 *
179 * @tparam T Participant type
180 * @param decl Clang entity declaration
181 * @return Optional reference to participant diagram element
182 */
183 template <typename T = model::participant>
184 common::optional_ref<T> get_participant(const clang::Decl *decl) const
185 {
186 assert(decl != nullptr);
187
188 auto unique_participant_id = get_unique_id(eid_t{decl->getID()});
189 if (!unique_participant_id.has_value())
190 return {};
191
192 return get_participant<T>(unique_participant_id.value());
193 }
194
195 /**
196 * @brief Get participant by global element id
197 *
198 * @tparam T Participant type
199 * @param id Global element id
200 * @return Optional reference to participant diagram element
201 */
202 template <typename T = model::participant>
204 {
205 if (diagram().participants().find(id) == diagram().participants().end())
206 return {};
207
209 *(static_cast<T *>(diagram().participants().at(id).get())));
210 }
211
212 /**
213 * @brief Get participant by global element id
214 *
215 * @tparam T Participant type
216 * @param id Global element id
217 * @return Optional reference to participant diagram element
218 */
219 template <typename T = model::participant>
221 {
222 if (diagram().participants().find(id) == diagram().participants().end())
223 return {};
224
226 *(static_cast<T *>(diagram().participants().at(id).get())));
227 }
228
229 /**
230 * @brief Store the mapping from local clang entity id (obtained using
231 * getID()) method to clang-uml global id
232 *
233 * @todo Refactor to @ref ast_id_mapper
234 *
235 * @param local_id Local AST element id
236 * @param global_id Globa diagram element id
237 */
238 void set_unique_id(int64_t local_id, eid_t global_id);
239
240 /**
241 * @brief Retrieve the global `clang-uml` entity id based on the Clang
242 * local id
243 * @param local_id AST local element id
244 * @return Global diagram element id
245 */
246 std::optional<eid_t> get_unique_id(eid_t local_id) const;
247
248 /**
249 * @brief Finalize diagram model for this translation unit
250 */
251 void finalize();
252
253 std::unique_ptr<sequence_diagram::model::class_> create_element(
254 const clang::NamedDecl *decl) const;
255
256private:
257 /**
258 * @brief Check if the diagram should include a declaration.
259 *
260 * @param decl Clang declaration.
261 * @return True, if the entity should be included in the diagram.
262 */
263 bool should_include(const clang::TagDecl *decl) const;
264
265 /**
266 * @brief Check if the diagram should include a lambda expression.
267 *
268 * @param expr Lambda expression.
269 * @return True, if the expression should be included in the diagram.
270 */
271 bool should_include(const clang::LambdaExpr *expr) const;
272
273 /**
274 * @brief Check if the diagram should include a call expression.
275 *
276 * @param expr Call expression.
277 * @return True, if the expression should be included in the diagram.
278 */
279 bool should_include(const clang::CallExpr *expr) const;
280
281 /**
282 * @brief Check if the diagram should include a declaration.
283 *
284 * @param decl Clang declaration.
285 * @return True, if the entity should be included in the diagram.
286 */
287 bool should_include(const clang::CXXMethodDecl *decl) const;
288
289 /**
290 * @brief Check if the diagram should include a declaration.
291 *
292 * @param decl Clang declaration.
293 * @return True, if the entity should be included in the diagram.
294 */
295 bool should_include(const clang::FunctionDecl *decl) const;
296
297 /**
298 * @brief Check if the diagram should include a declaration.
299 *
300 * @param decl Clang declaration.
301 * @return True, if the entity should be included in the diagram.
302 */
303 bool should_include(const clang::FunctionTemplateDecl *decl) const;
304
305 /**
306 * @brief Check if the diagram should include a declaration.
307 *
308 * @param decl Clang declaration.
309 * @return True, if the entity should be included in the diagram.
310 */
311 bool should_include(const clang::ClassTemplateDecl *decl) const;
312
313 std::unique_ptr<clanguml::sequence_diagram::model::class_>
314 create_class_model(clang::CXXRecordDecl *cls);
315
316 std::unique_ptr<clanguml::sequence_diagram::model::method>
317 create_method_model(clang::CXXMethodDecl *cls);
318
319 std::unique_ptr<clanguml::sequence_diagram::model::method>
320 create_lambda_method_model(clang::CXXMethodDecl *cls);
321
322 std::unique_ptr<model::function_template>
323 build_function_template_instantiation(const clang::FunctionDecl &pDecl);
324
325 std::unique_ptr<model::function> build_function_model(
326 const clang::FunctionDecl &declaration);
327
328 std::unique_ptr<model::function_template> build_function_template(
329 const clang::FunctionTemplateDecl &declaration);
330
331 std::unique_ptr<model::class_> process_class_template_specialization(
332 clang::ClassTemplateSpecializationDecl *cls);
333
334 std::string simplify_system_template(const std::string &full_name) const;
335
336 /**
337 * @brief Assuming `cls` is a lambda, create it's full name.
338 *
339 * @note Currently, lambda names are generated using their source code
340 * location including file path, line and column to ensure
341 * unique names.
342 *
343 * @param cls Lambda declaration
344 * @return Full lambda unique name
345 */
346 std::string make_lambda_name(const clang::CXXRecordDecl *cls) const;
347
348 /**
349 * @brief Render lambda source location to string
350 *
351 * Returns exact source code location of the lambda expression in the form
352 * <filepath>:<line>:<column>.
353 *
354 * The filepath is relative to the `relative_to` config option.
355 *
356 * @param source_location Clang SourceLocation instance associated with
357 * lambda expression
358 * @return String representation of the location
359 */
360 std::string lambda_source_location(
361 const clang::SourceLocation &source_location) const;
362
363 /**
364 * @brief Check if template is a smart pointer
365 *
366 * @param primary_template Template declaration
367 * @return True, if template declaration is a smart pointer
368 */
369 bool is_smart_pointer(const clang::TemplateDecl *primary_template) const;
370
371 /**
372 * @brief Check, the callee is a template specialization
373 *
374 * @param dependent_member_expr Dependent member expression
375 * @return True, if the callee is a template specialization
376 */
378 const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const;
379
380 /**
381 * @brief Handle CXX constructor call
382 *
383 * @param m Message model
384 * @param construct_expr CXX Construct expression
385 * @return True, if `m` contains a valid constructor call
386 */
388 model::message &m, const clang::CXXConstructExpr *construct_expr);
389
390 /**
391 * @brief Handle a operator call expression
392 *
393 * @param m Message model
394 * @param operator_call_expr Operator call expression
395 * @return True, if `m` contains now a valid call expression model
396 */
398 const clang::CXXOperatorCallExpr *operator_call_expr);
399
401 model::message &m, const clang::CUDAKernelCallExpr *cuda_call_expr);
402
403 /**
404 * @brief Handle a class method call expresion
405 *
406 * @param m Message model
407 * @param method_call_expr Operator call expression
408 * @return True, if `m` contains now a valid call expression model
409 */
411 model::message &m, const clang::CXXMemberCallExpr *method_call_expr);
412
413 /**
414 * @brief Handle a class template method call expresion
415 *
416 * @param m Message model
417 * @param expr Class template method call expression
418 * @return True, if `m` contains now a valid call expression model
419 */
421 model::message &m, const clang::CallExpr *expr);
422
423 /**
424 * @brief Handle a function call expresion
425 *
426 * @param m Message model
427 * @param expr Function call expression
428 * @return True, if `m` contains now a valid call expression model
429 */
431 model::message &m, const clang::CallExpr *expr);
432
433 /**
434 * @brief Handle an unresolved lookup call expresion
435 *
436 * Unresolved lookup expression is a reference to a name which Clang was
437 * not able to look up during parsing but could not resolve to a
438 * specific declaration.
439 *
440 * @param m Message model
441 * @param expr Call expression
442 * @return True, if `m` contains now a valid call expression model
443 */
445 model::message &m, const clang::CallExpr *expr) const;
446
448 model::message &m, const clang::CallExpr *expr) const;
449
450 /**
451 * @brief Register a message model `m` with a call expression
452 *
453 * This is used to know whether a model for a specific call expression
454 * has already been created, but not yet added to the diagram.
455 *
456 * @param expr Call expresion
457 * @param m Message model
458 */
459 void push_message(clang::CallExpr *expr, model::message &&m);
460 void push_message(clang::CXXConstructExpr *expr, model::message &&m);
461
462 /**
463 * @brief Move a message model to diagram.
464 *
465 * @param expr Call expression
466 */
467 void pop_message_to_diagram(clang::CallExpr *expr);
468 void pop_message_to_diagram(clang::CXXConstructExpr *expr);
469
470 std::optional<std::pair<unsigned int, std::string>> get_expression_comment(
471 const clang::SourceManager &sm, const clang::ASTContext &context,
472 eid_t caller_id, const clang::Stmt *stmt);
473
474 /**
475 * @brief Initializes model message from comment call directive
476 *
477 * @param m Message instance
478 * @return True, if the comment associated with the call expression
479 * contained a call directive and it was parsed correctly.
480 */
482
483 /**
484 * @brief Get template builder reference
485 *
486 * @return Reference to 'template_builder' instance
487 */
489
491
493
495
496 /**
497 * This is used to generate messages in proper order in case of nested call
498 * expressions (e.g. a(b(c(), d())), as they need to be added to the diagram
499 * sequence after the visitor leaves the call expression AST node
500 */
501 std::map<clang::CallExpr *, model::message> call_expr_message_map_;
502 std::map<clang::CXXConstructExpr *, model::message>
504
505 std::map<eid_t, std::unique_ptr<clanguml::sequence_diagram::model::class_>>
507
508 std::map<int64_t /* local anonymous struct id */,
509 std::tuple<std::string /* field name */, common::model::relationship_t,
512
514 mutable std::set<const clang::Expr *>
516
517 mutable std::set<std::pair<int64_t, const clang::RawComment *>>
519
521};
522} // namespace clanguml::sequence_diagram::visitor