Added template insantiation relation
This commit is contained in:
@@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
#include "cx/type.h"
|
#include "cx/type.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace clanguml {
|
namespace clanguml {
|
||||||
namespace cx {
|
namespace cx {
|
||||||
|
|
||||||
@@ -214,6 +217,11 @@ public:
|
|||||||
return clang_Cursor_getTemplateArgumentValue(m_cursor, i);
|
return clang_Cursor_getTemplateArgumentValue(m_cursor, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursor specialized_cursor_template() const
|
||||||
|
{
|
||||||
|
return clang_getSpecializedCursorTemplate(m_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
std::string usr() const { return to_string(clang_getCursorUSR(m_cursor)); }
|
std::string usr() const { return to_string(clang_getCursorUSR(m_cursor)); }
|
||||||
|
|
||||||
const CXCursor &get() const { return m_cursor; }
|
const CXCursor &get() const { return m_cursor; }
|
||||||
|
|||||||
42
src/cx/type.cc
Normal file
42
src/cx/type.cc
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* src/cx/type.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.
|
||||||
|
*/
|
||||||
|
#include "type.h"
|
||||||
|
#include "cursor.h"
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace cx {
|
||||||
|
|
||||||
|
cursor type::type_declaration() const
|
||||||
|
{
|
||||||
|
return {clang_getTypeDeclaration(m_type)};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string type::instantiation_template() const
|
||||||
|
{
|
||||||
|
assert(is_template_instantiation());
|
||||||
|
|
||||||
|
auto s = spelling();
|
||||||
|
auto it = s.find('<');
|
||||||
|
auto template_base_name = s.substr(0, it);
|
||||||
|
|
||||||
|
auto cur = type_declaration();
|
||||||
|
|
||||||
|
return cur.fully_qualified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ namespace cx {
|
|||||||
|
|
||||||
using util::to_string;
|
using util::to_string;
|
||||||
|
|
||||||
|
class cursor;
|
||||||
|
|
||||||
class type {
|
class type {
|
||||||
public:
|
public:
|
||||||
type(CXType &&t)
|
type(CXType &&t)
|
||||||
@@ -50,6 +52,8 @@ public:
|
|||||||
|
|
||||||
type canonical() const { return {clang_getCanonicalType(m_type)}; }
|
type canonical() const { return {clang_getCanonicalType(m_type)}; }
|
||||||
|
|
||||||
|
bool is_unexposed() const { return kind() == CXType_Unexposed; }
|
||||||
|
|
||||||
bool is_const_qualified() const
|
bool is_const_qualified() const
|
||||||
{
|
{
|
||||||
return clang_isConstQualifiedType(m_type);
|
return clang_isConstQualifiedType(m_type);
|
||||||
@@ -72,12 +76,7 @@ public:
|
|||||||
|
|
||||||
type pointee_type() const { return {clang_getPointeeType(m_type)}; }
|
type pointee_type() const { return {clang_getPointeeType(m_type)}; }
|
||||||
|
|
||||||
/*
|
cursor type_declaration() const;
|
||||||
*cursor type_declaration() const
|
|
||||||
*{
|
|
||||||
* return {clang_getTypeDeclaration(m_type)};
|
|
||||||
*}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*std::string type_kind_spelling() const
|
*std::string type_kind_spelling() const
|
||||||
@@ -151,7 +150,7 @@ public:
|
|||||||
bool is_relationship() const
|
bool is_relationship() const
|
||||||
{
|
{
|
||||||
return is_pointer() || is_record() || is_reference() || !is_pod() ||
|
return is_pointer() || is_record() || is_reference() || !is_pod() ||
|
||||||
is_array() ||
|
is_array() || is_template() ||
|
||||||
(spelling().find("std::array") ==
|
(spelling().find("std::array") ==
|
||||||
0 /* There must be a better way... */);
|
0 /* There must be a better way... */);
|
||||||
}
|
}
|
||||||
@@ -204,6 +203,15 @@ public:
|
|||||||
return clang_Type_getCXXRefQualifier(m_type);
|
return clang_Type_getCXXRefQualifier(m_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_template_instantiation() const
|
||||||
|
{
|
||||||
|
auto s = spelling();
|
||||||
|
auto it = s.find('<');
|
||||||
|
return it != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string instantiation_template() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove all qualifiers from field declaration.
|
* @brief Remove all qualifiers from field declaration.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ public:
|
|||||||
return "+--";
|
return "+--";
|
||||||
case relationship_t::kAssociation:
|
case relationship_t::kAssociation:
|
||||||
return "-->";
|
return "-->";
|
||||||
|
case relationship_t::kInstantiation:
|
||||||
|
return "..|>";
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -159,11 +161,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &r : c.relationships) {
|
for (const auto &r : c.relationships) {
|
||||||
|
std::string destination;
|
||||||
|
if (r.type == relationship_t::kInstantiation) {
|
||||||
|
destination = m_model.usr_to_name(
|
||||||
|
m_config.using_namespace, r.destination);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
destination = r.destination;
|
||||||
|
}
|
||||||
|
|
||||||
ostr << m_model.to_alias(m_config.using_namespace,
|
ostr << m_model.to_alias(m_config.using_namespace,
|
||||||
ns_relative(m_config.using_namespace, c.name))
|
ns_relative(m_config.using_namespace,
|
||||||
|
c.full_name(m_config.using_namespace)))
|
||||||
<< " " << to_string(r.type) << " "
|
<< " " << to_string(r.type) << " "
|
||||||
<< m_model.to_alias(m_config.using_namespace,
|
<< m_model.to_alias(m_config.using_namespace,
|
||||||
ns_relative(m_config.using_namespace, r.destination));
|
ns_relative(m_config.using_namespace, destination));
|
||||||
|
|
||||||
if (!r.label.empty())
|
if (!r.label.empty())
|
||||||
ostr << " : " << r.label;
|
ostr << " : " << r.label;
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ enum class relationship_t {
|
|||||||
kAggregation,
|
kAggregation,
|
||||||
kContainment,
|
kContainment,
|
||||||
kOwnership,
|
kOwnership,
|
||||||
kAssociation
|
kAssociation,
|
||||||
|
kInstantiation
|
||||||
};
|
};
|
||||||
|
|
||||||
class element {
|
class element {
|
||||||
@@ -112,14 +113,17 @@ struct class_template {
|
|||||||
|
|
||||||
class class_ : public element {
|
class class_ : public element {
|
||||||
public:
|
public:
|
||||||
|
std::string usr;
|
||||||
bool is_struct{false};
|
bool is_struct{false};
|
||||||
bool is_template{false};
|
bool is_template{false};
|
||||||
|
bool is_template_instantiation{false};
|
||||||
std::vector<class_member> members;
|
std::vector<class_member> members;
|
||||||
std::vector<class_method> methods;
|
std::vector<class_method> methods;
|
||||||
std::vector<class_parent> bases;
|
std::vector<class_parent> bases;
|
||||||
std::vector<std::string> inner_classes;
|
std::vector<std::string> inner_classes;
|
||||||
std::vector<class_relationship> relationships;
|
std::vector<class_relationship> relationships;
|
||||||
std::vector<class_template> templates;
|
std::vector<class_template> templates;
|
||||||
|
std::string base_template_usr;
|
||||||
|
|
||||||
std::string full_name(
|
std::string full_name(
|
||||||
const std::vector<std::string> &using_namespaces) const
|
const std::vector<std::string> &using_namespaces) const
|
||||||
@@ -186,6 +190,17 @@ struct diagram {
|
|||||||
|
|
||||||
return full_name;
|
return full_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string usr_to_name(const std::vector<std::string> &using_namespaces,
|
||||||
|
const std::string &usr) const
|
||||||
|
{
|
||||||
|
for (const auto &c : classes) {
|
||||||
|
if (c.usr == usr)
|
||||||
|
return c.full_name(using_namespaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Cannot resolve USR: " + usr);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,13 +56,15 @@ struct tu_context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> struct element_visitor_context {
|
template <typename T> struct element_visitor_context {
|
||||||
element_visitor_context(T &e)
|
element_visitor_context(diagram &d_, T &e)
|
||||||
: element(e)
|
: element(e)
|
||||||
|
, d{d_}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
tu_context *ctx;
|
tu_context *ctx;
|
||||||
|
|
||||||
T &element;
|
T &element;
|
||||||
|
diagram &d;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CXChildVisitResult visit_if_cursor_valid(
|
enum CXChildVisitResult visit_if_cursor_valid(
|
||||||
@@ -238,7 +240,8 @@ static enum CXChildVisitResult class_visitor(
|
|||||||
spdlog::info("Class {} has {} template arguments.", c.name,
|
spdlog::info("Class {} has {} template arguments.", c.name,
|
||||||
cursor.template_argument_count());
|
cursor.template_argument_count());
|
||||||
|
|
||||||
auto class_ctx = element_visitor_context<class_>(c);
|
auto class_ctx =
|
||||||
|
element_visitor_context<class_>(ctx->ctx->d, c);
|
||||||
class_ctx.ctx = ctx->ctx;
|
class_ctx.ctx = ctx->ctx;
|
||||||
|
|
||||||
clang_visitChildren(cursor.get(), class_visitor, &class_ctx);
|
clang_visitChildren(cursor.get(), class_visitor, &class_ctx);
|
||||||
@@ -266,7 +269,7 @@ static enum CXChildVisitResult class_visitor(
|
|||||||
e.name = cursor.fully_qualified();
|
e.name = cursor.fully_qualified();
|
||||||
e.namespace_ = ctx->ctx->namespace_;
|
e.namespace_ = ctx->ctx->namespace_;
|
||||||
|
|
||||||
auto enum_ctx = element_visitor_context<enum_>(e);
|
auto enum_ctx = element_visitor_context<enum_>(ctx->ctx->d, e);
|
||||||
enum_ctx.ctx = ctx->ctx;
|
enum_ctx.ctx = ctx->ctx;
|
||||||
|
|
||||||
clang_visitChildren(cursor.get(), enum_visitor, &enum_ctx);
|
clang_visitChildren(cursor.get(), enum_visitor, &enum_ctx);
|
||||||
@@ -367,6 +370,43 @@ static enum CXChildVisitResult class_visitor(
|
|||||||
spdlog::info("Adding member {} {}::{} {}", m.type,
|
spdlog::info("Adding member {} {}::{} {}", m.type,
|
||||||
ctx->element.name, cursor.spelling(), t);
|
ctx->element.name, cursor.spelling(), t);
|
||||||
|
|
||||||
|
if (t.is_unexposed()) {
|
||||||
|
if (t.is_template_instantiation() &&
|
||||||
|
t.type_declaration()
|
||||||
|
.specialized_cursor_template()
|
||||||
|
.kind() != CXCursor_InvalidFile) {
|
||||||
|
spdlog::info(
|
||||||
|
"Found template instantiation: {} ..|> {}",
|
||||||
|
t.type_declaration(),
|
||||||
|
t.type_declaration()
|
||||||
|
.specialized_cursor_template());
|
||||||
|
class_ tinst;
|
||||||
|
tinst.name = t.type_declaration().spelling();
|
||||||
|
tinst.is_template_instantiation = true;
|
||||||
|
tinst.usr = t.type_declaration().usr();
|
||||||
|
for (int i = 0; i < t.template_arguments_count();
|
||||||
|
i++) {
|
||||||
|
class_template ct;
|
||||||
|
ct.type =
|
||||||
|
t.template_argument_type(i).spelling();
|
||||||
|
tinst.templates.emplace_back(std::move(ct));
|
||||||
|
}
|
||||||
|
tinst.base_template_usr =
|
||||||
|
t.type_declaration()
|
||||||
|
.specialized_cursor_template()
|
||||||
|
.usr();
|
||||||
|
|
||||||
|
class_relationship r;
|
||||||
|
r.destination = tinst.base_template_usr;
|
||||||
|
r.type = relationship_t::kInstantiation;
|
||||||
|
r.label = "";
|
||||||
|
|
||||||
|
tinst.relationships.emplace_back(std::move(r));
|
||||||
|
|
||||||
|
ctx->d.classes.emplace_back(std::move(tinst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
relationship_t relationship_type = relationship_t::kNone;
|
relationship_t relationship_type = relationship_t::kNone;
|
||||||
|
|
||||||
auto name = t.canonical().unqualified();
|
auto name = t.canonical().unqualified();
|
||||||
@@ -407,9 +447,13 @@ static enum CXChildVisitResult class_visitor(
|
|||||||
|
|
||||||
ctx->element.members.emplace_back(std::move(m));
|
ctx->element.members.emplace_back(std::move(m));
|
||||||
});
|
});
|
||||||
ret = CXChildVisit_Continue;
|
ret = CXChildVisit_Recurse;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CXCursor_ClassTemplatePartialSpecialization: {
|
||||||
|
spdlog::info("Found template specialization: {}", cursor);
|
||||||
|
ret = CXChildVisit_Continue;
|
||||||
|
} break;
|
||||||
case CXCursor_CXXBaseSpecifier: {
|
case CXCursor_CXXBaseSpecifier: {
|
||||||
if (!config.should_include(cursor.referenced().fully_qualified())) {
|
if (!config.should_include(cursor.referenced().fully_qualified())) {
|
||||||
ret = CXChildVisit_Continue;
|
ret = CXChildVisit_Continue;
|
||||||
@@ -493,11 +537,12 @@ static enum CXChildVisitResult translation_unit_visitor(
|
|||||||
|
|
||||||
visit_if_cursor_valid(cursor, [ctx, is_struct](cx::cursor cursor) {
|
visit_if_cursor_valid(cursor, [ctx, is_struct](cx::cursor cursor) {
|
||||||
class_ c{};
|
class_ c{};
|
||||||
|
c.usr = cursor.usr();
|
||||||
c.is_struct = is_struct;
|
c.is_struct = is_struct;
|
||||||
c.name = cursor.fully_qualified();
|
c.name = cursor.fully_qualified();
|
||||||
c.namespace_ = ctx->namespace_;
|
c.namespace_ = ctx->namespace_;
|
||||||
|
|
||||||
auto class_ctx = element_visitor_context<class_>(c);
|
auto class_ctx = element_visitor_context<class_>(ctx->d, c);
|
||||||
class_ctx.ctx = ctx;
|
class_ctx.ctx = ctx;
|
||||||
|
|
||||||
clang_visitChildren(cursor.get(), class_visitor, &class_ctx);
|
clang_visitChildren(cursor.get(), class_visitor, &class_ctx);
|
||||||
@@ -520,7 +565,7 @@ static enum CXChildVisitResult translation_unit_visitor(
|
|||||||
e.name = cursor.fully_qualified();
|
e.name = cursor.fully_qualified();
|
||||||
e.namespace_ = ctx->namespace_;
|
e.namespace_ = ctx->namespace_;
|
||||||
|
|
||||||
auto enum_ctx = element_visitor_context<enum_>(e);
|
auto enum_ctx = element_visitor_context<enum_>(ctx->d, e);
|
||||||
enum_ctx.ctx = ctx;
|
enum_ctx.ctx = ctx;
|
||||||
|
|
||||||
clang_visitChildren(cursor.get(), enum_visitor, &enum_ctx);
|
clang_visitChildren(cursor.get(), enum_visitor, &enum_ctx);
|
||||||
|
|||||||
12
tests/t00009/.clanguml
Normal file
12
tests/t00009/.clanguml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00009_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00009/t00009.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00009
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00009
|
||||||
43
tests/t00009/t00009.cc
Normal file
43
tests/t00009/t00009.cc
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/*
|
||||||
|
@startuml
|
||||||
|
|
||||||
|
class "A<T>" as C_0000000046
|
||||||
|
class C_0000000046 {
|
||||||
|
+T value
|
||||||
|
}
|
||||||
|
|
||||||
|
class "A<int>" as ABC
|
||||||
|
class "A<std::string>" as ABCD
|
||||||
|
|
||||||
|
class "B" as C_0000000047
|
||||||
|
class C_0000000047 {
|
||||||
|
+A<int> aint
|
||||||
|
+A<std::string> astring
|
||||||
|
}
|
||||||
|
|
||||||
|
C_0000000046 <|.. ABC
|
||||||
|
C_0000000046 <|.. ABCD
|
||||||
|
|
||||||
|
ABC <-- C_0000000047 : aint
|
||||||
|
ABCD <-- C_0000000047 : astring
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
*/
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00009 {
|
||||||
|
|
||||||
|
template <typename T> class A {
|
||||||
|
public:
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class B {
|
||||||
|
public:
|
||||||
|
A<int> aint;
|
||||||
|
A<std::string> astring;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
64
tests/t00009/test_case.h
Normal file
64
tests/t00009/test_case.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* tests/t00009/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("Test t00009", "[unit-test]")
|
||||||
|
{
|
||||||
|
spdlog::set_level(spdlog::level::debug);
|
||||||
|
|
||||||
|
auto [config, db] = load_config("t00009");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t00009_class"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t00009_class");
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t00009"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->exclude.namespaces.size() == 0);
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00009::A"));
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00009::B"));
|
||||||
|
|
||||||
|
auto model = generate_class_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name == "t00009_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, IsClass(_A("B")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
REQUIRE_THAT(puml, IsField(Public("T value")));
|
||||||
|
REQUIRE_THAT(puml, IsField(Public("T * pointer")));
|
||||||
|
REQUIRE_THAT(puml, IsField(Public("T & reference")));
|
||||||
|
REQUIRE_THAT(puml, IsField(Public("std::vector<P> values")));
|
||||||
|
REQUIRE_THAT(puml, IsField(Public("std::array<int, N> ints")));
|
||||||
|
REQUIRE_THAT(puml, IsField(Public("bool (*)(int, int) comparator")));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("B", "T, C<>"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsField(Public("C<T> template_template")));
|
||||||
|
*/
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
@@ -119,3 +119,4 @@ using clanguml::test::matchers::Static;
|
|||||||
#include "t00006/test_case.h"
|
#include "t00006/test_case.h"
|
||||||
#include "t00007/test_case.h"
|
#include "t00007/test_case.h"
|
||||||
#include "t00008/test_case.h"
|
#include "t00008/test_case.h"
|
||||||
|
#include "t00009/test_case.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user