Added initial class template handling
This commit is contained in:
@@ -194,6 +194,26 @@ public:
|
||||
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)); }
|
||||
|
||||
const CXCursor &get() const { return m_cursor; }
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
|
||||
CXTypeKind kind() const { return m_type.kind; }
|
||||
|
||||
std::string kind_spelling()
|
||||
std::string kind_spelling() const
|
||||
{
|
||||
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_parameter() const
|
||||
{
|
||||
return canonical().spelling().find("type-parameter-") == 0;
|
||||
}
|
||||
|
||||
int template_arguments_count() const
|
||||
{
|
||||
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
|
||||
ostr << "class ";
|
||||
|
||||
ostr << ns_relative(m_config.using_namespace, c.name) << " {"
|
||||
<< std::endl;
|
||||
ostr << ns_relative(m_config.using_namespace, c.name);
|
||||
|
||||
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
|
||||
|
||||
@@ -87,6 +87,10 @@ struct class_relationship {
|
||||
std::string label;
|
||||
};
|
||||
|
||||
struct class_template {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct class_ : public element {
|
||||
bool is_struct{false};
|
||||
bool is_template{false};
|
||||
@@ -94,8 +98,8 @@ struct class_ : public element {
|
||||
std::vector<class_method> methods;
|
||||
std::vector<class_parent> bases;
|
||||
std::vector<std::string> inner_classes;
|
||||
|
||||
std::vector<class_relationship> relationships;
|
||||
std::vector<class_template> templates;
|
||||
|
||||
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_parent;
|
||||
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::enum_;
|
||||
using clanguml::model::class_diagram::relationship_t;
|
||||
@@ -235,6 +236,9 @@ static enum CXChildVisitResult class_visitor(
|
||||
c.name = cursor.fully_qualified();
|
||||
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);
|
||||
class_ctx.ctx = ctx->ctx;
|
||||
|
||||
@@ -280,6 +284,25 @@ static enum CXChildVisitResult class_visitor(
|
||||
});
|
||||
ret = CXChildVisit_Continue;
|
||||
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_Constructor:
|
||||
case CXCursor_Destructor:
|
||||
@@ -317,18 +340,18 @@ static enum CXChildVisitResult class_visitor(
|
||||
auto t = cursor.type();
|
||||
class_member m;
|
||||
m.name = cursor.spelling();
|
||||
m.type = t.is_template() ? t.unqualified()
|
||||
: t.canonical().unqualified();
|
||||
if (t.is_template())
|
||||
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(
|
||||
cursor.cxxaccess_specifier());
|
||||
m.is_static = cursor.is_static();
|
||||
|
||||
spdlog::info("Adding member {} {}::{} (type kind: {} | {} "
|
||||
"| {} | {} | {})",
|
||||
m.type, ctx->element.name, cursor.spelling(),
|
||||
t.kind_spelling(), t.pointee_type().spelling(),
|
||||
t.is_pod(), t.canonical().spelling(),
|
||||
t.is_relationship());
|
||||
spdlog::info("Adding member {} {}::{} {}", m.type,
|
||||
ctx->element.name, cursor.spelling(), t);
|
||||
|
||||
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::IsBaseClass;
|
||||
using clanguml::test::matchers::IsClass;
|
||||
using clanguml::test::matchers::IsClassTemplate;
|
||||
using clanguml::test::matchers::IsComposition;
|
||||
using clanguml::test::matchers::IsEnum;
|
||||
using clanguml::test::matchers::IsField;
|
||||
@@ -116,3 +117,4 @@ using clanguml::test::matchers::Static;
|
||||
#include "t00005/test_case.h"
|
||||
#include "t00006/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));
|
||||
}
|
||||
|
||||
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,
|
||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user