Merge pull request #7 from bkryza/add-nested-template-instantiation-testcase
Add nested template instantiation testcase
@@ -31,6 +31,7 @@
|
||||
* [t00030](./test_cases/t00030.md) - PlantUML relationship decorators test case
|
||||
* [t00031](./test_cases/t00031.md) - PlantUML style decorator test case
|
||||
* [t00032](./test_cases/t00032.md) - Class template with template base classes test case
|
||||
* [t00033](./test_cases/t00033.md) - Nested template instantiation dependency test case
|
||||
## Sequence diagrams
|
||||
* [t20001](./test_cases/t20001.md) - Basic sequence diagram
|
||||
## Configuration diagrams
|
||||
|
||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
52
docs/test_cases/t00033.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# t00033 - Nested template instantiation dependency test case
|
||||
## Config
|
||||
```yaml
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t00033_class:
|
||||
type: class
|
||||
glob:
|
||||
- ../../tests/t00033/t00033.cc
|
||||
using_namespace:
|
||||
- clanguml::t00033
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00033
|
||||
|
||||
```
|
||||
## Source code
|
||||
File t00033.cc
|
||||
```cpp
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t00033 {
|
||||
|
||||
template <typename T> struct A {
|
||||
T aaa;
|
||||
};
|
||||
|
||||
template <typename T> struct B {
|
||||
T bbb;
|
||||
};
|
||||
|
||||
template <typename T> struct C {
|
||||
T ccc;
|
||||
};
|
||||
|
||||
struct D {
|
||||
int ddd;
|
||||
};
|
||||
|
||||
struct R {
|
||||
A<B<C<D>>> abc;
|
||||
};
|
||||
|
||||
} // namespace t00033
|
||||
} // namespace clanguml
|
||||
|
||||
```
|
||||
## Generated UML diagrams
|
||||

