diff --git a/src/cx/util.cc b/src/cx/util.cc index 9dcfa63a..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) + 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 29a4ff37..32344af3 100644 --- a/src/puml/class_diagram_generator.h +++ b/src/puml/class_diagram_generator.h @@ -231,6 +231,8 @@ public: destination = r.destination; } + LOG_DBG("========= Destination is: {}", destination); + std::string puml_relation; if (!r.multiplicity_source.empty()) puml_relation += "\"" + r.multiplicity_source + "\" "; @@ -252,6 +254,8 @@ public: relstr << '\n'; + LOG_DBG("Adding relation {}", relstr.str()); + all_relations_str << relstr.str(); } catch (error::uml_alias_missing &e) { diff --git a/src/uml/class_diagram_model.h b/src/uml/class_diagram_model.h index e8d50a7c..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) diff --git a/src/uml/class_diagram_visitor.cc b/src/uml/class_diagram_visitor.cc index 5efdad3b..57039e5a 100644 --- a/src/uml/class_diagram_visitor.cc +++ b/src/uml/class_diagram_visitor.cc @@ -736,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); @@ -813,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(); @@ -827,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()); @@ -882,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)); + } } } } @@ -1256,10 +1301,11 @@ class_ tu_visitor::build_template_instantiation( full_template_name = cppast::to_string(t); } - LOG_DBG("BUILDING TEMPLATE INSTANTIATION FOR {}", full_template_name); + LOG_DBG("Building template instantiation for {}", full_template_name); // Extract namespace from base template name - auto ns_toks = clanguml::util::split( + std::vector ns_toks; + ns_toks = clanguml::util::split( full_template_name.substr(0, full_template_name.find('<')), "::"); std::string ns; @@ -1268,6 +1314,8 @@ 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; @@ -1307,18 +1355,12 @@ class_ tu_visitor::build_template_instantiation( if (parent) nnn = (*parent)->name; - LOG_ERROR("CALLING BTI WITH PARENT {} ### ({})", - ctx.config.should_include(fn) ? tinst.name : nnn, fn); - class_ nested_tinst = build_template_instantiation(nested_template_parameter, ctx.config.should_include(tinst.usr) ? std::make_optional(&tinst) : parent); - LOG_ERROR("++++ NESTED TEMPLATE FULL NAME IS {} ({})", - nested_tinst.usr, fn); - tinst_dependency.destination = nested_tinst.full_name(ctx.config.using_namespace); @@ -1329,16 +1371,15 @@ class_ tu_visitor::build_template_instantiation( } if (ctx.config.should_include(tinst.usr)) { - LOG_DBG( - "++ Creating nested template dependency to template " - "instantiation {}, {} -> {}", + 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 " + LOG_DBG("Creating nested template dependency to parent " "template " "instantiation {}, {} -> {}", fn, (*parent)->full_name(ctx.config.using_namespace), @@ -1347,7 +1388,7 @@ class_ tu_visitor::build_template_instantiation( (*parent)->add_relationship(std::move(tinst_dependency)); } else { - LOG_DBG("-- No nested template dependency to template " + LOG_DBG("No nested template dependency to template " "instantiation: {}, {} -> {}", fn, tinst.full_name(ctx.config.using_namespace), tinst_dependency.destination); @@ -1422,6 +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 280aa4cd..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"), "-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);