0.6.2
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Private Member Functions | Private Attributes | List of all members
clanguml::class_diagram::visitor::translation_unit_visitor Class Reference

Class diagram translation unit visitor. More...

Detailed Description

Class diagram translation unit visitor.

This class implements the clang::RecursiveASTVisitor interface for selected visitors relevant to generating class diagrams.

Definition at line 89 of file translation_unit_visitor.h.

#include <translation_unit_visitor.h>

Public Types

using template_builder_t = template_builder< translation_unit_visitor >
 
using config_t = ConfigT
 
using diagram_t = DiagramT
 
- Public Types inherited from clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >
using config_t = ConfigT
 
using diagram_t = DiagramT
 

Public Member Functions

 translation_unit_visitor (clang::SourceManager &sm, clanguml::class_diagram::model::diagram &diagram, const clanguml::config::class_diagram &config)
 Constructor.
 
bool shouldVisitTemplateInstantiations () const
 
bool shouldVisitImplicitCode () const
 
virtual bool VisitNamespaceDecl (clang::NamespaceDecl *ns)
 
virtual bool VisitRecordDecl (clang::RecordDecl *D)
 
virtual bool VisitCXXRecordDecl (clang::CXXRecordDecl *d)
 
virtual bool VisitTypedefDecl (clang::TypedefDecl *decl)
 
virtual bool VisitEnumDecl (clang::EnumDecl *e)
 
virtual bool VisitClassTemplateDecl (clang::ClassTemplateDecl *class_template_declaration)
 
virtual bool VisitClassTemplateSpecializationDecl (clang::ClassTemplateSpecializationDecl *cls)
 
virtual bool VisitTypeAliasTemplateDecl (clang::TypeAliasTemplateDecl *cls)
 
virtual bool TraverseConceptDecl (clang::ConceptDecl *cpt)
 
virtual bool VisitObjCCategoryDecl (clang::ObjCCategoryDecl *decl)
 
virtual bool VisitObjCProtocolDecl (clang::ObjCProtocolDecl *decl)
 
virtual bool VisitObjCInterfaceDecl (clang::ObjCInterfaceDecl *decl)
 
void finalize ()
 Finalize diagram model.
 
void add_class (std::unique_ptr< class_ > &&c)
 Add class (or template class) to the diagram.
 
void add_enum (std::unique_ptr< enum_ > &&e)
 Add enum to the diagram.
 
void add_concept (std::unique_ptr< concept_ > &&c)
 Add concept to the diagram.
 
void add_objc_interface (std::unique_ptr< objc_interface > &&c)
 
void add_diagram_element (std::unique_ptr< common::model::template_element > element) override
 
std::unique_ptr< class_create_element (const clang::NamedDecl *decl) const
 
void find_instantiation_relationships (common::model::template_element &template_instantiation_base, const std::string &full_name, eid_t templated_decl_id)
 
- Public Member Functions inherited from clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >
 translation_unit_visitor (clang::SourceManager &sm, DiagramT &diagram, const ConfigT &config)
 Constructor.
 
virtual ~translation_unit_visitor ()=default
 
void set_tu_path (const std::string &translation_unit_path)
 
const std::filesystem::path & tu_path () const
 Return relative path to current translation unit.
 
common::visitor::ast_id_mapperid_mapper () const
 Get reference to Clang AST to clang-uml id mapper.
 
clang::SourceManager & source_manager () const
 Get clang::SourceManager.
 
void set_source_location (const clang::Decl &decl, clanguml::common::model::source_location &element)
 Set source location in diagram element.
 
void set_source_location (const clang::Expr &expr, clanguml::common::model::source_location &element)
 Set source location in diagram element.
 
void set_source_location (const clang::Stmt &stmt, clanguml::common::model::source_location &element)
 
void set_qualified_name (const clang::NamedDecl &decl, clanguml::common::model::element &element)
 
void set_source_location (const clang::SourceLocation &location, clanguml::common::model::source_location &element)
 Set source location in diagram element.
 
void set_owning_module (const clang::Decl &decl, clanguml::common::model::element &element)
 
virtual void add_diagram_element (std::unique_ptr< common::model::template_element > element)
 
void process_comment (const clang::NamedDecl &decl, clanguml::common::model::decorated_element &e)
 Process comment directives in comment attached to a declaration.
 
std::string process_comment (const clang::RawComment *comment, clang::DiagnosticsEngine &de, clanguml::common::model::decorated_element &e)
 Process comment directives in raw comment.
 
bool skip_system_header_decl (const clang::NamedDecl *decl) const
 
bool should_include (const clang::NamedDecl *decl) const
 Check if the diagram should include a declaration.
 
DiagramT & diagram ()
 Get diagram model reference.
 
const DiagramT & diagram () const
 Get diagram model reference.
 
const ConfigT & config () const
 Get diagram config instance.
 

Private Member Functions

std::unique_ptr< clanguml::class_diagram::model::class_create_declaration (clang::CXXRecordDecl *cls)
 Create class element model from class declaration.
 
template<typename T , typename ElementT >
bool add_or_update (const T *cls, std::unique_ptr< ElementT > &&c_ptr)
 Add the element model or update update if the model already exists.
 
std::unique_ptr< clanguml::class_diagram::model::enum_create_declaration (const clang::EnumDecl *enm, const clang::TypedefDecl *typedef_decl)
 Create enum element model from enum (e.g. struct) declaration.
 
std::unique_ptr< clanguml::class_diagram::model::class_create_declaration (clang::RecordDecl *rec)
 Create class element model from record (e.g. struct) declaration.
 
std::unique_ptr< clanguml::class_diagram::model::objc_interfacecreate_objc_protocol_declaration (clang::ObjCProtocolDecl *decl)
 Create class element model from Objective-C protocol.
 
std::unique_ptr< clanguml::class_diagram::model::objc_interfacecreate_objc_interface_declaration (clang::ObjCInterfaceDecl *decl)
 Create class element model from Objective-C interface.
 
std::unique_ptr< clanguml::class_diagram::model::objc_interfacecreate_objc_category_declaration (clang::ObjCCategoryDecl *decl)
 Create class element model from Objective-C category.
 
std::unique_ptr< clanguml::class_diagram::model::concept_create_concept_declaration (clang::ConceptDecl *cpt)
 Create concept element model from concept declaration.
 
void process_declaration (const clang::CXXRecordDecl &cls, clanguml::class_diagram::model::class_ &c)
 Process class declaration.
 
void process_declaration (const clang::EnumDecl &enm, clanguml::class_diagram::model::enum_ &e)
 Process enum declaration.
 
void process_objc_category_declaration (const clang::ObjCCategoryDecl &cls, objc_interface &c)
 Process Objective-C category declaration.
 
void process_objc_protocol_declaration (const clang::ObjCProtocolDecl &cls, objc_interface &c)
 Process Objective-C protocol declaration.
 
void process_objc_interface_declaration (const clang::ObjCInterfaceDecl &cls, objc_interface &c)
 Process Objective-C interface declaration.
 
void process_class_bases (const clang::CXXRecordDecl *cls, clanguml::class_diagram::model::class_ &c)
 Process class declaration bases (parents), if any.
 
void process_children (const clang::CXXRecordDecl *cls, clanguml::class_diagram::model::class_ &c)
 Process class children elements (members and methods)
 
void process_declaration (const clang::RecordDecl &cls, class_ &c)
 Process record members.
 
std::unique_ptr< clanguml::class_diagram::model::class_process_template_specialization (clang::ClassTemplateSpecializationDecl *cls)
 Process class template specialization/instantiation.
 
void process_template_specialization_children (const clang::ClassTemplateSpecializationDecl *cls, class_ &c)
 Process template specialization children (members and methods)
 
void process_method (const clang::CXXMethodDecl &mf, clanguml::class_diagram::model::class_ &c)
 Process class method.
 
void process_objc_method (const clang::ObjCMethodDecl &mf, objc_interface &c)
 Process Objective-C method.
 
void process_method_properties (const clang::CXXMethodDecl &mf, const class_ &c, const std::string &method_name, class_method &method) const
 Process class method properties.
 
void process_template_method (const clang::FunctionTemplateDecl &mf, clanguml::class_diagram::model::class_ &c)
 Process class template method.
 
void process_static_field (const clang::VarDecl &field_declaration, clanguml::class_diagram::model::class_ &c)
 Process class static data member.
 
void process_field (const clang::FieldDecl &field_declaration, clanguml::class_diagram::model::class_ &c)
 Process class data member.
 
void process_objc_ivar (const clang::ObjCIvarDecl &ivar, objc_interface &c)
 Process Objective-C data member.
 
void process_objc_interface_base (const clang::ObjCInterfaceDecl &cls, objc_interface &c)
 Process Objective-C class base.
 
void process_function_parameter (const clang::ParmVarDecl &param, class_method &method, class_ &c, const std::set< std::string > &template_parameter_names={})
 Process function/method parameter.
 
void process_objc_method_parameter (const clang::ParmVarDecl &param, objc_method &method, objc_interface &c)
 Process Objective-C class method parameter.
 
void process_friend (const clang::FriendDecl &f, class_ &c)
 Process class friend.
 
bool find_relationships (const clang::Decl *decl, const clang::QualType &type, found_relationships_t &, clanguml::common::model::relationship_t relationship_hint)
 Find relationships in a specific type.
 
void add_relationships (diagram_element &c, const class_member_base &field, const found_relationships_t &relationships, bool break_on_first_aggregation=false)
 Add relationships from relationship list to a diagram element model.
 
std::pair< relationship_t, bool > override_relationship_hint (const std::string &type_name, int index, relationship_t hint)
 Try to override relationship hint using configuration file.
 
void process_record_parent (clang::RecordDecl *cls, class_ &c, const namespace_ &ns)
 Process record parent element (e.g. for nested classes)
 
void process_function_parameter_find_relationships_in_autotype (model::class_ &c, const clang::AutoType *atsp)
 Find relationships in function parameter.
 
void find_relationships_in_constraint_expression (clanguml::common::model::element &c, const clang::Expr *expr)
 Find relationships in concept constraint expression.
 
void add_incomplete_forward_declarations ()
 Register incomplete forward declaration to be updated later.
 
void resolve_local_to_global_ids ()
 Replace any AST local ids in diagram elements with global ones.
 
void process_constraint_requirements (const clang::ConceptDecl *cpt, const clang::Expr *expr, model::concept_ &concept_model) const
 Process concept constraint requirements.
 
void process_concept_specialization_relationships (common::model::element &c, const clang::ConceptSpecializationExpr *concept_specialization)
 Find concept specializations relationships.
 
void extract_constrained_template_param_name (const clang::ConceptSpecializationExpr *concept_specialization, const clang::ConceptDecl *cpt, std::vector< std::string > &constrained_template_params, size_t argument_index, std::string &type_name) const
 Extract template contraint parameter name from raw source code.
 
void add_processed_template_class (std::string qualified_name)
 Register already processed template class name.
 
bool has_processed_template_class (const std::string &qualified_name) const
 Check if template class has already been processed.
 
template_builder_ttbuilder ()
 Get template builder reference.
 
template<typename T >
void process_record_parent_by_type (eid_t parent_id, class_ &c, namespace_ parent_ns, const clang::RecordDecl *decl)
 
void find_record_parent_id (const clang::TagDecl *decl, std::optional< eid_t > &parent_id_opt, namespace_ &parent_ns) const
 

Private Attributes

template_builder_t template_builder_
 
forward_declarations_t< class_, enum_forward_declarations_
 
std::map< int64_t, std::tuple< std::string, common::model::relationship_t, common::model::access_t, std::optional< size_t > > > anonymous_struct_relationships_
 
std::map< const clang::EnumDecl *, const clang::TypedefDecl * > typedef_enum_decls_
 
std::set< std::string > processed_template_qualified_names_
 

Additional Inherited Members

- Protected Member Functions inherited from clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >
std::set< const clang::RawComment * > & processed_comments ()
 
std::string get_file_path (const std::string &file_location) const
 

Member Typedef Documentation

◆ config_t

using clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >::config_t = ConfigT

Definition at line 56 of file translation_unit_visitor.h.

◆ diagram_t

using clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >::diagram_t = DiagramT

Definition at line 57 of file translation_unit_visitor.h.

◆ template_builder_t

Definition at line 96 of file translation_unit_visitor.h.

Constructor & Destructor Documentation

◆ translation_unit_visitor()

clanguml::class_diagram::visitor::translation_unit_visitor::translation_unit_visitor ( clang::SourceManager &  sm,
clanguml::class_diagram::model::diagram diagram,
const clanguml::config::class_diagram config 
)
explicit

Constructor.

Parameters
smCurrent source manager reference
diagramDiagram model
configDiagram configuration

Definition at line 29 of file translation_unit_visitor.cc.

Member Function Documentation

◆ add_class()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_class ( std::unique_ptr< class_ > &&  c)

Add class (or template class) to the diagram.

Parameters
cClass model

Definition at line 2563 of file translation_unit_visitor.cc.

