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/class_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
24#include "common/model/enums.h"
28#include "config/config.h"
29
30#include <clang/AST/RecursiveASTVisitor.h>
31#include <clang/Basic/SourceManager.h>
32
33#include <deque>
34#include <functional>
35#include <map>
36#include <memory>
37#include <string>
38
40
63
67
68/**
69 * @brief Class diagram translation unit visitor
70 *
71 * This class implements the `clang::RecursiveASTVisitor` interface
72 * for selected visitors relevant to generating class diagrams.
73 */
75 : public clang::RecursiveASTVisitor<translation_unit_visitor>,
77public:
80
82
83 /**
84 * @brief Constructor.
85 *
86 * @param sm Current source manager reference
87 * @param diagram Diagram model
88 * @param config Diagram configuration
89 */
90 explicit translation_unit_visitor(clang::SourceManager &sm,
93
94 /**
95 * \defgroup Implementation of ResursiveASTVisitor methods
96 * @{
97 */
98 bool shouldVisitTemplateInstantiations() const { return false; }
99
100 bool shouldVisitImplicitCode() const { return false; }
101
102 virtual bool VisitNamespaceDecl(clang::NamespaceDecl *ns);
103
104 virtual bool VisitRecordDecl(clang::RecordDecl *D);
105
106 virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *d);
107
108 virtual bool VisitTypedefDecl(clang::TypedefDecl *decl);
109
110 virtual bool VisitEnumDecl(clang::EnumDecl *e);
111
112 virtual bool VisitClassTemplateDecl(
113 clang::ClassTemplateDecl *class_template_declaration);
114
116 clang::ClassTemplateSpecializationDecl *cls);
117
118 virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls);
119
120 virtual bool TraverseConceptDecl(clang::ConceptDecl *cpt);
121
122 virtual bool VisitObjCCategoryDecl(clang::ObjCCategoryDecl *decl);
123
124 virtual bool VisitObjCProtocolDecl(clang::ObjCProtocolDecl *decl);
125
126 virtual bool VisitObjCInterfaceDecl(clang::ObjCInterfaceDecl *decl);
127 /** @} */
128
129 /**
130 * @brief Finalize diagram model
131 *
132 * This method is called after the entire AST has been visited by this
133 * visitor. It is used to perform necessary post processing on the diagram
134 * (e.g. resolve translation unit local element ID's into global ID's based
135 * on elements full names).
136 */
137 void finalize();
138
139 /**
140 * @brief Add class (or template class) to the diagram.
141 *
142 * @param c Class model
143 */
144 void add_class(std::unique_ptr<class_> &&c);
145
146 /**
147 * @brief Add enum to the diagram.
148 *
149 * @param e Enum model
150 */
151 void add_enum(std::unique_ptr<enum_> &&e);
152
153 /**
154 * @brief Add concept to the diagram.
155 *
156 * @param c Concept model
157 */
158 void add_concept(std::unique_ptr<concept_> &&c);
159
160 void add_objc_interface(std::unique_ptr<objc_interface> &&c);
161
163 std::unique_ptr<common::model::template_element> element) override;
164
165 std::unique_ptr<class_> create_element(const clang::NamedDecl *decl) const;
166
168 common::model::template_element &template_instantiation_base,
169 const std::string &full_name, eid_t templated_decl_id);
170
171private:
172 /**
173 * @brief Create class element model from class declaration
174 *
175 * @param cls Class declaration
176 * @return Class diagram element model
177 */
178 std::unique_ptr<clanguml::class_diagram::model::class_>
179 create_class_declaration(clang::CXXRecordDecl *cls);
180
181 /**
182 * @brief Create enum element model from enum (e.g. struct) declaration
183 *
184 * @param rec Enum declaration
185 * @return Enum diagram element model
186 */
187 std::unique_ptr<clanguml::class_diagram::model::enum_>
189 const clang::EnumDecl *enm, const clang::TypedefDecl *typedef_decl);
190
191 /**
192 * @brief Create class element model from record (e.g. struct) declaration
193 *
194 * @param rec Record declaration
195 * @return Class diagram element model
196 */
197 std::unique_ptr<clanguml::class_diagram::model::class_>
198 create_record_declaration(clang::RecordDecl *rec);
199
200 /**
201 * @brief Create class element model from Objective-C protocol
202 *
203 * @param decl Objective-C protocol declaration
204 * @return Class diagram element model
205 */
206 std::unique_ptr<clanguml::class_diagram::model::objc_interface>
207 create_objc_protocol_declaration(clang::ObjCProtocolDecl *decl);
208
209 /**
210 * @brief Create class element model from Objective-C interface
211 *
212 * @param decl Objective-C protocol declaration
213 * @return Class diagram element model
214 */
215 std::unique_ptr<clanguml::class_diagram::model::objc_interface>
216 create_objc_interface_declaration(clang::ObjCInterfaceDecl *decl);
217
218 /**
219 * @brief Create class element model from Objective-C category
220 *
221 * @param decl Objective-C protocol declaration
222 * @return Class diagram element model
223 */
224 std::unique_ptr<clanguml::class_diagram::model::objc_interface>
225 create_objc_category_declaration(clang::ObjCCategoryDecl *decl);
226
227 /**
228 * @brief Create concept element model from concept declaration
229 * @param cpt Concept declaration
230 * @return Concept diagram element model
231 */
232 std::unique_ptr<clanguml::class_diagram::model::concept_>
233 create_concept_declaration(clang::ConceptDecl *cpt);
234
235 /**
236 * @brief Process class declaration
237 *
238 * @param cls Class declaration
239 * @param c Class diagram element returned from `create_class_declaration`
240 */
241 void process_class_declaration(const clang::CXXRecordDecl &cls,
243
244 /**
245 * @brief Process Objective-C category declaration
246 *
247 * @param cls Objective-C category declaration
248 * @param c Class diagram element returned from
249 * `create_objc_category_declaration`
250 */
252 const clang::ObjCCategoryDecl &cls, objc_interface &c);
253
254 /**
255 * @brief Process Objective-C protocol declaration
256 *
257 * @param cls Objective-C protocol declaration
258 * @param c Class diagram element returned from
259 * `create_objc_protocol_declaration`
260 */
262 const clang::ObjCProtocolDecl &cls, objc_interface &c);
263
264 /**
265 * @brief Process Objective-C interface declaration
266 *
267 * @param cls Objective-C interface declaration
268 * @param c Class diagram element returned from
269 * `create_objc_interface_declaration`
270 */
272 const clang::ObjCInterfaceDecl &cls, objc_interface &c);
273
274 /**
275 * @brief Process class declaration bases (parents), if any
276 *
277 * @param cls Class declaration
278 * @param c Class diagram element model
279 */
280 void process_class_bases(const clang::CXXRecordDecl *cls,
282
283 /**
284 * @brief Process class children elements (members and methods)
285 *
286 * @param cls Class declaration
287 * @param c Class diagram element model
288 */
289 void process_class_children(const clang::CXXRecordDecl *cls,
291
292 /**
293 * @brief Process class or record data members
294 * @param cls Class declaration
295 * @param c Class diagram element model
296 */
297 void process_record_members(const clang::RecordDecl *cls, class_ &c);
298
299 /**
300 * @brief Process class template specialization/instantiation
301 *
302 * @param cls Class template specialization declaration
303 * @return Class diagram element model
304 */
305 std::unique_ptr<clanguml::class_diagram::model::class_>
307 clang::ClassTemplateSpecializationDecl *cls);
308
309 /**
310 * @brief Process template specialization children (members and methods)
311 * @param cls Class template specialization declaration
312 * @param c Class diagram element model
313 */
315 const clang::ClassTemplateSpecializationDecl *cls, class_ &c);
316
317 /**
318 * @brief Process class method
319 *
320 * @param mf Method declaration
321 * @param c Class diagram element model
322 */
323 void process_method(const clang::CXXMethodDecl &mf,
325
326 /**
327 * @brief Process Objective-C method
328 *
329 * @param mf Method declaration
330 * @param c Class diagram element model
331 */
333 const clang::ObjCMethodDecl &mf, objc_interface &c);
334
335 /**
336 * @brief Process class method properties
337 * @param mf Method declaration
338 * @param c Class diagram element model
339 * @param method_name Method name
340 * @param method Method model
341 */
342 void process_method_properties(const clang::CXXMethodDecl &mf,
343 const class_ &c, const std::string &method_name,
344 class_method &method) const;
345
346 /**
347 * @brief Process class template method
348 *
349 * @param mf Method declaration
350 * @param c Class diagram element model
351 */
352 void process_template_method(const clang::FunctionTemplateDecl &mf,
354
355 /**
356 * @brief Process class static data member
357 *
358 * @param field_declaration Static data member declaration
359 * @param c Class diagram element model
360 */
361 void process_static_field(const clang::VarDecl &field_declaration,
363
364 /**
365 * @brief Process class data member
366 *
367 * @param field_declaration Data member declaration
368 * @param c Class diagram element model
369 */
370 void process_field(const clang::FieldDecl &field_declaration,
372
373 /**
374 * @brief Process Objective-C data member
375 *
376 * @param field_declaration Data member declaration
377 * @param c Class diagram element model
378 */
379 void process_objc_ivar(const clang::ObjCIvarDecl &ivar, objc_interface &c);
380
381 /**
382 * @brief Process Objective-C class base
383 *
384 * @param cls Objective-C interface declaration
385 * @param c Class diagram element model
386 */
388 const clang::ObjCInterfaceDecl &cls, objc_interface &c);
389
390 /**
391 * @brief Process function/method parameter
392 *
393 * @param param Parameter declaration
394 * @param method Class method model
395 * @param c Class diagram element model
396 * @param template_parameter_names Ignored
397 */
398 void process_function_parameter(const clang::ParmVarDecl &param,
399 class_method &method, class_ &c,
400 const std::set<std::string> &template_parameter_names = {});
401
402 /**
403 * @brief Process Objective-C class method parameter
404 *
405 * @param param Parameter declaration
406 * @param method Class method model
407 * @param c Class diagram element model
408 * @param template_parameter_names Ignored
409 */
410 void process_objc_method_parameter(const clang::ParmVarDecl &param,
411 objc_method &method, objc_interface &c);
412
413 /**
414 * @brief Process class friend
415 *
416 * @param f Friend declaration
417 * @param c Class diagram element model
418 */
419 void process_friend(const clang::FriendDecl &f, class_ &c);
420
421 /**
422 * @brief Find relationships in a specific type
423 *
424 * @param decl Source declaration from which this relationship originates
425 * @param type Type to search for relationships
426 * @param relationship_hint Default relationship type to infer from this
427 * type
428 * @return True, if any relationships were found
429 */
430 bool find_relationships(const clang::Decl *decl,
431 const clang::QualType &type, found_relationships_t & /*relationships*/,
433
434 /**
435 * @brief Add relationships from relationship list to a diagram element
436 * model
437 *
438 * This method takes a list of relationships whose originating element
439 * is class `c` and adds them to it, ignoring any duplicates and skipping
440 * relationships that should be excluded from the diagram.
441 *
442 * @param c Diagram element model
443 * @param field Class member model
444 * @param relationships List of found relationships
445 * @param break_on_first_aggregation Stop adding relatinoships, after first
446 * aggregation is found
447 */
449 const found_relationships_t &relationships,
450 bool break_on_first_aggregation = false);
451
452 /**
453 * @brief Process record parent element (e.g. for nested classes)
454 *
455 * This method handles nested classes or structs.
456 *
457 * @param cls Record declaration
458 * @param c Class diagram element model
459 * @param ns Package in the diagram to which the class `c` should belong
460 */
462 clang::RecordDecl *cls, class_ &c, const namespace_ &ns);
463
464 /**
465 * @brief Find relationships in function parameter
466 *
467 * @param c Class diagram element model
468 * @param atsp `auto` type
469 */
471 model::class_ &c, const clang::AutoType *atsp);
472
473 /**
474 * @brief Find relationships in concept constraint expression
475 *
476 * @param c Diagram element model (concept)
477 * @param expr Concept constraint expression
478 */
480 clanguml::common::model::element &c, const clang::Expr *expr);
481
482 /**
483 * @brief Register incomplete forward declaration to be updated later
484 */
486
487 /**
488 * @brief Replace any AST local ids in diagram elements with global ones
489 *
490 * Not all elements global ids can be set in relationships during
491 * traversal of the AST. In such cases, a local id (obtained from `getID()`)
492 * and at after the traversal is complete, the id is replaced with the
493 * global diagram id.
494 */
496
497 /**
498 * @brief Process concept constraint requirements
499 *
500 * @param cpt Concept declaration
501 * @param expr Requires expression
502 * @param concept_model Concept diagram element model
503 */
504 void process_constraint_requirements(const clang::ConceptDecl *cpt,
505 const clang::Expr *expr, model::concept_ &concept_model) const;
506
507 /**
508 * @brief Find concept specializations relationships
509 *
510 * @param c Concept element model
511 * @param concept_specialization Concept specialization expression
512 */
514 const clang::ConceptSpecializationExpr *concept_specialization);
515
516 /**
517 * @brief Extract template contraint parameter name from raw source code
518 *
519 * @param concept_specialization Concept specialization expression
520 * @param cpt Concept declaration
521 * @param constrained_template_params Found constraint template param names
522 * @param argument_index Argument index
523 * @param type_name Type parameter name - used if extraction fails
524 */
526 const clang::ConceptSpecializationExpr *concept_specialization,
527 const clang::ConceptDecl *cpt,
528 std::vector<std::string> &constrained_template_params,
529 size_t argument_index, std::string &type_name) const;
530
531 /**
532 * @brief Register already processed template class name
533 *
534 * @param qualified_name Fully qualified template class name
535 */
536 void add_processed_template_class(std::string qualified_name);
537
538 /**
539 * @brief Check if template class has already been processed
540 *
541 * @param qualified_name Fully qualified template class name
542 * @return True, if template class has already been processed
543 */
544 bool has_processed_template_class(const std::string &qualified_name) const;
545
546 /**
547 * @brief Get template builder reference
548 *
549 * @return Reference to 'template_builder' instance
550 */
552
553 template <typename T>
554 void process_record_parent_by_type(eid_t parent_id, class_ &c,
555 namespace_ parent_ns, const clang::RecordDecl *decl);
556
557 void find_record_parent_id(const clang::TagDecl *decl,
558 std::optional<eid_t> &parent_id_opt, namespace_ &parent_ns) const;
559
561
562 std::map<eid_t, std::unique_ptr<clanguml::class_diagram::model::class_>>
564
565 std::map<int64_t /* local anonymous struct id */,
566 std::tuple<std::string /* field name */, common::model::relationship_t,
568 std::optional<size_t> /* destination_multiplicity */>>
570
571 std::map<const clang::EnumDecl *, const clang::TypedefDecl *>
573
574 /**
575 * When visiting CXX records we need to know if they have already been
576 * process in VisitClassTemplateDecl or
577 * VisitClassTemplateSpecializationDecl. If yes, then we need to skip it
578 *
579 * @todo There must be a better way to do this...
580 */
582};
583
584template <typename T>
586 class_ &c, namespace_ parent_ns, const clang::RecordDecl *decl)
587{
588 // Here we have 2 options, either:
589 // - the parent is a regular C++ class/struct
590 // - the parent is a class template declaration/specialization
591 auto parent_class = diagram().find<T>(parent_id);
592
593 c.set_namespace(parent_ns);
594 const auto cls_name = decl->getNameAsString();
595 if (cls_name.empty()) {
596 // Nested structs can be anonymous
597 if (anonymous_struct_relationships_.count(decl->getID()) > 0) {
598 const auto &[label, hint, access, destination_multiplicity] =
599 anonymous_struct_relationships_[decl->getID()];
600
601 c.set_name(parent_class.value().name() + "##" +
602 fmt::format("({})", label));
603
604 std::string destination_multiplicity_str{};
605 if (destination_multiplicity.has_value()) {
606 destination_multiplicity_str =
607 std::to_string(*destination_multiplicity);
608 }
609
610 parent_class.value().add_relationship(
611 {hint, common::to_id(c.full_name(false)), access, label, "",
612 destination_multiplicity_str});
613 }
614 else
615 c.set_name(parent_class.value().name() + "##" +
616 fmt::format("(anonymous_{})", std::to_string(decl->getID())));
617 }
618 else {
619 c.set_name(
620 parent_class.value().name() + "##" + decl->getNameAsString());
621 }
622
623 c.set_id(common::to_id(c.full_name(false)));
624
625 if (!(decl->getNameAsString().empty())) {
626 // Don't add anonymous structs as contained in the class
627 // as they are already added as aggregations
628 c.add_relationship({relationship_t::kContainment, parent_id});
629 }
630
631 c.nested(true);
632}
633} // namespace clanguml::class_diagram::visitor