Fixed up to 26

This commit is contained in:
Bartek Kryza
2022-07-24 00:10:08 +02:00
parent d65864adaf
commit 8efbb2446e
23 changed files with 322 additions and 56 deletions

View File

@@ -140,6 +140,9 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
{
if (ns->isAnonymousNamespace() || ns->isInline())
return true;
auto package_path = namespace_{ns->getQualifiedNameAsString()};
auto package_parent = package_path;
@@ -181,6 +184,11 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm)
{
// Anonymous enum values should be rendered as class fields
// with type enum
if (enm->getNameAsString().empty())
return true;
auto e_ptr = std::make_unique<enum_>(config_.using_namespace());
auto &e = *e_ptr;
@@ -218,6 +226,43 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm)
return true;
}
bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl *cls)
{
if (source_manager_.isInSystemHeader(cls->getSourceRange().getBegin()))
return true;
// Skip forward declarations
if (!cls->isCompleteDefinition())
return true;
// Check if the class was already processed within VisitClassTemplateDecl()
if (diagram_.has_element(cls->getID()))
return true;
// TODO: Add support for classes defined in function/method bodies
if (cls->isLocalClass())
return true;
auto template_specialization_ptr = process_template_specialization(cls);
if (!template_specialization_ptr)
return true;
auto &template_specialization = *template_specialization_ptr;
process_template_specialization_children(cls, template_specialization);
template_specialization.add_relationship({relationship_t::kInstantiation,
cls->getSpecializedTemplate()->getID()});
if (diagram_.should_include(template_specialization)) {
diagram_.add_class(std::move(template_specialization_ptr));
}
return true;
}
bool translation_unit_visitor::VisitClassTemplateDecl(
clang::ClassTemplateDecl *cls)
{
@@ -247,7 +292,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
return true;
// Templated records are handled by VisitClassTemplateDecl()
if (cls->isTemplated())
if (cls->isTemplated() || cls->isTemplateDecl())
return true;
// Skip forward declarations
@@ -280,7 +325,12 @@ std::unique_ptr<class_> translation_unit_visitor::process_class_declaration(
auto c_ptr{std::make_unique<class_>(config_.using_namespace())};
auto &c = *c_ptr;
namespace_ ns{cls->getQualifiedNameAsString()};
// TODO: refactor to method get_qualified_name()
auto qualified_name = cls->getQualifiedNameAsString();
util::replace_all(qualified_name, "(anonymous namespace)", "");
util::replace_all(qualified_name, "::::", "::");
namespace_ ns{qualified_name};
ns.pop_back();
c.set_name(cls->getNameAsString());
c.set_namespace(ns);
@@ -400,8 +450,19 @@ void translation_unit_visitor::process_class_bases(
to_string(base.getType(), cls->getASTContext())};
cp.set_name(name_and_ns.to_string());
cp.set_id(
base.getType()->getAs<clang::RecordType>()->getDecl()->getID());
if (base.getType()->getAs<clang::RecordType>() != nullptr)
cp.set_id(
base.getType()->getAs<clang::RecordType>()->getDecl()->getID());
else if (base.getType()->getAs<clang::TemplateSpecializationType>() !=
nullptr) {
cp.set_id(base.getType()
->getAs<clang::TemplateSpecializationType>()
->getTemplateName()
.getAsTemplateDecl()
->getID());
}
cp.is_virtual(base.isVirtual());
cp.set_access(
@@ -413,15 +474,14 @@ void translation_unit_visitor::process_class_bases(
}
}
void translation_unit_visitor::process_class_children(
const clang::CXXRecordDecl *cls, class_ &c)
void translation_unit_visitor::process_template_specialization_children(
const clang::ClassTemplateSpecializationDecl *cls, class_ &c)
{
assert(cls != nullptr);
// Iterate over class methods (both regular and static)
for (const auto *method : cls->methods()) {
if (method != nullptr) {
process_method(*method, c);
}
}
@@ -454,6 +514,83 @@ void translation_unit_visitor::process_class_children(
process_static_field(*variable_declaration, c);
}
}
else if (decl->getKind() == clang::Decl::Enum) {
const auto *enum_decl =
clang::dyn_cast_or_null<clang::EnumDecl>(decl);
if (enum_decl == nullptr)
continue;
if (enum_decl->getNameAsString().empty()) {
for (const auto *enum_const : enum_decl->enumerators()) {
class_member m{detail::access_specifier_to_access_t(
enum_decl->getAccess()),
enum_const->getNameAsString(), "enum"};
c.add_member(std::move(m));
}
}
}
}
for (const auto *friend_declaration : cls->friends()) {
process_friend(*friend_declaration, c);
}
}
void translation_unit_visitor::process_class_children(
const clang::CXXRecordDecl *cls, class_ &c)
{
assert(cls != nullptr);
// Iterate over class methods (both regular and static)
for (const auto *method : cls->methods()) {
if (method != nullptr) {
process_method(*method, c);
}
}
// Iterate over class template methods
for (auto const *decl_iterator :
clang::dyn_cast_or_null<clang::DeclContext>(cls)->decls()) {
auto const *method_template =
llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl_iterator);
if (method_template == nullptr)
continue;
process_template_method(*method_template, c);
}
// Iterate over regular class fields
for (const auto *field : cls->fields()) {
if (field != nullptr)
process_field(*field, c);
}
// Static fields have to be processed by iterating over variable
// declarations
for (const auto *decl : cls->decls()) {
if (decl->getKind() == clang::Decl::Var) {
const clang::VarDecl *variable_declaration{
dynamic_cast<const clang::VarDecl *>(decl)};
if (variable_declaration &&
variable_declaration->isStaticDataMember()) {
process_static_field(*variable_declaration, c);
}
}
else if (decl->getKind() == clang::Decl::Enum) {
const auto *enum_decl =
clang::dyn_cast_or_null<clang::EnumDecl>(decl);
if (enum_decl == nullptr)
continue;
if (enum_decl->getNameAsString().empty()) {
for (const auto *enum_const : enum_decl->enumerators()) {
class_member m{detail::access_specifier_to_access_t(
enum_decl->getAccess()),
enum_const->getNameAsString(), "enum"};
c.add_member(std::move(m));
}
}
}
}
for (const auto *friend_declaration : cls->friends()) {
@@ -636,7 +773,7 @@ void translation_unit_visitor::process_function_parameter(
p.getType(), relationships, relationship_t::kDependency);
for (const auto &[type_element_id, relationship_type] : relationships) {
if (/*diagram().has_element(type_element_id) &&*/
if (type_element_id != c.id() &&
(relationship_type != relationship_t::kNone)) {
relationship r{relationship_t::kDependency, type_element_id};
@@ -709,9 +846,7 @@ void translation_unit_visitor::add_relationships(class_ &c,
std::optional<std::string> label)
{
for (const auto &[target, relationship_type] : relationships) {
if (diagram().has_element(target) &&
(relationship_type != relationship_t::kNone) /*&&
(target != c.name_and_ns())*/) {
if (relationship_type != relationship_t::kNone) {
relationship r{relationship_type, target};
if (label)
r.set_label(label.value());
@@ -760,6 +895,98 @@ void translation_unit_visitor::process_static_field(
c.add_member(std::move(field));
}
std::unique_ptr<class_>
translation_unit_visitor::process_template_specialization(
clang::ClassTemplateSpecializationDecl *cls)
{
auto c_ptr{std::make_unique<class_>(config_.using_namespace())};
auto &template_instantiation = *c_ptr;
// TODO: refactor to method get_qualified_name()
auto qualified_name = cls->getQualifiedNameAsString();
util::replace_all(qualified_name, "(anonymous namespace)", "");
util::replace_all(qualified_name, "::::", "::");
namespace_ ns{qualified_name};
ns.pop_back();
template_instantiation.set_name(cls->getNameAsString());
template_instantiation.set_namespace(ns);
template_instantiation.set_id(cls->getID());
template_instantiation.is_struct(cls->isStruct());
process_comment(*cls, template_instantiation);
set_source_location(*cls, template_instantiation);
if (template_instantiation.skip())
return {};
const auto template_args_count = cls->getTemplateArgs().size();
for (auto arg_it = 0U; arg_it < template_args_count; arg_it++) {
const auto arg = cls->getTemplateArgs().get(arg_it);
const auto argument_kind = arg.getKind();
if (argument_kind == clang::TemplateArgument::ArgKind::Type) {
template_parameter argument;
argument.is_template_parameter(false);
// If this is a nested template type - add nested templates as
// template arguments
if (arg.getAsType()->getAs<clang::TemplateSpecializationType>()) {
const auto *nested_template_type =
arg.getAsType()->getAs<clang::TemplateSpecializationType>();
const auto nested_template_name =
nested_template_type->getTemplateName()
.getAsTemplateDecl()
->getQualifiedNameAsString();
argument.set_name(nested_template_name);
auto nested_template_instantiation =
build_template_instantiation(
*arg.getAsType()
->getAs<clang::TemplateSpecializationType>());
for (const auto &t : nested_template_instantiation->templates())
argument.add_template_param(t);
// Check if this template should be simplified (e.g. system
// template aliases such as 'std:basic_string<char>' should be
// simply 'std::string')
simplify_system_template(argument,
argument.to_string(config().using_namespace(), false));
}
else {
argument.set_name(
to_string(arg.getAsType(), cls->getASTContext()));
}
template_instantiation.add_template(std::move(argument));
}
else if (argument_kind == clang::TemplateArgument::ArgKind::Integral) {
template_parameter argument;
argument.is_template_parameter(false);
argument.set_type(
std::to_string(arg.getAsIntegral().getExtValue()));
template_instantiation.add_template(std::move(argument));
}
else if (argument_kind ==
clang::TemplateArgument::ArgKind::Expression) {
template_parameter argument;
argument.is_template_parameter(false);
argument.set_type(get_source_text(
arg.getAsExpr()->getSourceRange(), source_manager_));
template_instantiation.add_template(std::move(argument));
}
else {
LOG_ERROR("UNSUPPORTED ARGUMENT KIND FOR ARG {}", arg.getKind());
}
}
return c_ptr;
}
std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
const clang::TemplateSpecializationType &template_type,
std::optional<clanguml::class_diagram::model::class_ *> parent)
@@ -797,7 +1024,6 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
// If this is a nested template type - add nested templates as
// template arguments
if (arg.getAsType()->getAs<clang::TemplateSpecializationType>()) {
const auto *nested_template_type =
arg.getAsType()->getAs<clang::TemplateSpecializationType>();
@@ -822,6 +1048,14 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
// simply 'std::string')
simplify_system_template(argument,
argument.to_string(config().using_namespace(), false));
auto nested_template_instantiation_full_name =
nested_template_instantiation->full_name(false);
if (diagram().should_include(
nested_template_instantiation_full_name)) {
diagram().add_class(
std::move(nested_template_instantiation));
}
}
else {
argument.set_name(
@@ -848,6 +1082,16 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
else {
LOG_ERROR("UNSUPPORTED ARGUMENT KIND FOR ARG {}", arg.getKind());
}
// In case any of the template arguments are base classes, add
// them as parents of the current template instantiation class
// TODO: Add to fix t000019
// if (template_decl.) {
// has_variadic_params =
// build_template_instantiation_add_base_classes(tinst,
// template_base_params, arg_index,
// has_variadic_params, ct);
// }
}
if (diagram().has_element(
@@ -880,6 +1124,9 @@ void translation_unit_visitor::process_field(
if (field.skip())
return;
if (field_name == "divider")
LOG_ERROR("EEEEEEEEEEEEEEEEEEEEEEE");
if (field_type->isPointerType()) {
relationship_hint = relationship_t::kAssociation;
field_type = field_type->getPointeeType();
@@ -900,19 +1147,24 @@ void translation_unit_visitor::process_field(
template_field_type->getTemplateName()
.getAsTemplateDecl()
->getQualifiedNameAsString();
if (diagram().should_include(template_field_decl_name)) {
auto template_specialization_ptr = build_template_instantiation(
*field_type->getAs<clang::TemplateSpecializationType>());
auto template_specialization_ptr = build_template_instantiation(
*field_type->getAs<clang::TemplateSpecializationType>());
if (diagram().should_include(template_field_decl_name)) {
relationship r{
relationship_hint, template_specialization_ptr->id()};
r.set_label(field_declaration.getNameAsString());
r.set_access(detail::access_specifier_to_access_t(
field_declaration.getAccess()));
diagram().add_class(std::move(template_specialization_ptr));
bool added =
diagram().add_class(std::move(template_specialization_ptr));
LOG_DBG("ADDED TEMPLATE SPECIALIZATION TO DIAGRAM");
if (added)
LOG_DBG("ADDED TEMPLATE SPECIALIZATION TO DIAGRAM");
else
LOG_ERROR("NOT ADDED ");
c.add_relationship(std::move(r));
}

View File

@@ -53,6 +53,9 @@ public:
virtual bool VisitClassTemplateDecl(
clang::ClassTemplateDecl *class_template_declaration);
virtual bool VisitClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl *cls);
// virtual bool VisitVarDecl(clang::VarDecl *variable_declaration);
clanguml::class_diagram::model::diagram &diagram() { return diagram_; }
@@ -64,12 +67,20 @@ private:
std::unique_ptr<clanguml::class_diagram::model::class_>
process_class_declaration(clang::CXXRecordDecl *cls);
std::unique_ptr<clanguml::class_diagram::model::class_>
process_template_specialization(
clang::ClassTemplateSpecializationDecl *cls);
void process_class_bases(const clang::CXXRecordDecl *cls,
clanguml::class_diagram::model::class_ &c) const;
void process_class_children(const clang::CXXRecordDecl *cls,
clanguml::class_diagram::model::class_ &c);
void process_template_specialization_children(
const clang::ClassTemplateSpecializationDecl *cls,
clanguml::class_diagram::model::class_ &c);
bool process_template_parameters(
const clang::ClassTemplateDecl &template_declaration,
clanguml::class_diagram::model::class_ &c);

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00014", "[test-case][class]")
REQUIRE(diagram->name == "t00014_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00014_class");
REQUIRE(model->should_include("clanguml::t00014::B"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00015", "[test-case][class]")
REQUIRE(diagram->name == "t00015_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00015_class");
REQUIRE(model->should_include("clanguml::t00015::ns1::ns2::A"));

View File

@@ -5,11 +5,15 @@ template <typename> struct is_numeric {
enum { value = false };
};
template <> struct is_numeric<float> {
enum { value = true };
};
template <> struct is_numeric<char> {
enum { value = true };
};
template <> struct is_numeric<unsigned char> {
template <> struct is_numeric<unsigned int> {
enum { value = true };
};

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00016", "[test-case][class]")
REQUIRE(diagram->name == "t00016_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00016_class");
REQUIRE(model->should_include("clanguml::t00016::is_numeric"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00017", "[test-case][class]")
REQUIRE(diagram->name == "t00017_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00017_class");
@@ -47,8 +47,8 @@ TEST_CASE("t00017", "[test-case][class]")
REQUIRE_THAT(puml, IsClass(_A("R")));
REQUIRE_THAT(puml, (IsField<Private>("some_int", "int")));
REQUIRE_THAT(puml, (IsField<Private>("some_int_pointer", "int*")));
REQUIRE_THAT(puml, (IsField<Private>("some_int_pointer_pointer", "int**")));
REQUIRE_THAT(puml, (IsField<Private>("some_int_pointer", "int *")));
REQUIRE_THAT(puml, (IsField<Private>("some_int_pointer_pointer", "int **")));
// Relationship members should not be rendered as part of this testcase
REQUIRE_THAT(puml, !(IsField<Private>("a", _A("A"))));

View File

@@ -4,7 +4,6 @@ diagrams:
t00018_class:
type: class
glob:
- ../../tests/t00018/**.h
- ../../tests/t00018/**.cc
using_namespace:
- clanguml::t00018

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00018", "[test-case][class]")
REQUIRE(diagram->name == "t00018_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00018_class");
REQUIRE(model->should_include("clanguml::t00018::widget"));
@@ -40,6 +40,7 @@ TEST_CASE("t00018", "[test-case][class]")
REQUIRE_THAT(
puml, IsAggregation(_A("widget"), _A("impl::widget"), "-pImpl"));
REQUIRE_THAT(puml, IsDependency(_A("impl::widget"), _A("widget")));
REQUIRE_THAT(puml, !IsDependency(_A("widget"), _A("widget")));
save_puml(
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);

View File

@@ -4,7 +4,6 @@ diagrams:
t00019_class:
type: class
glob:
- ../../tests/t00019/**.h
- ../../tests/t00019/**.cc
using_namespace:
- clanguml::t00019

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00019", "[test-case][class]")
REQUIRE(diagram->name == "t00019_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00019_class");

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00020", "[test-case][class]")
REQUIRE(diagram->name == "t00020_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00020_class");
REQUIRE(model->should_include("clanguml::t00020::ProductA"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00021", "[test-case][class]")
REQUIRE(diagram->name == "t00021_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00021_class");
REQUIRE(model->should_include("clanguml::t00021::Visitor"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00022", "[test-case][class]")
REQUIRE(diagram->name == "t00022_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00022_class");
REQUIRE(model->should_include("clanguml::t00022::A"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00023", "[test-case][class]")
REQUIRE(diagram->name == "t00023_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00023_class");
REQUIRE(model->should_include("clanguml::t00023::Visitor"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00024", "[test-case][class]")
REQUIRE(diagram->name == "t00024_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00024_class");
REQUIRE(model->should_include("clanguml::t00024::A"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00025", "[test-case][class]")
REQUIRE(diagram->name == "t00025_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00025_class");
REQUIRE(model->should_include("clanguml::t00025::A"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00026", "[test-case][class]")
REQUIRE(diagram->name == "t00026_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00026_class");
REQUIRE(model->should_include("clanguml::t00026::A"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00027", "[test-case][class]")
REQUIRE(diagram->name == "t00027_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00027_class");
REQUIRE(model->should_include("clanguml::t00027::A"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00028", "[test-case][class]")
REQUIRE(diagram->name == "t00028_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00028_class");
REQUIRE(model->should_include("clanguml::t00028::A"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00029", "[test-case][class]")
REQUIRE(diagram->name == "t00029_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00029_class");
REQUIRE(model->should_include("clanguml::t00029::A"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00030", "[test-case][class]")
REQUIRE(diagram->name == "t00030_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00030_class");
REQUIRE(model->should_include("clanguml::t00030::A"));

View File

@@ -202,23 +202,23 @@ using namespace clanguml::test::matchers;
#include "t00011/test_case.h"
#include "t00012/test_case.h"
#include "t00013/test_case.h"
//#include "t00014/test_case.h"
//#include "t00015/test_case.h"
//#include "t00016/test_case.h"
//#include "t00017/test_case.h"
//#include "t00018/test_case.h"
//#include "t00019/test_case.h"
//#include "t00020/test_case.h"
//#include "t00021/test_case.h"
//#include "t00022/test_case.h"
//#include "t00023/test_case.h"
//#include "t00024/test_case.h"
//#include "t00025/test_case.h"
//#include "t00026/test_case.h"
//#include "t00027/test_case.h"
//#include "t00028/test_case.h"
//#include "t00029/test_case.h"
//#include "t00030/test_case.h"
#include "t00014/test_case.h"
#include "t00015/test_case.h"
#include "t00016/test_case.h"
#include "t00017/test_case.h"
#include "t00018/test_case.h"
#include "t00019/test_case.h"
#include "t00020/test_case.h"
#include "t00021/test_case.h"
#include "t00022/test_case.h"
#include "t00023/test_case.h"
#include "t00024/test_case.h"
#include "t00025/test_case.h"
#include "t00026/test_case.h"
#include "t00027/test_case.h"
#include "t00028/test_case.h"
#include "t00029/test_case.h"
#include "t00030/test_case.h"
//#include "t00031/test_case.h"
//#include "t00032/test_case.h"
//#include "t00033/test_case.h"