2564{
2565 if ((config().generate_packages() &&
2566 config().package_type() == config::package_type_t::kDirectory)) {
2567 assert(!c->file().empty());
2568
2569 const auto file = config().make_path_relative(c->file());
2570
2571 common::model::path p{
2573 p.pop_back();
2574
2575 diagram().add(p, std::move(c));
2576 }
2577 else if ((config().generate_packages() &&
2578 config().package_type() == config::package_type_t::kModule)) {
2579
2580 const auto module_path = config().make_module_relative(c->module());
2581
2582 common::model::path p{module_path, common::model::path_type::kModule};
2583
2584 diagram().add(p, std::move(c));
2585 }
2586 else {
2587 diagram().add(c->path(), std::move(c));
2588 }
2589}

◆ add_concept()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_concept ( std::unique_ptr< concept_ > &&  c)

Add concept to the diagram.

Parameters
cConcept model

Definition at line 2639 of file translation_unit_visitor.cc.

2640{
2641 if ((config().generate_packages() &&
2642 config().package_type() == config::package_type_t::kDirectory)) {
2643 assert(!c->file().empty());
2644
2645 const auto file = config().make_path_relative(c->file());
2646
2647 common::model::path p{
2649 p.pop_back();
2650
2651 diagram().add(p, std::move(c));
2652 }
2653 else if ((config().generate_packages() &&
2654 config().package_type() == config::package_type_t::kModule)) {
2655
2656 const auto module_path = config().make_module_relative(c->module());
2657
2658 common::model::path p{module_path, common::model::path_type::kModule};
2659
2660 diagram().add(p, std::move(c));
2661 }
2662 else {
2663 diagram().add(c->path(), std::move(c));
2664 }
2665}

◆ add_diagram_element()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_diagram_element ( std::unique_ptr< common::model::template_element element)
overridevirtual

Reimplemented from clanguml::common::visitor::translation_unit_visitor< ConfigT, DiagramT >.

Definition at line 2557 of file translation_unit_visitor.cc.

2559{
2560 add_class(util::unique_pointer_cast<class_>(std::move(element)));
2561}

◆ add_enum()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_enum ( std::unique_ptr< enum_ > &&  e)

Add enum to the diagram.

Parameters
eEnum model

Definition at line 2611 of file translation_unit_visitor.cc.

2612{
2613 if ((config().generate_packages() &&
2614 config().package_type() == config::package_type_t::kDirectory)) {
2615 assert(!e->file().empty());
2616
2617 const auto file = config().make_path_relative(e->file());
2618
2619 common::model::path p{
2621 p.pop_back();
2622
2623 diagram().add(p, std::move(e));
2624 }
2625 else if ((config().generate_packages() &&
2626 config().package_type() == config::package_type_t::kModule)) {
2627
2628 const auto module_path = config().make_module_relative(e->module());
2629
2630 common::model::path p{module_path, common::model::path_type::kModule};
2631
2632 diagram().add(p, std::move(e));
2633 }
2634 else {
2635 diagram().add(e->path(), std::move(e));
2636 }
2637}

◆ add_incomplete_forward_declarations()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_incomplete_forward_declarations ( )
private

Register incomplete forward declaration to be updated later.

Definition at line 2468 of file translation_unit_visitor.cc.

2469{
2470 for (auto &[id, c] : forward_declarations_.get<class_>()) {
2471 if (!diagram().find<class_>(id).has_value() &&
2472 diagram().should_include(c->get_namespace())) {
2473 add_class(std::move(c));
2474 }
2475 }
2476 forward_declarations_.get<class_>().clear();
2477
2478 for (auto &[id, e] : forward_declarations_.get<enum_>()) {
2479 if (!diagram().find<enum_>(id).has_value() &&
2480 diagram().should_include(e->get_namespace())) {
2481 add_enum(std::move(e));
2482 }
2483 }
2484 forward_declarations_.get<enum_>().clear();
2485}

◆ add_objc_interface()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_objc_interface ( std::unique_ptr< objc_interface > &&  c)

Definition at line 2591 of file translation_unit_visitor.cc.

2593{
2594 if ((config().generate_packages() &&
2595 config().package_type() == config::package_type_t::kDirectory)) {
2596 assert(!c->file().empty());
2597
2598 const auto file = config().make_path_relative(c->file());
2599
2600 common::model::path p{
2602 p.pop_back();
2603
2604 diagram().add(p, std::move(c));
2605 }
2606 else {
2607 diagram().add(c->path(), std::move(c));
2608 }
2609}

◆ add_or_update()

template<typename T , typename ElementT >
bool clanguml::class_diagram::visitor::translation_unit_visitor::add_or_update ( const T *  cls,
std::unique_ptr< ElementT > &&  c_ptr 
)
private

Add the element model or update update if the model already exists.

Template Parameters
TType of clang declaration
ElementTType of model element
Parameters
clsPointer to clang declaration
c_ptrPointer to the element model
Returns

Definition at line 683 of file translation_unit_visitor.h.

685{
686 static_assert(
687 std::is_same_v<ElementT, class_> || std::is_same_v<ElementT, enum_>);
688
689 const auto cls_id = c_ptr->id();
690
691 id_mapper().add(cls->getID(), cls_id);
692
693 auto maybe_existing_model = diagram().find<ElementT>(cls_id);
694
695 ElementT &class_model =
696 maybe_existing_model.has_value() ? *maybe_existing_model.get() : *c_ptr;
697
698 auto id = class_model.id();
699
700 if (cls->isCompleteDefinition() && !class_model.complete()) {
701 process_declaration(*cls, class_model);
702
703 // Update the source location for the element, otherwise
704 // it can point to the location of first encountered forward
705 // declaration
706 set_source_location(*cls, class_model);
707 }
708
709 if (cls->isCompleteDefinition()) {
710 if (maybe_existing_model &&
711 config().package_type() == config::package_type_t::kDirectory) {
712 // Move the class model to current filesystem path
713 // Eventually, this should be refactored so that it's
714 // not needed
715 const auto file = config().make_path_relative(class_model.file());
716 common::model::path p{
718 p.pop_back();
719 diagram().move<ElementT>(id, p);
720 }
721 }
722 else {
723 forward_declarations_.get<ElementT>().emplace(id, std::move(c_ptr));
724 return true;
725 }
726
727 forward_declarations_.get<ElementT>().erase(id);
728
729 if constexpr (std::is_same_v<T, clang::ClassTemplateSpecializationDecl>) {
730 if (!class_model.template_specialization_found()) {
731 // Only do this if we haven't found a better specialization
732 // during construction of the template specialization
733 const eid_t ast_id{cls->getSpecializedTemplate()->getID()};
734 const auto maybe_id = id_mapper().get_global_id(ast_id);
735 if (maybe_id.has_value())
736 class_model.add_relationship(
737 {relationship_t::kInstantiation, maybe_id.value()});
738 }
739 }
740
741 if (maybe_existing_model)
742 return true;
743
744 if (diagram().should_include(class_model)) {
745 LOG_DBG("Adding {} {} with id {}", class_model.type_name(), class_model,
746 class_model.id());
747 if constexpr (std::is_same_v<ElementT, class_>)
748 add_class(std::move(c_ptr));
749 else
750 add_enum(std::move(c_ptr));
751 }
752 else {
753 LOG_DBG("Skipping {} {} with id {}", class_model.type_name(),
754 class_model, class_model.id());
755 }
756
757 return true;
758}

◆ add_processed_template_class()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_processed_template_class ( std::string  qualified_name)
private

Register already processed template class name.

Parameters
qualified_nameFully qualified template class name

Definition at line 2545 of file translation_unit_visitor.cc.

2547{
2548 processed_template_qualified_names_.emplace(std::move(qualified_name));
2549}

◆ add_relationships()

void clanguml::class_diagram::visitor::translation_unit_visitor::add_relationships ( diagram_element c,
const class_member_base field,
const found_relationships_t &  relationships,
bool  break_on_first_aggregation = false 
)
private

Add relationships from relationship list to a diagram element model.

This method takes a list of relationships whose originating element is class c and adds them to it, ignoring any duplicates and skipping relationships that should be excluded from the diagram.

Parameters
cDiagram element model
fieldClass member model
relationshipsList of found relationships
break_on_first_aggregationStop adding relatinoships, after first aggregation is found

Definition at line 2046 of file translation_unit_visitor.cc.

2049{
2050 auto [decorator_rtype, decorator_rmult] = field.get_relationship();
2051
2052 for (const auto &[target, relationship_type, source_decl] : relationships) {
2053 if (relationship_type != relationship_t::kNone) {
2054 relationship r{relationship_type, target};
2055 r.set_label(field.name());
2056 r.set_access(field.access());
2057 if (source_decl != nullptr) {
2058 set_source_location(*source_decl, r);
2059 }
2060 bool mulitplicity_provided_in_comment{false};
2061 if (decorator_rtype != relationship_t::kNone) {
2062 r.set_type(decorator_rtype);
2063 auto mult = util::split(decorator_rmult, ":", false);
2064 if (mult.size() == 2) {
2065 mulitplicity_provided_in_comment = true;
2066 r.set_multiplicity_source(mult[0]);
2067 r.set_multiplicity_destination(mult[1]);
2068 }
2069 }
2070 if (!mulitplicity_provided_in_comment &&
2071 field.destination_multiplicity().has_value()) {
2072 r.set_multiplicity_destination(
2073 std::to_string(*field.destination_multiplicity()));
2074 }
2075
2076 r.set_style(field.style_spec());
2077
2078 LOG_DBG("Adding relationship from {} to {} with label {}", c,
2079 r.destination(), r.type(), r.label());
2080
2081 c.add_relationship(std::move(r));
2082
2083 if (break_on_first_aggregation &&
2084 relationship_type == relationship_t::kAggregation)
2085 break;
2086 }
2087 }
2088}

◆ create_concept_declaration()

std::unique_ptr< clanguml::class_diagram::model::concept_ > clanguml::class_diagram::visitor::translation_unit_visitor::create_concept_declaration ( clang::ConceptDecl *  cpt)
private

Create concept element model from concept declaration.

Parameters
cptConcept declaration
Returns
Concept diagram element model

Definition at line 848 of file translation_unit_visitor.cc.

849{
850 assert(cpt != nullptr);
851
852 if (!should_include(cpt))
853 return {};
854
855 auto concept_ptr{
856 std::make_unique<model::concept_>(config().using_namespace())};
857 auto &concept_model = *concept_ptr;
858
859 auto ns = common::get_template_namespace(*cpt);
860
861 concept_model.set_name(cpt->getNameAsString());
862 concept_model.set_namespace(ns);
863 concept_model.set_id(common::to_id(concept_model.full_name(false)));
864
865 process_comment(*cpt, concept_model);
866 set_source_location(*cpt, concept_model);
867 set_owning_module(*cpt, concept_model);
868
869 if (concept_model.skip())
870 return {};
871
872 concept_model.set_style(concept_model.style_spec());
873
874 return concept_ptr;
875}

◆ create_declaration() [1/3]

std::unique_ptr< class_ > clanguml::class_diagram::visitor::translation_unit_visitor::create_declaration ( clang::CXXRecordDecl *  cls)
private

Create class element model from class declaration.

Parameters
clsClass declaration
Returns
Class diagram element model

Definition at line 923 of file translation_unit_visitor.cc.

925{
926 assert(cls != nullptr);
927
928 if (!should_include(cls))
929 return {};
930
931 auto c_ptr{std::make_unique<class_>(config().using_namespace())};
932 auto &c = *c_ptr;
933
934 auto ns{common::get_tag_namespace(*cls)};
935
936 process_record_parent(cls, c, ns);
937
938 if (!c.is_nested()) {
939 c.set_name(common::get_tag_name(*cls));
940 c.set_namespace(ns);
941 c.set_id(common::to_id(c.full_name(false)));
942 }
943
944 c.is_struct(cls->isStruct());
945
946 process_comment(*cls, c);
947 set_source_location(*cls, c);
948 set_owning_module(*cls, c);
949
950 if (c.skip())
951 return {};
952
953 c.set_style(c.style_spec());
954
955 return c_ptr;
956}

◆ create_declaration() [2/3]

std::unique_ptr< class_ > clanguml::class_diagram::visitor::translation_unit_visitor::create_declaration ( clang::RecordDecl *  rec)
private

Create class element model from record (e.g. struct) declaration.

Parameters
recRecord declaration
Returns
Class diagram element model

Definition at line 877 of file translation_unit_visitor.cc.

879{
880 assert(rec != nullptr);
881
882 if (!should_include(rec))
883 return {};
884
885 auto record_ptr{std::make_unique<class_>(config().using_namespace())};
886 auto &record = *record_ptr;
887
888 process_record_parent(rec, record, namespace_{});
889
890 if (!record.is_nested()) {
891 auto record_name = rec->getQualifiedNameAsString();
892
893#if LLVM_VERSION_MAJOR < 16
894 if (record_name == "(anonymous)") {
895 util::if_not_null(rec->getTypedefNameForAnonDecl(),
896 [&record_name](const clang::TypedefNameDecl *name) {
897 record_name = name->getNameAsString();
898 });
899 }
900#endif
901
902 record.set_name(record_name);
903 record.set_id(common::to_id(record.full_name(false)));
904 }
905
906 process_comment(*rec, record);
907 set_source_location(*rec, record);
908 set_owning_module(*rec, record);
909
910 const auto record_full_name = record_ptr->full_name(false);
911
912 record.is_struct(rec->isStruct());
913 record.is_union(rec->isUnion());
914
915 if (record.skip())
916 return {};
917
918 record.set_style(record.style_spec());
919
920 return record_ptr;
921}

◆ create_declaration() [3/3]

std::unique_ptr< clanguml::class_diagram::model::enum_ > clanguml::class_diagram::visitor::translation_unit_visitor::create_declaration ( const clang::EnumDecl *  enm,
const clang::TypedefDecl *  typedef_decl 
)
private

Create enum element model from enum (e.g. struct) declaration.

Parameters
recEnum declaration
Returns
Enum diagram element model

Definition at line 159 of file translation_unit_visitor.cc.

161{
162 auto e_ptr = std::make_unique<enum_>(config().using_namespace());
163 auto &e = *e_ptr;
164
165 auto ns{common::get_tag_namespace(*enm)};
166
167 // Id of parent class or struct in which this enum is potentially nested
168 std::optional<eid_t> parent_id_opt;
169 [[maybe_unused]] namespace_ parent_ns;
170 find_record_parent_id(enm, parent_id_opt, parent_ns);
171
172 std::string enm_name;
173 if (enm->getNameAsString().empty() && typedef_decl != nullptr)
174 enm_name = typedef_decl->getNameAsString();
175 else if (parent_id_opt)
176 enm_name = enm->getNameAsString();
177 else
178 enm_name = common::get_tag_name(*enm);
179
180 if (parent_id_opt && diagram().find<class_>(*parent_id_opt)) {
181 auto parent_class = diagram().find<class_>(*parent_id_opt);
182
183 e.set_namespace(ns);
184 e.set_name(parent_class.value().name(), enm_name);
185 e.set_id(common::to_id(e.full_name(false)));
186 e.add_relationship({relationship_t::kContainment, *parent_id_opt});
187 e.nested(true);
188 }
189 else if (parent_id_opt && diagram().find<objc_interface>(*parent_id_opt)) {
190 auto parent_class = diagram().find<objc_interface>(*parent_id_opt);
191
192 e.set_namespace(ns);
193 e.set_name(parent_class.value().name(), enm_name);
194 e.set_id(common::to_id(e.full_name(false)));
195 e.add_relationship({relationship_t::kContainment, *parent_id_opt});
196 e.nested(true);
197 }
198 else {
199 e.set_name(enm_name);
200 e.set_namespace(ns);
201 e.set_id(common::to_id(e.full_name(false)));
202 }
203
204 id_mapper().add(enm->getID(), e.id());
205
206 process_comment(*enm, e);
207 set_source_location(*enm, e);
208 set_owning_module(*enm, e);
209
210 if (e.skip())
211 return {};
212
213 e.set_style(e.style_spec());
214
215 return e_ptr;
216}

◆ create_element()

std::unique_ptr< class_ > clanguml::class_diagram::visitor::translation_unit_visitor::create_element ( const clang::NamedDecl *  decl) const

Definition at line 37 of file translation_unit_visitor.cc.

39{
40 auto cls = std::make_unique<class_>(config().using_namespace());
41 cls->is_struct(common::is_struct(decl));
42 return cls;
43}

◆ create_objc_category_declaration()

std::unique_ptr< clanguml::class_diagram::model::objc_interface > clanguml::class_diagram::visitor::translation_unit_visitor::create_objc_category_declaration ( clang::ObjCCategoryDecl *  decl)
private

Create class element model from Objective-C category.

Parameters
declObjective-C protocol declaration
Returns
Class diagram element model

Definition at line 959 of file translation_unit_visitor.cc.

961{
962 assert(decl != nullptr);
963
964 if (!should_include(decl))
965 return {};
966
967 auto c_ptr{std::make_unique<class_diagram::model::objc_interface>(
968 config().using_namespace())};
969 auto &c = *c_ptr;
970
971 decl->getClassInterface()->getNameAsString();
972 c.set_name(fmt::format("{}({})",
973 decl->getClassInterface()->getNameAsString(), decl->getNameAsString()));
974 c.set_id(common::to_id(fmt::format("__objc__category__{}", c.name())));
975 c.is_category(true);
976
977 process_comment(*decl, c);
978 set_source_location(*decl, c);
979
980 if (c.skip())
981 return {};
982
983 c.set_style(c.style_spec());
984
985 return c_ptr;
986}

◆ create_objc_interface_declaration()

std::unique_ptr< clanguml::class_diagram::model::objc_interface > clanguml::class_diagram::visitor::translation_unit_visitor::create_objc_interface_declaration ( clang::ObjCInterfaceDecl *  decl)
private

Create class element model from Objective-C interface.

Parameters
declObjective-C protocol declaration
Returns
Class diagram element model

Definition at line 1017 of file translation_unit_visitor.cc.

1019{
1020 assert(decl != nullptr);
1021
1022 if (!should_include(decl))
1023 return {};
1024
1025 auto c_ptr{std::make_unique<class_diagram::model::objc_interface>(
1026 config().using_namespace())};
1027 auto &c = *c_ptr;
1028
1029 c.set_name(decl->getNameAsString());
1030 c.set_id(common::to_id(*decl));
1031
1032 process_comment(*decl, c);
1033 set_source_location(*decl, c);
1034
1035 if (c.skip())
1036 return {};
1037
1038 c.set_style(c.style_spec());
1039
1040 return c_ptr;
1041}

◆ create_objc_protocol_declaration()

std::unique_ptr< clanguml::class_diagram::model::objc_interface > clanguml::class_diagram::visitor::translation_unit_visitor::create_objc_protocol_declaration ( clang::ObjCProtocolDecl *  decl)
private

Create class element model from Objective-C protocol.

Parameters
declObjective-C protocol declaration
Returns
Class diagram element model

Definition at line 989 of file translation_unit_visitor.cc.

991{
992 assert(decl != nullptr);
993
994 if (!should_include(decl))
995 return {};
996
997 auto c_ptr{std::make_unique<class_diagram::model::objc_interface>(
998 config().using_namespace())};
999 auto &c = *c_ptr;
1000
1001 c.set_name(decl->getNameAsString());
1002 c.set_id(common::to_id(*decl));
1003 c.is_protocol(true);
1004
1005 process_comment(*decl, c);
1006 set_source_location(*decl, c);
1007
1008 if (c.skip())
1009 return {};
1010
1011 c.set_style(c.style_spec());
1012
1013 return c_ptr;
1014}

◆ extract_constrained_template_param_name()

void clanguml::class_diagram::visitor::translation_unit_visitor::extract_constrained_template_param_name ( const clang::ConceptSpecializationExpr *  concept_specialization,
const clang::ConceptDecl *  cpt,
std::vector< std::string > &  constrained_template_params,
size_t  argument_index,
std::string &  type_name 
) const
private

Extract template contraint parameter name from raw source code.

Parameters
concept_specializationConcept specialization expression
cptConcept declaration
constrained_template_paramsFound constraint template param names
argument_indexArgument index
type_nameType parameter name - used if extraction fails

Definition at line 2518 of file translation_unit_visitor.cc.

2523{
2524 const auto full_declaration_text = common::get_source_text_raw(
2525 concept_specialization->getSourceRange(), source_manager());
2526
2527 if (!full_declaration_text.empty()) {
2528 // Handle typename constraint in requires clause
2529 if (type_name.find("type-parameter-") == 0) {
2530 const auto concept_declaration_text = full_declaration_text.substr(
2531 full_declaration_text.find(cpt->getNameAsString()) +
2532 cpt->getNameAsString().size() + 1);
2533
2534 auto template_params = common::parse_unexposed_template_params(
2535 concept_declaration_text, [](const auto &t) { return t; });
2536
2537 if (template_params.size() > argument_index)
2538 type_name = template_params[argument_index].to_string(
2539 config().using_namespace(), false);
2540 }
2541 constrained_template_params.push_back(type_name);
2542 }
2543}

◆ finalize()

void clanguml::class_diagram::visitor::translation_unit_visitor::finalize ( )

Finalize diagram model.

This method is called after the entire AST has been visited by this visitor. It is used to perform necessary post processing on the diagram (e.g. resolve translation unit local element ID's into global ID's based on elements full names).

Definition at line 2509 of file translation_unit_visitor.cc.

2510{
2513 if (config().skip_redundant_dependencies()) {
2514 diagram().remove_redundant_dependencies();
2515 }
2516}

◆ find_instantiation_relationships()

void clanguml::class_diagram::visitor::translation_unit_visitor::find_instantiation_relationships ( common::model::template_element template_instantiation_base,
const std::string &  full_name,
eid_t  templated_decl_id 
)

Definition at line 2667 of file translation_unit_visitor.cc.

2670{
2671 auto &template_instantiation = dynamic_cast<class_diagram::model::class_ &>(
2672 template_instantiation_base);
2673
2674 // First try to find the best match for this template in partially
2675 // specialized templates
2676 std::string destination{};
2677 std::string best_match_full_name{};
2678 auto full_template_name = template_instantiation.full_name(false);
2679 int best_match{};
2680 eid_t best_match_id{};
2681
2682 for (const auto templ : diagram().classes()) {
2683 if (templ.get() == template_instantiation)
2684 continue;
2685
2686 auto c_full_name = templ.get().full_name(false);
2687 auto match =
2688 template_instantiation.calculate_template_specialization_match(
2689 templ.get());
2690
2691 if (match > best_match) {
2692 best_match = match;
2693 best_match_full_name = c_full_name;
2694 best_match_id = templ.get().id();
2695 }
2696 }
2697
2698 auto templated_decl_global_id =
2699 id_mapper().get_global_id(templated_decl_id).value_or(eid_t{});
2700
2701 if (best_match_id.value() > 0) {
2702 destination = best_match_full_name;
2703 template_instantiation.add_relationship(
2705 template_instantiation.template_specialization_found(true);
2706 }
2707 // If we can't find optimal match for parent template specialization,
2708 // just use whatever clang suggests
2709 else if (diagram().has_element(templated_decl_global_id)) {
2710 template_instantiation.add_relationship(
2712 templated_decl_global_id});
2713 template_instantiation.template_specialization_found(true);
2714 }
2715 else if (id_mapper().get_global_id(templated_decl_id).has_value()) {
2716 template_instantiation.add_relationship(
2718 template_instantiation.template_specialization_found(true);
2719 }
2720 else if (diagram().should_include(common::model::namespace_{full_name})) {
2721 LOG_DBG("Skipping instantiation relationship from {} to {}",
2722 template_instantiation, templated_decl_global_id);
2723 }
2724 else {
2725 LOG_DBG("== Cannot determine global id for specialization template {} "
2726 "- delaying until the translation unit is complete ",
2727 templated_decl_global_id);
2728
2729 template_instantiation.add_relationship(
2731 }
2732}

◆ find_record_parent_id()

void clanguml::class_diagram::visitor::translation_unit_visitor::find_record_parent_id ( const clang::TagDecl *  decl,
std::optional< eid_t > &  parent_id_opt,
namespace_ &  parent_ns 
) const
private

Definition at line 2421 of file translation_unit_visitor.cc.

2423{
2424 const auto *parent = decl->getParent();
2425
2426 if (parent != nullptr) {
2427 if (const auto *parent_record_decl =
2428 clang::dyn_cast<clang::RecordDecl>(parent);
2429 parent_record_decl != nullptr) {
2430 parent_ns = common::get_tag_namespace(*parent_record_decl);
2431
2432 eid_t local_id{parent_record_decl->getID()};
2433
2434 // First check if the parent has been added to the diagram as
2435 // regular class
2436 parent_id_opt = id_mapper().get_global_id(local_id);
2437
2438 // If not, check if the parent template declaration is in the
2439 // model
2440 if (!parent_id_opt) {
2441 if (parent_record_decl->getDescribedTemplate() != nullptr) {
2442 local_id =
2443 parent_record_decl->getDescribedTemplate()->getID();
2444 parent_id_opt = id_mapper().get_global_id(local_id);
2445 }
2446 }
2447 }
2448 }
2449
2450 if (parent_id_opt)
2451 return;
2452
2453 const auto *lexical_parent = decl->getLexicalParent();
2454 if (lexical_parent != nullptr) {
2455 if (const auto *parent_interface_decl =
2456 clang::dyn_cast<clang::ObjCInterfaceDecl>(lexical_parent);
2457 parent_interface_decl != nullptr) {
2458
2459 eid_t ast_id{parent_interface_decl->getID()};
2460
2461 // First check if the parent has been added to the diagram as
2462 // regular class
2463 parent_id_opt = id_mapper().get_global_id(ast_id);
2464 }
2465 }
2466}

◆ find_relationships()

bool clanguml::class_diagram::visitor::translation_unit_visitor::find_relationships ( const clang::Decl *  decl,
const clang::QualType &  type,
found_relationships_t &  relationships,
clanguml::common::model::relationship_t  relationship_hint 
)
private

Find relationships in a specific type.

Parameters
declSource declaration from which this relationship originates
typeType to search for relationships
relationship_hintDefault relationship type to infer from this type
Returns
True, if any relationships were found

Definition at line 1731 of file translation_unit_visitor.cc.

1734{
1735 bool result{false};
1736
1737 if (type->isPointerType()) {
1738 relationship_hint = relationship_t::kAssociation;
1740 decl, type->getPointeeType(), relationships, relationship_hint);
1741 }
1742 else if (type->isRValueReferenceType()) {
1743 relationship_hint = relationship_t::kAggregation;
1745 decl, type.getNonReferenceType(), relationships, relationship_hint);
1746 }
1747 else if (type->isLValueReferenceType()) {
1748 relationship_hint = relationship_t::kAssociation;
1750 decl, type.getNonReferenceType(), relationships, relationship_hint);
1751 }
1752 else if (type->isArrayType()) {
1753 find_relationships(decl, type->getAsArrayTypeUnsafe()->getElementType(),
1754 relationships, relationship_t::kAggregation);
1755 }
1756 else if (type->isEnumeralType()) {
1757 if (const auto *enum_type = type->getAs<clang::EnumType>();
1758 enum_type != nullptr) {
1759 // Use AST's local ID here for relationship target, as we can't
1760 // calculate here properly the ID for nested enums. It will be
1761 // resolved properly in finalize().
1762 relationships.emplace_back(
1763 enum_type->getDecl()->getID(), relationship_hint, decl);
1764 }
1765 }
1766 // TODO: Objc support
1767 else if (type->isRecordType()) {
1768 const auto *type_instantiation_type =
1769 type->getAs<clang::TemplateSpecializationType>();
1770
1771 if (type_instantiation_type != nullptr) {
1772 const auto *type_instantiation_template_decl =
1773 type_instantiation_type->getTemplateName().getAsTemplateDecl();
1774
1775 // If this template should be included in the diagram
1776 // add it - and then process recursively its arguments
1777 if (should_include(type_instantiation_template_decl)) {
1778 relationships.emplace_back(
1779 type_instantiation_type->getTemplateName()
1780 .getAsTemplateDecl()
1781 ->getID(),
1782 relationship_hint, decl);
1783 }
1784
1785 auto idx{0};
1786 for (const auto &template_argument :
1787 type_instantiation_type->template_arguments()) {
1788
1789 auto [overridden_relationship_hint, overridden] =
1790 override_relationship_hint(type_instantiation_template_decl
1791 ->getQualifiedNameAsString(),
1792 idx, relationship_hint);
1793
1794 const auto template_argument_kind = template_argument.getKind();
1795 if (template_argument_kind ==
1796 clang::TemplateArgument::ArgKind::Integral) {
1797 // pass
1798 }
1799 else if (template_argument_kind ==
1800 clang::TemplateArgument::ArgKind::Null) {
1801 // pass
1802 }
1803 else if (template_argument_kind ==
1804 clang::TemplateArgument::ArgKind::Expression) {
1805 // pass
1806 }
1807 else if (template_argument.getKind() ==
1808 clang::TemplateArgument::ArgKind::NullPtr) {
1809 // pass
1810 }
1811 else if (template_argument_kind ==
1812 clang::TemplateArgument::ArgKind::Template) {
1813 // pass
1814 }
1815 else if (template_argument_kind ==
1816 clang::TemplateArgument::ArgKind::TemplateExpansion) {
1817 // pass
1818 }
1819 else if (const auto *function_type =
1820 template_argument.getAsType()
1821 ->getAs<clang::FunctionProtoType>();
1822 function_type != nullptr) {
1823 for (const auto &param_type :
1824 function_type->param_types()) {
1825 result = find_relationships(decl, param_type,
1826 relationships, relationship_t::kDependency);
1827 }
1828 }
1829 else if (template_argument_kind ==
1830 clang::TemplateArgument::ArgKind::Type) {
1831 result =
1832 find_relationships(decl, template_argument.getAsType(),
1833 relationships, overridden_relationship_hint);
1834 }
1835 idx++;
1836 }
1837 }
1838 else if (type->getAsCXXRecordDecl() != nullptr) {
1839 relationships.emplace_back(
1840 type->getAsCXXRecordDecl()->getID(), relationship_hint, decl);
1841 result = true;
1842 }
1843 else {
1844 relationships.emplace_back(
1845 type->getAsRecordDecl()->getID(), relationship_hint, decl);
1846 result = true;
1847 }
1848 }
1849 else if (const auto *template_specialization_type =
1850 type->getAs<clang::TemplateSpecializationType>();
1851 template_specialization_type != nullptr) {
1852 const auto *type_instantiation_template_decl =
1853 template_specialization_type->getTemplateName().getAsTemplateDecl();
1854 if (should_include(template_specialization_type->getTemplateName()
1855 .getAsTemplateDecl())) {
1856 relationships.emplace_back(
1857 template_specialization_type->getTemplateName()
1858 .getAsTemplateDecl()
1859 ->getID(),
1860 relationship_hint, decl);
1861 }
1862 auto idx{0};
1863 for (const auto &template_argument :
1864 template_specialization_type->template_arguments()) {
1865
1866 auto [overridden_relationship_hint, overridden] =
1867 override_relationship_hint(type_instantiation_template_decl
1868 ->getQualifiedNameAsString(),
1869 idx, relationship_hint);
1870
1871 const auto template_argument_kind = template_argument.getKind();
1872 if (template_argument_kind ==
1873 clang::TemplateArgument::ArgKind::Integral) {
1874 // pass
1875 }
1876 else if (template_argument_kind ==
1877 clang::TemplateArgument::ArgKind::Null) {
1878 // pass
1879 }
1880 else if (template_argument_kind ==
1881 clang::TemplateArgument::ArgKind::Expression) {
1882 // pass
1883 }
1884 else if (template_argument.getKind() ==
1885 clang::TemplateArgument::ArgKind::NullPtr) {
1886 // pass
1887 }
1888 else if (template_argument_kind ==
1889 clang::TemplateArgument::ArgKind::Template) {
1890 // pass
1891 }
1892 else if (template_argument_kind ==
1893 clang::TemplateArgument::ArgKind::TemplateExpansion) {
1894 // pass
1895 }
1896 else if (const auto *function_type =
1897 template_argument.getAsType()
1898 ->getAs<clang::FunctionProtoType>();
1899 function_type != nullptr) {
1900 for (const auto &param_type : function_type->param_types()) {
1901 result = find_relationships(decl, param_type, relationships,
1902 relationship_t::kDependency);
1903 }
1904 }
1905 else if (template_argument_kind ==
1906 clang::TemplateArgument::ArgKind::Type) {
1907 result = find_relationships(decl, template_argument.getAsType(),
1908 relationships, overridden_relationship_hint);
1909 }
1910 idx++;
1911 }
1912 }
1913
1914 return result;
1915}

◆ find_relationships_in_constraint_expression()

void clanguml::class_diagram::visitor::translation_unit_visitor::find_relationships_in_constraint_expression ( clanguml::common::model::element c,
const clang::Expr *  expr 
)
private

Find relationships in concept constraint expression.

Parameters
cDiagram element model (concept)
exprConcept constraint expression

Definition at line 688 of file translation_unit_visitor.cc.

690{
691 if (expr == nullptr)
692 return;
693 found_relationships_t relationships;
694
695 common::if_dyn_cast<clang::UnresolvedLookupExpr>(
696 expr, [&](const clang::UnresolvedLookupExpr *ul) {
697 for (const auto ta : ul->template_arguments()) {
698 if (ta.getArgument().getKind() !=
699 clang::TemplateArgument::ArgKind::Type)
700 continue;
701 find_relationships({}, ta.getArgument().getAsType(),
702 relationships, relationship_t::kConstraint);
703 }
704 });
705
706 common::if_dyn_cast<clang::ConceptSpecializationExpr>(
707 expr, [&](const auto *cs) {
709 });
710
711 common::if_dyn_cast<clang::RequiresExpr>(expr, [&](const auto *re) {
712 // TODO
713 });
714
715 common::if_dyn_cast<clang::BinaryOperator>(expr, [&](const auto *op) {
718 });
719
720 common::if_dyn_cast<clang::UnaryOperator>(expr, [&](const auto *op) {
722 });
723
724 for (const auto &[type_element_id, relationship_type, source_decl] :
725 relationships) {
726 if (type_element_id != c.id() &&
727 (relationship_type != relationship_t::kNone)) {
728
729 relationship r{relationship_type, type_element_id};
730
731 if (source_decl != nullptr) {
732 set_source_location(*source_decl, r);
733 }
734
735 c.add_relationship(std::move(r));
736 }
737 }
738}

◆ has_processed_template_class()

bool clanguml::class_diagram::visitor::translation_unit_visitor::has_processed_template_class ( const std::string &  qualified_name) const
private

Check if template class has already been processed.

Parameters
qualified_nameFully qualified template class name
Returns
True, if template class has already been processed

Definition at line 2551 of file translation_unit_visitor.cc.

2553{
2555}

◆ override_relationship_hint()

std::pair< relationship_t, bool > clanguml::class_diagram::visitor::translation_unit_visitor::override_relationship_hint ( const std::string &  type_name,
int  index,
relationship_t  hint 
)
private

Try to override relationship hint using configuration file.

Parameters
type_name
hint
Returns
Maybe overridden relationship type hint

Definition at line 2091 of file translation_unit_visitor.cc.

2093{
2094 bool overridden{false};
2095
2096 for (const auto &[pattern, rel_hint] : config().relationship_hints()) {
2097 if (type_name.find(pattern) == 0) {
2098 if (index == -1)
2099 hint = rel_hint.default_hint;
2100 else
2101 hint = rel_hint.get(index, hint);
2102
2103 overridden = true;
2104 break;
2105 }
2106 }
2107
2108 return {hint, overridden};
2109}

◆ process_children()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_children ( const clang::CXXRecordDecl *  cls,
clanguml::class_diagram::model::class_ c 
)
private

Process class children elements (members and methods)

Parameters
clsClass declaration
cClass diagram element model

Definition at line 1323 of file translation_unit_visitor.cc.

1325{
1326 assert(cls != nullptr);
1327
1328 // Iterate over class methods (both regular and static)
1329 for (const auto *method : cls->methods()) {
1330 if (method != nullptr) {
1331 process_method(*method, c);
1332 }
1333 }
1334
1335 // Iterate over class template methods
1336 if (const auto *cls_decl_context =
1337 clang::dyn_cast_or_null<clang::DeclContext>(cls);
1338 cls_decl_context != nullptr) {
1339 for (auto const *decl_iterator : cls_decl_context->decls()) {
1340 auto const *method_template =
1341 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
1342 decl_iterator);
1343 if (method_template == nullptr)
1344 continue;
1345
1346 process_template_method(*method_template, c);
1347 }
1348 }
1349
1350 // Iterate over regular class fields
1351 for (const auto *field : cls->fields()) {
1352 if (field != nullptr)
1353 process_field(*field, c);
1354 }
1355
1356 // First we have to collect any `typedef enum` declarations, which should
1357 // not be rendered as members (typedefs are visited here after enums)
1358 std::set<const clang::EnumDecl *> typedeffed_enums;
1359 for (const auto *decl : cls->decls()) {
1360 if (decl->getKind() == clang::Decl::Typedef) {
1361 const auto *typedeffed_enum = common::get_typedef_enum_decl(
1362 clang::dyn_cast<clang::TypedefDecl>(decl));
1363 if (typedeffed_enum != nullptr)
1364 typedeffed_enums.emplace(typedeffed_enum);
1365 }
1366 }
1367
1368 // Static fields have to be processed by iterating over variable
1369 // declarations
1370 for (const auto *decl : cls->decls()) {
1371 if (decl->getKind() == clang::Decl::Var) {
1372 const clang::VarDecl *variable_declaration{
1373 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
1374 if ((variable_declaration != nullptr) &&
1375 variable_declaration->isStaticDataMember()) {
1376 process_static_field(*variable_declaration, c);
1377 }
1378 }
1379 else if (decl->getKind() == clang::Decl::Enum &&
1380 typedeffed_enums.count(
1381 clang::dyn_cast_or_null<clang::EnumDecl>(decl)) == 0) {
1382 const auto *enum_decl =
1383 clang::dyn_cast_or_null<clang::EnumDecl>(decl);
1384 if (enum_decl == nullptr)
1385 continue;
1386
1387 if (enum_decl->getNameAsString().empty()) {
1388 for (const auto *enum_const : enum_decl->enumerators()) {
1390 enum_decl->getAccess()),
1391 enum_const->getNameAsString(), "enum"};
1392 c.add_member(std::move(m));
1393 }
1394 }
1395 }
1396 }
1397
1398 if (cls->isCompleteDefinition())
1399 for (const auto *friend_declaration : cls->friends()) {
1400 if (friend_declaration != nullptr)
1401 process_friend(*friend_declaration, c);
1402 }
1403}

◆ process_class_bases()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_class_bases ( const clang::CXXRecordDecl *  cls,
clanguml::class_diagram::model::class_ c 
)
private

Process class declaration bases (parents), if any.

Parameters
clsClass declaration
cClass diagram element model

Definition at line 1271 of file translation_unit_visitor.cc.

1273{
1274 for (const auto &base : cls->bases()) {
1275 eid_t parent_id;
1276 if (const auto *tsp =
1277 base.getType()->getAs<clang::TemplateSpecializationType>();
1278 tsp != nullptr) {
1279 auto template_specialization_ptr =
1280 std::make_unique<class_>(config().using_namespace());
1282 *cls, *template_specialization_ptr, cls, *tsp, {});
1283
1284 parent_id = template_specialization_ptr->id();
1285
1286 if (diagram().should_include(*template_specialization_ptr)) {
1287 add_class(std::move(template_specialization_ptr));
1288 }
1289 }
1290 else if (const auto *record_type =
1291 base.getType()->getAs<clang::RecordType>();
1292 record_type != nullptr) {
1293 parent_id = common::to_id(*record_type->getDecl());
1294 }
1295 else
1296 // This could be a template parameter - we don't want it here
1297 continue;
1298
1299 common::model::relationship cp{parent_id,
1300 common::access_specifier_to_access_t(base.getAccessSpecifier()),
1301 base.isVirtual()};
1302
1303 LOG_DBG("Found base class {} [{}] for class {}",
1304 common::to_string(base.getType(), cls->getASTContext()),
1305 parent_id.value(), c.name());
1306
1307 c.add_relationship(std::move(cp));
1308 }
1309}

◆ process_concept_specialization_relationships()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_concept_specialization_relationships ( common::model::element c,
const clang::ConceptSpecializationExpr *  concept_specialization 
)
private

Find concept specializations relationships.

Parameters
cConcept element model
concept_specializationConcept specialization expression

Definition at line 740 of file translation_unit_visitor.cc.

743{
744
745 if (const auto *cpt = concept_specialization->getNamedConcept();
746 should_include(cpt)) {
747
748 const auto cpt_name = cpt->getNameAsString();
749 const eid_t ast_id{cpt->getID()};
750 const auto maybe_id = id_mapper().get_global_id(ast_id);
751 if (!maybe_id)
752 return;
753
754 const auto target_id = maybe_id.value();
755
756 std::vector<std::string> constrained_template_params;
757
758 size_t argument_index{};
759
760 for (const auto ta : concept_specialization->getTemplateArguments()) {
761 if (ta.getKind() == clang::TemplateArgument::Type) {
762 auto type_name =
763 common::to_string(ta.getAsType(), cpt->getASTContext());
764 extract_constrained_template_param_name(concept_specialization,
765 cpt, constrained_template_params, argument_index,
766 type_name);
767 }
768 else if (ta.getKind() == clang::TemplateArgument::Pack) {
769 if (!ta.getPackAsArray().empty() &&
770 ta.getPackAsArray().front().isPackExpansion()) {
771 const auto &pack_head =
772 ta.getPackAsArray().front().getAsType();
773 auto type_name =
774 common::to_string(pack_head, cpt->getASTContext());
776 concept_specialization, cpt,
777 constrained_template_params, argument_index, type_name);
778 }
779 }
780 else {
781 LOG_DBG("Unsupported concept type parameter in concept: {}",
782 cpt_name);
783 }
784 argument_index++;
785 }
786
787 if (!constrained_template_params.empty())
788 c.add_relationship(
789 {relationship_t::kConstraint, target_id, access_t::kNone,
790 fmt::format(
791 "{}", fmt::join(constrained_template_params, ","))});
792 }
793}

