diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 00d71c14..50f9ab19 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -78,10 +78,6 @@ std::optional> diagram::get_class( { for (const auto &c : classes_) { const auto full_name = c.get().full_name(false); - if (name == - "clanguml::t00012::C>>>,3,3,3>") - LOG_ERROR("Comparing {} with {}", full_name, name); if (full_name == name) { return {c}; diff --git a/src/class_diagram/model/template_parameter.cc b/src/class_diagram/model/template_parameter.cc index e41c93d1..bec416d3 100644 --- a/src/class_diagram/model/template_parameter.cc +++ b/src/class_diagram/model/template_parameter.cc @@ -153,7 +153,7 @@ std::string template_parameter::to_string( } void template_parameter::find_nested_relationships( - std::vector> + std::vector> &nested_relationships, common::model::relationship_t hint, std::function condition) const @@ -161,15 +161,16 @@ void template_parameter::find_nested_relationships( // If this type argument should be included in the relationship // just add it and skip recursion (e.g. this is a user defined type) if (condition(name())) { - nested_relationships.push_back({to_string({}, false), hint}); + if(id()) + nested_relationships.push_back({id().value(), hint}); } // Otherwise (e.g. this is a std::shared_ptr) and we're actually // interested what is stored inside it else { for (const auto &template_argument : template_params()) { - if (condition(template_argument.name())) { + if (condition(template_argument.name()) && template_argument.id()) { nested_relationships.push_back( - {template_argument.to_string({}, false), hint}); + {template_argument.id().value(), hint}); } else { template_argument.find_nested_relationships( diff --git a/src/class_diagram/model/template_parameter.h b/src/class_diagram/model/template_parameter.h index eecefa3c..39afd314 100644 --- a/src/class_diagram/model/template_parameter.h +++ b/src/class_diagram/model/template_parameter.h @@ -20,6 +20,7 @@ #include "common/model/enums.h" #include "common/model/namespace.h" +#include #include #include @@ -41,6 +42,9 @@ public: void set_type(const std::string &type); std::string type() const; + void set_id(const int64_t id) { id_ = id; } + std::optional id() const { return id_; } + void set_name(const std::string &name); std::string name() const; @@ -87,7 +91,7 @@ public: void clear_params() { template_params_.clear(); } void find_nested_relationships( - std::vector> + std::vector> &nested_relationships, common::model::relationship_t hint, std::function condition) const; @@ -116,5 +120,7 @@ private: // Nested template parameters std::vector template_params_; + + std::optional id_; }; } diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 9b0912f3..f4248c37 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -271,6 +271,38 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( return true; } +bool translation_unit_visitor::VisitTypeAliasTemplateDecl( + clang::TypeAliasTemplateDecl *cls) +{ + if (source_manager_.isInSystemHeader(cls->getSourceRange().getBegin())) + return true; + + auto *template_type_specialization_ptr = + cls->getTemplatedDecl() + ->getUnderlyingType() + ->getAs(); + + if (template_type_specialization_ptr == nullptr) + return true; + + // cls->dump(); + + auto template_specialization_ptr = + build_template_instantiation(*template_type_specialization_ptr); + + if (!template_specialization_ptr) + return true; + + if (diagram_.should_include(*template_specialization_ptr)) { + LOG_DBG("Adding class {} with id {}", + template_specialization_ptr->full_name(), + template_specialization_ptr->id()); + diagram_.add_class(std::move(template_specialization_ptr)); + } + + return true; +} + bool translation_unit_visitor::VisitClassTemplateDecl( clang::ClassTemplateDecl *cls) { @@ -281,7 +313,7 @@ bool translation_unit_visitor::VisitClassTemplateDecl( if (!cls->getTemplatedDecl()->isCompleteDefinition()) return true; - auto c_ptr = process_class_declaration(cls->getTemplatedDecl()); + auto c_ptr = create_class_declaration(cls->getTemplatedDecl()); if (!c_ptr) return true; @@ -294,7 +326,9 @@ bool translation_unit_visitor::VisitClassTemplateDecl( process_template_parameters(*cls, *c_ptr); - if(!cls->getTemplatedDecl()->isCompleteDefinition()) { + process_class_declaration(*cls->getTemplatedDecl(), *c_ptr); + + if (!cls->getTemplatedDecl()->isCompleteDefinition()) { forward_declarations_.emplace(id, std::move(c_ptr)); return true; } @@ -327,13 +361,15 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) if (cls->isLocalClass()) return true; - auto c_ptr = process_class_declaration(cls); + auto c_ptr = create_class_declaration(cls); if (!c_ptr) return true; + process_class_declaration(*cls, *c_ptr); + auto id = c_ptr->id(); - if(!cls->isCompleteDefinition()) { + if (!cls->isCompleteDefinition()) { forward_declarations_.emplace(id, std::move(c_ptr)); return true; } @@ -347,7 +383,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) return true; } -std::unique_ptr translation_unit_visitor::process_class_declaration( +std::unique_ptr translation_unit_visitor::create_class_declaration( clang::CXXRecordDecl *cls) { auto c_ptr{std::make_unique(config_.using_namespace())}; @@ -377,17 +413,21 @@ std::unique_ptr translation_unit_visitor::process_class_declaration( if (!cls->isCompleteDefinition()) return c_ptr; + return c_ptr; +} + +void translation_unit_visitor::process_class_declaration( + const clang::CXXRecordDecl &cls, class_ &c) +{ // Process class child entities - process_class_children(cls, c); + process_class_children(&cls, c); // Process class bases - process_class_bases(cls, c); + process_class_bases(&cls, c); - if (cls->getParent()->isRecord()) { - process_record_containment(*cls, c); + if (cls.getParent()->isRecord()) { + process_record_containment(cls, c); } - - return c_ptr; } bool translation_unit_visitor::process_template_parameters( @@ -443,7 +483,7 @@ bool translation_unit_visitor::process_template_parameters( } else { LOG_DBG("============= UNKNOWN TEMPLATE PARAMETER TYPE:"); - parameter->dump(); + // parameter->dump(); } } @@ -761,12 +801,57 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, const auto *type_instantiation_decl = type->getAs(); + if (type_instantiation_decl != nullptr) { + if (type_instantiation_decl->isTypeAlias()) + type_instantiation_decl = + type_instantiation_decl->getAliasedType() + ->getAs(); + // if(type_instantiation_decl != nullptr) + // type_instantiation_decl->dump(); + } + if (type_instantiation_decl != nullptr) { for (const auto &template_argument : *type_instantiation_decl) { - if (template_argument.getKind() == - clang::TemplateArgument::ArgKind::Type) + const auto template_argument_kind = template_argument.getKind(); + if (template_argument_kind == + clang::TemplateArgument::ArgKind::Integral) { + // pass + } + else if (template_argument_kind == + clang::TemplateArgument::ArgKind::Null) { + // pass + } + else if (template_argument_kind == + clang::TemplateArgument::ArgKind::Expression) { + // pass + } + else if (template_argument.getKind() == + clang::TemplateArgument::ArgKind::NullPtr) { + // pass + } + else if (template_argument_kind == + clang::TemplateArgument::ArgKind::Template) { + // pass + } + else if (template_argument_kind == + clang::TemplateArgument::ArgKind::TemplateExpansion) { + // pass + } + else if (template_argument.getAsType() + ->getAs()) { + for (const auto ¶m_type : + template_argument.getAsType() + ->getAs() + ->param_types()) { + result = find_relationships(param_type, relationships, + relationship_t::kDependency); + } + } + else if (template_argument_kind == + clang::TemplateArgument::ArgKind::Type) { result = find_relationships(template_argument.getAsType(), relationships, relationship_hint); + } } } else { @@ -991,6 +1076,8 @@ translation_unit_visitor::process_template_specialization( *arg.getAsType() ->getAs()); + argument.set_id(nested_template_instantiation->id()); + for (const auto &t : nested_template_instantiation->templates()) argument.add_template_param(t); @@ -1040,11 +1127,18 @@ translation_unit_visitor::process_template_specialization( } std::unique_ptr translation_unit_visitor::build_template_instantiation( - const clang::TemplateSpecializationType &template_type, + const clang::TemplateSpecializationType &template_type_decl, std::optional parent) { // TODO: Make sure we only build instantiation once + auto *template_type_ptr = &template_type_decl; + if (template_type_decl.isTypeAlias()) + template_type_ptr = template_type_decl.getAliasedType() + ->getAs(); + + auto &template_type = *template_type_ptr; + // // Create class_ instance to hold the template instantiation // @@ -1085,7 +1179,49 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( // If this is a nested template type - add nested templates as // template arguments - if (arg.getAsType()->getAs()) { + if (arg.getAsType()->getAs()) { + for (const auto ¶m_type : + arg.getAsType() + ->getAs() + ->param_types()) { + + std::string param_type_name; + if(param_type->isRecordType()) { + param_type_name = + param_type->getAsRecordDecl() + ->getQualifiedNameAsString(); +// param_type.getDesugaredType()dump(); + + LOG_ERROR("### {}", param_type_name); + } + + + const auto* nested_template_type = + param_type->getAs(); + + if(nested_template_type) { + const auto nested_template_name = + nested_template_type->getTemplateName() + .getAsTemplateDecl() + ->getQualifiedNameAsString(); + + auto [tinst_ns, tinst_name] = + cx::util::split_ns(nested_template_name); + + auto nested_template_instantiation = + build_template_instantiation( + *param_type + ->getAs< + clang::TemplateSpecializationType>(), + diagram().should_include(tinst_ns, tinst_name) + ? std::make_optional( + &template_instantiation) + : parent); + } + } + } + else if (arg.getAsType() + ->getAs()) { const auto *nested_template_type = arg.getAsType()->getAs(); @@ -1094,13 +1230,20 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( .getAsTemplateDecl() ->getQualifiedNameAsString(); + auto [tinst_ns, tinst_name] = + cx::util::split_ns(nested_template_name); + argument.set_name(nested_template_name); auto nested_template_instantiation = build_template_instantiation( *arg.getAsType() ->getAs(), - parent); + diagram().should_include(tinst_ns, tinst_name) + ? std::make_optional(&template_instantiation) + : parent); + + argument.set_id(nested_template_instantiation->id()); for (const auto &t : nested_template_instantiation->templates()) argument.add_template_param(t); @@ -1119,7 +1262,14 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( std::move(nested_template_instantiation)); } } + else if (arg.getAsType()->getAs()) { + argument.is_template_parameter(true); + argument.set_name( + to_string(arg.getAsType(), template_decl->getASTContext())); + } else { + // This is just a regular type + argument.is_template_parameter(false); argument.set_name( to_string(arg.getAsType(), template_decl->getASTContext())); } @@ -1142,7 +1292,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( template_instantiation.add_template(std::move(argument)); } else { - LOG_ERROR("UNSUPPORTED ARGUMENT KIND FOR ARG {}", arg.getKind()); + LOG_ERROR("Unsupported argument type {}", arg.getKind()); } // In case any of the template arguments are base classes, add @@ -1156,8 +1306,40 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( // } } - if (diagram().has_element( - template_type.getTemplateName().getAsTemplateDecl()->getID())) { + // First try to find the best match for this template in partially + // specialized templates + std::string destination{}; + std::string best_match_full_name{}; + auto full_template_name = template_instantiation.full_name(false); + int best_match{}; + common::model::diagram_element::id_t best_match_id{}; + + for (const auto c : diagram().classes()) { + if (c.get() == template_instantiation) + continue; + + auto c_full_name = c.get().full_name(false); + auto match = c.get().calculate_template_specialization_match( + template_instantiation, template_instantiation.name_and_ns()); + + if (match > best_match) { + best_match = match; + best_match_full_name = c_full_name; + best_match_id = c.get().id(); + } + } + + if (!best_match_full_name.empty()) { + destination = best_match_full_name; + template_instantiation.add_relationship( + {relationship_t::kInstantiation, best_match_id}); + } + + // If we can't find optimal match for parent template specialization, just + // use whatever clang suggests + else if (diagram().has_element(template_type.getTemplateName() + .getAsTemplateDecl() + ->getID())) { template_instantiation.add_relationship({relationship_t::kInstantiation, template_type.getTemplateName().getAsTemplateDecl()->getID()}); } @@ -1198,31 +1380,86 @@ void translation_unit_visitor::process_field( field_type = field_type.getNonReferenceType(); } - // Process field decorators const auto *template_field_type = field_type->getAs(); found_relationships_t relationships; + // TODO: Refactor to an unalias_type() method + if (template_field_type != nullptr) + if (template_field_type->isTypeAlias()) + template_field_type = + template_field_type->getAliasedType() + ->getAs(); + + bool field_type_is_template_template_parameter{false}; if (template_field_type != nullptr) { + // Skip types which are templatetemplate parameters of the parent + // template + for (const auto &class_template_param : c.templates()) { + if (class_template_param.name() == + template_field_type->getTemplateName() + .getAsTemplateDecl() + ->getNameAsString() + + "<>") { + field_type_is_template_template_parameter = true; + } + } + } + + // Process the type which is template instantiation of some sort + if (template_field_type != nullptr && + !field_type_is_template_template_parameter) { const auto template_field_decl_name = template_field_type->getTemplateName() .getAsTemplateDecl() ->getQualifiedNameAsString(); auto template_specialization_ptr = build_template_instantiation( - *field_type->getAs()); + *field_type->getAs(), {&c}); - if (template_specialization_ptr && - diagram().should_include(template_field_decl_name)) { - found_relationships_t::value_type r{ - template_specialization_ptr->id(), relationship_hint}; + if (template_specialization_ptr) { + if (diagram().should_include( + template_specialization_ptr->full_name(false))) { + found_relationships_t::value_type r{ + template_specialization_ptr->id(), relationship_hint}; - bool added = - diagram().add_class(std::move(template_specialization_ptr)); + bool added = + diagram().add_class(std::move(template_specialization_ptr)); - if (added) { - relationships.emplace_back(std::move(r)); + if (added) { + relationships.emplace_back(std::move(r)); + } + } + else if (!field.skip_relationship()) { + found_relationships_t nested_relationships; + + // for (const auto &cref : diagram().classes()) { + // LOG_ERROR("#### {} -> {}", + // cref.get().id(), + // cref.get().full_name(false)); + // } + + for (const auto &template_argument : + template_specialization_ptr->templates()) { + template_argument.find_nested_relationships( + nested_relationships, relationship_hint, + [&d = diagram()](const std::string &full_name) { + if (full_name.empty()) + return false; + auto [ns, name] = cx::util::split_ns(full_name); + return d.should_include(ns, name); + }); + } + + add_relationships(c, field, nested_relationships); + + // // find relationship for the type + // find_relationships( + // field_type, relationships, + // relationship_hint); + // + // add_relationships(c, field, relationships); } } } @@ -1247,16 +1484,18 @@ void translation_unit_visitor::set_source_location( } } -void translation_unit_visitor::add_incomplete_forward_declarations() { - for(auto & [id, c] : forward_declarations_) { - if(diagram().should_include(c->full_name(false))) { +void translation_unit_visitor::add_incomplete_forward_declarations() +{ + for (auto &[id, c] : forward_declarations_) { + if (diagram().should_include(c->full_name(false))) { diagram().add_class(std::move(c)); } } forward_declarations_.clear(); } -void translation_unit_visitor::finalize() { +void translation_unit_visitor::finalize() +{ add_incomplete_forward_declarations(); } diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 25f513cc..2ca7d551 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -56,6 +56,8 @@ public: virtual bool VisitClassTemplateSpecializationDecl( clang::ClassTemplateSpecializationDecl *cls); + virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls); + // virtual bool VisitVarDecl(clang::VarDecl *variable_declaration); clanguml::class_diagram::model::diagram &diagram() { return diagram_; } @@ -67,7 +69,10 @@ public: private: std::unique_ptr - process_class_declaration(clang::CXXRecordDecl *cls); + create_class_declaration(clang::CXXRecordDecl *cls); + + void process_class_declaration(const clang::CXXRecordDecl &cls, + clanguml::class_diagram::model::class_ &c); std::unique_ptr process_template_specialization( diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5869c618..a5de41ea 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 17) -set(TEST_DISABLE_WARNINGS "-Wno-unused-parameter -Wno-unused-variable -Wno-attributes") +set(TEST_DISABLE_WARNINGS "-Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable -Wno-attributes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS} ${TEST_DISABLE_WARNINGS}")