diff --git a/src/class_diagram/generators/json/class_diagram_generator.h b/src/class_diagram/generators/json/class_diagram_generator.h index c9ce8b56..2b1aca36 100644 --- a/src/class_diagram/generators/json/class_diagram_generator.h +++ b/src/class_diagram/generators/json/class_diagram_generator.h @@ -56,36 +56,110 @@ using clanguml::common::model::relationship_t; using namespace clanguml::util; +/** + * @brief Class diagram JSON generator + */ class generator : public common_generator { public: generator(diagram_config &config, diagram_model &model); + /** + * @brief Main generator method. + * + * This method is called first and coordinates the entire diagram + * generation. + * + * @param ostr Output stream. + */ void generate(std::ostream &ostr) const override; + /** + * Render class element into a JSON node. + * + * @param c class diagram element + * @param parent JSON node + */ void generate(const class_ &c, nlohmann::json &parent) const; + /** + * Render enum element into a JSON node. + * + * @param c enum diagram element + * @param parent JSON node + */ void generate(const enum_ &c, nlohmann::json &parent) const; + /** + * Render concept element into a JSON node. + * + * @param c concept diagram element + * @param parent JSON node + */ void generate(const concept_ &c, nlohmann::json &parent) const; + /** + * Render package element into a JSON node. + * + * @param p package diagram element + * @param parent JSON node + */ void generate(const package &p, nlohmann::json &parent) const; + /** + * @brief In a nested diagram, generate the top level elements. + * + * This method iterates over the top level elements. In case the diagram + * is nested (i.e. includes packages), for each package it recursively + * call generation of elements contained in each package. + * + * @param parent JSON node + */ void generate_top_level_elements(nlohmann::json &parent) const; + /** + * @brief Generate all relationships in the diagram. + * + * @param parent JSON node + */ void generate_relationships(nlohmann::json &parent) const; + /** + * @brief Generate all relationships originating at a class element. + * + * @param c Class diagram element + * @param parent JSON node + */ void generate_relationships(const class_ &c, nlohmann::json &parent) const; + /** + * @brief Generate all relationships originating at an enum element. + * + * @param c Enum diagram element + * @param parent JSON node + */ void generate_relationships(const enum_ &c, nlohmann::json &parent) const; + /** + * @brief Generate all relationships originating at a concept element. + * + * @param c Concept diagram element + * @param parent JSON node + */ void generate_relationships( const concept_ &c, nlohmann::json &parent) const; + /** + * @brief Generate all relationships in a package. + * + * If the diagram is nested, it recursively calls relationship generation + * for all subelements. + * + * @param p Package diagram element + * @param parent JSON node + */ void generate_relationships(const package &p, nlohmann::json &parent) const; private: - std::string render_name(std::string name) const; - mutable nlohmann::json json_; }; diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.h b/src/class_diagram/generators/plantuml/class_diagram_generator.h index 1dd0c55c..54adce86 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.h +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.h @@ -59,60 +59,208 @@ using clanguml::common::model::relationship_t; using namespace clanguml::util; +/** + * @brief Class diagram PlantUML generator + */ class generator : public common_generator { using method_groups_t = std::map>; public: generator(diagram_config &config, diagram_model &model); + /** + * @brief Main generator method. + * + * This method is called first and coordinates the entire diagram + * generation. + * + * @param ostr Output stream. + */ + void generate(std::ostream &ostr) const override; + + /** + * @brief In a nested diagram, generate the top level elements. + * + * This method iterates over the top level elements. In case the diagram + * is nested (i.e. includes packages), for each package it recursively + * call generation of elements contained in each package. + * + * @param parent JSON node + */ + void generate_top_level_elements(std::ostream &ostr) const; + + /** + * @brief Generate a hyperlink for a class element. + * + * @param ostr Output stream + * @param e Class element (e.g. a method) + */ void generate_link(std::ostream &ostr, const class_element &e) const; + /** + * @brief Generate PlantUML alias for a class element. + * + * @param c Class element + * @param ostr Output stream + */ void generate_alias(const class_ &c, std::ostream &ostr) const; + /** + * @brief Generate PlantUML alias for a enum element. + * + * @param e Enum element + * @param ostr Output stream + */ void generate_alias(const enum_ &e, std::ostream &ostr) const; + /** + * @brief Generate PlantUML alias for a concept element. + * + * @param c Concept element + * @param ostr Output stream + */ void generate_alias(const concept_ &c, std::ostream &ostr) const; + /** + * @brief Render class element to PlantUML + * + * @param c Class element + * @param ostr Output stream + */ void generate(const class_ &c, std::ostream &ostr) const; + /** + * @brief Render class methods to PlantUML + * + * @param methods List of class methods + * @param ostr Output stream + */ void generate_methods( const std::vector &methods, std::ostream &ostr) const; + /** + * @brief Render class methods to PlantUML in groups + * + * @param methods Methods grouped by method type + * @param ostr Output stream + */ void generate_methods( const method_groups_t &methods, std::ostream &ostr) const; + /** + * @brief Render class method to PlantUML + * + * @param m Class method + * @param ostr Output stream + */ void generate_method(const class_method &m, std::ostream &ostr) const; + /** + * @brief Render class member to PlantUML + * + * @param m Class member + * @param ostr Output stream + */ void generate_member(const class_member &m, std::ostream &ostr) const; - void generate_top_level_elements(std::ostream &ostr) const; - + /** + * @brief Render all relationships in the diagram to PlantUML + * + * @param ostr Output stream + */ void generate_relationships(std::ostream &ostr) const; + /** + * @brief Render all relationships originating from class element. + * + * @param c Class element + * @param ostr Output stream + */ void generate_relationships(const class_ &c, std::ostream &ostr) const; + /** + * @brief Render a specific relationship to PlantUML. + * + * @param r Relationship model + * @param rendered_relations Set of already rendered relationships, to + * ensure that there are no duplicate + * relationships + */ void generate_relationship( const relationship &r, std::set &rendered_relations) const; + /** + * @brief Render enum element to PlantUML + * + * @param e Enum element + * @param ostr Output stream + */ void generate(const enum_ &e, std::ostream &ostr) const; + /** + * @brief Render all relationships originating from enum element. + * + * @param c Enum element + * @param ostr Output stream + */ void generate_relationships(const enum_ &c, std::ostream &ostr) const; + /** + * @brief Render concept element to PlantUML + * + * @param c Concept element + * @param ostr Output stream + */ void generate(const concept_ &c, std::ostream &ostr) const; + /** + * @brief Render all relationships originating from concept element. + * + * @param c Concept element + * @param ostr Output stream + */ void generate_relationships(const concept_ &c, std::ostream &ostr) const; + /** + * @brief Render package element to PlantUML + * + * @param p Package element + * @param ostr Output stream + */ void generate(const package &p, std::ostream &ostr) const; + /** + * @brief Render all relationships originating from package element. + * + * @param p Package element + * @param ostr Output stream + */ void generate_relationships(const package &p, std::ostream &ostr) const; + /** + * @brief Generate any notes attached specifically to some class element. + * + * @param ostream Output stream + * @param member Class element (member or method) + * @param alias PlantUML class alias + */ void generate_member_notes(std::ostream &ostream, - const class_element &member, const std::string &basicString) const; + const class_element &member, const std::string &alias) const; + /** + * @brief Generate elements grouped together in `together` groups. + * + * @param ostr Output stream + */ void generate_groups(std::ostream &ostr) const; - void generate(std::ostream &ostr) const override; - + /** + * @brief Group class methods based on method type. + * + * @param methods List of class methods. + * + * @return Map of method groups. + */ method_groups_t group_methods( const std::vector &methods) const; diff --git a/src/class_diagram/model/class_element.h b/src/class_diagram/model/class_element.h index 0025a3d1..ec7c50eb 100644 --- a/src/class_diagram/model/class_element.h +++ b/src/class_diagram/model/class_element.h @@ -26,6 +26,9 @@ namespace clanguml::class_diagram::model { +/** + * @brief Base class for class elements (e.g. member or method). + */ class class_element : public common::model::decorated_element, public common::model::source_location { public: @@ -34,13 +37,46 @@ public: ~class_element() override = default; + /** + * @brief Get elements access scope. + * + * @return Elements access scope. + */ common::model::access_t access() const; + + /** + * @brief Get elements name. + * + * @return Elements name. + */ std::string name() const; + + /** + * @brief Set elements name. + * + * @param name Elements name. + */ void set_name(const std::string &name); + /** + * @brief Get elements type as string. + * + * @return Elements type as string. + */ std::string type() const; + + /** + * @brief Set elements type as string. + * + * @param type Elements type as string. + */ void set_type(const std::string &type); + /** + * @brief Get elements inja context in JSON. + * + * @return Context in JSON + */ virtual inja::json context() const; private: diff --git a/src/class_diagram/model/class_member.h b/src/class_diagram/model/class_member.h index 982ac445..f68fdeb0 100644 --- a/src/class_diagram/model/class_member.h +++ b/src/class_diagram/model/class_member.h @@ -23,18 +23,38 @@ namespace clanguml::class_diagram::model { +/** + * @brief Class member model. + */ class class_member : public class_element { public: + /** + * @brief Constructor. + * + * @param access Members access scope (e.g. public) + * @param name Members name. + * @param type Members type as string. + */ class_member(common::model::access_t access, const std::string &name, const std::string &type); ~class_member() override = default; + /** + * @brief Whether the member is static. + * + * @return True, if the member is static. + */ bool is_static() const; + + /** + * @brief Set members static status. + * + * @param is_static True, if the member is static. + */ void is_static(bool is_static); private: - bool is_relationship_{false}; bool is_static_{false}; }; diff --git a/src/class_diagram/model/class_method.h b/src/class_diagram/model/class_method.h index 439e72bd..8f89f5ae 100644 --- a/src/class_diagram/model/class_method.h +++ b/src/class_diagram/model/class_method.h @@ -29,56 +29,225 @@ namespace clanguml::class_diagram::model { using clanguml::common::model::template_trait; +/** + * @brief Class method model. + */ class class_method : public class_element, public template_trait { public: + /** + * @brief Constructor. + * + * @param access Methods access scope (e.g. public) + * @param name Methods name. + * @param type Methods return type as string. + */ class_method(common::model::access_t access, const std::string &name, const std::string &type); ~class_method() override = default; + /** + * @brief Whether the method is pure virtual. + * + * @return True, if the method is pure virtual + */ bool is_pure_virtual() const; void is_pure_virtual(bool is_pure_virtual); + /** + * @brief Whether the method is virtual. + * + * @return True, if the method is virtual + */ bool is_virtual() const; + + /** + * @brief Set whether the method is virtual. + * + * @param is_virtual True, if the method is virtual + */ void is_virtual(bool is_virtual); + /** + * @brief Whether the method is const. + * + * @return True, if the method is const + */ bool is_const() const; + + /** + * @brief Set whether the method is const. + * + * @param is_const True, if the method is const + */ void is_const(bool is_const); + /** + * @brief Whether the method is defaulted. + * + * @return True, if the method is defaulted + */ bool is_defaulted() const; + + /** + * @brief Set whether the method is defaulted. + * + * @param is_defaulted True, if the method is defaulted + */ void is_defaulted(bool is_defaulted); + /** + * @brief Whether the method is deleted. + * + * @return True, if the method is deleted + */ bool is_deleted() const; + + /** + * @brief Set whether the method is deleted. + * + * @param is_deleted True, if the method is deleted + */ void is_deleted(bool is_deleted); + /** + * @brief Whether the method is static. + * + * @return True, if the method is static + */ bool is_static() const; + + /** + * @brief Set whether the method is static. + * + * @param is_static True, if the method is static + */ void is_static(bool is_static); + /** + * @brief Whether the method is constexpr. + * + * @return True, if the method is constexpr + */ bool is_constexpr() const; + + /** + * @brief Set whether the method is constexpr. + * + * @param is_constexpr True, if the method is constexpr + */ void is_constexpr(bool is_constexpr); + /** + * @brief Whether the method is consteval. + * + * @return True, if the method is consteval + */ bool is_consteval() const; + + /** + * @brief Set whether the method is consteval. + * + * @param is_consteval True, if the method is consteval + */ void is_consteval(bool is_consteval); + /** + * @brief Whether the method is noexcept. + * + * @return True, if the method is noexcept + */ bool is_noexcept() const; + + /** + * @brief Set whether the method is noexcept. + * + * @param is_noexcept True, if the method is noexcept + */ void is_noexcept(bool is_noexcept); + /** + * @brief Whether the method is a constructor. + * + * @return True, if the method is a constructor + */ bool is_constructor() const; + + /** + * @brief Set whether the method is a constructor. + * + * @param is_constructor True, if the method is a constructor + */ void is_constructor(bool is_constructor); + /** + * @brief Whether the method is a destructor. + * + * @return True, if the method is a destructor + */ bool is_destructor() const; + + /** + * @brief Set whether the method is a destructor. + * + * @param is_destructor True, if the method is a destructor + */ void is_destructor(bool is_destructor); + /** + * @brief Whether the method is move assignment. + * + * @return True, if the method is move assignment + */ bool is_move_assignment() const; + + /** + * @brief Set whether the method is a move assignment. + * + * @param is_move_assignment True, if the method is a move assignment + */ void is_move_assignment(bool is_move_assignment); + /** + * @brief Whether the method is copy assignment. + * + * @return True, if the method is copy assignment + */ bool is_copy_assignment() const; + + /** + * @brief Set whether the method is a copy assignment. + * + * @param is_copy_assignment True, if the method is a copy assignment + */ void is_copy_assignment(bool is_copy_assignment); + /** + * @brief Whether the method is an operator. + * + * @return True, if the method is an operator + */ bool is_operator() const; + + /** + * @brief Set whether the method is an operator. + * + * @param is_copy_assignment True, if the method is an operator + */ void is_operator(bool is_operator); + /** + * @brief Get the method parameters. + * + * @return List of methods parameters + */ const std::vector ¶meters() const; + + /** + * @brief Add methods parameter. + * + * @param parameter Method parameter. + */ void add_parameter(method_parameter &¶meter); private: diff --git a/src/class_diagram/model/class_parent.cc b/src/class_diagram/model/class_parent.cc index 89d0cc31..f12e84fe 100644 --- a/src/class_diagram/model/class_parent.cc +++ b/src/class_diagram/model/class_parent.cc @@ -24,6 +24,10 @@ void class_parent::set_name(const std::string &name) { name_ = name; } std::string class_parent::name() const { return name_; } +void class_parent::set_id(clanguml::common::id_t id) { id_ = id; } + +clanguml::common::id_t class_parent::id() const noexcept { return id_; } + void class_parent::is_virtual(bool is_virtual) { is_virtual_ = is_virtual; } bool class_parent::is_virtual() const { return is_virtual_; } diff --git a/src/class_diagram/model/class_parent.h b/src/class_diagram/model/class_parent.h index 19245750..cf64fb07 100644 --- a/src/class_diagram/model/class_parent.h +++ b/src/class_diagram/model/class_parent.h @@ -25,25 +25,75 @@ namespace clanguml::class_diagram::model { +/** + * @brief Class parent relationship model. + * + * @todo Consider refactoring this class to a regular relationship. + */ class class_parent { public: class_parent() = default; + class_parent(const std::string &name) { set_name(name); set_id(common::to_id(name)); } + /** + * @brief Set the fully qualified name of class parent. + * + * @param name Fully qualified name of the parent class. + */ void set_name(const std::string &name); + + /** + * @brief Get the fully qualified name of class parent. + * + * @return Fully qualified name of the parent class. + */ std::string name() const; - clanguml::common::id_t id() const noexcept { return id_; } - void set_id(clanguml::common::id_t id) { id_ = id; } + /** + * @brief Set the id of class parent. + * + * @param id Id of the parent class. + */ + void set_id(clanguml::common::id_t id); + /** + * @brief Get the id of class parent. + * + * @return Id of the parent class. + */ + clanguml::common::id_t id() const noexcept; + + /** + * @brief Set whether the parent is virtual. + * + * @param is_virtual True if the parent is virtual + */ void is_virtual(bool is_virtual); + + /** + * @brief Get whether the parent is virtual. + * + * @return True, if the parent is virtual + */ bool is_virtual() const; + /** + * @brief Set the parents access scope + * + * @param access Parents access scope + */ void set_access(common::model::access_t access); + + /** + * @brief Get parents access scope. + * + * @return Parents access scope. + */ common::model::access_t access() const; private: diff --git a/src/class_diagram/model/concept.h b/src/class_diagram/model/concept.h index 0ee435c4..cde00db7 100644 --- a/src/class_diagram/model/concept.h +++ b/src/class_diagram/model/concept.h @@ -29,11 +29,9 @@ namespace clanguml::class_diagram::model { -struct requires_expression { - common::model::template_parameter parameter; - std::vector requirements; -}; - +/** + * @brief Model of C++ concept. + */ class concept_ : public common::model::element, public common::model::stylable_element, public common::model::template_trait { @@ -45,6 +43,11 @@ public: concept_ &operator=(const concept_ &) = delete; concept_ &operator=(concept_ &&) = delete; + /** + * @brief Get the elements type name. + * + * @return 'concept' + */ std::string type_name() const override { return "concept"; } friend bool operator==(const concept_ &l, const concept_ &r); @@ -53,12 +56,35 @@ public: std::string full_name_no_ns() const override; + /** + * @brief Add concept parameter + * + * Concept class for convenience uses the same method parameter model + * as regular methods and functions. + * + * @param mp Concept parameter + */ void add_parameter(const method_parameter &mp); + /** + * @brief Get concepts requires expression parameters + * + * @return List of concept requires expression parameters + */ const std::vector &requires_parameters() const; + /** + * @brief Add a concept statement + * + * @param stmt Concept statement + */ void add_statement(std::string stmt); + /** + * @brief Get the concepts requires statements + * + * @return List of concepts requires statements + */ const std::vector &requires_statements() const; private: diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index bda182e3..01f12363 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -45,6 +45,9 @@ using nested_trait_ns = clanguml::common::model::nested_trait; +/** + * @brief Class representing a class diagram. + */ class diagram : public common::model::diagram, public element_view, public element_view, @@ -58,36 +61,132 @@ public: diagram &operator=(const diagram &) = delete; diagram &operator=(diagram &&) = default; + /** + * @brief Get the diagram model type - in this case class. + * + * @return Type of class diagram. + */ diagram_t type() const override; + /** + * Inherit the should_include methods from the common diagram model. + */ using common::model::diagram::should_include; + /** + * @brief Whether a class_member should be included in the diagram. + * + * @param m Class member + * @return True, if class member should be included in the diagram. + */ bool should_include(const class_member &m) const; + /** + * @brief Whether a class_method should be included in the diagram. + * + * @param m Class method + * @return True, if class method should be included in the diagram. + */ bool should_include(const class_method &m) const; + /** + * @brief Search for element in the diagram by fully qualified name. + * + * @param full_name Fully qualified element name. + * @return Optional reference to a diagram element. + */ opt_ref get(const std::string &full_name) const override; + /** + * @brief Search for element in the diagram by id. + * + * @param id Element id. + * @return Optional reference to a diagram element. + */ opt_ref get(diagram_element::id_t id) const override; + /** + * @brief Get list of references to classes in the diagram model. + * + * @return List of references to classes in the diagram model. + */ const common::reference_vector &classes() const; + /** + * @brief Get list of references to enums in the diagram model. + * + * @return List of references to enums in the diagram model. + */ const common::reference_vector &enums() const; + /** + * @brief Get list of references to concepts in the diagram model. + * + * @return List of references to concepts in the diagram model. + */ const common::reference_vector &concepts() const; + /** + * @brief Check, if diagram contains a specific element. + * + * @tparam ElementT Type of diagram element (e.g. class_) + * @param e Element to check + * @return True, if element already exists in the diagram + */ template bool contains(const ElementT &e); + /** + * @brief Find an element in the diagram by name. + * + * This method allows for typed search, where the type of searched for + * element is determined from template specialization. + * + * @tparam ElementT Type of element (e.g. class_) + * @param name Fully qualified name of the element + * @return Optional reference to a diagram element + */ template opt_ref find(const std::string &name) const; + /** + * @brief Find elements in the diagram by regex pattern. + * + * This method allows for typed search, where the type of searched for + * element is determined from template specialization. + * + * @tparam ElementT Type of element (e.g. class_) + * @param name String or regex pattern + * @return List of optional references to matched elements. + */ template std::vector> find( const clanguml::common::string_or_regex &pattern) const; + /** + * @brief Find an element in the diagram by id. + * + * This method allows for typed search, where the type of searched for + * element is determined from template specialization. + * + * @tparam ElementT Type of element (e.g. class_) + * @param id Id of the element + * @return Optional reference to a diagram element + */ template opt_ref find(diagram_element::id_t id) const; + /** + * @brief Add element to the diagram at a specified nested path. + * + * Adds an element to a diagram, at a specific package (if any exist). + * The package is specified by the `parent_path`, which can be either + * a namespace or a directory path. + * + * @tparam ElementT Type of diagram element. + * @param parent_path Path to the parent package of the new diagram element. + * @param e Diagram element to be added. + * @return True, if the element was added to the diagram. + */ template bool add(const path &parent_path, std::unique_ptr &&e) { @@ -98,14 +197,41 @@ public: return add_with_filesystem_path(parent_path, std::move(e)); } + /** + * @brief Convert element id to PlantUML alias. + * + * @todo This method does not belong here - refactor to PlantUML specific + * code. + * + * @param id Id of the diagram element. + * @return PlantUML alias. + */ std::string to_alias(diagram_element::id_t id) const; + /** + * @brief Given an initial set of classes, add all their parents to the + * argument. + * @param parents In and out parameter with the parent classes. + */ void get_parents(clanguml::common::reference_set &parents) const; friend void print_diagram_tree(const diagram &d, int level); + /** + * @brief Check if diagram contains element by id. + * + * @todo Remove in favour of 'contains' + * + * @param id Id of the element. + * @return True, if diagram contains an element with a specific id. + */ bool has_element(diagram_element::id_t id) const override; + /** + * @brief Return the elements JSON context for inja templates. + * + * @return JSON node with elements context. + */ inja::json context() const override; private: diff --git a/src/class_diagram/model/enum.h b/src/class_diagram/model/enum.h index 7916f082..e69d1e2d 100644 --- a/src/class_diagram/model/enum.h +++ b/src/class_diagram/model/enum.h @@ -24,6 +24,9 @@ namespace clanguml::class_diagram::model { +/* + * @brief Diagram element representing an enum. + */ class enum_ : public common::model::element, public common::model::stylable_element { public: @@ -36,15 +39,29 @@ public: std::string type_name() const override { return "enum"; } - // TODO: Do we need this? friend bool operator==(const enum_ &l, const enum_ &r); std::string full_name(bool relative = true) const override; + /** + * @brief Get the enums constants. + * + * @return Enums constants names list. + */ std::vector &constants(); + /** + * @brief Get the enums constants. + * + * @return Enums constants names list. + */ const std::vector &constants() const; + /** + * @brief Get Doxygen link to documentation page for this element. + * + * @return Doxygen link for this element. + */ std::optional doxygen_link() const override; private: diff --git a/src/class_diagram/model/method_parameter.h b/src/class_diagram/model/method_parameter.h index 7e3ca935..f6db2529 100644 --- a/src/class_diagram/model/method_parameter.h +++ b/src/class_diagram/model/method_parameter.h @@ -25,23 +25,73 @@ namespace clanguml::class_diagram::model { +/** + * @brief Model of a method parameter. + */ class method_parameter : public common::model::decorated_element { public: method_parameter() = default; + + /** + * @brief Constructor. + * + * @param type Type of the method parameter as string. + * @param name Name of the method parameter. + * @param default_value Default value of the parameter or empty. + */ method_parameter( std::string type, std::string name, std::string default_value = {}); ~method_parameter() override = default; + /** + * @brief Set parameters type. + * + * @param type Parameters type as string. + */ void set_type(const std::string &type); + + /** + * @brief Get parameters type. + * + * @return Parameters type as string. + */ std::string type() const; + /** + * @brief Set parameters name. + * + * @param type Parameters name. + */ void set_name(const std::string &name); + + /** + * @brief Get parameters name. + * + * @return Parameters name. + */ std::string name() const; + /** + * @brief Set parameters default value. + * + * @param type Parameters default value as string. + */ void set_default_value(const std::string &value); + + /** + * @brief Get parameters name. + * + * @return Parameters name. + */ std::string default_value() const; + /** + * @brief Render the method parameter to a string. + * + * @param using_namespaces If provided, make all namespaces relative to it. + * @return String representation of the parameter. + */ std::string to_string( const common::model::namespace_ &using_namespaces) const; diff --git a/src/class_diagram/visitor/template_builder.cc b/src/class_diagram/visitor/template_builder.cc index d3ff69f7..e9c9fe71 100644 --- a/src/class_diagram/visitor/template_builder.cc +++ b/src/class_diagram/visitor/template_builder.cc @@ -235,7 +235,7 @@ std::unique_ptr template_builder::build(const clang::NamedDecl *cls, auto templated_decl_id = template_type.getTemplateName().getAsTemplateDecl()->getID(); - auto templated_decl_local_id = + auto templated_decl_global_id = id_mapper().get_global_id(templated_decl_id).value_or(0); if (best_match_id > 0) { @@ -246,12 +246,13 @@ std::unique_ptr template_builder::build(const clang::NamedDecl *cls, } // If we can't find optimal match for parent template specialization, // just use whatever clang suggests - else if (diagram().has_element(templated_decl_local_id)) { + else if (diagram().has_element(templated_decl_global_id)) { template_instantiation.add_relationship( - {relationship_t::kInstantiation, templated_decl_local_id}); + {relationship_t::kInstantiation, templated_decl_global_id}); template_instantiation.template_specialization_found(true); } - else if (diagram().should_include(full_template_specialization_name)) { + else if (diagram().should_include( + namespace_{full_template_specialization_name})) { LOG_DBG("Skipping instantiation relationship from {} to {}", template_instantiation_ptr->full_name(false), templated_decl_id); } @@ -351,7 +352,7 @@ template_builder::build_from_class_template_specialization( {relationship_t::kInstantiation, templated_decl_local_id}); template_instantiation.template_specialization_found(true); } - else if (diagram().should_include(qualified_name)) { + else if (diagram().should_include(namespace_{qualified_name})) { LOG_DBG("Skipping instantiation relationship from {} to {}", template_instantiation_ptr->full_name(false), templated_decl_id); } @@ -380,7 +381,7 @@ void template_builder::process_template_arguments( // default values, and add them when they are specifically // overridden if (!diagram().should_include( - template_decl->getQualifiedNameAsString())) { + namespace_{template_decl->getQualifiedNameAsString()})) { const auto *maybe_type_parm_decl = clang::dyn_cast( template_decl->getTemplateParameters()->getParam( @@ -1012,7 +1013,8 @@ template_builder::try_as_template_specialization_type( argument.set_type(nested_type_name); auto nested_template_instantiation = build(cls, *nested_template_type, - diagram().should_include(template_decl->getQualifiedNameAsString()) + diagram().should_include( + namespace_{template_decl->getQualifiedNameAsString()}) ? std::make_optional(&template_instantiation) : parent); @@ -1031,9 +1033,10 @@ template_builder::try_as_template_specialization_type( nested_template_instantiation->full_name(false); if (nested_template_instantiation && - diagram().should_include(nested_template_instantiation_full_name)) { + diagram().should_include( + namespace_{nested_template_instantiation_full_name})) { if (diagram().should_include( - template_decl->getQualifiedNameAsString())) { + namespace_{template_decl->getQualifiedNameAsString()})) { template_instantiation.add_relationship( {relationship_t::kDependency, nested_template_instantiation->id()}); @@ -1045,7 +1048,8 @@ template_builder::try_as_template_specialization_type( } } - if (diagram().should_include(nested_template_instantiation_full_name)) { + if (diagram().should_include( + namespace_{nested_template_instantiation_full_name})) { visitor_.set_source_location( *template_decl, *nested_template_instantiation); visitor_.add_class(std::move(nested_template_instantiation)); @@ -1155,7 +1159,7 @@ std::optional template_builder::try_as_record_type( template_instantiation.add_relationship(std::move(r)); } - if (diagram().should_include(tag_argument->full_name(false))) { + if (diagram().should_include(tag_argument->get_namespace())) { if (parent.has_value()) parent.value()->add_relationship( {relationship_t::kDependency, tag_argument->id()}); @@ -1166,7 +1170,7 @@ std::optional template_builder::try_as_record_type( } else if (const auto *record_type_decl = record_type->getAsRecordDecl(); record_type_decl != nullptr) { - if (diagram().should_include(type_name)) { + if (diagram().should_include(namespace_{type_name})) { // Add dependency relationship to the parent // template template_instantiation.add_relationship( diff --git a/src/class_diagram/visitor/template_builder.h b/src/class_diagram/visitor/template_builder.h index 4f2103ef..0ea8612b 100644 --- a/src/class_diagram/visitor/template_builder.h +++ b/src/class_diagram/visitor/template_builder.h @@ -36,35 +36,111 @@ using found_relationships_t = class translation_unit_visitor; +/** + * @brief Class responsible for building all kinds of templates from Clang AST. + */ class template_builder { public: + /** + * @brief Constructor. + * + * @param visitor Reference to class diagram translation_unit_visitor + */ template_builder( clanguml::class_diagram::visitor::translation_unit_visitor &visitor); + /** + * @brief Get reference to the current diagram model + * + * @return Reference to the current diagram model + */ class_diagram::model::diagram &diagram(); + /** + * @brief Get reference to the current diagram configuration + * + * @return Reference to the current diagram configuration + */ const config::class_diagram &config() const; + /** + * @brief Get diagram relative namespace + * + * @return Diagram relative namespace + */ const namespace_ &using_namespace() const; + /** + * @brief Simplify system templates + * + * This method tries to simplify all fully qualified template names + * in the `full_name` using substitutions from the configuration file + * (). + * + * Typical example is replace every `std::basic_string` with + * `std::string`. + * + * @param ct Template parameter + * @param full_name Full template name + * @return + */ bool simplify_system_template( template_parameter &ct, const std::string &full_name) const; + /** + * @brief Basic template class build method + * + * @param cls Clang template declaration + * @param template_type_decl Template specialization type + * @param parent Optional class in which this template is contained + * @return Created template class model + */ std::unique_ptr build( const clang::NamedDecl *cls, const clang::TemplateSpecializationType &template_type_decl, std::optional parent = {}); + /** + * @brief Build template class from class template specialization decl + * + * @param template_specialization Class template specialization declaration + * @param parent Optional class in which this template is contained + * @return Created template class model + */ std::unique_ptr build_from_class_template_specialization( const clang::ClassTemplateSpecializationDecl &template_specialization, std::optional parent = {}); + /** + * @brief Add base classes to the template class, if any. + * + * This method adds base classes to a template declaration or + * specialization, including inferring whether variadic template + * parameter bases. + * + * @param tinst Class template model + * @param template_base_params List of base class template parameters + * @param arg_index Index of the template argument used for base class + * @param variadic_params Whether the parameter is variadic + * @param ct Template parameter model + * @return True, if any base classes were added + */ bool add_base_classes(clanguml::class_diagram::model::class_ &tinst, std::deque> &template_base_params, int arg_index, bool variadic_params, const clanguml::common::model::template_parameter &ct); + /** + * @brief Process template class parameters and arguments + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_base_params List of base class template parameters + * @param template_args List of template arguments + * @param template_instantiation Template class model to add template args + * @param template_decl Base template declaration + */ void process_template_arguments( std::optional &parent, const clang::NamedDecl *cls, @@ -73,6 +149,18 @@ public: model::class_ &template_instantiation, const clang::TemplateDecl *template_decl); + /** + * @brief Process template arguments based on their type + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_instantiation Template class model to add template args + * @param template_decl Base template declaration + * @param arg Template argument + * @param argument_index Argument index + * @param argument Output list of arguments (can be more than one for + * variadic parameters) + */ void argument_process_dispatch( std::optional &parent, const clang::NamedDecl *cls, class_ &template_instantiation, @@ -80,18 +168,64 @@ public: const clang::TemplateArgument &arg, size_t argument_index, std::vector &argument); + /** + * @brief Process `clang::TemplateArgument::Expression` + * + * @note The template argument is a pack expansion of a template name that + * was provided for a template template parameter. + * + * @param arg Template argument + * @return Return template argument model + */ template_parameter process_expression_argument( const clang::TemplateArgument &arg); + /** + * @brief Process `clang::TemplateArgument::Integral` + * + * @note The template argument is an integral value stored in an + * llvm::APSInt that was provided for an integral non-type template + * parameter. + * + * @param arg Template argument + * @return Return template argument model + */ template_parameter process_integral_argument( const clang::TemplateArgument &arg); + /** + * @brief Process `clang::TemplateArgument::NullPtr` + * + * @note The template argument is a null pointer or null pointer to member + * that was provided for a non-type template parameter. + * + * @param arg Template argument + * @return Return template argument model + */ template_parameter process_nullptr_argument( const clang::TemplateArgument &arg); + /** + * @brief Process `clang::TemplateArgument::Null` + * + * @note Represents an empty template argument, e.g., one that has not + * been deduced. + * + * @param arg Template argument + * @return Return template argument model + */ template_parameter process_null_argument( const clang::TemplateArgument &arg); + /** + * @brief Process `clang::TemplateArgument::Pack` + * + * @note The template argument is actually a parameter pack. Arguments are + * stored in the Args struct. + * + * @param arg Template argument + * @return Return template argument model + */ std::vector process_pack_argument( std::optional &parent, const clang::NamedDecl *cls, class_ &template_instantiation, @@ -99,85 +233,260 @@ public: const clang::TemplateArgument &arg, size_t argument_index, std::vector &argument); + /** + * @brief Process `clang::TemplateArgument::Type` + * + * @note The template argument is a type. + * + * @param arg Template argument + * @return Return template argument model + */ template_parameter process_type_argument( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *base_template_decl, clang::QualType type, model::class_ &template_instantiation, size_t argument_index); + /** + * @brief Process `clang::TemplateArgument::Template` + * + * @note The template argument is a template name that was provided for a + * template template parameter. + * + * @param arg Template argument + * @return Return template argument model + */ common::model::template_parameter process_template_argument( const clang::TemplateArgument &arg); + /** + * @brief Process `clang::TemplateArgument::TemplateExpansion` + * + * @note The template argument is a pack expansion of a template name that + * was provided for a template template parameter. + * + * @param arg Template argument + * @return Return template argument model + */ common::model::template_parameter process_template_expansion( const clang::TemplateArgument &arg); + /** + * @brief Try to process template type argument as function template + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @param argument_index Argument index + * @return Function template argument if succeeds, or std::nullopt + */ std::optional try_as_function_prototype( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation, size_t argument_index); + /** + * @brief Try to process template type argument as array + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @param argument_index Argument index + * @return Array template argument if succeeds, or std::nullopt + */ std::optional try_as_array( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation, size_t argument_index); + /** + * @brief Try to process template type argument as specialization type + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @param argument_index Argument index + * @return Template specialization template argument if succeeds, + * or std::nullopt + */ std::optional try_as_template_specialization_type( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation, size_t argument_index); + /** + * @brief Try to process template type argument as template parameter + * + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @return Template parameter if succeeds, or std::nullopt + */ std::optional try_as_template_parm_type( const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type); + /** + * @brief Try to process template type argument as lambda + * + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @return Lambda template argument if succeeds, or std::nullopt + */ std::optional try_as_lambda(const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type); + /** + * @brief Try to process template type argument as record type + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @param argument_index Argument index + * @return Record type template argument if succeeds, or std::nullopt + */ std::optional try_as_record_type( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation, size_t argument_index); + /** + * @brief Try to process template type argument as enum type + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @return Enum type template argument if succeeds, or std::nullopt + */ std::optional try_as_enum_type( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation); + /** + * @brief Try to process template type argument as builtin type + * + * @param parent Optional class in which this template is contained + * @param type Template type + * @param template_decl Base template declaration + * @return Builtin type template argument if succeeds, or std::nullopt + */ std::optional try_as_builtin_type( std::optional &parent, clang::QualType &type, const clang::TemplateDecl *template_decl); + /** + * @brief Try to process template type argument as member pointer type + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @param argument_index Argument index + * @return Member pointer type template argument if succeeds, + * or std::nullopt + */ std::optional try_as_member_pointer( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation, size_t argument_index); + /** + * @brief Try to process template type argument as `decltype()` type + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @param argument_index Argument index + * @return `decltype()` type template argument if succeeds, or std::nullopt + */ std::optional try_as_decl_type( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation, size_t argument_index); + /** + * @brief Try to process template type argument as typedef/using type + * + * @param parent Optional class in which this template is contained + * @param cls Template class specialization declaration + * @param template_decl Base template declaration + * @param type Template type + * @param template_instantiation Template class model + * @param argument_index Argument index + * @return Typedef type template argument if succeeds, or std::nullopt + */ std::optional try_as_typedef_type( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation, size_t argument_index); + /** + * @brief Remove types context (e.g. const or reference qualifiers) + * + * This method removes all const and reference/pointer qualifiers from + * `type`, adds them to the template parameter model `tp` and returns + * a type without context. + * + * @param type Type to remove context from + * @param tp Template model to add context to + * @return Type without context + */ clang::QualType consume_context( clang::QualType type, template_parameter &tp) const; + /** + * @brief Try to find additional relationships in unexposed parameters + * + * Sometimes, Clang will report a template parameter as unexposed, which + * means all we get a is a string representation of the type, sometimes + * with template parameter names replaced with `type-parameter-X-Y` + * string. + * + * This method tries to find any type names, which might be relevant for + * the diagram relationships. + * + * @param ct Template argument model + * @param relationships List of discovered relationships + * @return True, if any relationships were found + */ bool find_relationships_in_unexposed_template_params( const template_parameter &ct, class_diagram::visitor::found_relationships_t &relationships); + /** + * @brief Get reference to Clang AST to clang-uml id mapper + * + * @return Reference to Clang AST to clang-uml id mapper + */ common::visitor::ast_id_mapper &id_mapper(); + /** + * @brief Get reference to the current source manager + * + * @return Reference to the current source manager + */ clang::SourceManager &source_manager() const; private: diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 0ae4d4a5..c335c201 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -100,7 +100,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) if (enm->getNameAsString().empty()) return true; - if (!diagram().should_include(enm->getQualifiedNameAsString())) + if (!should_include(enm)) return true; LOG_DBG("= Visiting enum declaration {} at {}", @@ -169,8 +169,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) e.constants().push_back(ev->getNameAsString()); } - if (diagram().should_include(qualified_name)) - add_enum(std::move(e_ptr)); + add_enum(std::move(e_ptr)); return true; } @@ -178,10 +177,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( clang::ClassTemplateSpecializationDecl *cls) { - if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) - return true; - - if (!diagram().should_include(cls->getQualifiedNameAsString())) + if (!should_include(cls)) return true; LOG_DBG("= Visiting template specialization declaration {} at {} " @@ -241,10 +237,7 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( bool translation_unit_visitor::VisitTypeAliasTemplateDecl( clang::TypeAliasTemplateDecl *cls) { - if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) - return true; - - if (!diagram().should_include(cls->getQualifiedNameAsString())) + if (!should_include(cls)) return true; LOG_DBG("= Visiting template type alias declaration {} at {}", @@ -280,10 +273,7 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl( bool translation_unit_visitor::VisitClassTemplateDecl( clang::ClassTemplateDecl *cls) { - if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) - return true; - - if (!diagram().should_include(cls->getQualifiedNameAsString())) + if (!should_include(cls)) return true; LOG_DBG("= Visiting class template declaration {} at {}", @@ -339,16 +329,12 @@ bool translation_unit_visitor::VisitClassTemplateDecl( bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec) { - // Skip system headers - if (source_manager().isInSystemHeader(rec->getSourceRange().getBegin())) - return true; - if (clang::dyn_cast_or_null(rec) != nullptr) // This is handled by VisitCXXRecordDecl() return true; // It seems we are in a C (not C++) translation unit - if (!diagram().should_include(rec->getQualifiedNameAsString())) + if (!should_include(rec)) return true; LOG_DBG("= Visiting record declaration {} at {}", @@ -396,11 +382,7 @@ bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec) bool translation_unit_visitor::TraverseConceptDecl(clang::ConceptDecl *cpt) { - // Skip system headers - if (source_manager().isInSystemHeader(cpt->getSourceRange().getBegin())) - return true; - - if (!diagram().should_include(cpt->getQualifiedNameAsString())) + if (!should_include(cpt)) return true; LOG_DBG("= Visiting concept (isType: {}) declaration {} at {}", @@ -1314,7 +1296,7 @@ void translation_unit_visitor::process_method( *unaliased_type, &c); if (diagram().should_include( - template_specialization_ptr->full_name(false))) { + template_specialization_ptr->get_namespace())) { relationships.emplace_back(template_specialization_ptr->id(), relationship_t::kDependency); @@ -1686,7 +1668,7 @@ void translation_unit_visitor::process_function_parameter( templ->getTemplateName().getAsTemplateDecl(), *templ, &c); if (diagram().should_include( - template_specialization_ptr->full_name(false))) { + template_specialization_ptr->get_namespace())) { relationships.emplace_back(template_specialization_ptr->id(), relationship_t::kDependency); @@ -1956,7 +1938,7 @@ void translation_unit_visitor::process_field( // it's a std::vector<>, it's nested types might be added bool add_template_instantiation_to_diagram{false}; if (diagram().should_include( - template_specialization.full_name(false))) { + template_specialization.get_namespace())) { found_relationships_t::value_type r{ template_specialization.id(), relationship_hint}; @@ -2034,7 +2016,7 @@ void translation_unit_visitor::process_field( void translation_unit_visitor::add_incomplete_forward_declarations() { for (auto &[id, c] : forward_declarations_) { - if (diagram().should_include(c->full_name(false))) { + if (diagram().should_include(c->get_namespace())) { add_class(std::move(c)); } } @@ -2107,8 +2089,21 @@ void translation_unit_visitor::extract_constrained_template_param_name( bool translation_unit_visitor::should_include(const clang::NamedDecl *decl) { - return decl != nullptr && - diagram().should_include(decl->getQualifiedNameAsString()); + if (decl == nullptr) + return false; + + if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin())) + return false; + + auto should_include_namespace = + diagram().should_include(namespace_{decl->getQualifiedNameAsString()}); + + const auto decl_file = decl->getLocation().printToString(source_manager()); + + const auto should_include_decl_file = + diagram().should_include(common::model::source_file{decl_file}); + + return should_include_namespace && should_include_decl_file; } void translation_unit_visitor::add_processed_template_class( diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 8dc22d07..a5ddec96 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -64,10 +64,21 @@ class translation_unit_visitor : public clang::RecursiveASTVisitor, public common::visitor::translation_unit_visitor { public: + /** + * @brief Constructor. + * + * @param sm Current source manager reference + * @param diagram Diagram model + * @param config Diagram configuration + */ explicit translation_unit_visitor(clang::SourceManager &sm, clanguml::class_diagram::model::diagram &diagram, const clanguml::config::class_diagram &config); + /** + * \defgroup Implementation of ResursiveASTVisitor methods + * @{ + */ bool shouldVisitTemplateInstantiations() const { return false; } bool shouldVisitImplicitCode() const { return false; } @@ -89,6 +100,7 @@ public: virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls); virtual bool TraverseConceptDecl(clang::ConceptDecl *cpt); + /** @} */ /** * @brief Get diagram model reference @@ -97,6 +109,11 @@ public: */ clanguml::class_diagram::model::diagram &diagram() { return diagram_; } + /** + * @brief Get diagram model reference + * + * @return Reference to diagram model created by the visitor + */ const clanguml::class_diagram::model::diagram &diagram() const { return diagram_; @@ -119,112 +136,335 @@ public: */ void finalize(); + /** + * @brief Get reference to Clang AST to clang-uml id mapper + * + * @return Reference to Clang AST to clang-uml id mapper + */ common::visitor::ast_id_mapper &id_mapper() const { return id_mapper_; } + /** + * @brief Add class (or template class) to the diagram. + * + * @param c Class model + */ void add_class(std::unique_ptr &&c); + + /** + * @brief Add enum to the diagram. + * + * @param e Enum model + */ void add_enum(std::unique_ptr &&e); + + /** + * @brief Add concept to the diagram. + * + * @param c Concept model + */ void add_concept(std::unique_ptr &&c); void ensure_lambda_type_is_relative(std::string ¶meter_type) const; private: + /** + * @brief Check if the diagram should include a declaration. + * + * @param decl Clang declaration. + * @return True, if the entity should be included in the diagram. + */ bool should_include(const clang::NamedDecl *decl); + /** + * @brief Create class element model from class declaration + * + * @param cls Class declaration + * @return Class diagram element model + */ std::unique_ptr create_class_declaration(clang::CXXRecordDecl *cls); + /** + * @brief Create class element model from record (e.g. struct) declaration + * + * @param rec Record declaration + * @return Class diagram element model + */ std::unique_ptr create_record_declaration(clang::RecordDecl *rec); + /** + * @brief Create concept element model from concept declaration + * @param cpt Concept declaration + * @return Concept diagram element model + */ std::unique_ptr create_concept_declaration(clang::ConceptDecl *cpt); + /** + * @brief Process class declaration + * + * @param cls Class declaration + * @param c Class diagram element return from `create_class_declaration` + */ void process_class_declaration(const clang::CXXRecordDecl &cls, clanguml::class_diagram::model::class_ &c); + /** + * @brief Process class declaration bases (parents), if any + * + * @param cls Class declaration + * @param c Class diagram element model + */ void process_class_bases(const clang::CXXRecordDecl *cls, clanguml::class_diagram::model::class_ &c); + /** + * @brief Process class children elements (members and methods) + * + * @param cls Class declaration + * @param c Class diagram element model + */ void process_class_children(const clang::CXXRecordDecl *cls, clanguml::class_diagram::model::class_ &c); + /** + * @brief Process class or record data members + * @param cls Class declaration + * @param c Class diagram element model + */ void process_record_members(const clang::RecordDecl *cls, class_ &c); + /** + * @brief Process class template specialization/instantiation + * + * @param cls Class template specialization declaration + * @return Class diagram element model + */ std::unique_ptr process_template_specialization( clang::ClassTemplateSpecializationDecl *cls); + /** + * @brief Process template specialiaztion children (members and methods) + * @param cls Class template specialization declaration + * @param c Class diagram element model + */ void process_template_specialization_children( const clang::ClassTemplateSpecializationDecl *cls, class_ &c); + /** + * @brief Process template parameters + * + * @param template_declaration Template declaration + * @param t `template_trait` instance to which the parameters should be + * added + * @param templated_element Optional templated diagram element (e.g. class_) + * @return Ignored + */ bool process_template_parameters( const clang::TemplateDecl &template_declaration, clanguml::common::model::template_trait &t, common::optional_ref templated_element = {}); + /** + * @brief Process class method + * + * @param mf Method declaration + * @param c Class diagram element model + */ void process_method(const clang::CXXMethodDecl &mf, clanguml::class_diagram::model::class_ &c); + /** + * @brief Process class method properties + * @param mf Method declaration + * @param c Class diagram element model + * @param method_name Method name + * @param method Method model + */ + void process_method_properties(const clang::CXXMethodDecl &mf, + const class_ &c, const std::string &method_name, + class_method &method) const; + + /** + * @brief Process class template method + * + * @param mf Method declaration + * @param c Class diagram element model + */ void process_template_method(const clang::FunctionTemplateDecl &mf, clanguml::class_diagram::model::class_ &c); + /** + * @brief Process class static data member + * + * @param field_declaration Static data member declaration + * @param c Class diagram element model + */ void process_static_field(const clang::VarDecl &field_declaration, clanguml::class_diagram::model::class_ &c); + /** + * @brief Process class data member + * + * @param field_declaration Data member declaration + * @param c Class diagram element model + */ void process_field(const clang::FieldDecl &field_declaration, clanguml::class_diagram::model::class_ &c); + /** + * @brief Process function/method parameter + * + * @param param Parameter declaration + * @param method Class method model + * @param c Class diagram element model + * @param template_parameter_names Ignored + */ void process_function_parameter(const clang::ParmVarDecl ¶m, clanguml::class_diagram::model::class_method &method, clanguml::class_diagram::model::class_ &c, const std::set &template_parameter_names = {}); + /** + * @brief Process class friend + * + * @param f Friend declaration + * @param c Class diagram element model + */ void process_friend( const clang::FriendDecl &f, clanguml::class_diagram::model::class_ &c); + /** + * @brief Find relationships in a specific type + * + * @param type Type to search for relationships + * @param relationship_hint Default relationship type to infer from this + * type + * @return True, if any relationships were found + */ bool find_relationships(const clang::QualType &type, found_relationships_t & /*relationships*/, clanguml::common::model::relationship_t relationship_hint); + /** + * @brief Add relationships from relationship list to a class 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. + * + * @param c Class diagram element model + * @param field Class member model + * @param relationships List of found relationships + * @param break_on_first_aggregation Stop adding relatinoships, after first + * aggregation is found + */ void add_relationships(clanguml::class_diagram::model::class_ &c, const clanguml::class_diagram::model::class_member &field, const found_relationships_t &relationships, bool break_on_first_aggregation = false); + /** + * @brief Process record parent element (e.g. for nested classes) + * + * This method handles nested classes or structs. + * + * @param cls Record declaration + * @param c Class diagram element model + * @param ns Package in the diagram to which the class `c` should belong + */ void process_record_parent( clang::RecordDecl *cls, class_ &c, const namespace_ &ns); + /** + * @brief Find relationships in function parameter + * + * @param c Class diagram element model + * @param atsp `auto` type + */ void process_function_parameter_find_relationships_in_autotype( model::class_ &c, const clang::AutoType *atsp); + /** + * @brief Find relationships in concept constraint expression + * + * @param c Diagram element model (concept) + * @param expr Concept constraint expression + */ void find_relationships_in_constraint_expression( clanguml::common::model::element &c, const clang::Expr *expr); + /** + * @brief Register incomplete forward declaration to be updated later + */ void add_incomplete_forward_declarations(); + /** + * @brief 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. + */ void resolve_local_to_global_ids(); + /** + * @brief Process concept constraint requirements + * + * @param cpt Concept declaration + * @param expr Requires expression + * @param concept_model Concept diagram element model + */ void process_constraint_requirements(const clang::ConceptDecl *cpt, const clang::Expr *expr, model::concept_ &concept_model) const; + /** + * @brief Find concept specializations relationships + * + * @param c Concept element model + * @param concept_specialization Concept specialization expression + */ void process_concept_specialization_relationships(common::model::element &c, const clang::ConceptSpecializationExpr *concept_specialization); + /** + * @brief Extract template contraint parameter name from raw source code + * + * @param concept_specialization Concept specialization expression + * @param cpt Concept declaration + * @param constrained_template_params Found constraint template param names + * @param argument_index Argument index + * @param type_name Type parameter name - used if extraction fails + */ void extract_constrained_template_param_name( const clang::ConceptSpecializationExpr *concept_specialization, const clang::ConceptDecl *cpt, std::vector &constrained_template_params, size_t argument_index, std::string &type_name) const; - /// Store the mapping from local clang entity id (obtained using - /// getID()) method to clang-uml global id - void set_ast_local_id( - int64_t local_id, common::model::diagram_element::id_t global_id); - + /** + * @brief Register already processed template class name + * + * @param qualified_name Fully qualified template class name + */ void add_processed_template_class(std::string qualified_name); + /** + * @brief Check if template class has already been processed + * + * @param qualified_name Fully qualified template class name + * @return True, if template class has already been processed + */ bool has_processed_template_class(const std::string &qualified_name) const; + /** + * @brief Get template builder reference + * + * @return Reference to 'template_builder' instance + */ template_builder &tbuilder() { return template_builder_; } // Reference to the output diagram model @@ -246,13 +486,13 @@ private: common::model::access_t>> anonymous_struct_relationships_; - // 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... + /** + * 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... + */ std::set processed_template_qualified_names_; - void process_method_properties(const clang::CXXMethodDecl &mf, - const class_ &c, const std::string &method_name, - class_method &method) const; }; } // namespace clanguml::class_diagram::visitor diff --git a/src/common/model/diagram.h b/src/common/model/diagram.h index 52f35deb..ae78914a 100644 --- a/src/common/model/diagram.h +++ b/src/common/model/diagram.h @@ -137,6 +137,8 @@ public: bool should_include(relationship r) const; bool should_include(relationship_t r) const; bool should_include(access_t s) const; + // Disallow std::string overload + bool should_include(const std::string &s) const = delete; virtual bool has_element(const diagram_element::id_t /*id*/) const { diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index f64dc9cc..26d1b0b4 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -553,8 +553,9 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, clang::cast(namespace_context); if (namespace_declaration != nullptr && - diagram().should_include(common::get_qualified_name( - *namespace_declaration))) { + diagram().should_include( + namespace_{common::get_qualified_name( + *namespace_declaration)})) { const auto target_id = get_package_id(cxxrecord_decl); relationships.emplace_back( target_id, relationship_hint); @@ -563,8 +564,9 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, } } else { - if (diagram().should_include(common::get_qualified_name( - *type->getAsCXXRecordDecl()))) { + if (diagram().should_include( + namespace_{common::get_qualified_name( + *type->getAsCXXRecordDecl())})) { const auto target_id = get_package_id(type->getAsCXXRecordDecl()); relationships.emplace_back(target_id, relationship_hint); @@ -578,7 +580,7 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, // need to consider namespaces here if (config().package_type() == config::package_type_t::kDirectory) { if (diagram().should_include( - common::get_qualified_name(*record_decl))) { + namespace_{common::get_qualified_name(*record_decl)})) { const auto target_id = get_package_id(record_decl); relationships.emplace_back(target_id, relationship_hint); result = true; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 07959395..143a13bb 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -1222,18 +1222,15 @@ translation_unit_visitor::create_class_model(clang::CXXRecordDecl *cls) config().using_namespace())}; auto &c = *c_ptr; - // TODO: refactor to method get_qualified_name() - auto qualified_name = - cls->getQualifiedNameAsString(); // common::get_qualified_name(*cls); + auto qualified_name = cls->getQualifiedNameAsString(); if (!cls->isLambda()) - if (!diagram().should_include(qualified_name)) + if (!should_include(cls)) return {}; auto ns = common::get_tag_namespace(*cls); - if (cls->isLambda() && - !diagram().should_include(ns.to_string() + "::lambda")) + if (cls->isLambda() && !diagram().should_include(ns | "lambda")) return {}; const auto *parent = cls->getParent(); @@ -2202,7 +2199,8 @@ bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const const auto decl_file = decl->getLocation().printToString(source_manager()); - return diagram().should_include(decl->getQualifiedNameAsString()) && + return diagram().should_include( + namespace_{decl->getQualifiedNameAsString()}) && diagram().should_include(common::model::source_file{decl_file}); } @@ -2255,7 +2253,8 @@ bool translation_unit_visitor::should_include( { const auto decl_file = decl->getLocation().printToString(source_manager()); - return diagram().should_include(decl->getQualifiedNameAsString()) && + return diagram().should_include( + namespace_{decl->getQualifiedNameAsString()}) && diagram().should_include(common::model::source_file{decl_file}); } @@ -2273,7 +2272,8 @@ bool translation_unit_visitor::should_include( const auto decl_file = decl->getLocation().printToString(source_manager()); - return diagram().should_include(decl->getQualifiedNameAsString()) && + return diagram().should_include( + namespace_{decl->getQualifiedNameAsString()}) && diagram().should_include(common::model::source_file{decl_file}); } } // namespace clanguml::sequence_diagram::visitor diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index b1d203f8..49ca52c5 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -30,6 +30,7 @@ namespace clanguml::sequence_diagram::visitor { +using common::model::namespace_; using common::model::template_parameter; std::string to_string(const clang::FunctionTemplateDecl *decl); diff --git a/tests/t00003/test_case.h b/tests/t00003/test_case.h index 9d06bd2f..33f1774b 100644 --- a/tests/t00003/test_case.h +++ b/tests/t00003/test_case.h @@ -31,7 +31,6 @@ TEST_CASE("t00003", "[test-case][class]") auto model = generate_class_diagram(*db, diagram); REQUIRE(model->name() == "t00003_class"); - REQUIRE(model->should_include(std::string("clanguml::t00003::A"))); { auto puml = generate_class_puml(diagram, *model);