◆ process_constraint_requirements()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_constraint_requirements ( const clang::ConceptDecl *  cpt,
const clang::Expr *  expr,
model::concept_ concept_model 
) const
private

Process concept constraint requirements.

Parameters
cptConcept declaration
exprRequires expression
concept_modelConcept diagram element model

Definition at line 559 of file translation_unit_visitor.cc.

562{
563 if (const auto *constraint = llvm::dyn_cast<clang::RequiresExpr>(expr);
564 constraint) {
565
566 auto constraint_source = common::to_string(constraint);
567
568 LOG_DBG("== Processing constraint: '{}'", constraint_source);
569
570 for ([[maybe_unused]] const auto *requirement :
571 constraint->getRequirements()) {
572 // TODO
573 }
574
575 // process 'requires (...)' declaration
576 for (const auto *decl : constraint->getBody()->decls()) {
577 if (const auto *parm_var_decl =
578 llvm::dyn_cast<clang::ParmVarDecl>(decl);
579 parm_var_decl) {
580 parm_var_decl->getQualifiedNameAsString();
581
582 auto param_name = parm_var_decl->getNameAsString();
583 auto param_type = common::to_string(
584 parm_var_decl->getType(), cpt->getASTContext());
585
586 LOG_DBG("=== Processing parameter variable declaration: {}, {}",
587 param_type, param_name);
588
589 concept_model.add_parameter(
590 {std::move(param_type), std::move(param_name)});
591 }
592 else {
593 LOG_DBG("=== Processing some other concept declaration: {}",
594 decl->getID());
595 }
596 }
597
598 // process concept body requirements '{ }' if any
599 for (const auto *req : constraint->getRequirements()) {
600 if (req->getKind() == clang::concepts::Requirement::RK_Simple) {
601 const auto *simple_req =
602 llvm::dyn_cast<clang::concepts::ExprRequirement>(req);
603
604 if (simple_req != nullptr) {
606 simple_req->getExpr(), [&concept_model](const auto *e) {
607 auto simple_expr = common::to_string(e);
608
609 LOG_DBG("=== Processing expression requirement: {}",
610 simple_expr);
611
612 concept_model.add_statement(std::move(simple_expr));
613 });
614 }
615 }
616 else if (req->getKind() == clang::concepts::Requirement::RK_Type) {
618 llvm::dyn_cast<clang::concepts::TypeRequirement>(req),
619 [&concept_model, cpt](const auto *t) {
620 auto type_name = common::to_string(
621 t->getType()->getType(), cpt->getASTContext());
622
623 LOG_DBG(
624 "=== Processing type requirement: {}", type_name);
625
626 concept_model.add_statement(std::move(type_name));
627 });
628 }
629 else if (req->getKind() ==
630 clang::concepts::Requirement::RK_Nested) {
631 const auto *nested_req =
632 llvm::dyn_cast<clang::concepts::NestedRequirement>(req);
633
634 if (nested_req != nullptr) {
636 nested_req->getConstraintExpr(), [](const auto *e) {
637 LOG_DBG("=== Processing nested requirement: {}",
638 common::to_string(e));
639 });
640 }
641 }
642 else if (req->getKind() ==
643 clang::concepts::Requirement::RK_Compound) {
644 const auto *compound_req =
645 llvm::dyn_cast<clang::concepts::ExprRequirement>(req);
646
647 if (compound_req != nullptr) {
648 const auto *compound_expr_ptr = compound_req->getExpr();
649
650 if (compound_expr_ptr != nullptr) {
651 auto compound_expr =
652 common::to_string(compound_expr_ptr);
653
654 auto req_return_type =
655 compound_req->getReturnTypeRequirement();
656
657 if (!req_return_type.isEmpty()) {
658 compound_expr =
659 fmt::format("{{{}}} -> {}", compound_expr,
661 req_return_type.getTypeConstraint()));
662 }
663 else if (compound_req->hasNoexceptRequirement()) {
664 compound_expr =
665 fmt::format("{{{}}} noexcept", compound_expr);
666 }
667
668 LOG_DBG("=== Processing compound requirement: {}",
669 compound_expr);
670
671 concept_model.add_statement(std::move(compound_expr));
672 }
673 }
674 }
675 }
676 }
677 else if (const auto *binop = llvm::dyn_cast<clang::BinaryOperator>(expr);
678 binop) {
679 process_constraint_requirements(cpt, binop->getLHS(), concept_model);
680 process_constraint_requirements(cpt, binop->getRHS(), concept_model);
681 }
682 else if (const auto *unop = llvm::dyn_cast<clang::UnaryOperator>(expr);
683 unop) {
684 process_constraint_requirements(cpt, unop->getSubExpr(), concept_model);
685 }
686}

