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