Merge pull request #7 from bkryza/add-nested-template-instantiation-testcase

Add nested template instantiation testcase
This commit is contained in:
Bartek Kryza
2021-08-07 21:36:01 +02:00
committed by GitHub
12 changed files with 238 additions and 39 deletions

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

52
docs/test_cases/t00033.md Normal file
View 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
![t00033_class](./t00033_class.png "Nested template instantiation dependency test case")

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -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
View 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
View 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
View 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);
}

View File

@@ -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

View File

@@ -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