◆ process_declaration() [1/3]

void clanguml::class_diagram::visitor::translation_unit_visitor::process_declaration ( const clang::CXXRecordDecl &  cls,
clanguml::class_diagram::model::class_ c 
)
private

Process class declaration.

Parameters
clsClass declaration
cClass diagram element returned from create_declaration

Definition at line 1061 of file translation_unit_visitor.cc.

1063{
1064 // Process class child entities
1065 process_children(&cls, c);
1066
1067 // Process class bases
1068 process_class_bases(&cls, c);
1069
1070 c.complete(true);
1071}

◆ process_declaration() [2/3]

void clanguml::class_diagram::visitor::translation_unit_visitor::process_declaration ( const clang::EnumDecl &  enm,
clanguml::class_diagram::model::enum_ e 
)
private

Process enum declaration.

Parameters
enmEnum declaration
eEnum diagram element returned from create_declaration

Definition at line 218 of file translation_unit_visitor.cc.

220{
221 for (const auto &ev : enm.enumerators()) {
222 e.constants().push_back(ev->getNameAsString());
223 }
224
225 e.complete(true);
226}

◆ process_declaration() [3/3]

void clanguml::class_diagram::visitor::translation_unit_visitor::process_declaration ( const clang::RecordDecl &  cls,
class_ c 
)
private

