diff --git a/src/cx/util.cc b/src/cx/util.cc index 564debce..b7abeebc 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -17,9 +17,11 @@ */ #include "cx/util.h" +#include "util/util.h" #include #include +#include #include namespace clanguml { @@ -62,6 +64,19 @@ std::string full_name(const cppast::cpp_entity &e) return scopes + e.name(); } +std::string full_name(const cppast::cpp_type &t, + const cppast::cpp_entity_index &idx, bool inside_class) +{ + std::string t_ns; + if (!inside_class) + t_ns = ns(t, idx); + + if (t_ns.size() > 0) + return t_ns + "::" + cppast::to_string(t); + + return cppast::to_string(t); +} + std::string ns(const cppast::cpp_entity &e) { std::vector res{}; @@ -73,8 +88,45 @@ std::string ns(const cppast::cpp_entity &e) } it = it.value().parent(); } + std::reverse(res.begin(), res.end()); - return fmt::format("{}", fmt::join(res.rbegin(), res.rend(), "::")); + return fmt::format("{}", fmt::join(res, "::")); +} + +bool is_inside_class(const cppast::cpp_entity &e) +{ + auto it = e.parent(); + while (it) { + if (it.value().kind() == cppast::cpp_entity_kind::class_t) { + return true; + } + it = it.value().parent(); + } + return false; +} + +std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx) +{ + auto canon = cppast::to_string(t.canonical()); + auto full_name = canon.substr(0, canon.find("<")); + if (canon.find("type-parameter-") == std::string::npos) { + // This is an easy case, canonical representation contains full + // namespace + auto ns_toks = clanguml::util::split(full_name, "::"); + if (ns_toks.size() > 0) + ns_toks.pop_back(); + return fmt::format( + "{}", fmt::join(ns_toks.begin(), ns_toks.end(), "::")); + } + else { + // This is a bug/feature in libclang, where canonical representation of + // a template type with incomplete specialization doesn't have a full + // namespace We have to extract it from te primary template + const auto &primary_template = + static_cast(t) + .primary_template(); + return ns(primary_template.get(idx)[0].get()); + } } std::string fully_prefixed(const cppast::cpp_entity &e) diff --git a/src/cx/util.h b/src/cx/util.h index 73071791..b0735481 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -42,10 +42,19 @@ std::string to_string(CXString &&cxs); std::string full_name(const cppast::cpp_entity &e); +std::string full_name(const cppast::cpp_type &t, + const cppast::cpp_entity_index &idx, bool inside_class); + std::string fully_prefixed(const cppast::cpp_entity &e); const cppast::cpp_type &unreferenced(const cppast::cpp_type &t); +std::string ns(const cppast::cpp_entity &e); + +std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx); + +bool is_inside_class(const cppast::cpp_entity &e); + } // namespace util } // namespace cx } // namespace clanguml diff --git a/src/uml/class_diagram_model.h b/src/uml/class_diagram_model.h index 76771b33..662db761 100644 --- a/src/uml/class_diagram_model.h +++ b/src/uml/class_diagram_model.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -140,6 +141,11 @@ struct class_template { } }; +struct type_alias { + std::string alias; + std::string underlying_type; +}; + class class_ : public element { public: std::string usr; @@ -153,12 +159,20 @@ public: std::vector relationships; std::vector templates; std::string base_template_usr; + std::map type_aliases; friend bool operator==(const class_ &l, const class_ &r) { return (l.usr == r.usr) && (l.templates == r.templates); } + void add_type_alias(type_alias &&ta) + { + spdlog::debug( + "Adding class alias: {} -> {}", ta.alias, ta.underlying_type); + type_aliases[ta.alias] = std::move(ta); + } + void add_relationship(class_relationship &&cr) { auto it = std::find(relationships.begin(), relationships.end(), cr); @@ -225,6 +239,7 @@ struct diagram { std::string name; std::vector classes; std::vector enums; + std::map type_aliases; bool has_class(const std::string &usr) const { @@ -232,6 +247,14 @@ struct diagram { [&usr](const auto &c) { return c.usr == usr; }); } + void add_type_alias(type_alias &&ta) + { + spdlog::debug( + "Adding global alias: {} -> {}", ta.alias, ta.underlying_type); + + type_aliases[ta.alias] = std::move(ta); + } + void add_class(class_ &&c) { spdlog::debug("Adding class: {}, {}", c.name, c.usr); diff --git a/src/uml/class_diagram_visitor.cc b/src/uml/class_diagram_visitor.cc index 49774ae0..204660ff 100644 --- a/src/uml/class_diagram_visitor.cc +++ b/src/uml/class_diagram_visitor.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ using clanguml::model::class_diagram::enum_; using clanguml::model::class_diagram::method_parameter; using clanguml::model::class_diagram::relationship_t; using clanguml::model::class_diagram::scope_t; +using clanguml::model::class_diagram::type_alias; namespace detail { scope_t cpp_access_specifier_to_scope(cppast::cpp_access_specifier_kind as) @@ -97,6 +99,18 @@ void tu_visitor::operator()(const cppast::cpp_entity &file) if (ctx.config.should_include(cx::util::fully_prefixed(enm))) process_enum_declaration(enm); } + else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) { + spdlog::debug("========== Visiting '{}' - {}", + cx::util::full_name(e), cppast::to_string(e.kind())); + + auto &ta = static_cast(e); + type_alias t; + t.alias = cx::util::full_name(ta); + t.underlying_type = cx::util::full_name(ta.underlying_type(), + ctx.entity_index, cx::util::is_inside_class(e)); + + ctx.d.add_type_alias(std::move(t)); + } }); } diff --git a/tests/t00014/t00014.cc b/tests/t00014/t00014.cc index 12791b89..881de202 100644 --- a/tests/t00014/t00014.cc +++ b/tests/t00014/t00014.cc @@ -6,6 +6,12 @@ #include #include +template struct clanguml_t00014_A { + T value; +}; + +using clanguml_t00014_AString = clanguml_t00014_A; + namespace clanguml { namespace t00014 { @@ -18,9 +24,9 @@ template using AString = A; using AIntString = AString; using AStringString = AString; -using BStringString = AStringString; class R { + using BStringString = AStringString; A boolstring; AString floatstring; AIntString intstring;