diff --git a/src/uml/class_diagram_visitor.cc b/src/uml/class_diagram_visitor.cc index 30c9fea8..fb6e108a 100644 --- a/src/uml/class_diagram_visitor.cc +++ b/src/uml/class_diagram_visitor.cc @@ -1185,6 +1185,8 @@ class_ tu_visitor::build_template_instantiation( class_ tinst; std::string full_template_name; + std::vector> template_base_params{}; + if (t.primary_template().get(ctx.entity_index).size()) { const auto &primary_template_ref = static_cast( @@ -1201,6 +1203,54 @@ class_ tu_visitor::build_template_instantiation( if (full_template_name.back() == ':') tinst.name = full_template_name + tinst.name; + std::vector> template_parameter_names{}; + if (primary_template_ref.scope_name().has_value()) { + for (const auto &tp : primary_template_ref.scope_name() + .value() + .template_parameters()) { + template_parameter_names.emplace_back( + tp.name(), tp.is_variadic()); + } + } + + // Check if the primary template has any base classes + int base_index = 0; + for (const auto &base : primary_template_ref.bases()) { + if (base.kind() == cppast::cpp_entity_kind::base_class_t) { + const auto &base_class = + static_cast(base); + + const auto base_class_name = + cppast::to_string(base_class.type()); + + LOG_ERROR("FOUND TEMPLATE INSTANTIATION BASE: {}, {}, {}", + cppast::to_string(base.kind()), base_class_name, + base_index); + + // Check if any of the primary template arguments has a + // parameter equal to this type + auto it = std::find_if(template_parameter_names.begin(), + template_parameter_names.end(), + [&base_class_name]( + const auto &p) { return p.first == base_class_name; }); + + if (it != template_parameter_names.end()) { + // Found base class which is a template parameter + LOG_ERROR("FOUND BASE CLASS WHICH IS A TEMPLATE PARAMETER " + "{}, {}, {}", + it->first, it->second, + std::distance(template_parameter_names.begin(), it)); + template_base_params.emplace_back(it->first, it->second, + std::distance(template_parameter_names.begin(), it)); + } + else { + // This is a regular base class - it is handled by + // process_template + } + } + base_index++; + } + if (primary_template_ref.user_data()) { tinst.base_template_usr = static_cast(primary_template_ref.user_data()); @@ -1232,6 +1282,7 @@ class_ tu_visitor::build_template_instantiation( tinst.is_template_instantiation = true; + // Process template argumetns for (const auto &targ : t.arguments().value()) { class_template ct; if (targ.type()) { @@ -1255,6 +1306,8 @@ class_ tu_visitor::build_template_instantiation( tinst.templates.emplace_back(std::move(ct)); } + // Check if template inherits from any of the template arguments + tinst.usr = tinst.full_name(ctx.config.using_namespace); if (tinst.usr.substr(0, tinst.usr.find('<')).find("::") == std::string::npos) { diff --git a/tests/t00032/.clang-uml b/tests/t00032/.clang-uml new file mode 100644 index 00000000..58018aa0 --- /dev/null +++ b/tests/t00032/.clang-uml @@ -0,0 +1,12 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00032_class: + type: class + glob: + - ../../tests/t00032/t00032.cc + using_namespace: + - clanguml::t00032 + include: + namespaces: + - clanguml::t00032 diff --git a/tests/t00032/t00032.cc b/tests/t00032/t00032.cc new file mode 100644 index 00000000..99cf3b32 --- /dev/null +++ b/tests/t00032/t00032.cc @@ -0,0 +1,37 @@ +#include +#include + +namespace clanguml { +namespace t00032 { + +struct Base { +}; + +struct TBase { +}; + +struct A { + void operator()() { } +}; + +struct B { + void operator()() { } +}; + +struct C { + void operator()() { } +}; + +template +struct Overload : public Base, public T, public Ts... { + using Ts::operator()...; +}; + +template Overload(Ts...) -> Overload; + +struct R { + Overload overload; +}; + +} // namespace t00032 +} // namespace clanguml diff --git a/tests/t00032/test_case.h b/tests/t00032/test_case.h new file mode 100644 index 00000000..da339e08 --- /dev/null +++ b/tests/t00032/test_case.h @@ -0,0 +1,49 @@ +/** + * tests/t00032/test_case.cc + * + * Copyright (c) 2021 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +TEST_CASE("t00032", "[test-case][class]") +{ + auto [config, db] = load_config("t00032"); + + auto diagram = config.diagrams["t00032_class"]; + + REQUIRE(diagram->name == "t00032_class"); + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t00032"})); + + REQUIRE(diagram->exclude.namespaces.size() == 0); + + REQUIRE(diagram->should_include("clanguml::t00032::A")); + + auto model = generate_class_diagram(db, diagram); + + REQUIRE(model.name == "t00032_class"); + + auto puml = generate_class_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsClass(_A("A"))); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/test_cases.cc b/tests/test_cases.cc index a3f2fe62..d410f137 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -135,6 +135,7 @@ using namespace clanguml::test::matchers; #include "t00029/test_case.h" #include "t00030/test_case.h" #include "t00031/test_case.h" +#include "t00032/test_case.h" // // Sequence diagram tests