Process record members.

Parameters
clsClass declaration
cClass diagram element model

Definition at line 1311 of file translation_unit_visitor.cc.

1313{
1314 // Iterate over C struct fields
1315 for (const auto *field : cls.fields()) {
1316 if (field != nullptr)
1317 process_field(*field, c);
1318 }
1319
1320 c.complete(true);
1321}

◆ process_field()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_field ( const clang::FieldDecl &  field_declaration,
clanguml::class_diagram::model::class_ c 
)
private

Process class data member.

Parameters
field_declarationData member declaration
cClass diagram element model

Definition at line 2194 of file translation_unit_visitor.cc.

2196{
2197 LOG_DBG(
2198 "== Visiting record member {}", field_declaration.getNameAsString());
2199
2200 // Default hint for relationship is aggregation
2201 auto relationship_hint = relationship_t::kAggregation;
2202 // If the first type of the template instantiation of this field type
2203 // has been added as aggregation relationship with class 'c', don't
2204 // add it's nested template types as aggregation
2205 [[maybe_unused]] bool template_instantiation_added_as_aggregation{false};
2206 // The actual field type
2207 auto field_type = field_declaration.getType();
2208
2209 // String representation of the field type
2210 auto type_name =
2211 common::to_string(field_type, field_declaration.getASTContext());
2212 // The field name
2213 const auto field_name = field_declaration.getNameAsString();
2214
2215 auto field_type_str =
2216 common::to_string(field_type, field_declaration.getASTContext(), false);
2217
2219
2220 class_member field{
2221 common::access_specifier_to_access_t(field_declaration.getAccess()),
2222 field_name, config().simplify_template_type(field_type_str)};
2223
2224 field.set_qualified_name(field_declaration.getQualifiedNameAsString());
2225
2226 // Parse the field comment
2227 process_comment(field_declaration, field);
2228 // Register the source location of the field declaration
2229 set_source_location(field_declaration, field);
2230
2231 // If the comment contains a skip directive, just return
2232 if (field.skip())
2233 return;
2234
2235 if (field_type->isPointerType()) {
2236 relationship_hint = relationship_t::kAssociation;
2237 field_type = field_type->getPointeeType();
2238 }
2239 else if (field_type->isLValueReferenceType()) {
2240 relationship_hint = relationship_t::kAssociation;
2241 field_type = field_type.getNonReferenceType();
2242 }
2243 else if (field_type->isArrayType()) {
2244 relationship_hint = relationship_t::kAggregation;
2245 while (field_type->isArrayType()) {
2246 auto current_multiplicity = field.destination_multiplicity();
2247 if (!current_multiplicity)
2248 field.set_destination_multiplicity(common::get_array_size(
2249 *field_type->getAsArrayTypeUnsafe()));
2250 else {
2251 auto maybe_array_size =
2252 common::get_array_size(*field_type->getAsArrayTypeUnsafe());
2253 if (maybe_array_size.has_value()) {
2254 field.set_destination_multiplicity(
2255 current_multiplicity.value() *
2256 maybe_array_size.value());
2257 }
2258 }
2259
2260 field_type = field_type->getAsArrayTypeUnsafe()->getElementType();
2261 }
2262 }
2263 else if (field_type->isRValueReferenceType()) {
2264 field_type = field_type.getNonReferenceType();
2265 }
2266
2267 auto [overridden_relationship_hint, overridden] =
2268 override_relationship_hint(type_name, -1, relationship_hint);
2269 if (overridden)
2270 relationship_hint = overridden_relationship_hint;
2271
2272 found_relationships_t relationships;
2273
2274 const auto *template_field_type =
2275 field_type->getAs<clang::TemplateSpecializationType>();
2276 // TODO: Refactor to an unalias_type() method
2277 if (template_field_type != nullptr)
2278 if (template_field_type->isTypeAlias())
2279 template_field_type =
2280 template_field_type->getAliasedType()
2281 ->getAs<clang::TemplateSpecializationType>();
2282
2283 bool field_type_is_template_template_parameter{false};
2284 if (template_field_type != nullptr) {
2285 // Skip types which are template template parameters of the parent
2286 // template
2287 for (const auto &class_template_param : c.template_params()) {
2288 if (class_template_param.name() ==
2289 template_field_type->getTemplateName()
2290 .getAsTemplateDecl()
2291 ->getNameAsString() +
2292 "<>") {
2293 field_type_is_template_template_parameter = true;
2294 }
2295 }
2296 }
2297
2298 // Process the type which is template instantiation of some sort
2299 if (template_field_type != nullptr &&
2300 !field_type_is_template_template_parameter) {
2301 // Build the template instantiation for the field type
2302 auto template_specialization_ptr =
2303 std::make_unique<class_>(config().using_namespace());
2305 *template_specialization_ptr,
2306 field_type->getAs<clang::TemplateSpecializationType>()
2307 ->getTemplateName()
2308 .getAsTemplateDecl(),
2309 *template_field_type, {&c});
2310 template_specialization_ptr->is_template(true);
2311
2312 if (!field.skip_relationship() && template_specialization_ptr) {
2313 const auto &template_specialization = *template_specialization_ptr;
2314
2315 // Check if this template instantiation should be added to the
2316 // current diagram. Even if the top level template type for
2317 // this instantiation should not be part of the diagram, e.g.
2318 // it's a std::vector<>, it's nested types might be added
2319 bool add_template_instantiation_to_diagram{false};
2320 if (diagram().should_include(template_specialization)) {
2321
2322 found_relationships_t::value_type r{
2323 template_specialization.id(), relationship_hint,
2324 &field_declaration};
2325
2326 add_template_instantiation_to_diagram = true;
2327
2328 // If the template instantiation for the build type has been
2329 // added as aggregation, skip its nested templates
2330 template_instantiation_added_as_aggregation =
2331 relationship_hint == relationship_t::kAggregation;
2332 relationships.emplace_back(std::move(r));
2333 }
2334
2335 // Try to find relationships to types nested in the template
2336 // instantiation
2337 found_relationships_t nested_relationships;
2338 auto idx{0};
2339 if (!template_instantiation_added_as_aggregation) {
2340 for (const auto &template_argument :
2341 template_specialization.template_params()) {
2342
2343 auto template_argument_str = template_argument.to_string(
2344 config().using_namespace(), false);
2345
2346 LOG_DBG("Looking for nested relationships from {}::{} in "
2347 "template argument {}",
2348 c, field_name, template_argument_str);
2349
2350 auto [overridden_relationship_hint_param,
2351 overridden_param] =
2353 template_specialization.full_name(false), idx,
2354 relationship_hint);
2355
2356 template_instantiation_added_as_aggregation =
2357 template_argument.find_nested_relationships(
2358 &field_declaration, nested_relationships,
2359 overridden_relationship_hint_param,
2360 !overridden_param,
2361 [&d = diagram()](const std::string &full_name) {
2362 if (full_name.empty())
2363 return false;
2364 auto [ns, name] = common::split_ns(full_name);
2365 return d.should_include(ns, name);
2366 });
2367 }
2368
2369 // Add any relationships to the class 'c' to the diagram,
2370 // unless the top level type has been added as aggregation
2371 add_relationships(c, field, nested_relationships,
2372 /* break on first aggregation */ false);
2373
2374 idx++;
2375 }
2376
2377 // Add the template instantiation object to the diagram if it
2378 // matches the include pattern
2379 if (add_template_instantiation_to_diagram)
2380 add_class(std::move(template_specialization_ptr));
2381 }
2382 }
2383
2384 if (!field.skip_relationship()) {
2385 // Find relationship for the type if the type has not been added
2386 // as aggregation
2387 if (!template_instantiation_added_as_aggregation) {
2388 if ((field_type->getAsRecordDecl() != nullptr) &&
2389 field_type->getAsRecordDecl()->getNameAsString().empty()) {
2390 // Relationships to fields whose type is an anonymous nested
2391 // struct have to be handled separately here
2392 anonymous_struct_relationships_[field_type->getAsRecordDecl()
2393 ->getID()] =
2394 std::make_tuple(field.name(), relationship_hint,
2395 field.access(), field.destination_multiplicity());
2396 }
2397 else
2398 find_relationships(&field_declaration, field_type,
2399 relationships, relationship_hint);
2400 }
2401
2402 add_relationships(c, field, relationships);
2403 }
2404
2405 // If this is an anonymous struct - replace the anonymous_XYZ part with
2406 // field name
2407 if ((field_type->getAsRecordDecl() != nullptr) &&
2408 field_type->getAsRecordDecl()->getNameAsString().empty()) {
2409 if (util::contains(field.type(), "(anonymous_")) {
2410 std::regex anonymous_re("anonymous_(\\d*)");
2411 field.set_type(
2412 std::regex_replace(field.type(), anonymous_re, field_name));
2413 }
2414 }
2415
2416 if (diagram().should_include(field)) {
2417 c.add_member(std::move(field));
2418 }
2419}

