Fixed handling of template class specializations nested in other classes

This commit is contained in:
Bartek Kryza
2023-03-04 15:02:35 +01:00
parent d4d749ae34
commit a58b633d01
5 changed files with 71 additions and 11 deletions

View File

@@ -182,9 +182,13 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
if (!diagram().should_include(cls->getQualifiedNameAsString()))
return true;
LOG_DBG("= Visiting template specialization declaration {} at {}",
LOG_DBG("= Visiting template specialization declaration {} at {} "
"(described class id {})",
cls->getQualifiedNameAsString(),
cls->getLocation().printToString(source_manager()));
cls->getLocation().printToString(source_manager()),
cls->getSpecializedTemplate()
? cls->getSpecializedTemplate()->getTemplatedDecl()->getID()
: 0);
// TODO: Add support for classes defined in function/method bodies
if (cls->isLocalClass() != nullptr)
@@ -212,10 +216,11 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
{relationship_t::kInstantiation, maybe_id.value()});
if (diagram_.should_include(template_specialization)) {
const auto name = template_specialization.full_name(false);
const auto full_name = template_specialization.full_name(false);
const auto id = template_specialization.id();
LOG_DBG("Adding class template specialization {} with id {}", name, id);
LOG_DBG("Adding class template specialization {} with id {}", full_name,
id);
diagram_.add_class(std::move(template_specialization_ptr));
}
@@ -280,6 +285,8 @@ bool translation_unit_visitor::VisitClassTemplateDecl(
if (!c_ptr)
return true;
add_processed_template_class(cls->getQualifiedNameAsString());
// Override the id with the template id, for now we don't care about the
// underlying templated class id
@@ -679,12 +686,18 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
LOG_DBG("== isTemplateDecl() = {}", cls->isTemplateDecl());
LOG_DBG("== isTemplated() = {}", cls->isTemplated());
LOG_DBG("== getParent()->isRecord()() = {}", cls->getParent()->isRecord());
if (cls->getParent()->isRecord()) {
if (const auto *parent_record =
clang::dyn_cast<clang::RecordDecl>(cls->getParent());
parent_record != nullptr) {
LOG_DBG("== getParent()->getQualifiedNameAsString() = {}",
clang::dyn_cast<clang::RecordDecl>(cls->getParent())
->getQualifiedNameAsString());
parent_record->getQualifiedNameAsString());
}
if (has_processed_template_class(cls->getQualifiedNameAsString()))
// If we have already processed the template of this class
// skip it
return true;
if (cls->isTemplated() && (cls->getDescribedTemplate() != nullptr)) {
// If the described templated of this class is already in the model
// skip it:
@@ -837,11 +850,14 @@ void translation_unit_visitor::process_record_parent(
std::optional<common::model::diagram_element::id_t> id_opt;
auto parent_ns = ns;
if (parent != nullptr) {
const auto *parent_record_decl =
clang::dyn_cast<clang::RecordDecl>(parent);
if (parent_record_decl != nullptr) {
parent_ns = common::get_tag_namespace(*parent_record_decl);
int64_t local_id = parent_record_decl->getID();
// First check if the parent has been added to the diagram as
@@ -868,7 +884,7 @@ void translation_unit_visitor::process_record_parent(
assert(parent_class);
c.set_namespace(ns);
c.set_namespace(parent_ns);
const auto cls_name = cls->getNameAsString();
if (cls_name.empty()) {
// Nested structs can be anonymous
@@ -1745,7 +1761,7 @@ translation_unit_visitor::process_template_specialization(
template_instantiation.is_struct(cls->isStruct());
process_record_parent(cls, template_instantiation, namespace_{});
process_record_parent(cls, template_instantiation, ns);
if (!template_instantiation.is_nested()) {
template_instantiation.set_name(common::get_tag_name(*cls));
@@ -2833,4 +2849,16 @@ bool translation_unit_visitor::should_include(const clang::NamedDecl *decl)
diagram().should_include(decl->getQualifiedNameAsString());
}
void translation_unit_visitor::add_processed_template_class(
std::string qualified_name)
{
processed_template_qualified_names_.emplace(std::move(qualified_name));
}
bool translation_unit_visitor::has_processed_template_class(
const std::string &qualified_name) const
{
return util::contains(processed_template_qualified_names_, qualified_name);
}
} // namespace clanguml::class_diagram::visitor

View File

@@ -287,6 +287,10 @@ private:
void set_ast_local_id(
int64_t local_id, common::model::diagram_element::id_t global_id);
void add_processed_template_class(std::string qualified_name);
bool has_processed_template_class(const std::string &qualified_name) const;
/// Retrieve the global clang-uml entity id based on the clang local id
std::optional<common::model::diagram_element::id_t> get_ast_local_id(
int64_t local_id) const;
@@ -307,5 +311,11 @@ private:
std::tuple<std::string /* field name */, common::model::relationship_t,
common::model::access_t>>
anonymous_struct_relationships_;
// When visiting CXX records we need to know if they have already been
// process in VisitClassTemplateDecl or VisitClassTemplateSpecializationDecl
// If yes, then we need to skip it
// TODO: There must be a better way to do this...
std::set<std::string> processed_template_qualified_names_;
};
} // namespace clanguml::class_diagram::visitor

View File

@@ -46,8 +46,8 @@ void diagram_element::add_relationship(relationship &&cr)
return;
}
LOG_DBG("Adding relationship: '{}' - {} - '{}'", cr.destination(),
to_string(cr.type()), full_name(true));
LOG_DBG("Adding relationship from: '{}' ({}) - {} - '{}'", id(),
full_name(true), to_string(cr.type()), cr.destination());
if (!util::contains(relationships_, cr))
relationships_.emplace_back(std::move(cr));

View File

@@ -32,5 +32,20 @@ struct D {
void add(int i) { ints.template_template.values.push_back(i); }
};
struct E {
template <typename ET> struct nested_template {
using DT = ET;
static DT *get(ET *d) { return d; }
};
};
template <> struct E::nested_template<char> {
using DeclType = char;
static DeclType *getDecl(char *c) { return c; }
};
} // namespace t00008
} // namespace clanguml

View File

@@ -48,5 +48,12 @@ TEST_CASE("t00008", "[test-case][class]")
// REQUIRE_THAT(puml, IsField(Public("bool (*)(int, int) comparator")));
REQUIRE_THAT(puml, (IsField<Public>("comparator", "CMP")));
REQUIRE_THAT(puml, !IsClass(_A("E::nested_template")));
REQUIRE_THAT(puml, IsClassTemplate("E::nested_template", "ET"));
REQUIRE_THAT(puml, IsClassTemplate("E::nested_template", "char"));
REQUIRE_THAT(puml,
IsInstantiation(
_A("E::nested_template<ET>"), _A("E::nested_template<char>")));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
}