|
||||
BIN
docs/test_cases/t00033_class.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
@@ -169,16 +169,6 @@ void tu_visitor::operator()(const cppast::cpp_entity &file)
|
||||
const cppast::cpp_template_instantiation_type &>(
|
||||
at.type_alias().underlying_type()));
|
||||
|
||||
class_relationship r;
|
||||
r.destination = tinst.base_template_usr;
|
||||
r.type = relationship_t::kInstantiation;
|
||||
r.label = "";
|
||||
r.scope = scope_t::kNone;
|
||||
tinst.add_relationship(std::move(r));
|
||||
|
||||
LOG_DBG("Created template instantiation: {}, {}, {}",
|
||||
tinst.name, tinst.usr, tinst.base_template_usr);
|
||||
|
||||
ctx.d.add_class(std::move(tinst));
|
||||
}
|
||||
});
|
||||
@@ -549,26 +539,8 @@ bool tu_visitor::process_field_with_template_instantiation(
|
||||
resolve_alias(template_instantiation_type));
|
||||
class_ tinst = build_template_instantiation(unaliased);
|
||||
|
||||
class_relationship r;
|
||||
const auto &tt = cppast::remove_cv(
|
||||
cx::util::unreferenced(template_instantiation_type));
|
||||
auto fn = cx::util::full_name(tt, ctx.entity_index, false);
|
||||
fn = util::split(fn, "<")[0];
|
||||
|
||||
if (ctx.has_type_alias(fn)) {
|
||||
// If this is a template alias - set the instantiation
|
||||
// relationship to the first alias target
|
||||
r.destination = cppast::to_string(ctx.get_type_alias(fn).get());
|
||||
}
|
||||
else {
|
||||
// Otherwise point to the base template
|
||||
r.destination = tinst.base_template_usr;
|
||||
}
|
||||
r.type = relationship_t::kInstantiation;
|
||||
r.label = "";
|
||||
r.scope = scope_t::kNone;
|
||||
tinst.add_relationship(std::move(r));
|
||||
|
||||
// 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 ||
|
||||
@@ -580,6 +552,7 @@ bool tu_visitor::process_field_with_template_instantiation(
|
||||
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;
|
||||
@@ -937,15 +910,6 @@ void tu_visitor::process_function_parameter(
|
||||
class_ tinst = build_template_instantiation(
|
||||
template_instantiation_type);
|
||||
|
||||
LOG_DBG("Created template instantiation: {}, {}",
|
||||
tinst.name, tinst.usr);
|
||||
|
||||
class_relationship r;
|
||||
r.destination = tinst.base_template_usr;
|
||||
r.type = relationship_t::kInstantiation;
|
||||
r.label = "";
|
||||
tinst.add_relationship(std::move(r));
|
||||
|
||||
class_relationship rr;
|
||||
rr.destination = tinst.usr;
|
||||
rr.type = relationship_t::kDependency;
|
||||
@@ -1292,6 +1256,60 @@ class_ tu_visitor::build_template_instantiation(
|
||||
class_template ct;
|
||||
if (targ.type()) {
|
||||
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) {
|
||||
class_ nested_tinst =
|
||||
build_template_instantiation(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 = "";
|
||||
|
||||
tinst_dependency.destination =
|
||||
nested_tinst.full_name(ctx.config.using_namespace);
|
||||
|
||||
LOG_DBG("Creating nested template dependency to template "
|
||||
"instantiation {} -> {}",
|
||||
tinst.full_name(ctx.config.using_namespace),
|
||||
tinst_dependency.destination);
|
||||
|
||||
tinst.add_relationship(std::move(tinst_dependency));
|
||||
|
||||
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),
|
||||
tinst_dependency.destination);
|
||||
|
||||
tinst.add_relationship(std::move(tinst_dependency));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (targ.expression()) {
|
||||
const auto &exp = targ.expression().value();
|
||||
@@ -1304,6 +1322,8 @@ class_ tu_visitor::build_template_instantiation(
|
||||
static_cast<const cppast::cpp_unexposed_expression &>(exp)
|
||||
.expression()
|
||||
.as_string();
|
||||
|
||||
LOG_DBG("Template argument is an expression {}", ct.type);
|
||||
}
|
||||
|
||||
// In case any of the template arguments are base classes, add
|
||||
@@ -1342,6 +1362,27 @@ class_ tu_visitor::build_template_instantiation(
|
||||
tinst.usr = ns + tinst.usr;
|
||||
}
|
||||
|
||||
// Add instantiation relationship to primary template of this
|
||||
// instantiation
|
||||
class_relationship r;
|
||||
const auto &tt = cppast::remove_cv(cx::util::unreferenced(t));
|
||||
auto fn = cx::util::full_name(tt, ctx.entity_index, false);
|
||||
fn = util::split(fn, "<")[0];
|
||||
|
||||
if (ctx.has_type_alias(fn)) {
|
||||
// If this is a template alias - set the instantiation
|
||||
// relationship to the first alias target
|
||||
r.destination = cppast::to_string(ctx.get_type_alias(fn).get());
|
||||
}
|
||||
else {
|
||||
// Otherwise point to the base template
|
||||
r.destination = tinst.base_template_usr;
|
||||
}
|
||||
r.type = relationship_t::kInstantiation;
|
||||
r.label = "";
|
||||
r.scope = scope_t::kNone;
|
||||
tinst.add_relationship(std::move(r));
|
||||
|
||||
return tinst;
|
||||
}
|
||||
|
||||
|
||||
12
tests/t00033/.clang-uml
Normal file
@@ -0,0 +1,12 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t00033_class:
|
||||
type: class
|
||||
glob:
|
||||
- ../../tests/t00033/t00033.cc
|
||||
using_namespace:
|
||||
- clanguml::t00033
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00033
|
||||
28
tests/t00033/t00033.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t00033 {
|
||||
|
||||
template <typename T> struct A {
|
||||
T aaa;
|
||||
};
|
||||
|
||||
template <typename T> struct B {
|
||||
T bbb;
|
||||
};
|
||||
|
||||
template <typename T> struct C {
|
||||
T ccc;
|
||||
};
|
||||
|
||||
struct D {
|
||||
int ddd;
|
||||
};
|
||||
|
||||
struct R {
|
||||
A<B<C<D>>> abc;
|
||||
};
|
||||
|
||||
} // namespace t00033
|
||||
} // namespace clanguml
|
||||
61
tests/t00033/test_case.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* tests/t00033/test_case.cc
|
||||
*
|
||||
* Copyright (c) 2021 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
* 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("t00033", "[test-case][class]")
|
||||
{
|
||||
auto [config, db] = load_config("t00033");
|
||||
|
||||
auto diagram = config.diagrams["t00033_class"];
|
||||
|
||||
REQUIRE(diagram->name == "t00033_class");
|
||||
|
||||
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||
REQUIRE_THAT(diagram->include.namespaces,
|
||||
VectorContains(std::string{"clanguml::t00033"}));
|
||||
|
||||
REQUIRE(diagram->exclude.namespaces.size() == 0);
|
||||
|
||||
REQUIRE(diagram->should_include("clanguml::t00033::A"));
|
||||
|
||||
auto model = generate_class_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model.name == "t00033_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, IsClassTemplate("A", "T"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("B", "T"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("C", "T"));
|
||||
REQUIRE_THAT(puml, IsClass(_A("D")));
|
||||
REQUIRE_THAT(puml, IsClass(_A("R")));
|
||||
|
||||
REQUIRE_THAT(puml, IsDependency(_A("A<B<C<D>>>"), _A("B<C<D>>")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("B<C<D>>"), _A("C<D>")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("C<D>"), _A("D")));
|
||||
|
||||
REQUIRE_THAT(puml, IsInstantiation(_A("C<T>"), _A("C<D>")));
|
||||
REQUIRE_THAT(puml, IsInstantiation(_A("B<T>"), _A("B<C<D>>")));
|
||||
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<B<C<D>>>")));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
@@ -136,6 +136,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t00030/test_case.h"
|
||||
#include "t00031/test_case.h"
|
||||
#include "t00032/test_case.h"
|
||||
#include "t00033/test_case.h"
|
||||
|
||||
//
|
||||
// Sequence diagram tests
|
||||
|
||||
@@ -93,6 +93,9 @@ test_cases:
|
||||
- name: t00032
|
||||
title: Class template with template base classes test case
|
||||
description:
|
||||
- name: t00033
|
||||
title: Nested template instantiation dependency test case
|
||||
description:
|
||||
Sequence diagrams:
|
||||
- name: t20001
|
||||
title: Basic sequence diagram
|
||||
|
||||