◆ process_friend()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_friend ( const clang::FriendDecl &  f,
class_ c 
)
private

Process class friend.

Parameters
fFriend declaration
cClass diagram element model

Definition at line 1405 of file translation_unit_visitor.cc.

1407{
1408 if (const auto *friend_type_info = f.getFriendType()) {
1409 const auto friend_type = friend_type_info->getType();
1410 if (friend_type->getAs<clang::TemplateSpecializationType>() !=
1411 nullptr) {
1412 // TODO: handle template friend
1413 }
1414 else if (friend_type->getAs<clang::RecordType>() != nullptr) {
1415 if (should_include(friend_type->getAsRecordDecl())) {
1416 relationship r{relationship_t::kFriendship,
1417 common::to_id(*friend_type->getAsRecordDecl()),
1419 "<<friend>>"};
1420
1421 c.add_relationship(std::move(r));
1422 }
1423 }
1424 }
1425}

◆ process_function_parameter()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_function_parameter ( const clang::ParmVarDecl &  param,
class_method method,
class_ c,
const std::set< std::string > &  template_parameter_names = {} 
)
private

Process function/method parameter.

Parameters
paramParameter declaration
methodClass method model
cClass diagram element model
template_parameter_namesIgnored

Definition at line 1964 of file translation_unit_visitor.cc.

1967{
1968 method_parameter parameter;
1969 parameter.set_name(p.getNameAsString());
1970
1971 process_comment(p, parameter);
1972
1973 if (parameter.skip())
1974 return;
1975
1976 auto parameter_type = common::to_string(p.getType(), p.getASTContext());
1977
1978 // Is there no better way to determine that 'type' is a lambda?
1980
1981 parameter.set_type(parameter_type);
1982
1983 if (p.hasDefaultArg()) {
1984 const auto *default_arg = p.getDefaultArg();
1985 if (default_arg != nullptr) {
1986 auto default_arg_str = common::get_source_text(
1987 default_arg->getSourceRange(), source_manager());
1988 parameter.set_default_value(default_arg_str);
1989 }
1990 }
1991
1992 if (!parameter.skip_relationship()) {
1993 // find relationship for the type
1994 found_relationships_t relationships;
1995
1996 LOG_DBG("Looking for relationships in type: {}",
1997 common::to_string(p.getType(), p.getASTContext()));
1998
1999 if (const auto *templ =
2000 p.getType()
2001 .getNonReferenceType()
2002 .getUnqualifiedType()
2003 ->getAs<clang::TemplateSpecializationType>();
2004 templ != nullptr) {
2005 auto template_specialization_ptr =
2006 std::make_unique<class_>(config().using_namespace());
2008 *template_specialization_ptr,
2009 templ->getTemplateName().getAsTemplateDecl(), *templ, &c);
2010
2011 template_specialization_ptr->is_template(true);
2012
2013 if (diagram().should_include(*template_specialization_ptr)) {
2014 relationships.emplace_back(template_specialization_ptr->id(),
2015 relationship_t::kDependency, &p);
2016
2017 add_class(std::move(template_specialization_ptr));
2018 }
2019 }
2020
2022 &p, p.getType(), relationships, relationship_t::kDependency);
2023
2024 for (const auto &[type_element_id, relationship_type, source_decl] :
2025 relationships) {
2026 if (type_element_id != c.id() &&
2027 (relationship_type != relationship_t::kNone)) {
2028 relationship r{relationship_t::kDependency, type_element_id};
2029
2030 if (source_decl != nullptr) {
2031 set_source_location(*source_decl, r);
2032 }
2033
2034 LOG_DBG("Adding function parameter relationship from {} to "
2035 "{}: {}",
2036 c, r.type(), r.label());
2037
2038 c.add_relationship(std::move(r));
2039 }
2040 }
2041 }
2042
2043 method.add_parameter(std::move(parameter));
2044}

◆ process_function_parameter_find_relationships_in_autotype()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_function_parameter_find_relationships_in_autotype ( model::class_ c,
const clang::AutoType *  atsp 
)
private

Find relationships in function parameter.

Parameters
cClass diagram element model
atspauto type

Definition at line 1633 of file translation_unit_visitor.cc.

1636{
1637 auto desugared_atsp = atsp->getDeducedType();
1638
1639 if (atsp->isSugared()) {
1640 const auto *deduced_type =
1641 atsp->desugar()->getAs<clang::DeducedTemplateSpecializationType>();
1642
1643 if (deduced_type != nullptr)
1644 desugared_atsp = deduced_type->getDeducedType();
1645 }
1646
1647 if (desugared_atsp.isNull())
1648 return;
1649
1650 const auto *deduced_record_type = desugared_atsp->isRecordType()
1651 ? desugared_atsp->getAs<clang::RecordType>()
1652 : nullptr;
1653
1654 if (deduced_record_type != nullptr) {
1655 if (auto *deduced_auto_decl =
1656 llvm::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
1657 deduced_record_type->getDecl());
1658 deduced_auto_decl != nullptr) {
1659
1660 const auto diagram_class_count_before_visit =
1661 diagram().classes().size();
1662
1663 VisitClassTemplateSpecializationDecl(deduced_auto_decl);
1664
1665 const bool visitor_added_new_template_specialization =
1666 (diagram().classes().size() -
1667 diagram_class_count_before_visit) > 0;
1668
1669 if (visitor_added_new_template_specialization) {
1670 const auto &template_specialization_model =
1671 diagram().classes().back();
1672
1673 if (should_include(deduced_auto_decl)) {
1674 relationship r{relationship_t::kDependency,
1675 template_specialization_model.get().id()};
1676
1677 c.add_relationship(std::move(r));
1678 }
1679 }
1680 }
1681 }
1682}

◆ process_method()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_method ( const clang::CXXMethodDecl &  mf,
clanguml::class_diagram::model::class_ c 
)
private

Process class method.

Parameters
mfMethod declaration
cClass diagram element model

Definition at line 1427 of file translation_unit_visitor.cc.

1429{
1430 // TODO: For now skip implicitly default methods
1431 // in the future, add config option to choose
1432 if (mf.isDefaulted() && !mf.isExplicitlyDefaulted())
1433 return;
1434
1435 auto method_return_type =
1436 common::to_string(mf.getReturnType(), mf.getASTContext());
1437
1438 common::ensure_lambda_type_is_relative(config(), method_return_type);
1439
1440 auto method_name = mf.getNameAsString();
1441 if (mf.isTemplated()) {
1442 // Sometimes in template specializations method names contain the
1443 // template parameters for some reason - drop them
1444 // Is there a better way to do this?
1445 method_name = method_name.substr(0, method_name.find('<'));
1446 }
1447
1448 class_method method{common::access_specifier_to_access_t(mf.getAccess()),
1449 util::trim(method_name),
1450 config().simplify_template_type(method_return_type)};
1451
1452 method.set_qualified_name(mf.getQualifiedNameAsString());
1453
1454 process_method_properties(mf, c, method_name, method);
1455
1456 process_comment(mf, method);
1457
1458 // Register the source location of the field declaration
1459 set_source_location(mf, method);
1460
1461 if (method.skip())
1462 return;
1463
1464 for (const auto *param : mf.parameters()) {
1465 if (param != nullptr)
1466 process_function_parameter(*param, method, c);
1467 }
1468
1469 // find relationship for return type
1470 found_relationships_t relationships;
1471
1472 // Move dereferencing to build() method of template_builder
1473 if (const auto *templ = mf.getReturnType()
1474 .getNonReferenceType()
1475 .getUnqualifiedType()
1476 ->getAs<clang::TemplateSpecializationType>();
1477 templ != nullptr) {
1478 const auto *unaliased_type = templ;
1479 if (unaliased_type->isTypeAlias())
1480 unaliased_type = unaliased_type->getAliasedType()
1481 ->getAs<clang::TemplateSpecializationType>();
1482
1483 if (unaliased_type != nullptr) {
1484 auto template_specialization_ptr =
1485 std::make_unique<class_>(config().using_namespace());
1487 *template_specialization_ptr,
1488 unaliased_type->getTemplateName().getAsTemplateDecl(),
1489 *unaliased_type, &c);
1490
1491 template_specialization_ptr->is_template();
1492
1493 if (diagram().should_include(*template_specialization_ptr)) {
1494 relationships.emplace_back(template_specialization_ptr->id(),
1495 relationship_t::kDependency, &mf);
1496
1497 add_class(std::move(template_specialization_ptr));
1498 }
1499 }
1500 }
1501
1503 &mf, mf.getReturnType(), relationships, relationship_t::kDependency);
1504
1505 for (const auto &[type_element_id, relationship_type, source_decl] :
1506 relationships) {
1507 if (type_element_id != c.id() &&
1508 (relationship_type != relationship_t::kNone)) {
1509 relationship r{relationship_t::kDependency, type_element_id};
1510
1511 if (source_decl != nullptr) {
1512 set_source_location(*source_decl, r);
1513 }
1514
1515 LOG_DBG("Adding method return type relationship from {}::{} to "
1516 "{}: {}",
1517 c, mf.getNameAsString(), r.type(), r.label());
1518
1519 c.add_relationship(std::move(r));
1520 }
1521 }
1522
1523 // Also consider the container itself if it is a template
1524 // instantiation it's arguments could count as reference to relevant
1525 // types
1526 auto underlying_type = mf.getReturnType();
1527 if (underlying_type->isReferenceType())
1528 underlying_type = underlying_type.getNonReferenceType();
1529 if (underlying_type->isPointerType())
1530 underlying_type = underlying_type->getPointeeType();
1531
1532 if (const auto *atsp = underlying_type->getAs<clang::AutoType>();
1533 atsp != nullptr) {
1535 }
1536
1537 method.update(config().using_namespace());
1538
1539 if (diagram().should_include(method)) {
1540 LOG_DBG("Adding method: {}", method.name());
1541
1542 c.add_method(std::move(method));
1543 }
1544}

