Added initial class template handling
This commit is contained in:
@@ -194,6 +194,26 @@ public:
|
|||||||
return clang_getCXXAccessSpecifier(m_cursor);
|
return clang_getCXXAccessSpecifier(m_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int template_argument_count() const
|
||||||
|
{
|
||||||
|
return clang_Cursor_getNumTemplateArguments(m_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
CXTemplateArgumentKind template_argument_kind(unsigned i) const
|
||||||
|
{
|
||||||
|
return clang_Cursor_getTemplateArgumentKind(m_cursor, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
cx::type template_argument_type(unsigned i) const
|
||||||
|
{
|
||||||
|
return clang_Cursor_getTemplateArgumentType(m_cursor, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
long long template_argument_value(unsigned i) const
|
||||||
|
{
|
||||||
|
return clang_Cursor_getTemplateArgumentValue(m_cursor, i);
|
||||||
|
}
|
||||||
|
|
||||||
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; }
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public:
|
|||||||
|
|
||||||
CXTypeKind kind() const { return m_type.kind; }
|
CXTypeKind kind() const { return m_type.kind; }
|
||||||
|
|
||||||
std::string kind_spelling()
|
std::string kind_spelling() const
|
||||||
{
|
{
|
||||||
return to_string(clang_getTypeKindSpelling(m_type.kind));
|
return to_string(clang_getTypeKindSpelling(m_type.kind));
|
||||||
}
|
}
|
||||||
@@ -182,6 +182,11 @@ public:
|
|||||||
|
|
||||||
bool is_template() const { return template_arguments_count() > 0; }
|
bool is_template() const { return template_arguments_count() > 0; }
|
||||||
|
|
||||||
|
bool is_template_parameter() const
|
||||||
|
{
|
||||||
|
return canonical().spelling().find("type-parameter-") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
int template_arguments_count() const
|
int template_arguments_count() const
|
||||||
{
|
{
|
||||||
return clang_Type_getNumTemplateArguments(m_type);
|
return clang_Type_getNumTemplateArguments(m_type);
|
||||||
@@ -225,3 +230,20 @@ private:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> struct fmt::formatter<clanguml::cx::type> {
|
||||||
|
template <typename ParseContext> constexpr auto parse(ParseContext &ctx)
|
||||||
|
{
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const clanguml::cx::type &t, FormatContext &ctx)
|
||||||
|
{
|
||||||
|
return fmt::format_to(ctx.out(),
|
||||||
|
"(cx::type spelling={}, kind={}, pointee={}, "
|
||||||
|
"is_pod={}, canonical={}, is_relationship={})",
|
||||||
|
t.spelling(), t.kind_spelling(), t.pointee_type().spelling(),
|
||||||
|
t.is_pod(), t.canonical().spelling(), t.is_relationship());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -100,8 +100,17 @@ public:
|
|||||||
else
|
else
|
||||||
ostr << "class ";
|
ostr << "class ";
|
||||||
|
|
||||||
ostr << ns_relative(m_config.using_namespace, c.name) << " {"
|
ostr << ns_relative(m_config.using_namespace, c.name);
|
||||||
<< std::endl;
|
|
||||||
|
if (!c.templates.empty()) {
|
||||||
|
std::vector<std::string> tnames;
|
||||||
|
std::transform(c.templates.cbegin(), c.templates.cend(),
|
||||||
|
std::back_inserter(tnames),
|
||||||
|
[](const auto &tmplt) { return tmplt.name; });
|
||||||
|
ostr << fmt::format("<{}>", fmt::join(tnames, ", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
ostr << " {" << std::endl;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Process methods
|
// Process methods
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ struct class_relationship {
|
|||||||
std::string label;
|
std::string label;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct class_template {
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
struct class_ : public element {
|
struct class_ : public element {
|
||||||
bool is_struct{false};
|
bool is_struct{false};
|
||||||
bool is_template{false};
|
bool is_template{false};
|
||||||
@@ -94,8 +98,8 @@ struct class_ : public element {
|
|||||||
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;
|
||||||
|
|
||||||
bool is_abstract() const
|
bool is_abstract() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ using clanguml::model::class_diagram::class_member;
|
|||||||
using clanguml::model::class_diagram::class_method;
|
using clanguml::model::class_diagram::class_method;
|
||||||
using clanguml::model::class_diagram::class_parent;
|
using clanguml::model::class_diagram::class_parent;
|
||||||
using clanguml::model::class_diagram::class_relationship;
|
using clanguml::model::class_diagram::class_relationship;
|
||||||
|
using clanguml::model::class_diagram::class_template;
|
||||||
using clanguml::model::class_diagram::diagram;
|
using clanguml::model::class_diagram::diagram;
|
||||||
using clanguml::model::class_diagram::enum_;
|
using clanguml::model::class_diagram::enum_;
|
||||||
using clanguml::model::class_diagram::relationship_t;
|
using clanguml::model::class_diagram::relationship_t;
|
||||||
@@ -235,6 +236,9 @@ static enum CXChildVisitResult class_visitor(
|
|||||||
c.name = cursor.fully_qualified();
|
c.name = cursor.fully_qualified();
|
||||||
c.namespace_ = ctx->ctx->namespace_;
|
c.namespace_ = ctx->ctx->namespace_;
|
||||||
|
|
||||||
|
spdlog::info("Class {} has {} template arguments.", c.name,
|
||||||
|
cursor.template_argument_count());
|
||||||
|
|
||||||
auto class_ctx = element_visitor_context<class_>(c);
|
auto class_ctx = element_visitor_context<class_>(c);
|
||||||
class_ctx.ctx = ctx->ctx;
|
class_ctx.ctx = ctx->ctx;
|
||||||
|
|
||||||
@@ -280,6 +284,25 @@ static enum CXChildVisitResult class_visitor(
|
|||||||
});
|
});
|
||||||
ret = CXChildVisit_Continue;
|
ret = CXChildVisit_Continue;
|
||||||
break;
|
break;
|
||||||
|
case CXCursor_TemplateTypeParameter: {
|
||||||
|
spdlog::info(
|
||||||
|
"Found template type parameter: {}", cursor.spelling());
|
||||||
|
class_template ct;
|
||||||
|
ct.name = cursor.spelling();
|
||||||
|
ctx->element.templates.emplace_back(std::move(ct));
|
||||||
|
|
||||||
|
ret = CXChildVisit_Continue;
|
||||||
|
} break;
|
||||||
|
case CXCursor_NonTypeTemplateParameter:
|
||||||
|
spdlog::info(
|
||||||
|
"Found template nontype parameter: {}", cursor.spelling());
|
||||||
|
ret = CXChildVisit_Continue;
|
||||||
|
break;
|
||||||
|
case CXCursor_TemplateTemplateParameter:
|
||||||
|
spdlog::info(
|
||||||
|
"Found template template parameter: {}", cursor.spelling());
|
||||||
|
ret = CXChildVisit_Continue;
|
||||||
|
break;
|
||||||
case CXCursor_CXXMethod:
|
case CXCursor_CXXMethod:
|
||||||
case CXCursor_Constructor:
|
case CXCursor_Constructor:
|
||||||
case CXCursor_Destructor:
|
case CXCursor_Destructor:
|
||||||
@@ -317,18 +340,18 @@ static enum CXChildVisitResult class_visitor(
|
|||||||
auto t = cursor.type();
|
auto t = cursor.type();
|
||||||
class_member m;
|
class_member m;
|
||||||
m.name = cursor.spelling();
|
m.name = cursor.spelling();
|
||||||
m.type = t.is_template() ? t.unqualified()
|
if (t.is_template())
|
||||||
: t.canonical().unqualified();
|
m.type = t.unqualified();
|
||||||
|
else if (t.is_template_parameter())
|
||||||
|
m.type = t.spelling();
|
||||||
|
else
|
||||||
|
m.type = t.canonical().unqualified();
|
||||||
m.scope = cx_access_specifier_to_scope(
|
m.scope = cx_access_specifier_to_scope(
|
||||||
cursor.cxxaccess_specifier());
|
cursor.cxxaccess_specifier());
|
||||||
m.is_static = cursor.is_static();
|
m.is_static = cursor.is_static();
|
||||||
|
|
||||||
spdlog::info("Adding member {} {}::{} (type kind: {} | {} "
|
spdlog::info("Adding member {} {}::{} {}", m.type,
|
||||||
"| {} | {} | {})",
|
ctx->element.name, cursor.spelling(), t);
|
||||||
m.type, ctx->element.name, cursor.spelling(),
|
|
||||||
t.kind_spelling(), t.pointee_type().spelling(),
|
|
||||||
t.is_pod(), t.canonical().spelling(),
|
|
||||||
t.is_relationship());
|
|
||||||
|
|
||||||
relationship_t relationship_type = relationship_t::kNone;
|
relationship_t relationship_type = relationship_t::kNone;
|
||||||
|
|
||||||
|
|||||||
12
tests/t00008/.clanguml
Normal file
12
tests/t00008/.clanguml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00008_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00008/t00008.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00008
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00008
|
||||||
13
tests/t00008/t00008.cc
Normal file
13
tests/t00008/t00008.cc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00008 {
|
||||||
|
template <typename T, typename P = T> class A {
|
||||||
|
public:
|
||||||
|
T value;
|
||||||
|
T *pointer;
|
||||||
|
T &reference;
|
||||||
|
std::vector<P> values;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
55
tests/t00008/test_case.h
Normal file
55
tests/t00008/test_case.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* tests/t00008/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 t00008", "[unit-test]")
|
||||||
|
{
|
||||||
|
spdlog::set_level(spdlog::level::debug);
|
||||||
|
|
||||||
|
auto [config, db] = load_config("t00008");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t00008_class"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t00008_class");
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t00008"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->exclude.namespaces.size() == 0);
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00008::A"));
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00008::B"));
|
||||||
|
|
||||||
|
auto model = generate_class_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name == "t00008_class");
|
||||||
|
|
||||||
|
auto puml = generate_class_puml(diagram, model);
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||||
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("A", "T, P"));
|
||||||
|
|
||||||
|
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")));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
@@ -100,6 +100,7 @@ using clanguml::test::matchers::IsAggregation;
|
|||||||
using clanguml::test::matchers::IsAssociation;
|
using clanguml::test::matchers::IsAssociation;
|
||||||
using clanguml::test::matchers::IsBaseClass;
|
using clanguml::test::matchers::IsBaseClass;
|
||||||
using clanguml::test::matchers::IsClass;
|
using clanguml::test::matchers::IsClass;
|
||||||
|
using clanguml::test::matchers::IsClassTemplate;
|
||||||
using clanguml::test::matchers::IsComposition;
|
using clanguml::test::matchers::IsComposition;
|
||||||
using clanguml::test::matchers::IsEnum;
|
using clanguml::test::matchers::IsEnum;
|
||||||
using clanguml::test::matchers::IsField;
|
using clanguml::test::matchers::IsField;
|
||||||
@@ -116,3 +117,4 @@ using clanguml::test::matchers::Static;
|
|||||||
#include "t00005/test_case.h"
|
#include "t00005/test_case.h"
|
||||||
#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"
|
||||||
|
|||||||
@@ -196,6 +196,14 @@ ContainsMatcher IsClass(std::string const &str,
|
|||||||
return ContainsMatcher(CasedString("class " + str, caseSensitivity));
|
return ContainsMatcher(CasedString("class " + str, caseSensitivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContainsMatcher IsClassTemplate(std::string const &str,
|
||||||
|
std::string const &tmplt,
|
||||||
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
|
{
|
||||||
|
return ContainsMatcher(
|
||||||
|
CasedString(fmt::format("class {}<{}>", str, tmplt), caseSensitivity));
|
||||||
|
}
|
||||||
|
|
||||||
ContainsMatcher IsEnum(std::string const &str,
|
ContainsMatcher IsEnum(std::string const &str,
|
||||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user