0.6.3
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-2026 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 <deque>
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 VisitReturnStmt(clang::ReturnStmt *stmt);
79
80 bool VisitCallExpr(clang::CallExpr *expr);
81
82 bool VisitObjCMessageExpr(clang::ObjCMessageExpr *expr);
83
84 bool VisitObjCPropertyRefExpr(clang::ObjCPropertyRefExpr *expr);
85
86 bool TraverseVarDecl(clang::VarDecl *VD);
87
88 bool TraverseCoyieldExpr(clang::CoyieldExpr *expr);
89
90 bool TraverseCoawaitExpr(clang::CoawaitExpr *expr);
91
92 bool TraverseCoreturnStmt(clang::CoreturnStmt *stmt);
93
94 bool TraverseCallExpr(clang::CallExpr *expr);
95
96 bool TraverseObjCMessageExpr(clang::ObjCMessageExpr *expr);
97
98 bool TraverseCUDAKernelCallExpr(clang::CUDAKernelCallExpr *expr);
99
100 bool TraverseCXXMemberCallExpr(clang::CXXMemberCallExpr *expr);
101
102 bool TraverseCXXOperatorCallExpr(clang::CXXOperatorCallExpr *expr);
103
104 bool VisitCXXConstructExpr(clang::CXXConstructExpr *expr);
105
106 bool TraverseCXXConstructExpr(clang::CXXConstructExpr *expr);
107
108 bool TraverseCXXTemporaryObjectExpr(clang::CXXTemporaryObjectExpr *expr);
109
110 bool TraverseReturnStmt(clang::ReturnStmt *stmt);
111
112 bool VisitCoreturnStmt(clang::CoreturnStmt *stmt);
113
114 bool VisitCoyieldExpr(clang::CoyieldExpr *expr);
115
116 bool VisitCoawaitExpr(clang::CoawaitExpr *expr);
117
118 bool VisitLambdaExpr(clang::LambdaExpr *expr);
119
120 bool TraverseLambdaExpr(clang::LambdaExpr *expr);
121
122 bool TraverseCXXMethodDecl(clang::CXXMethodDecl *declaration);
123
124 bool TraverseObjCMethodDecl(clang::ObjCMethodDecl *declaration);
125
126 bool VisitObjCMethodDecl(clang::ObjCMethodDecl *declaration);
127
128 bool VisitCXXMethodDecl(clang::CXXMethodDecl *declaration);
129
130 bool TraverseCXXRecordDecl(clang::CXXRecordDecl *declaration);
131
132 bool VisitCXXRecordDecl(clang::CXXRecordDecl *declaration);
133
134 bool VisitClassTemplateDecl(clang::ClassTemplateDecl *declaration);
135
137 clang::ClassTemplateSpecializationDecl *declaration);
138
139 bool TraverseFunctionDecl(clang::FunctionDecl *declaration);
140
141 bool VisitFunctionDecl(clang::FunctionDecl *declaration);
142
143 bool TraverseFunctionTemplateDecl(clang::FunctionTemplateDecl *declaration);
144
146 clang::FunctionTemplateDecl *function_declaration);
147
149 clang::ObjCInterfaceDecl *interface_declaration);
150
151 bool VisitObjCProtocolDecl(clang::ObjCProtocolDecl *protocol_declaration);
152
153 bool TraverseCompoundStmt(clang::CompoundStmt *stmt);
154
155 bool TraverseIfStmt(clang::IfStmt *stmt);
156
157 bool TraverseWhileStmt(clang::WhileStmt *stmt);
158
159 bool TraverseDoStmt(clang::DoStmt *stmt);
160
161 bool TraverseForStmt(clang::ForStmt *stmt);
162
163 bool TraverseCXXForRangeStmt(clang::CXXForRangeStmt *stmt);
164
165 bool TraverseCXXTryStmt(clang::CXXTryStmt *stmt);
166
167 bool TraverseCXXCatchStmt(clang::CXXCatchStmt *stmt);
168
169 bool TraverseSwitchStmt(clang::SwitchStmt *stmt);
170
171 bool TraverseCaseStmt(clang::CaseStmt *stmt);
172
173 bool TraverseDefaultStmt(clang::DefaultStmt *stmt);
174
175 bool TraverseConditionalOperator(clang::ConditionalOperator *stmt);
176 /** @} */
177
178 /**
179 * @brief Get current call expression context reference
180 *
181 * @return Reference to the current call expression context
182 */
184
185 /**
186 * @brief Get current call expression context reference
187 *
188 * @return Reference to the current call expression context
189 */
190 const call_expression_context &context() const;
191
192 /**
193 * @brief Get participant by declaration
194 *
195 * @tparam T Participant type
196 * @param decl Clang entity declaration
197 * @return Optional reference to participant diagram element
198 */
199 template <typename T = model::participant>
201 {
202 assert(decl != nullptr);
203
204 auto unique_participant_id = get_unique_id(eid_t{decl->getID()});
205 if (!unique_participant_id.has_value())
206 return {};
207
208 return get_participant<T>(unique_participant_id.value());
209 }
210
211 /**
212 * @brief Get participant by declaration
213 *
214 * @tparam T Participant type
215 * @param decl Clang entity declaration
216 * @return Optional reference to participant diagram element
217 */
218 template <typename T = model::participant>
219 common::optional_ref<T> get_participant(const clang::Decl *decl) const
220 {
221 assert(decl != nullptr);
222
223 auto unique_participant_id = get_unique_id(eid_t{decl->getID()});
224 if (!unique_participant_id.has_value())
225 return {};
226
227 return get_participant<T>(unique_participant_id.value());
228 }
229
230 /**
231 * @brief Get participant by global element id
232 *
233 * @tparam T Participant type
234 * @param id Global element id
235 * @return Optional reference to participant diagram element
236 */
237 template <typename T = model::participant>
239 {
240 if (diagram().participants().find(id) == diagram().participants().end())
241 return {};
242
244 *(static_cast<T *>(diagram().participants().at(id).get())));
245 }
246
247 /**
248 * @brief Get participant by global element id
249 *
250 * @tparam T Participant type
251 * @param id Global element id
252 * @return Optional reference to participant diagram element
253 */
254 template <typename T = model::participant>
256 {
257 if (diagram().participants().find(id) == diagram().participants().end())
258 return {};
259
261 *(static_cast<T *>(diagram().participants().at(id).get())));
262 }
263
264 /**
265 * @brief Store the mapping from local clang entity id (obtained using
266 * getID()) method to clang-uml global id
267 *
268 * @todo Refactor to @ref ast_id_mapper
269 *
270 * @param local_id Local AST element id
271 * @param global_id Globa diagram element id
272 */
273 void set_unique_id(int64_t local_id, eid_t global_id);
274
275 /**
276 * @brief Retrieve the global `clang-uml` entity id based on the Clang
277 * local id
278 * @param local_id AST local element id
279 * @return Global diagram element id
280 */
281 std::optional<eid_t> get_unique_id(eid_t local_id) const;
282
283 /**
284 * @brief Finalize diagram model for this translation unit
285 */
286 void finalize();
287
288 std::unique_ptr<sequence_diagram::model::class_> create_element(
289 const clang::NamedDecl *decl) const;
290
291private:
292 /**
293 * @brief Get existing participant or fallback to provided model
294 *
295 * @param element_id ID of the participant to lookup
296 * @param fallback_model Fallback model to use if participant doesn't exist
297 * @return Reference to existing participant or fallback model
298 */
299 template <typename T = sequence_diagram::model::class_>
300 T &get_participant(eid_t element_id, T &fallback_model)
301 {
302 return diagram().get_participant<T>(element_id).has_value()
303 ? *diagram().get_participant<T>(element_id).get()
304 : fallback_model;
305 }
306
307 /**
308 * @brief Check if the diagram should include a declaration.
309 *
310 * @param decl Clang declaration.
311 * @return True, if the entity should be included in the diagram.
312 */
313 bool should_include(const clang::TagDecl *decl) const;
314
315 /**
316 * @brief Check if the diagram should include an ObjC declaration.
317 *
318 * @param decl Clang declaration.
319 * @return True, if the entity should be included in the diagram.
320 */
321 bool should_include(const clang::ObjCContainerDecl *decl) const;
322
323 /**
324 * @brief Check if the diagram should include a lambda expression.
325 *
326 * @param expr Lambda expression.
327 * @return True, if the expression should be included in the diagram.
328 */
329 bool should_include(const clang::LambdaExpr *expr) const;
330
331 /**
332 * @brief Check if the diagram should include a call expression.
333 *
334 * @param expr Call expression.
335 * @return True, if the expression should be included in the diagram.
336 */
337 bool should_include(const clang::CallExpr *expr) const;
338
339 /**
340 * @brief Check if the diagram should include an ObjC message expression.
341 *
342 * @param expr ObjC message expression.
343 * @return True, if the expression should be included in the diagram.
344 */
345 bool should_include(const clang::ObjCMessageExpr *expr) const;
346
347 /**
348 * @brief Check if the diagram should include a declaration.
349 *
350 * @param decl Clang declaration.
351 * @return True, if the entity should be included in the diagram.
352 */
353 bool should_include(const clang::CXXMethodDecl *decl) const;
354
355 bool should_include(const clang::ObjCMethodDecl *decl) const;
356
357 /**
358 * @brief Check if the diagram should include a declaration.
359 *
360 * @param decl Clang declaration.
361 * @return True, if the entity should be included in the diagram.
362 */
363 bool should_include(const clang::FunctionDecl *decl) const;
364
365 /**
366 * @brief Check if the diagram should include a declaration.
367 *
368 * @param decl Clang declaration.
369 * @return True, if the entity should be included in the diagram.
370 */
371 bool should_include(const clang::FunctionTemplateDecl *decl) const;
372
373 /**
374 * @brief Check if the diagram should include a declaration.
375 *
376 * @param decl Clang declaration.
377 * @return True, if the entity should be included in the diagram.
378 */
379 bool should_include(const clang::ClassTemplateDecl *decl) const;
380
381 std::unique_ptr<clanguml::sequence_diagram::model::class_>
382 create_objc_interface_model(clang::ObjCInterfaceDecl *cls);
383
384 std::unique_ptr<clanguml::sequence_diagram::model::class_>
385 create_objc_protocol_model(clang::ObjCProtocolDecl *cls);
386
387 std::unique_ptr<clanguml::sequence_diagram::model::class_>
388 create_class_model(clang::CXXRecordDecl *cls);
389
390 std::unique_ptr<clanguml::sequence_diagram::model::method>
391 create_method_model(clang::CXXMethodDecl *cls);
392
393 std::unique_ptr<clanguml::sequence_diagram::model::objc_method>
394 create_objc_method_model(clang::ObjCMethodDecl *cls);
395
396 std::unique_ptr<clanguml::sequence_diagram::model::method>
397 create_lambda_method_model(clang::CXXMethodDecl *cls);
398
399 std::unique_ptr<model::function_template>
400 build_function_template_instantiation(const clang::FunctionDecl &pDecl);
401
402 std::unique_ptr<model::function> build_function_model(
403 const clang::FunctionDecl &declaration);
404
405 std::unique_ptr<model::function_template> build_function_template(
406 const clang::FunctionTemplateDecl &declaration);
407
408 std::unique_ptr<model::class_> process_class_template_specialization(
409 clang::ClassTemplateSpecializationDecl *cls);
410
411 std::string simplify_system_template(const std::string &full_name) const;
412
413 /**
414 * @brief Assuming `cls` is a lambda, create it's full name.
415 *
416 * @note Currently, lambda names are generated using their source code
417 * location including file path, line and column to ensure
418 * unique names.
419 *
420 * @param cls Lambda declaration
421 * @return Full lambda unique name
422 */
423 std::string make_lambda_name(const clang::CXXRecordDecl *cls) const;
424
425 /**
426 * @brief Render lambda source location to string
427 *
428 * Returns exact source code location of the lambda expression in the form
429 * <filepath>:<line>:<column>.
430 *
431 * The filepath is relative to the `relative_to` config option.
432 *
433 * @param source_location Clang SourceLocation instance associated with
434 * lambda expression
435 * @return String representation of the location
436 */
437 std::string lambda_source_location(
438 const clang::SourceLocation &source_location) const;
439
440 /**
441 * @brief Check if template is a smart pointer
442 *
443 * @param primary_template Template declaration
444 * @return True, if template declaration is a smart pointer
445 */
446 bool is_smart_pointer(const clang::TemplateDecl *primary_template) const;
447
448 /**
449 * @brief Check, the callee is a template specialization
450 *
451 * @param dependent_member_expr Dependent member expression
452 * @return True, if the callee is a template specialization
453 */
455 const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const;
456
457 bool process_callee(clang::CallExpr *expr, model::message &m,
458 bool generated_message_from_comment);
459
460 /**
461 * @brief Handle CXX constructor call
462 *
463 * @param m Message model
464 * @param construct_expr CXX Construct expression
465 * @return True, if `m` contains a valid constructor call
466 */
468 model::message &m, const clang::CXXConstructExpr *construct_expr);
469
470 /**
471 * @brief Handle a operator call expression
472 *
473 * @param m Message model
474 * @param operator_call_expr Operator call expression
475 * @return True, if `m` contains now a valid call expression model
476 */
478 const clang::CXXOperatorCallExpr *operator_call_expr);
479
481 model::message &m, const clang::CUDAKernelCallExpr *cuda_call_expr);
482
483 /**
484 * @brief Handle a class method call expresion
485 *
486 * @param m Message model
487 * @param method_call_expr Operator call expression
488 * @return True, if `m` contains now a valid call expression model
489 */
491 model::message &m, const clang::CXXMemberCallExpr *method_call_expr);
492
494 model::message &m, const clang::ObjCMessageExpr *message_expr);
495
496 /**
497 * @brief Handle a class template method call expresion
498 *
499 * @param m Message model
500 * @param expr Class template method call expression
501 * @return True, if `m` contains now a valid call expression model
502 */
504 model::message &m, const clang::CallExpr *expr);
505
506 /**
507 * @brief Handle a function call expresion
508 *
509 * @param m Message model
510 * @param expr Function call expression
511 * @return True, if `m` contains now a valid call expression model
512 */
514 model::message &m, const clang::CallExpr *expr);
515
516 /**
517 * @brief Handle an unresolved lookup call expresion
518 *
519 * Unresolved lookup expression is a reference to a name which Clang was
520 * not able to look up during parsing but could not resolve to a
521 * specific declaration.
522 *
523 * @param m Message model
524 * @param expr Call expression
525 * @return True, if `m` contains now a valid call expression model
526 */
528 model::message &m, const clang::CallExpr *expr) const;
529
531 model::message &m, const clang::CallExpr *expr) const;
532
533 /**
534 * @brief Register a message model `m` with a call expression
535 *
536 * This is used to know whether a model for a specific call expression
537 * has already been created, but not yet added to the diagram.
538 *
539 * @param expr Call expresion
540 * @param m Message model
541 */
542 void push_message(clang::CallExpr *expr, model::message &&m);
543 void push_message(clang::CXXConstructExpr *expr, model::message &&m);
544 void push_message(clang::ObjCMessageExpr *expr, model::message &&m);
545 void push_message(clang::ReturnStmt *stmt, model::message &&m);
546 void push_message(clang::CoreturnStmt *stmt, model::message &&m);
547 void push_message(clang::CoyieldExpr *stmt, model::message &&m);
548 void push_message(clang::CoawaitExpr *stmt, model::message &&m);
549
550 /**
551 * @brief Move a message model to diagram.
552 *
553 * @param expr Call expression
554 */
555 void pop_message_to_diagram(clang::CallExpr *expr);
556 void pop_message_to_diagram(clang::CXXConstructExpr *expr);
557 void pop_message_to_diagram(clang::ObjCMessageExpr *expr);
558 void pop_message_to_diagram(clang::ReturnStmt *stmt);
559 void pop_message_to_diagram(clang::CoreturnStmt *stmt);
560 void pop_message_to_diagram(clang::CoyieldExpr *expr);
561 void pop_message_to_diagram(clang::CoawaitExpr *expr);
562
563 std::optional<std::pair<unsigned int, std::string>> get_expression_comment(
564 const clang::SourceManager &sm, const clang::ASTContext &context,
565 eid_t caller_id, const clang::Stmt *stmt);
566
567 /**
568 * @brief Initializes model message from comment call directive
569 *
570 * @param m Message instance
571 * @return True, if the comment associated with the call expression
572 * contained a call directive and it was parsed correctly.
573 */
575
576 /**
577 * @brief Get template builder reference
578 *
579 * @return Reference to 'template_builder' instance
580 */
582
584
586
588
590
591 void process_function_parameters(const clang::FunctionDecl &declaration,
592 model::function &method_model) const;
593
595
596 /**
597 * This is used to generate messages in proper order in case of nested call
598 * expressions (e.g. a(b(c(), d())), as they need to be added to the diagram
599 * sequence after the visitor leaves the call expression AST node
600 */
601 std::map<clang::CallExpr *, std::deque<model::message>>
603 std::map<clang::ReturnStmt *, model::message> return_stmt_message_map_;
604 std::map<clang::CoreturnStmt *, model::message> co_return_stmt_message_map_;
605 std::map<clang::CoyieldExpr *, model::message> co_yield_stmt_message_map_;
606 std::map<clang::CoawaitExpr *, model::message> co_await_stmt_message_map_;
607
608 std::map<clang::CXXConstructExpr *, model::message>
610 std::map<clang::ObjCMessageExpr *, model::message> objc_message_map_;
611
612 std::map<eid_t, std::unique_ptr<clanguml::sequence_diagram::model::class_>>
614
615 std::map<int64_t /* local anonymous struct id */,
616 std::tuple<std::string /* field name */, common::model::relationship_t,
619
620 std::map<eid_t, std::set<eid_t>> activity_callers_;
621
623 mutable std::set<const clang::Expr *>
625
626 mutable std::set<std::pair<int64_t, const clang::RawComment *>>
628
630};
631} // namespace clanguml::sequence_diagram::visitor