◆ process_method_properties()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_method_properties ( const clang::CXXMethodDecl &  mf,
const class_ c,
const std::string &  method_name,
class_method method 
) const
private

Process class method properties.

Parameters
mfMethod declaration
cClass diagram element model
method_nameMethod name
methodMethod model

Definition at line 1605 of file translation_unit_visitor.cc.

1608{
1609 const bool is_constructor = c.name() == method_name;
1610 const bool is_destructor = fmt::format("~{}", c.name()) == method_name;
1611
1612#if LLVM_VERSION_MAJOR > 17
1613 method.is_pure_virtual(mf.isPureVirtual());
1614#else
1615 method.is_pure_virtual(mf.isPure());
1616#endif
1617 method.is_virtual(mf.isVirtual());
1618 method.is_const(mf.isConst());
1619 method.is_defaulted(mf.isDefaulted());
1620 method.is_deleted(mf.isDeleted());
1621 method.is_static(mf.isStatic());
1622 method.is_operator(mf.isOverloadedOperator());
1623 method.is_constexpr(mf.isConstexprSpecified() && !is_constructor);
1624 method.is_consteval(mf.isConsteval());
1625 method.is_constructor(is_constructor);
1626 method.is_destructor(is_destructor);
1627 method.is_move_assignment(mf.isMoveAssignmentOperator());
1628 method.is_copy_assignment(mf.isCopyAssignmentOperator());
1629 method.is_noexcept(isNoexceptExceptionSpec(mf.getExceptionSpecType()));
1630 method.is_coroutine(common::is_coroutine(mf));
1631}

◆ process_objc_category_declaration()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_objc_category_declaration ( const clang::ObjCCategoryDecl &  cls,
objc_interface c 
)
private

Process Objective-C category declaration.

Parameters
clsObjective-C category declaration
cClass diagram element returned from create_objc_category_declaration

Definition at line 1073 of file translation_unit_visitor.cc.

1075{
1076 assert(c.is_category());
1077
1078 // Iterate over class methods (both regular and static)
1079 for (const auto *method : cls.methods()) {
1080 if (method != nullptr) {
1081 process_objc_method(*method, c);
1082 }
1083 }
1084
1085 // Add relationship to the ObjC Interface being extended by this
1086 // category
1087 if (cls.getClassInterface() != nullptr) {
1088 eid_t objc_interface_id = common::to_id(*cls.getClassInterface());
1089 common::model::relationship r{
1090 relationship_t::kInstantiation, objc_interface_id, access_t::kNone};
1091
1092 LOG_DBG("Found protocol {} [{}] for ObjC interface {}",
1093 cls.getClassInterface()->getNameAsString(),
1094 objc_interface_id.value(), c.name());
1095
1096 c.add_relationship(std::move(r));
1097 }
1098
1099 c.complete(true);
1100}

◆ process_objc_interface_base()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_objc_interface_base ( const clang::ObjCInterfaceDecl &  cls,
objc_interface c 
)
private

Process Objective-C class base.

Parameters
clsObjective-C interface declaration
cClass diagram element model

Definition at line 1139 of file translation_unit_visitor.cc.

1141{
1142 if (const auto *base = cls.getSuperClass(); base != nullptr) {
1143 eid_t parent_id = common::to_id(*base);
1144 common::model::relationship cp{parent_id, access_t::kNone, false};
1145
1146 LOG_DBG("Found base class {} [{}] for ObjC interface {}",
1147 base->getNameAsString(), parent_id.value(), c.name());
1148
1149 c.add_relationship(std::move(cp));
1150 }
1151
1152 for (const auto *protocol : cls.protocols()) {
1153 eid_t parent_id = common::to_id(*protocol);
1154 common::model::relationship cp{
1155 relationship_t::kInstantiation, parent_id, access_t::kNone};
1156
1157 LOG_DBG("Found protocol {} [{}] for ObjC interface {}",
1158 protocol->getNameAsString(), parent_id.value(), c.name());
1159
1160 c.add_relationship(std::move(cp));
1161 }
1162}

◆ process_objc_interface_declaration()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_objc_interface_declaration ( const clang::ObjCInterfaceDecl &  cls,
objc_interface c 
)
private

Process Objective-C interface declaration.

Parameters
clsObjective-C interface declaration
cClass diagram element returned from create_objc_interface_declaration

Definition at line 1117 of file translation_unit_visitor.cc.

1119{
1120 assert(!c.is_protocol() && !c.is_category());
1121
1122 // Iterate over class methods (both regular and static)
1123 for (const auto *method : cls.methods()) {
1124 if (method != nullptr) {
1125 process_objc_method(*method, c);
1126 }
1127 }
1128
1129 for (const auto *ivar : cls.ivars()) {
1130 if (ivar != nullptr) {
1131 process_objc_ivar(*ivar, c);
1132 }
1133 }
1134
1136
1137 c.complete(true);
1138}

◆ process_objc_ivar()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_objc_ivar ( const clang::ObjCIvarDecl &  ivar,
objc_interface c 
)
private

Process Objective-C data member.

Parameters
field_declarationData member declaration
cClass diagram element model

Definition at line 1164 of file translation_unit_visitor.cc.

1166{
1167 LOG_DBG("== Visiting ObjC ivar {}", ivar.getNameAsString());
1168
1169 // Default hint for relationship is aggregation
1170 auto relationship_hint = relationship_t::kAggregation;
1171
1172 auto field_type = ivar.getType();
1173 auto field_type_str =
1174 common::to_string(field_type, ivar.getASTContext(), false);
1175
1176 auto type_name = common::to_string(field_type, ivar.getASTContext());
1177
1178 const auto field_name = ivar.getNameAsString();
1179
1180 objc_member field{
1181 common::access_specifier_to_access_t(ivar.getAccessControl()),
1182 field_name, field_type_str};
1183
1184 field.set_qualified_name(ivar.getQualifiedNameAsString());
1185
1186 process_comment(ivar, field);
1187 set_source_location(ivar, field);
1188
1189 if (field.skip())
1190 return;
1191
1192 if (field_type->isObjCObjectPointerType()) {
1193 relationship_hint = relationship_t::kAggregation;
1194 field_type = field_type->getPointeeType();
1195 }
1196 else if (field_type->isPointerType()) {
1197 relationship_hint = relationship_t::kAssociation;
1198 field_type = field_type->getPointeeType();
1199 }
1200 else if (field_type->isLValueReferenceType()) {
1201 relationship_hint = relationship_t::kAssociation;
1202 field_type = field_type.getNonReferenceType();
1203 }
1204 else if (field_type->isArrayType()) {
1205 relationship_hint = relationship_t::kAggregation;
1206 while (field_type->isArrayType()) {
1207 auto current_multiplicity = field.destination_multiplicity();
1208 if (!current_multiplicity)
1209 field.set_destination_multiplicity(common::get_array_size(
1210 *field_type->getAsArrayTypeUnsafe()));
1211 else {
1212 auto maybe_array_size =
1213 common::get_array_size(*field_type->getAsArrayTypeUnsafe());
1214 if (maybe_array_size.has_value()) {
1215 field.set_destination_multiplicity(
1216 current_multiplicity.value() *
1217 maybe_array_size.value());
1218 }
1219 }
1220
1221 field_type = field_type->getAsArrayTypeUnsafe()->getElementType();
1222 }
1223 }
1224 else if (field_type->isRValueReferenceType()) {
1225 field_type = field_type.getNonReferenceType();
1226 }
1227
1228 found_relationships_t relationships;
1229
1230 if (!field.skip_relationship()) {
1231 // Find relationship for the type if the type has not been added
1232 // as aggregation
1233 if (field_type->getAsObjCInterfaceType() != nullptr &&
1234 field_type->getAsObjCInterfaceType()->getInterface() != nullptr) {
1235 const auto *objc_iface =
1236 field_type->getAsObjCInterfaceType()->getInterface();
1237
1238 relationships.emplace_back(common::to_id(*objc_iface),
1239 relationship_t::kAggregation, &ivar);
1240 }
1241 else if ((field_type->getAsRecordDecl() != nullptr) &&
1242 field_type->getAsRecordDecl()->getNameAsString().empty()) {
1243 // Relationships to fields whose type is an anonymous and nested
1244 // struct have to be handled separately here
1245 anonymous_struct_relationships_[field_type->getAsRecordDecl()
1246 ->getID()] =
1247 std::make_tuple(field.name(), relationship_hint, field.access(),
1248 field.destination_multiplicity());
1249 }
1250 else
1252 &ivar, field_type, relationships, relationship_hint);
1253
1254 add_relationships(c, field, relationships);
1255 }
1256
1257 // If this is an anonymous struct - replace the anonymous_XYZ part with
1258 // field name
1259 if ((field_type->getAsRecordDecl() != nullptr) &&
1260 field_type->getAsRecordDecl()->getNameAsString().empty()) {
1261 if (util::contains(field.type(), "(anonymous_")) {
1262 std::regex anonymous_re("anonymous_(\\d*)");
1263 field.set_type(
1264 std::regex_replace(field.type(), anonymous_re, field_name));
1265 }
1266 }
1267
1268 c.add_member(std::move(field));
1269}

◆ process_objc_method()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_objc_method ( const clang::ObjCMethodDecl &  mf,
objc_interface c 
)
private

Process Objective-C method.

Parameters
mfMethod declaration
cClass diagram element model

Definition at line 1546 of file translation_unit_visitor.cc.

1548{
1549 auto method_return_type =
1550 common::to_string(mf.getReturnType(), mf.getASTContext());
1551
1552 objc_method method{common::access_specifier_to_access_t(mf.getAccess()),
1553 util::trim(mf.getNameAsString()), method_return_type};
1554
1555 method.set_qualified_name(mf.getQualifiedNameAsString());
1556
1557 process_comment(mf, method);
1558
1559 // Register the source location of the field declaration
1560 set_source_location(mf, method);
1561
1562 if (method.skip())
1563 return;
1564
1565 method.is_static(mf.isClassMethod());
1566 method.is_optional(mf.isOptional());
1567
1568 for (const auto *param : mf.parameters()) {
1569 if (param != nullptr)
1570 process_objc_method_parameter(*param, method, c);
1571 }
1572
1573 // find relationship for return type
1574 found_relationships_t relationships;
1575
1577 &mf, mf.getReturnType(), relationships, relationship_t::kDependency);
1578
1579 for (const auto &[type_element_id, relationship_type, source_decl] :
1580 relationships) {
1581 if (type_element_id != c.id() &&
1582 (relationship_type != relationship_t::kNone)) {
1583 relationship r{relationship_t::kDependency, type_element_id};
1584
1585 if (source_decl != nullptr) {
1586 set_source_location(*source_decl, r);
1587 }
1588
1589 LOG_DBG("Adding method return type relationship from {}::{} to "
1590 "{}: {}",
1591 c, mf.getNameAsString(), r.type(), r.label());
1592
1593 c.add_relationship(std::move(r));
1594 }
1595 }
1596
1597 // TODO
1598 if (diagram().should_include(method)) {
1599 LOG_DBG("Adding ObjC method: {}", method.name());
1600
1601 c.add_method(std::move(method));
1602 }
1603}

◆ process_objc_method_parameter()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_objc_method_parameter ( const clang::ParmVarDecl &  param,
objc_method method,
objc_interface c 
)
private

Process Objective-C class method parameter.

