0.6.3
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 2575 of file translation_unit_visitor.cc.

2576{
2577 if ((config().generate_packages() &&
2578 config().package_type() == config::package_type_t::kDirectory)) {
2579 assert(!c->file().empty());
2580
2581 const auto file = config().make_path_relative(c->file());
2582
2583 common::model::path p{
2585 p.pop_back();
2586
2587 diagram().add(p, std::move(c));
2588 }
2589 else if ((config().generate_packages() &&
2590 config().package_type() == config::package_type_t::kModule)) {
2591
2592 const auto module_path = config().make_module_relative(c->module());
2593
2594 common::model::path p{module_path, common::model::path_type::kModule};
2595
2596 diagram().add(p, std::move(c));
2597 }
2598 else {
2599 diagram().add(c->path(), std::move(c));
2600 }
2601}

◆ 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 2651 of file translation_unit_visitor.cc.

2652{
2653 if ((config().generate_packages() &&
2654 config().package_type() == config::package_type_t::kDirectory)) {
2655 assert(!c->file().empty());
2656
2657 const auto file = config().make_path_relative(c->file());
2658
2659 common::model::path p{
2661 p.pop_back();
2662
2663 diagram().add(p, std::move(c));
2664 }
2665 else if ((config().generate_packages() &&
2666 config().package_type() == config::package_type_t::kModule)) {
2667
2668 const auto module_path = config().make_module_relative(c->module());
2669
2670 common::model::path p{module_path, common::model::path_type::kModule};
2671
2672 diagram().add(p, std::move(c));
2673 }
2674 else {
2675 diagram().add(c->path(), std::move(c));
2676 }
2677}

◆ 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 2569 of file translation_unit_visitor.cc.

2571{
2572 add_class(util::unique_pointer_cast<class_>(std::move(element)));
2573}

◆ 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 2623 of file translation_unit_visitor.cc.

2624{
2625 if ((config().generate_packages() &&
2626 config().package_type() == config::package_type_t::kDirectory)) {
2627 assert(!e->file().empty());
2628
2629 const auto file = config().make_path_relative(e->file());
2630
2631 common::model::path p{
2633 p.pop_back();
2634
2635 diagram().add(p, std::move(e));
2636 }
2637 else if ((config().generate_packages() &&
2638 config().package_type() == config::package_type_t::kModule)) {
2639
2640 const auto module_path = config().make_module_relative(e->module());
2641
2642 common::model::path p{module_path, common::model::path_type::kModule};
2643
2644 diagram().add(p, std::move(e));
2645 }
2646 else {
2647 diagram().add(e->path(), std::move(e));
2648 }
2649}

◆ 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 2467 of file translation_unit_visitor.cc.

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

◆ add_objc_interface()

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

Definition at line 2603 of file translation_unit_visitor.cc.

2605{
2606 if ((config().generate_packages() &&
2607 config().package_type() == config::package_type_t::kDirectory)) {
2608 assert(!c->file().empty());
2609
2610 const auto file = config().make_path_relative(c->file());
2611
2612 common::model::path p{
2614 p.pop_back();
2615
2616 diagram().add(p, std::move(c));
2617 }
2618 else {
2619 diagram().add(c->path(), std::move(c));
2620 }
2621}

◆ 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 2557 of file translation_unit_visitor.cc.

2559{
2560 processed_template_qualified_names_.emplace(std::move(qualified_name));
2561}

◆ 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 2044 of file translation_unit_visitor.cc.

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

◆ 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 2530 of file translation_unit_visitor.cc.

2535{
2536 const auto full_declaration_text = common::get_source_text_raw(
2537 concept_specialization->getSourceRange(), source_manager());
2538
2539 if (!full_declaration_text.empty()) {
2540 // Handle typename constraint in requires clause
2541 if (type_name.find("type-parameter-") == 0) {
2542 const auto concept_declaration_text = full_declaration_text.substr(
2543 full_declaration_text.find(cpt->getNameAsString()) +
2544 cpt->getNameAsString().size() + 1);
2545
2546 auto template_params = common::parse_unexposed_template_params(
2547 concept_declaration_text, [](const auto &t) { return t; });
2548
2549 if (template_params.size() > argument_index)
2550 type_name = template_params[argument_index].to_string(
2551 config().using_namespace(), false);
2552 }
2553 constrained_template_params.push_back(type_name);
2554 }
2555}

◆ 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 2521 of file translation_unit_visitor.cc.

2522{
2525 if (config().skip_redundant_dependencies()) {
2526 diagram().remove_redundant_dependencies();
2527 }
2528}

◆ 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 2679 of file translation_unit_visitor.cc.

2682{
2683 auto &template_instantiation = dynamic_cast<class_diagram::model::class_ &>(
2684 template_instantiation_base);
2685
2686 // First try to find the best match for this template in partially
2687 // specialized templates
2688 std::string destination{};
2689 std::string best_match_full_name{};
2690 auto full_template_name = template_instantiation.full_name(false);
2691 int best_match{};
2692 eid_t best_match_id{};
2693
2694 for (const auto templ : diagram().classes()) {
2695 if (templ.get() == template_instantiation)
2696 continue;
2697
2698 auto c_full_name = templ.get().full_name(false);
2699 auto match =
2700 template_instantiation.calculate_template_specialization_match(
2701 templ.get());
2702
2703 if (match > best_match) {
2704 best_match = match;
2705 best_match_full_name = c_full_name;
2706 best_match_id = templ.get().id();
2707 }
2708 }
2709
2710 auto templated_decl_global_id =
2711 id_mapper().get_global_id(templated_decl_id).value_or(eid_t{});
2712
2713 if (best_match_id.value() > 0 &&
2714 best_match_id != template_instantiation.id()) {
2715 destination = best_match_full_name;
2716 template_instantiation.add_relationship(
2718 template_instantiation.template_specialization_found(true);
2719 }
2720 // If we can't find optimal match for parent template specialization,
2721 // just use whatever clang suggests
2722 else if (diagram().has_element(templated_decl_global_id) &&
2723 templated_decl_global_id != template_instantiation.id()) {
2724 template_instantiation.add_relationship(
2726 templated_decl_global_id});
2727 template_instantiation.template_specialization_found(true);
2728 }
2729 else if (id_mapper().get_global_id(templated_decl_id).has_value() &&
2730 id_mapper().get_global_id(templated_decl_id).value() !=
2731 template_instantiation.id()) {
2732 template_instantiation.add_relationship(
2734 template_instantiation.template_specialization_found(true);
2735 }
2736 else if (!diagram().has_element(templated_decl_global_id) &&
2737 !id_mapper().get_global_id(templated_decl_id).has_value()) {
2738 // Add a deferred relationship using the local Clang AST id - it will
2739 // be resolved to a global id in resolve_local_to_global_ids() during
2740 // finalize(). This handles both the case where the template is in the
2741 // included namespace (condition previously skipped this), and the case
2742 // where the global id is not yet known.
2743 LOG_DBG("== Cannot determine global id for specialization template {} "
2744 "- delaying until the translation unit is complete ",
2745 templated_decl_global_id);
2746
2747 template_instantiation.add_relationship(
2749 }
2750}

◆ 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 2420 of file translation_unit_visitor.cc.

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

◆ 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 = template_argument.getAsType()
1897 ->getAs<clang::FunctionProtoType>();
1898 function_type != nullptr) {
1899 for (const auto &param_type : function_type->param_types()) {
1900 result = find_relationships(decl, param_type, relationships,
1901 relationship_t::kDependency);
1902 }
1903 }
1904 else if (template_argument_kind ==
1905 clang::TemplateArgument::ArgKind::Type) {
1906 result = find_relationships(decl, template_argument.getAsType(),
1907 relationships, overridden_relationship_hint);
1908 }
1909 idx++;
1910 }
1911 }
1912
1913 return result;
1914}

◆ 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 2563 of file translation_unit_visitor.cc.

2565{
2567}

◆ 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 2089 of file translation_unit_visitor.cc.

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

◆ 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 2192 of file translation_unit_visitor.cc.

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

◆ 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 1963 of file translation_unit_visitor.cc.

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

◆ 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 1916 of file translation_unit_visitor.cc.

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

◆ 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 2109 of file translation_unit_visitor.cc.

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

◆ 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 2147 of file translation_unit_visitor.cc.

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

◆ 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 2486 of file translation_unit_visitor.cc.

2487{
2488 diagram().for_all_elements([&](auto &element_view) {
2489 for (const auto &el : element_view) {
2490 for (auto &rel : el.get().relationships()) {
2491 if (!rel.destination().is_global()) {
2492 const auto maybe_id =
2493 id_mapper().get_global_id(rel.destination());
2494 if (maybe_id) {
2495 LOG_TRACE("= Resolved instantiation destination "
2496 "from local "
2497 "id {} to global id {}",
2498 rel.destination(), *maybe_id);
2499 rel.set_destination(*maybe_id);
2500 }
2501 }
2502 }
2503 el.get().remove_duplicate_relationships();
2504
2505 // Remove self-referential instantiation relationships.
2506 // These can arise when a partial specialization's deferred local
2507 // Clang ID resolves to its own global UML ID after id_mapper
2508 // registration (e.g. conditional_t<Else> ..|> conditional_t<Else>)
2509 auto &rels = el.get().relationships();
2510 rels.erase(std::remove_if(rels.begin(), rels.end(),
2511 [&el](const relationship &r) {
2512 return r.type() ==
2513 relationship_t::kInstantiation &&
2514 r.destination() == el.get().id();
2515 }),
2516 rels.end());
2517 }
2518 });
2519}

◆ 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: