diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5503fcc5..4fd30b9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,12 +10,8 @@ jobs: uses: actions/checkout@v2 with: submodules: recursive - - name: Install add-apt-repository - run: sudo apt-get install software-properties-common - - name: Add llvm repository - run: sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' && sudo apt update - name: Install deps - run: sudo apt-get install ccache cmake libyaml-cpp-dev libfmt-dev libspdlog-dev clang-12 libclang-12-dev libclang-cpp12-dev + run: sudo apt install ccache cmake libyaml-cpp-dev libfmt-dev libspdlog-dev clang-12 libclang-12-dev libclang-cpp12-dev - name: Build and unit test run: | make debug diff --git a/docs/test_cases/t00002_class.png b/docs/test_cases/t00002_class.png index 1b2e5cc2..a2d1b5fe 100644 Binary files a/docs/test_cases/t00002_class.png and b/docs/test_cases/t00002_class.png differ diff --git a/docs/test_cases/t00003_class.png b/docs/test_cases/t00003_class.png index a455d7e9..38013897 100644 Binary files a/docs/test_cases/t00003_class.png and b/docs/test_cases/t00003_class.png differ diff --git a/docs/test_cases/t00004_class.png b/docs/test_cases/t00004_class.png index b9a11c54..b3959579 100644 Binary files a/docs/test_cases/t00004_class.png and b/docs/test_cases/t00004_class.png differ diff --git a/docs/test_cases/t00005_class.png b/docs/test_cases/t00005_class.png index 86d49b15..a3e20bd9 100644 Binary files a/docs/test_cases/t00005_class.png and b/docs/test_cases/t00005_class.png differ diff --git a/docs/test_cases/t00006_class.png b/docs/test_cases/t00006_class.png index 6b052977..2da7c717 100644 Binary files a/docs/test_cases/t00006_class.png and b/docs/test_cases/t00006_class.png differ diff --git a/docs/test_cases/t00007_class.png b/docs/test_cases/t00007_class.png index 4a39df2c..28ccdff4 100644 Binary files a/docs/test_cases/t00007_class.png and b/docs/test_cases/t00007_class.png differ diff --git a/docs/test_cases/t00008_class.png b/docs/test_cases/t00008_class.png index 8e373923..0cd44f40 100644 Binary files a/docs/test_cases/t00008_class.png and b/docs/test_cases/t00008_class.png differ diff --git a/docs/test_cases/t00009_class.png b/docs/test_cases/t00009_class.png index 980ea789..0dab4be7 100644 Binary files a/docs/test_cases/t00009_class.png and b/docs/test_cases/t00009_class.png differ diff --git a/docs/test_cases/t00010_class.png b/docs/test_cases/t00010_class.png index 49cb17bd..d6f78220 100644 Binary files a/docs/test_cases/t00010_class.png and b/docs/test_cases/t00010_class.png differ diff --git a/docs/test_cases/t00011_class.png b/docs/test_cases/t00011_class.png index 4e951c01..62e35eff 100644 Binary files a/docs/test_cases/t00011_class.png and b/docs/test_cases/t00011_class.png differ diff --git a/docs/test_cases/t00012_class.png b/docs/test_cases/t00012_class.png index 5e37aae9..63bc6179 100644 Binary files a/docs/test_cases/t00012_class.png and b/docs/test_cases/t00012_class.png differ diff --git a/docs/test_cases/t00013_class.png b/docs/test_cases/t00013_class.png index ed99af2a..fb39642f 100644 Binary files a/docs/test_cases/t00013_class.png and b/docs/test_cases/t00013_class.png differ diff --git a/docs/test_cases/t00014_class.png b/docs/test_cases/t00014_class.png index fc130072..0954ed16 100644 Binary files a/docs/test_cases/t00014_class.png and b/docs/test_cases/t00014_class.png differ diff --git a/docs/test_cases/t00015_class.png b/docs/test_cases/t00015_class.png index 0ea5904f..1689be22 100644 Binary files a/docs/test_cases/t00015_class.png and b/docs/test_cases/t00015_class.png differ diff --git a/docs/test_cases/t00016_class.png b/docs/test_cases/t00016_class.png index a2916a1e..0259e361 100644 Binary files a/docs/test_cases/t00016_class.png and b/docs/test_cases/t00016_class.png differ diff --git a/docs/test_cases/t00017_class.png b/docs/test_cases/t00017_class.png index 34a81e12..32d0fa72 100644 Binary files a/docs/test_cases/t00017_class.png and b/docs/test_cases/t00017_class.png differ diff --git a/docs/test_cases/t00018.md b/docs/test_cases/t00018.md index 760615a8..1b72af09 100644 --- a/docs/test_cases/t00018.md +++ b/docs/test_cases/t00018.md @@ -17,6 +17,36 @@ diagrams: ``` ## Source code +File t00018_impl.cc +```cpp +#include "t00018_impl.h" +#include "t00018.h" + +namespace clanguml { +namespace t00018 { +namespace impl { + +widget::widget(int n) + : n(n) +{ +} + +void widget::draw(const clanguml::t00018::widget &w) const +{ + if (w.shown()) + std::cout << "drawing a const widget " << n << '\n'; +} + +void widget::draw(const clanguml::t00018::widget &w) +{ + if (w.shown()) + std::cout << "drawing a non-const widget " << n << '\n'; +} +} +} +} + +``` File t00018_impl.h ```cpp #pragma once @@ -101,36 +131,6 @@ widget &widget::operator=(widget &&) = default; } } -``` -File t00018_impl.cc -```cpp -#include "t00018_impl.h" -#include "t00018.h" - -namespace clanguml { -namespace t00018 { -namespace impl { - -widget::widget(int n) - : n(n) -{ -} - -void widget::draw(const clanguml::t00018::widget &w) const -{ - if (w.shown()) - std::cout << "drawing a const widget " << n << '\n'; -} - -void widget::draw(const clanguml::t00018::widget &w) -{ - if (w.shown()) - std::cout << "drawing a non-const widget " << n << '\n'; -} -} -} -} - ``` ## Generated UML diagrams ![t00018_class](./t00018_class.png "Pimpl pattern") diff --git a/docs/test_cases/t00018_class.png b/docs/test_cases/t00018_class.png index 9ae9211b..c3e3fa3b 100644 Binary files a/docs/test_cases/t00018_class.png and b/docs/test_cases/t00018_class.png differ diff --git a/docs/test_cases/t00019.md b/docs/test_cases/t00019.md index 5d835bfc..6a7a8b01 100644 --- a/docs/test_cases/t00019.md +++ b/docs/test_cases/t00019.md @@ -22,25 +22,21 @@ diagrams: ``` ## Source code -File t00019_layer2.h +File t00019.cc ```cpp -#pragma once +#include "t00019_base.h" +#include "t00019_layer1.h" +#include "t00019_layer2.h" +#include "t00019_layer3.h" + +#include namespace clanguml { namespace t00019 { -template class Layer2 : public LowerLayer { - - using LowerLayer::LowerLayer; - - using LowerLayer::m1; - - using LowerLayer::m2; - - int all_calls_count() const - { - return LowerLayer::m1_calls() + LowerLayer::m2_calls(); - } +class A { +public: + std::unique_ptr>>> layers; }; } } @@ -98,26 +94,6 @@ template class Layer1 : public LowerLayer { } } -``` -File t00019.cc -```cpp -#include "t00019_base.h" -#include "t00019_layer1.h" -#include "t00019_layer2.h" -#include "t00019_layer3.h" - -#include - -namespace clanguml { -namespace t00019 { - -class A { -public: - std::unique_ptr>>> layers; -}; -} -} - ``` File t00019_layer3.h ```cpp @@ -155,6 +131,30 @@ private: } } +``` +File t00019_layer2.h +```cpp +#pragma once + +namespace clanguml { +namespace t00019 { + +template class Layer2 : public LowerLayer { + + using LowerLayer::LowerLayer; + + using LowerLayer::m1; + + using LowerLayer::m2; + + int all_calls_count() const + { + return LowerLayer::m1_calls() + LowerLayer::m2_calls(); + } +}; +} +} + ``` ## Generated UML diagrams ![t00019_class](./t00019_class.png "Layercake pattern") diff --git a/docs/test_cases/t00019_class.png b/docs/test_cases/t00019_class.png index bbf61c87..9ad66bac 100644 Binary files a/docs/test_cases/t00019_class.png and b/docs/test_cases/t00019_class.png differ diff --git a/docs/test_cases/t00020_class.png b/docs/test_cases/t00020_class.png index 2c98afcd..c7df1d23 100644 Binary files a/docs/test_cases/t00020_class.png and b/docs/test_cases/t00020_class.png differ diff --git a/docs/test_cases/t00021_class.png b/docs/test_cases/t00021_class.png index 0d4664e5..aacdaa00 100644 Binary files a/docs/test_cases/t00021_class.png and b/docs/test_cases/t00021_class.png differ diff --git a/docs/test_cases/t00022_class.png b/docs/test_cases/t00022_class.png index 6d9d2c54..6fc33aea 100644 Binary files a/docs/test_cases/t00022_class.png and b/docs/test_cases/t00022_class.png differ diff --git a/docs/test_cases/t00023_class.png b/docs/test_cases/t00023_class.png index a24f17a3..a94059a5 100644 Binary files a/docs/test_cases/t00023_class.png and b/docs/test_cases/t00023_class.png differ diff --git a/docs/test_cases/t00024_class.png b/docs/test_cases/t00024_class.png index 2713ba67..d21b6826 100644 Binary files a/docs/test_cases/t00024_class.png and b/docs/test_cases/t00024_class.png differ diff --git a/docs/test_cases/t00025_class.png b/docs/test_cases/t00025_class.png index 9b76a458..e04810f0 100644 Binary files a/docs/test_cases/t00025_class.png and b/docs/test_cases/t00025_class.png differ diff --git a/docs/test_cases/t00026_class.png b/docs/test_cases/t00026_class.png index 1809d486..76da28b8 100644 Binary files a/docs/test_cases/t00026_class.png and b/docs/test_cases/t00026_class.png differ diff --git a/docs/test_cases/t00027_class.png b/docs/test_cases/t00027_class.png index 281e0aed..8b18184c 100644 Binary files a/docs/test_cases/t00027_class.png and b/docs/test_cases/t00027_class.png differ diff --git a/docs/test_cases/t00028_class.png b/docs/test_cases/t00028_class.png index f27f1d23..36b290d1 100644 Binary files a/docs/test_cases/t00028_class.png and b/docs/test_cases/t00028_class.png differ diff --git a/docs/test_cases/t00029_class.png b/docs/test_cases/t00029_class.png index 95586287..ec015724 100644 Binary files a/docs/test_cases/t00029_class.png and b/docs/test_cases/t00029_class.png differ diff --git a/docs/test_cases/t00030_class.png b/docs/test_cases/t00030_class.png index ce4b8b67..f76932e7 100644 Binary files a/docs/test_cases/t00030_class.png and b/docs/test_cases/t00030_class.png differ diff --git a/docs/test_cases/t00031_class.png b/docs/test_cases/t00031_class.png index 9fe6a879..bc5ca0fb 100644 Binary files a/docs/test_cases/t00031_class.png and b/docs/test_cases/t00031_class.png differ diff --git a/docs/test_cases/t00032_class.png b/docs/test_cases/t00032_class.png index cbd72e5c..ff515338 100644 Binary files a/docs/test_cases/t00032_class.png and b/docs/test_cases/t00032_class.png differ diff --git a/docs/test_cases/t00033.md b/docs/test_cases/t00033.md index 25ef901d..e1c0d073 100644 --- a/docs/test_cases/t00033.md +++ b/docs/test_cases/t00033.md @@ -41,7 +41,7 @@ struct D { }; struct R { - A>> abc; + A>>> abc; }; } // namespace t00033 diff --git a/docs/test_cases/t00033_class.png b/docs/test_cases/t00033_class.png index 987938ca..ded3a806 100644 Binary files a/docs/test_cases/t00033_class.png and b/docs/test_cases/t00033_class.png differ diff --git a/docs/test_cases/t20001_sequence.png b/docs/test_cases/t20001_sequence.png index e972c3f5..99576880 100644 Binary files a/docs/test_cases/t20001_sequence.png and b/docs/test_cases/t20001_sequence.png differ diff --git a/docs/test_cases/t90000_class.png b/docs/test_cases/t90000_class.png index 3a516c01..643f4b36 100644 Binary files a/docs/test_cases/t90000_class.png and b/docs/test_cases/t90000_class.png differ diff --git a/src/cx/util.cc b/src/cx/util.cc index 941d4a0f..96357015 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -62,8 +62,9 @@ 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 (!inside_class) { + t_ns = ns(cppast::remove_cv(unreferenced(t)), idx); + } auto t_name = cppast::to_string(t); @@ -121,12 +122,13 @@ std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx) if (static_cast(t) .primary_template() .get(idx) - .size() > 0) + .size() > 0) { return ns( static_cast(t) .primary_template() .get(idx)[0] .get()); + } else return ""; } diff --git a/src/puml/class_diagram_generator.h b/src/puml/class_diagram_generator.h index 0b153195..722f2100 100644 --- a/src/puml/class_diagram_generator.h +++ b/src/puml/class_diagram_generator.h @@ -206,10 +206,13 @@ public: std::set rendered_relations; std::stringstream all_relations_str; + std::set unique_relations; for (const auto &r : c.relationships) { if (!m_config.should_include_relationship(name(r.type))) continue; + LOG_DBG("== Processing relationship {}", to_string(r.type)); + std::stringstream relstr; std::string destination; try { @@ -229,6 +232,8 @@ public: destination = r.destination; } + LOG_DBG("=== Destination is: {}", destination); + std::string puml_relation; if (!r.multiplicity_source.empty()) puml_relation += "\"" + r.multiplicity_source + "\" "; @@ -248,12 +253,18 @@ public: rendered_relations.emplace(r.label); } - relstr << '\n'; + if (unique_relations.count(relstr.str()) == 0) { + unique_relations.emplace(relstr.str()); - all_relations_str << relstr.str(); + relstr << '\n'; + + LOG_DBG("=== Adding relation {}", relstr.str()); + + all_relations_str << relstr.str(); + } } catch (error::uml_alias_missing &e) { - LOG_ERROR("Skipping {} relation from {} to {} due " + LOG_ERROR("=== Skipping {} relation from {} to {} due " "to: {}", to_string(r.type), c.full_name(uns), destination, e.what()); } @@ -291,8 +302,9 @@ public: ostr << relstr.str(); } catch (error::uml_alias_missing &e) { - LOG_ERROR("Skipping inheritance relation from {} to {} due " - "to: {}", + LOG_ERROR( + "=== Skipping inheritance relation from {} to {} due " + "to: {}", b.name, c.name, e.what()); } } diff --git a/src/uml/class_diagram_model.h b/src/uml/class_diagram_model.h index 961d85a5..fa5aa40f 100644 --- a/src/uml/class_diagram_model.h +++ b/src/uml/class_diagram_model.h @@ -334,7 +334,7 @@ struct diagram { if (!has_class(c.usr)) classes.emplace_back(std::move(c)); else - LOG_DBG("Class {} already in the model", c.name); + LOG_DBG("Class {} ({}) already in the model", c.name, c.usr); } void add_enum(enum_ &&e) @@ -350,6 +350,8 @@ struct diagram { std::string to_alias(const std::vector &using_namespaces, const std::string &full_name) const { + LOG_DBG("Looking for alias for {}", full_name); + for (const auto &c : classes) { if (c.full_name(using_namespaces) == full_name) { return c.alias(); diff --git a/src/uml/class_diagram_visitor.cc b/src/uml/class_diagram_visitor.cc index 0eef9f45..57039e5a 100644 --- a/src/uml/class_diagram_visitor.cc +++ b/src/uml/class_diagram_visitor.cc @@ -513,71 +513,60 @@ bool tu_visitor::process_field_with_template_instantiation( const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr, class_ &c, class_member &m, cppast::cpp_access_specifier_kind as) { - LOG_DBG("Processing field with template instatiation type {}", + LOG_DBG("Processing field with template instantiation type {}", cppast::to_string(tr)); bool res = false; const auto &template_instantiation_type = static_cast(tr); - if (template_instantiation_type.primary_template() - .get(ctx.entity_index) - .size()) { - // Here we need the name of the primary template with full namespace - // prefix to apply config inclusion filters - auto primary_template_name = cx::util::full_name(ctx.namespace_, - template_instantiation_type.primary_template() - .get(ctx.entity_index)[0] - .get()); - LOG_DBG("Maybe building instantiation for: {}{}", primary_template_name, - cppast::to_string(tr)); + const auto &unaliased = + static_cast( + resolve_alias(template_instantiation_type)); - if (ctx.config.should_include(primary_template_name)) { - const auto &unaliased = - static_cast( - resolve_alias(template_instantiation_type)); - class_ tinst = build_template_instantiation(unaliased); + class_ tinst = build_template_instantiation(unaliased); + tinst.usr = tinst.full_name(ctx.config.using_namespace); - // Infer the relationship of this field to the template - // instantiation - class_relationship rr; - rr.destination = tinst.usr; - if (mv.type().kind() == cppast::cpp_type_kind::pointer_t || - mv.type().kind() == cppast::cpp_type_kind::reference_t) - rr.type = relationship_t::kAssociation; - else - rr.type = relationship_t::kAggregation; - rr.label = mv.name(); - rr.scope = detail::cpp_access_specifier_to_scope(as); - rr.style = m.style_spec(); + // Infer the relationship of this field to the template + // instantiation + class_relationship rr; + rr.destination = tinst.full_name(ctx.config.using_namespace); + if (mv.type().kind() == cppast::cpp_type_kind::pointer_t || + mv.type().kind() == cppast::cpp_type_kind::reference_t) + rr.type = relationship_t::kAssociation; + else + rr.type = relationship_t::kAggregation; + rr.label = mv.name(); + rr.scope = detail::cpp_access_specifier_to_scope(as); + rr.style = m.style_spec(); - // Process field decorators - auto [decorator_rtype, decorator_rmult] = m.relationship(); - if (decorator_rtype != relationship_t::kNone) { - rr.type = decorator_rtype; - auto mult = util::split(decorator_rmult, ":"); - if (mult.size() == 2) { - rr.multiplicity_source = mult[0]; - rr.multiplicity_destination = mult[1]; - } - } - - LOG_DBG("Adding field instantiation relationship {} {} {} : {}", - rr.destination, model::class_diagram::to_string(rr.type), c.usr, - rr.label); - - c.add_relationship(std::move(rr)); - - res = true; - - LOG_DBG("Created template instantiation: {}, {}", tinst.name, - tinst.usr); - - ctx.d.add_class(std::move(tinst)); + // Process field decorators + auto [decorator_rtype, decorator_rmult] = m.relationship(); + if (decorator_rtype != relationship_t::kNone) { + rr.type = decorator_rtype; + auto mult = util::split(decorator_rmult, ":"); + if (mult.size() == 2) { + rr.multiplicity_source = mult[0]; + rr.multiplicity_destination = mult[1]; } } + if (ctx.config.should_include(tinst.name)) { + LOG_DBG("Adding field instantiation relationship {} {} {} : {}", + rr.destination, model::class_diagram::to_string(rr.type), c.usr, + rr.label); + + c.add_relationship(std::move(rr)); + + res = true; + + LOG_DBG( + "Created template instantiation: {}, {}", tinst.name, tinst.usr); + + ctx.d.add_class(std::move(tinst)); + } + return res; } @@ -747,8 +736,13 @@ void tu_visitor::process_template_method( if (m.skip()) return; + std::set template_parameter_names; + for (const auto &template_parameter : mf.parameters()) { + template_parameter_names.emplace(template_parameter.name()); + } + for (auto ¶m : mf.function().parameters()) - process_function_parameter(param, m, c); + process_function_parameter(param, m, c, template_parameter_names); LOG_DBG("Adding template method: {}", m.name); @@ -824,7 +818,8 @@ void tu_visitor::process_destructor(const cppast::cpp_destructor &mf, class_ &c, } void tu_visitor::process_function_parameter( - const cppast::cpp_function_parameter ¶m, class_method &m, class_ &c) + const cppast::cpp_function_parameter ¶m, class_method &m, class_ &c, + const std::set &template_parameter_names) { method_parameter mp; mp.name = param.name(); @@ -838,7 +833,7 @@ void tu_visitor::process_function_parameter( const auto ¶m_type = cppast::remove_cv(cx::util::unreferenced(param.type())); if (param_type.kind() == cppast::cpp_type_kind::template_instantiation_t) { - // Template instantiation parameters are not fully prefixed + // TODO: Template instantiation parameters are not fully prefixed // so we have to deduce the correct namespace prefix of the // template which is being instantiated mp.type = cppast::to_string(param.type()); @@ -893,35 +888,74 @@ void tu_visitor::process_function_parameter( if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) { auto &template_instantiation_type = static_cast(t); + if (template_instantiation_type.primary_template() .get(ctx.entity_index) .size()) { + // Here we need the name of the primary template with full // namespace prefix to apply config inclusion filters auto primary_template_name = cx::util::full_name(ctx.namespace_, template_instantiation_type.primary_template() .get(ctx.entity_index)[0] .get()); + // Now check if the template arguments of this function param + // are a subset of the method template params - if yes this is + // not an instantiation but just a reference to an existing + // template + bool template_is_not_instantiation{false}; + for (const auto &template_argument : + template_instantiation_type.arguments().value()) { + const auto template_argument_name = + cppast::to_string(template_argument.type().value()); + if (template_parameter_names.count(template_argument_name) > + 0) { + template_is_not_instantiation = true; + break; + } + } LOG_DBG("Maybe building instantiation for: {}", primary_template_name); if (ctx.config.should_include(primary_template_name)) { - class_ tinst = build_template_instantiation( - template_instantiation_type); - class_relationship rr; - rr.destination = tinst.usr; - rr.type = relationship_t::kDependency; - rr.label = ""; - LOG_DBG( - "Adding field dependency relationship {} {} {} : {}", - rr.destination, - model::class_diagram::to_string(rr.type), c.usr, - rr.label); - c.add_relationship(std::move(rr)); + if (template_is_not_instantiation) { + LOG_DBG("Template is not an instantiation - " + "only adding reference to template {}", + cx::util::full_name( + cppast::remove_cv(t), ctx.entity_index, false)); + class_relationship rr; + rr.destination = cx::util::full_name( + cppast::remove_cv(t), ctx.entity_index, false); + rr.type = relationship_t::kDependency; + rr.label = ""; + LOG_DBG("Adding field template dependency relationship " + "{} {} {} " + ": {}", + rr.destination, + model::class_diagram::to_string(rr.type), c.usr, + rr.label); + c.add_relationship(std::move(rr)); + } + else { + // First check if tinst already exists + class_ tinst = build_template_instantiation( + template_instantiation_type); - ctx.d.add_class(std::move(tinst)); + class_relationship rr; + rr.destination = tinst.usr; + rr.type = relationship_t::kDependency; + rr.label = ""; + LOG_DBG("Adding field dependency relationship {} {} {} " + ": {}", + rr.destination, + model::class_diagram::to_string(rr.type), c.usr, + rr.label); + c.add_relationship(std::move(rr)); + + ctx.d.add_class(std::move(tinst)); + } } } } @@ -1045,8 +1079,11 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_, { bool found{false}; - LOG_DBG("Finding relationships for type {}, {}", cppast::to_string(t_), - t_.kind()); + const auto fn = + cx::util::full_name(cppast::remove_cv(t_), ctx.entity_index, false); + + LOG_DBG("Finding relationships for type {}, {}, {}", cppast::to_string(t_), + t_.kind(), fn); relationship_t relationship_type = relationship_hint; const auto &t = cppast::remove_cv(cx::util::unreferenced(t_)); @@ -1088,8 +1125,6 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_, cppast::to_string(t), relationship_t::kAggregation); // Check if t_ has an alias in the alias index - const auto fn = - cx::util::full_name(cppast::remove_cv(t_), ctx.entity_index, false); if (ctx.has_type_alias(fn)) { LOG_DBG("Find relationship in alias of {} | {}", fn, cppast::to_string(ctx.get_type_alias(fn).get())); @@ -1132,6 +1167,29 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_, found = find_relationships(args[0u].type().value(), relationships, relationship_t::kAggregation); } + else if (ctx.config.should_include(fn)) { + LOG_DBG("User defined template instantiation: {} | {}", + cppast::to_string(t_), cppast::to_string(t_.canonical())); + + if (relationship_type != relationship_t::kNone) + relationships.emplace_back( + cppast::to_string(t), relationship_type); + else + relationships.emplace_back( + cppast::to_string(t), relationship_t::kAggregation); + + // Check if t_ has an alias in the alias index + if (ctx.has_type_alias(fn)) { + LOG_DBG("Find relationship in alias of {} | {}", fn, + cppast::to_string(ctx.get_type_alias(fn).get())); + found = find_relationships(ctx.get_type_alias(fn).get(), + relationships, relationship_type); + if (found) + return found; + } + + return found; + } else { for (const auto &arg : args) { if (arg.type()) { @@ -1146,23 +1204,33 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_, } class_ tu_visitor::build_template_instantiation( - const cppast::cpp_template_instantiation_type &t) + const cppast::cpp_template_instantiation_type &t, + std::optional parent) { class_ tinst; std::string full_template_name; std::deque> template_base_params{}; + // Determine the full template name if (t.primary_template().get(ctx.entity_index).size()) { const auto &primary_template_ref = static_cast( t.primary_template().get(ctx.entity_index)[0].get()) .class_(); + if (parent) + LOG_DBG("Template parent is {}", + (*parent)->full_name(ctx.config.using_namespace)); + else + LOG_DBG("Template parent is empty"); + full_template_name = cx::util::full_name(ctx.namespace_, primary_template_ref); - LOG_DBG("Found template instantiation: {} ({}) ..|> {}, {}", + LOG_DBG("Found template instantiation: " + "type={}, canonical={}, primary_template={}, full_" + "template={}", cppast::to_string(t), cppast::to_string(t.canonical()), t.primary_template().name(), full_template_name); @@ -1233,10 +1301,12 @@ class_ tu_visitor::build_template_instantiation( full_template_name = cppast::to_string(t); } + LOG_DBG("Building template instantiation for {}", full_template_name); + // Extract namespace from base template name - auto ns_toks = clanguml::util::split( - tinst.base_template_usr.substr(0, tinst.base_template_usr.find('<')), - "::"); + std::vector ns_toks; + ns_toks = clanguml::util::split( + full_template_name.substr(0, full_template_name.find('<')), "::"); std::string ns; if (ns_toks.size() > 1) { @@ -1244,10 +1314,18 @@ class_ tu_visitor::build_template_instantiation( "{}::", fmt::join(ns_toks.begin(), ns_toks.end() - 1, "::")); } + LOG_DBG("Template namespace is {}", ns); + tinst.name = ns + util::split(cppast::to_string(t), "<")[0]; tinst.is_template_instantiation = true; + tinst.usr = tinst.full_name(ctx.config.using_namespace); + if (tinst.usr.substr(0, tinst.usr.find('<')).find("::") == + std::string::npos) { + tinst.usr = ns + tinst.usr; + } + // Process template argumetns int arg_index{0}; bool variadic_params{false}; @@ -1258,57 +1336,86 @@ class_ tu_visitor::build_template_instantiation( ct.type = cppast::to_string(targ.type().value()); LOG_DBG("Template argument is a type {}", ct.type); - auto fn = cx::util::full_name( cppast::remove_cv(cx::util::unreferenced(targ.type().value())), ctx.entity_index, false); - if (ctx.config.should_include(fn)) { + if (targ.type().value().kind() == + cppast::cpp_type_kind::template_instantiation_t) { - if (targ.type().value().kind() == - cppast::cpp_type_kind::template_instantiation_t) { - class_ nested_tinst = - build_template_instantiation(static_cast< - const cppast::cpp_template_instantiation_type &>( - targ.type().value())); + const auto &nested_template_parameter = static_cast< + const cppast::cpp_template_instantiation_type &>( + targ.type().value()); - fn = util::split(fn, "<")[0]; + class_relationship tinst_dependency; + tinst_dependency.type = relationship_t::kDependency; + tinst_dependency.label = ""; - class_relationship tinst_dependency; - tinst_dependency.type = relationship_t::kDependency; - tinst_dependency.label = ""; + std::string nnn{"empty"}; + if (parent) + nnn = (*parent)->name; - tinst_dependency.destination = - nested_tinst.full_name(ctx.config.using_namespace); + class_ nested_tinst = + build_template_instantiation(nested_template_parameter, + ctx.config.should_include(tinst.usr) + ? std::make_optional(&tinst) + : parent); - LOG_DBG("Creating nested template dependency to template " - "instantiation {} -> {}", - tinst.full_name(ctx.config.using_namespace), - tinst_dependency.destination); + tinst_dependency.destination = + nested_tinst.full_name(ctx.config.using_namespace); - tinst.add_relationship(std::move(tinst_dependency)); + auto nested_tinst_usr = nested_tinst.usr; + if (ctx.config.should_include(fn)) { ctx.d.add_class(std::move(nested_tinst)); } - else if (targ.type().value().kind() == - cppast::cpp_type_kind::user_defined_t) { - class_relationship tinst_dependency; - tinst_dependency.type = relationship_t::kDependency; - tinst_dependency.label = ""; - tinst_dependency.destination = cx::util::full_name( - cppast::remove_cv( - cx::util::unreferenced(targ.type().value())), - ctx.entity_index, false); - - LOG_DBG( - "Creating nested template dependency to user defined " - "type {} -> {}", - tinst.full_name(ctx.config.using_namespace), + if (ctx.config.should_include(tinst.usr)) { + LOG_DBG("Creating nested template dependency to template " + "instantiation {}, {} -> {}", + fn, tinst.full_name(ctx.config.using_namespace), tinst_dependency.destination); tinst.add_relationship(std::move(tinst_dependency)); } + else if (parent) { + LOG_DBG("Creating nested template dependency to parent " + "template " + "instantiation {}, {} -> {}", + fn, (*parent)->full_name(ctx.config.using_namespace), + tinst_dependency.destination); + + (*parent)->add_relationship(std::move(tinst_dependency)); + } + else { + LOG_DBG("No nested template dependency to template " + "instantiation: {}, {} -> {}", + fn, tinst.full_name(ctx.config.using_namespace), + tinst_dependency.destination); + } + } + else if (targ.type().value().kind() == + cppast::cpp_type_kind::user_defined_t) { + class_relationship tinst_dependency; + tinst_dependency.type = relationship_t::kDependency; + tinst_dependency.label = ""; + + tinst_dependency.destination = cx::util::full_name( + cppast::remove_cv( + cx::util::unreferenced(targ.type().value())), + ctx.entity_index, false); + + LOG_DBG("Creating nested template dependency to user defined " + "type {} -> {}", + tinst.full_name(ctx.config.using_namespace), + tinst_dependency.destination); + + if (ctx.config.should_include(fn)) { + tinst.add_relationship(std::move(tinst_dependency)); + } + else if (parent) { + (*parent)->add_relationship(std::move(tinst_dependency)); + } } } else if (targ.expression()) { @@ -1356,12 +1463,16 @@ class_ tu_visitor::build_template_instantiation( tinst.templates.emplace_back(std::move(ct)); } + // Now update usr with the template arguments of the + // instantiations... (there must be a better way) tinst.usr = tinst.full_name(ctx.config.using_namespace); if (tinst.usr.substr(0, tinst.usr.find('<')).find("::") == std::string::npos) { tinst.usr = ns + tinst.usr; } + LOG_DBG("Setting tinst usr to {}", tinst.usr); + // Add instantiation relationship to primary template of this // instantiation class_relationship r; diff --git a/src/uml/class_diagram_visitor.h b/src/uml/class_diagram_visitor.h index f2656bf8..a2e891f1 100644 --- a/src/uml/class_diagram_visitor.h +++ b/src/uml/class_diagram_visitor.h @@ -195,7 +195,8 @@ public: void process_function_parameter(const cppast::cpp_function_parameter ¶m, clanguml::model::class_diagram::class_method &m, - clanguml::model::class_diagram::class_ &c); + clanguml::model::class_diagram::class_ &c, + const std::set &template_parameter_names = {}); bool find_relationships(const cppast::cpp_type &t, std::vector parent = {}); const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t); diff --git a/tests/t00013/test_case.h b/tests/t00013/test_case.h index 4bdf9772..d8dd16d3 100644 --- a/tests/t00013/test_case.h +++ b/tests/t00013/test_case.h @@ -59,8 +59,8 @@ TEST_CASE("t00013", "[test-case][class]") REQUIRE_THAT( puml, IsAggregation(_A("R"), _A("E"), "-estring")); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("ABCD::F"))); - REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F"), _A("ABCD::F"))); - REQUIRE_THAT(puml, IsDependency(_A("R"), _A("ABCD::F"))); + REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F"), _A("F"))); + REQUIRE_THAT(puml, IsDependency(_A("R"), _A("F"))); save_puml( "./" + config.output_directory + "/" + diagram->name + ".puml", puml); diff --git a/tests/t00033/t00033.cc b/tests/t00033/t00033.cc index 80453c92..66e7df06 100644 --- a/tests/t00033/t00033.cc +++ b/tests/t00033/t00033.cc @@ -21,7 +21,7 @@ struct D { }; struct R { - A>> abc; + A>>> abc; }; } // namespace t00033 diff --git a/tests/t00033/test_case.h b/tests/t00033/test_case.h index a1f3bbec..5d33b085 100644 --- a/tests/t00033/test_case.h +++ b/tests/t00033/test_case.h @@ -48,13 +48,18 @@ TEST_CASE("t00033", "[test-case][class]") REQUIRE_THAT(puml, IsClass(_A("D"))); REQUIRE_THAT(puml, IsClass(_A("R"))); - REQUIRE_THAT(puml, IsDependency(_A("A>>"), _A("B>"))); - REQUIRE_THAT(puml, IsDependency(_A("B>"), _A("C"))); + REQUIRE_THAT(puml, + IsDependency( + _A("A>>>"), _A("B>>"))); + REQUIRE_THAT( + puml, IsDependency(_A("B>>"), _A("C"))); REQUIRE_THAT(puml, IsDependency(_A("C"), _A("D"))); REQUIRE_THAT(puml, IsInstantiation(_A("C"), _A("C"))); - REQUIRE_THAT(puml, IsInstantiation(_A("B"), _A("B>"))); - REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A>>"))); + REQUIRE_THAT( + puml, IsInstantiation(_A("B"), _A("B>>"))); + REQUIRE_THAT( + puml, IsInstantiation(_A("A"), _A("A>>>"))); save_puml( "./" + config.output_directory + "/" + diagram->name + ".puml", puml);