Parameters
paramParameter declaration
methodClass method model
cClass diagram element model
template_parameter_namesIgnored

Definition at line 1917 of file translation_unit_visitor.cc.

1919{
1920 method_parameter parameter;
1921 parameter.set_name(param.getNameAsString());
1922
1923 process_comment(param, parameter);
1924
1925 if (parameter.skip())
1926 return;
1927
1928 auto parameter_type =
1929 common::to_string(param.getType(), param.getASTContext());
1930 parameter.set_type(parameter_type);
1931
1932 if (!parameter.skip_relationship()) {
1933 // find relationship for the type
1934 found_relationships_t relationships;
1935
1936 LOG_DBG("Looking for relationships in type: {}",
1937 common::to_string(param.getType(), param.getASTContext()));
1938
1939 find_relationships(&param, param.getType(), relationships,
1940 relationship_t::kDependency);
1941
1942 for (const auto &[type_element_id, relationship_type, source_decl] :
1943 relationships) {
1944 if (type_element_id != c.id() &&
1945 (relationship_type != relationship_t::kNone)) {
1946 relationship r{relationship_t::kDependency, type_element_id};
1947
1948 if (source_decl != nullptr) {
1949 set_source_location(*source_decl, r);
1950 }
1951
1952 LOG_DBG("Adding ObjC method parameter relationship from {} to "
1953 "{}: {}",
1954 c, r.type(), r.label());
1955
1956 c.add_relationship(std::move(r));
1957 }
1958 }
1959 }
1960
1961 method.add_parameter(std::move(parameter));
1962}

◆ process_objc_protocol_declaration()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_objc_protocol_declaration ( const clang::ObjCProtocolDecl &  cls,
objc_interface c 
)
private

Process Objective-C protocol declaration.

Parameters
clsObjective-C protocol declaration
cClass diagram element returned from create_objc_protocol_declaration

Definition at line 1102 of file translation_unit_visitor.cc.

1104{
1105 assert(c.is_protocol());
1106
1107 // Iterate over class methods (both regular and static)
1108 for (const auto *method : cls.methods()) {
1109 if (method != nullptr) {
1110 process_objc_method(*method, c);
1111 }
1112 }
1113
1114 c.complete(true);
1115}

◆ process_record_parent()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_record_parent ( clang::RecordDecl *  cls,
class_ c,
const namespace_ &  ns 
)
private

Process record parent element (e.g. for nested classes)

This method handles nested classes or structs.

Parameters
clsRecord declaration
cClass diagram element model
nsPackage in the diagram to which the class c should belong

Definition at line 1043 of file translation_unit_visitor.cc.

1045{
1046 std::optional<eid_t> id_opt;
1047 namespace_ parent_ns = ns;
1048
1049 find_record_parent_id(cls, id_opt, parent_ns);
1050
1051 if (id_opt && diagram().find<class_>(*id_opt)) {
1052 process_record_parent_by_type<class_>(*id_opt, c, parent_ns, cls);
1053 }
1054
1055 if (id_opt && diagram().find<objc_interface>(*id_opt)) {
1056 process_record_parent_by_type<objc_interface>(
1057 *id_opt, c, parent_ns, cls);
1058 }
1059}

◆ process_record_parent_by_type()

template<typename T >
void clanguml::class_diagram::visitor::translation_unit_visitor::process_record_parent_by_type ( eid_t  parent_id,
class_ c,
namespace_  parent_ns,
const clang::RecordDecl *  decl 
)
private

Definition at line 633 of file translation_unit_visitor.h.

635{
636 // Here we have 2 options, either:
637 // - the parent is a regular C++ class/struct
638 // - the parent is a class template declaration/specialization
639 auto parent_class = diagram().find<T>(parent_id);
640
641 c.set_namespace(parent_ns);
642 const auto cls_name = decl->getNameAsString();
643 if (cls_name.empty()) {
644 // Nested structs can be anonymous
645 if (anonymous_struct_relationships_.count(decl->getID()) > 0) {
646 const auto &[label, hint, access, destination_multiplicity] =
647 anonymous_struct_relationships_[decl->getID()];
648
649 c.set_name(parent_class.value().name() + "##" +
650 fmt::format("({})", label));
651
652 std::string destination_multiplicity_str{};
653 if (destination_multiplicity.has_value()) {
654 destination_multiplicity_str =
655 std::to_string(*destination_multiplicity);
656 }
657
658 parent_class.value().add_relationship(
659 {hint, common::to_id(c.full_name(false)), access, label, "",
660 destination_multiplicity_str});
661 }
662 else
663 c.set_name(parent_class.value().name() + "##" +
664 fmt::format("(anonymous_{})", std::to_string(decl->getID())));
665 }
666 else {
667 c.set_name(
668 parent_class.value().name() + "##" + decl->getNameAsString());
669 }
670
671 c.set_id(common::to_id(c.full_name(false)));
672
673 if (!(decl->getNameAsString().empty())) {
674 // Don't add anonymous structs as contained in the class
675 // as they are already added as aggregations
676 c.add_relationship({relationship_t::kContainment, parent_id});
677 }
678
679 c.nested(true);
680}

◆ process_static_field()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_static_field ( const clang::VarDecl &  field_declaration,
clanguml::class_diagram::model::class_ c 
)
private

Process class static data member.

Parameters
field_declarationStatic data member declaration
cClass diagram element model

Definition at line 2111 of file translation_unit_visitor.cc.

2113{
2114 const auto field_type = field_declaration.getType();
2115 auto type_name =
2116 common::to_string(field_type, field_declaration.getASTContext());
2117 if (type_name.empty())
2118 type_name = "<<anonymous>>";
2119
2120 class_member field{
2121 common::access_specifier_to_access_t(field_declaration.getAccess()),
2122 field_declaration.getNameAsString(),
2123 config().simplify_template_type(type_name)};
2124
2125 field.set_qualified_name(field_declaration.getQualifiedNameAsString());
2126
2127 field.is_static(true);
2128
2129 process_comment(field_declaration, field);
2130 set_source_location(field_declaration, field);
2131
2132 if (field.skip() || !diagram().should_include(field))
2133 return;
2134
2135 if (!field.skip_relationship()) {
2136 found_relationships_t relationships;
2137
2138 // find relationship for the type
2139 find_relationships(&field_declaration, field_declaration.getType(),
2140 relationships, relationship_t::kAssociation);
2141
2142 add_relationships(c, field, relationships);
2143 }
2144
2145 c.add_member(std::move(field));
2146}

◆ process_template_method()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_template_method ( const clang::FunctionTemplateDecl &  mf,
clanguml::class_diagram::model::class_ c 
)
private

Process class template method.

Parameters
mfMethod declaration
cClass diagram element model

Definition at line 1684 of file translation_unit_visitor.cc.

1686{
1687 // TODO: For now skip implicitly default methods
1688 // in the future, add config option to choose
1689 if (mf.getTemplatedDecl()->isDefaulted() &&
1690 !mf.getTemplatedDecl()->isExplicitlyDefaulted())
1691 return;
1692
1693 auto method_name = mf.getNameAsString();
1694 if (mf.isTemplated()) {
1695 // Sometimes in template specializations method names contain the
1696 // template parameters for some reason - drop them
1697 // Is there a better way to do this?
1698 method_name = util::trim(method_name.substr(0, method_name.find('<')));
1699 }
1700
1701 class_method method{common::access_specifier_to_access_t(mf.getAccess()),
1702 method_name, mf.getTemplatedDecl()->getReturnType().getAsString()};
1703
1705 clang::dyn_cast<clang::CXXMethodDecl>(mf.getTemplatedDecl()),
1706 [&](const auto *decl) {
1707 process_method_properties(*decl, c, method_name, method);
1708 });
1709
1711
1712 process_comment(mf, method);
1713
1714 if (method.skip())
1715 return;
1716
1717 for (const auto *param : mf.getTemplatedDecl()->parameters()) {
1718 if (param != nullptr)
1719 process_function_parameter(*param, method, c);
1720 }
1721
1722 method.update(config().using_namespace());
1723
1724 if (diagram().should_include(method)) {
1725 LOG_DBG("Adding method: {}", method.name());
1726
1727 c.add_method(std::move(method));
1728 }
1729}

◆ process_template_specialization()

std::unique_ptr< class_ > clanguml::class_diagram::visitor::translation_unit_visitor::process_template_specialization ( clang::ClassTemplateSpecializationDecl *  cls)
private

Process class template specialization/instantiation.

Parameters
clsClass template specialization declaration
Returns
Class diagram element model

Definition at line 2149 of file translation_unit_visitor.cc.

2151{
2152 LOG_DBG("Processing template specialization {} at {}",
2153 cls->getQualifiedNameAsString(),
2154 cls->getLocation().printToString(source_manager()));
2155
2156 auto c_ptr = std::make_unique<class_>(config().using_namespace());
2158
2159 auto &template_instantiation = *c_ptr;
2160 template_instantiation.is_template(true);
2161
2162 // TODO: refactor to method get_qualified_name()
2163 auto qualified_name = cls->getQualifiedNameAsString();
2164 util::replace_all(qualified_name, "(anonymous namespace)", "");
2165 util::replace_all(qualified_name, "::::", "::");
2166
2167 namespace_ ns{qualified_name};
2168 ns.pop_back();
2169 template_instantiation.set_name(cls->getNameAsString());
2170 template_instantiation.set_namespace(ns);
2171
2172 template_instantiation.is_struct(cls->isStruct());
2173
2174 process_record_parent(cls, template_instantiation, ns);
2175
2176 if (!template_instantiation.is_nested()) {
2177 template_instantiation.set_name(common::get_tag_name(*cls));
2178 template_instantiation.set_id(
2179 common::to_id(template_instantiation.full_name(false)));
2180 }
2181
2182 process_comment(*cls, template_instantiation);
2183 set_source_location(*cls, template_instantiation);
2184 set_owning_module(*cls, template_instantiation);
2185
2186 if (template_instantiation.skip())
2187 return {};
2188
2189 id_mapper().add(cls->getID(), template_instantiation.id());
2190
2191 return c_ptr;
2192}

◆ process_template_specialization_children()

void clanguml::class_diagram::visitor::translation_unit_visitor::process_template_specialization_children ( const clang::ClassTemplateSpecializationDecl *  cls,
class_ c 
)
private

Process template specialization children (members and methods)

Parameters
clsClass template specialization declaration
cClass diagram element model

◆ resolve_local_to_global_ids()

void clanguml::class_diagram::visitor::translation_unit_visitor::resolve_local_to_global_ids ( )
private

Replace any AST local ids in diagram elements with global ones.

Not all elements global ids can be set in relationships during traversal of the AST. In such cases, a local id (obtained from getID()) and at after the traversal is complete, the id is replaced with the global diagram id.

Definition at line 2487 of file translation_unit_visitor.cc.

2488{
2489 diagram().for_all_elements([&](auto &element_view) {
2490 for (const auto &el : element_view) {
2491 for (auto &rel : el.get().relationships()) {
2492 if (!rel.destination().is_global()) {
2493 const auto maybe_id =
2494 id_mapper().get_global_id(rel.destination());
2495 if (maybe_id) {
2496 LOG_TRACE("= Resolved instantiation destination "
2497 "from local "
2498 "id {} to global id {}",
2499 rel.destination(), *maybe_id);
2500 rel.set_destination(*maybe_id);
2501 }
2502 }
2503 }
2504 el.get().remove_duplicate_relationships();
2505 }
2506 });
2507}

◆ tbuilder()

template_builder_t & clanguml::class_diagram::visitor::translation_unit_visitor::tbuilder ( )
inlineprivate

Get template builder reference.

Returns
Reference to 'template_builder' instance

Definition at line 600 of file translation_unit_visitor.h.

600{ return template_builder_; }

Member Data Documentation

◆ anonymous_struct_relationships_

std::map<int64_t , std::tuple<std::string , common::model::relationship_t, common::model::access_t, std::optional<size_t> > > clanguml::class_diagram::visitor::translation_unit_visitor::anonymous_struct_relationships_
private

Definition at line 617 of file translation_unit_visitor.h.

◆ forward_declarations_

forward_declarations_t<class_, enum_> clanguml::class_diagram::visitor::translation_unit_visitor::forward_declarations_
private

Definition at line 611 of file translation_unit_visitor.h.

◆ processed_template_qualified_names_

std::set<std::string> clanguml::class_diagram::visitor::translation_unit_visitor::processed_template_qualified_names_
private

When visiting CXX records we need to know if they have already been process in VisitClassTemplateDecl or VisitClassTemplateSpecializationDecl. If yes, then we need to skip it

Todo:
There must be a better way to do this...

Definition at line 629 of file translation_unit_visitor.h.

◆ template_builder_

template_builder_t clanguml::class_diagram::visitor::translation_unit_visitor::template_builder_
private

Definition at line 609 of file translation_unit_visitor.h.

◆ typedef_enum_decls_

std::map<const clang::EnumDecl *, const clang::TypedefDecl *> clanguml::class_diagram::visitor::translation_unit_visitor::typedef_enum_decls_
private

Definition at line 620 of file translation_unit_visitor.h.


The documentation for this class was generated from the following files: