mirror of
https://github.com/bkryza/clang-uml.git
synced 2025-04-20 18:51:23 +08:00
Fixed handling of relationship_hints option (#394)
This commit is contained in:
parent
1cc3cb2874
commit
6b95e203c4
@ -1740,22 +1740,32 @@ bool translation_unit_visitor::find_relationships(const clang::Decl *decl,
|
||||
}
|
||||
// TODO: Objc support
|
||||
else if (type->isRecordType()) {
|
||||
const auto *type_instantiation_decl =
|
||||
const auto *type_instantiation_type =
|
||||
type->getAs<clang::TemplateSpecializationType>();
|
||||
|
||||
if (type_instantiation_decl != nullptr) {
|
||||
if (type_instantiation_type != nullptr) {
|
||||
const auto *type_instantiation_template_decl =
|
||||
type_instantiation_type->getTemplateName().getAsTemplateDecl();
|
||||
|
||||
// If this template should be included in the diagram
|
||||
// add it - and then process recursively its arguments
|
||||
if (should_include(type_instantiation_decl->getTemplateName()
|
||||
.getAsTemplateDecl())) {
|
||||
if (should_include(type_instantiation_template_decl)) {
|
||||
relationships.emplace_back(
|
||||
type_instantiation_decl->getTemplateName()
|
||||
type_instantiation_type->getTemplateName()
|
||||
.getAsTemplateDecl()
|
||||
->getID(),
|
||||
relationship_hint, decl);
|
||||
}
|
||||
|
||||
auto idx{0U};
|
||||
for (const auto &template_argument :
|
||||
type_instantiation_decl->template_arguments()) {
|
||||
type_instantiation_type->template_arguments()) {
|
||||
|
||||
auto [overridden_relationship_hint, overridden] =
|
||||
override_relationship_hint(type_instantiation_template_decl
|
||||
->getQualifiedNameAsString(),
|
||||
idx, relationship_hint);
|
||||
|
||||
const auto template_argument_kind = template_argument.getKind();
|
||||
if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Integral) {
|
||||
@ -1795,8 +1805,9 @@ bool translation_unit_visitor::find_relationships(const clang::Decl *decl,
|
||||
clang::TemplateArgument::ArgKind::Type) {
|
||||
result =
|
||||
find_relationships(decl, template_argument.getAsType(),
|
||||
relationships, relationship_hint);
|
||||
relationships, overridden_relationship_hint);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
else if (type->getAsCXXRecordDecl() != nullptr) {
|
||||
@ -1813,6 +1824,8 @@ bool translation_unit_visitor::find_relationships(const clang::Decl *decl,
|
||||
else if (const auto *template_specialization_type =
|
||||
type->getAs<clang::TemplateSpecializationType>();
|
||||
template_specialization_type != nullptr) {
|
||||
const auto *type_instantiation_template_decl =
|
||||
template_specialization_type->getTemplateName().getAsTemplateDecl();
|
||||
if (should_include(template_specialization_type->getTemplateName()
|
||||
.getAsTemplateDecl())) {
|
||||
relationships.emplace_back(
|
||||
@ -1821,8 +1834,15 @@ bool translation_unit_visitor::find_relationships(const clang::Decl *decl,
|
||||
->getID(),
|
||||
relationship_hint, decl);
|
||||
}
|
||||
auto idx{0U};
|
||||
for (const auto &template_argument :
|
||||
template_specialization_type->template_arguments()) {
|
||||
|
||||
auto [overridden_relationship_hint, overridden] =
|
||||
override_relationship_hint(type_instantiation_template_decl
|
||||
->getQualifiedNameAsString(),
|
||||
idx, relationship_hint);
|
||||
|
||||
const auto template_argument_kind = template_argument.getKind();
|
||||
if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Integral) {
|
||||
@ -1860,8 +1880,9 @@ bool translation_unit_visitor::find_relationships(const clang::Decl *decl,
|
||||
else if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Type) {
|
||||
result = find_relationships(decl, template_argument.getAsType(),
|
||||
relationships, relationship_hint);
|
||||
relationships, overridden_relationship_hint);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2041,6 +2062,27 @@ void translation_unit_visitor::add_relationships(
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<relationship_t, bool>
|
||||
translation_unit_visitor::override_relationship_hint(
|
||||
const std::string &type_name, int index, relationship_t hint)
|
||||
{
|
||||
bool overridden{false};
|
||||
|
||||
for (const auto &[pattern, rel_hint] : config().relationship_hints()) {
|
||||
if (type_name.find(pattern) == 0) {
|
||||
if (index == -1)
|
||||
hint = rel_hint.default_hint;
|
||||
else
|
||||
hint = rel_hint.get(index, hint);
|
||||
|
||||
overridden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {hint, overridden};
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_static_field(
|
||||
const clang::VarDecl &field_declaration, class_ &c)
|
||||
{
|
||||
@ -2197,10 +2239,10 @@ void translation_unit_visitor::process_field(
|
||||
field_type = field_type.getNonReferenceType();
|
||||
}
|
||||
|
||||
if (type_name.find("std::shared_ptr") == 0)
|
||||
relationship_hint = relationship_t::kAssociation;
|
||||
if (type_name.find("std::weak_ptr") == 0)
|
||||
relationship_hint = relationship_t::kAssociation;
|
||||
auto [overridden_relationship_hint, overridden] =
|
||||
override_relationship_hint(type_name, -1, relationship_hint);
|
||||
if (overridden)
|
||||
relationship_hint = overridden_relationship_hint;
|
||||
|
||||
found_relationships_t relationships;
|
||||
|
||||
@ -2268,20 +2310,29 @@ void translation_unit_visitor::process_field(
|
||||
// Try to find relationships to types nested in the template
|
||||
// instantiation
|
||||
found_relationships_t nested_relationships;
|
||||
auto idx{0U};
|
||||
if (!template_instantiation_added_as_aggregation) {
|
||||
for (const auto &template_argument :
|
||||
template_specialization.template_params()) {
|
||||
|
||||
auto template_argument_str = template_argument.to_string(
|
||||
config().using_namespace(), false);
|
||||
|
||||
LOG_DBG("Looking for nested relationships from {}::{} in "
|
||||
"template argument {}",
|
||||
c, field_name,
|
||||
template_argument.to_string(
|
||||
config().using_namespace(), false));
|
||||
c, field_name, template_argument_str);
|
||||
|
||||
auto [overridden_relationship_hint_param,
|
||||
overridden_param] =
|
||||
override_relationship_hint(
|
||||
template_specialization.full_name(false), idx,
|
||||
relationship_hint);
|
||||
|
||||
template_instantiation_added_as_aggregation =
|
||||
template_argument.find_nested_relationships(
|
||||
&field_declaration, nested_relationships,
|
||||
relationship_hint,
|
||||
overridden_relationship_hint_param,
|
||||
!overridden_param,
|
||||
[&d = diagram()](const std::string &full_name) {
|
||||
if (full_name.empty())
|
||||
return false;
|
||||
@ -2294,6 +2345,8 @@ void translation_unit_visitor::process_field(
|
||||
// unless the top level type has been added as aggregation
|
||||
add_relationships(c, field, nested_relationships,
|
||||
/* break on first aggregation */ false);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Add the template instantiation object to the diagram if it
|
||||
@ -2357,7 +2410,8 @@ void translation_unit_visitor::find_record_parent_id(const clang::TagDecl *decl,
|
||||
// regular class
|
||||
parent_id_opt = id_mapper().get_global_id(local_id);
|
||||
|
||||
// If not, check if the parent template declaration is in the model
|
||||
// If not, check if the parent template declaration is in the
|
||||
// model
|
||||
if (!parent_id_opt) {
|
||||
if (parent_record_decl->getDescribedTemplate() != nullptr) {
|
||||
local_id =
|
||||
@ -2414,9 +2468,9 @@ void translation_unit_visitor::resolve_local_to_global_ids()
|
||||
const auto maybe_id =
|
||||
id_mapper().get_global_id(rel.destination());
|
||||
if (maybe_id) {
|
||||
LOG_TRACE(
|
||||
"= Resolved instantiation destination from local "
|
||||
"id {} to global id {}",
|
||||
LOG_TRACE("= Resolved instantiation destination "
|
||||
"from local "
|
||||
"id {} to global id {}",
|
||||
rel.destination(), *maybe_id);
|
||||
rel.set_destination(*maybe_id);
|
||||
}
|
||||
|
@ -487,6 +487,16 @@ private:
|
||||
const found_relationships_t &relationships,
|
||||
bool break_on_first_aggregation = false);
|
||||
|
||||
/**
|
||||
* @brief Try to override relationship hint using configuration file
|
||||
*
|
||||
* @param type_name
|
||||
* @param hint
|
||||
* @return Maybe overridden relationship type hint
|
||||
*/
|
||||
std::pair<relationship_t, bool> override_relationship_hint(
|
||||
const std::string &type_name, int index, relationship_t hint);
|
||||
|
||||
/**
|
||||
* @brief Process record parent element (e.g. for nested classes)
|
||||
*
|
||||
|
@ -492,7 +492,7 @@ std::string template_parameter::to_string(
|
||||
bool template_parameter::find_nested_relationships(const clang::Decl *decl,
|
||||
std::vector<std::tuple<eid_t, common::model::relationship_t,
|
||||
const clang::Decl *>> &nested_relationships,
|
||||
common::model::relationship_t hint,
|
||||
common::model::relationship_t hint, bool allow_hint_override,
|
||||
const std::function<bool(const std::string &full_name)> &should_include)
|
||||
const
|
||||
{
|
||||
@ -502,11 +502,11 @@ bool template_parameter::find_nested_relationships(const clang::Decl *decl,
|
||||
// just add it and skip recursion (e.g. this is a user defined type)
|
||||
const auto maybe_type = type();
|
||||
|
||||
if (is_function_template())
|
||||
if (allow_hint_override && is_function_template())
|
||||
hint = common::model::relationship_t::kDependency;
|
||||
|
||||
if (maybe_type && should_include(maybe_type.value())) {
|
||||
if (is_association())
|
||||
if (allow_hint_override && is_association())
|
||||
hint = common::model::relationship_t::kAssociation;
|
||||
|
||||
const auto maybe_id = id();
|
||||
@ -525,8 +525,7 @@ bool template_parameter::find_nested_relationships(const clang::Decl *decl,
|
||||
const auto maybe_arg_type = template_argument.type();
|
||||
|
||||
if (maybe_id && maybe_arg_type && should_include(*maybe_arg_type)) {
|
||||
|
||||
if (template_argument.is_association() &&
|
||||
if (allow_hint_override && template_argument.is_association() &&
|
||||
hint == common::model::relationship_t::kAggregation)
|
||||
hint = common::model::relationship_t::kAssociation;
|
||||
|
||||
@ -536,12 +535,14 @@ bool template_parameter::find_nested_relationships(const clang::Decl *decl,
|
||||
(hint == common::model::relationship_t::kAggregation);
|
||||
}
|
||||
else {
|
||||
if (template_argument.is_function_template())
|
||||
if (allow_hint_override &&
|
||||
template_argument.is_function_template())
|
||||
hint = common::model::relationship_t::kDependency;
|
||||
|
||||
added_aggregation_relationship =
|
||||
template_argument.find_nested_relationships(
|
||||
decl, nested_relationships, hint, should_include);
|
||||
template_argument.find_nested_relationships(decl,
|
||||
nested_relationships, hint, allow_hint_override,
|
||||
should_include);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ public:
|
||||
bool find_nested_relationships(const clang::Decl *decl,
|
||||
std::vector<std::tuple<eid_t, common::model::relationship_t,
|
||||
const clang::Decl *>> &nested_relationships,
|
||||
common::model::relationship_t hint,
|
||||
common::model::relationship_t hint, bool allow_hint_override,
|
||||
const std::function<bool(const std::string &full_name)> &should_include)
|
||||
const;
|
||||
|
||||
|
@ -550,12 +550,13 @@ struct relationship_hint_t {
|
||||
{
|
||||
}
|
||||
|
||||
common::model::relationship_t get(unsigned int argument_index) const
|
||||
common::model::relationship_t get(unsigned int argument_index,
|
||||
std::optional<common::model::relationship_t> def = {}) const
|
||||
{
|
||||
if (argument_hints.count(argument_index) > 0)
|
||||
return argument_hints.at(argument_index);
|
||||
|
||||
return default_hint;
|
||||
return def.has_value() ? *def : default_hint;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -418,6 +418,7 @@ root:
|
||||
type_aliases: !optional map_t<string;string>
|
||||
filter_mode: !optional filter_mode_t
|
||||
include_system_headers: !optional bool
|
||||
relationship_hints: !optional map_t<string;any>
|
||||
)";
|
||||
|
||||
} // namespace clanguml::config
|
21
tests/t00092/.clang-uml
Normal file
21
tests/t00092/.clang-uml
Normal file
@ -0,0 +1,21 @@
|
||||
diagrams:
|
||||
t00092_class:
|
||||
type: class
|
||||
glob:
|
||||
- t00092.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00092
|
||||
exclude:
|
||||
elements:
|
||||
- r: "clanguml::t00092::OwningPtr.*"
|
||||
- r: "clanguml::t00092::WeakPtr.*"
|
||||
- r: "clanguml::t00092::Map.*"
|
||||
relationship_hints:
|
||||
clanguml::t00092::OwningPtr: aggregation
|
||||
clanguml::t00092::WeakPtr: association
|
||||
clanguml::t00092::Map:
|
||||
default: association
|
||||
0: dependency
|
||||
1: aggregation
|
||||
using_namespace: clanguml::t00092
|
34
tests/t00092/t00092.cc
Normal file
34
tests/t00092/t00092.cc
Normal file
@ -0,0 +1,34 @@
|
||||
#include <map>
|
||||
|
||||
namespace clanguml::t00092 {
|
||||
|
||||
template <typename T> struct OwningPtr {
|
||||
T *ptr;
|
||||
};
|
||||
|
||||
template <typename T> struct WeakPtr {
|
||||
T *ptr;
|
||||
};
|
||||
|
||||
struct String {
|
||||
char *data;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
template <typename K, typename V> struct Map {
|
||||
std::map<K, V> map;
|
||||
};
|
||||
|
||||
class A { };
|
||||
|
||||
class B { };
|
||||
|
||||
class C { };
|
||||
|
||||
class R {
|
||||
public:
|
||||
OwningPtr<A> a;
|
||||
WeakPtr<B> b;
|
||||
Map<String, C> m;
|
||||
};
|
||||
} // namespace clanguml::t00092
|
39
tests/t00092/test_case.h
Normal file
39
tests/t00092/test_case.h
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* tests/t00092/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2025 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("t00092")
|
||||
{
|
||||
using namespace clanguml::test;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto [config, db, diagram, model] =
|
||||
CHECK_CLASS_MODEL("t00092", "t00092_class");
|
||||
|
||||
CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) {
|
||||
REQUIRE(IsClass(src, "A"));
|
||||
REQUIRE(IsClass(src, "B"));
|
||||
REQUIRE(IsClass(src, "C"));
|
||||
REQUIRE(IsClass(src, "R"));
|
||||
REQUIRE(IsClass(src, "String"));
|
||||
|
||||
REQUIRE(IsDependency(src, "R", "String"));
|
||||
REQUIRE(IsAggregation<Public>(src, "R", "A", "a"));
|
||||
REQUIRE(IsAssociation<Public>(src, "R", "B", "b"));
|
||||
REQUIRE(IsAggregation<Public>(src, "R", "C", "m"));
|
||||
});
|
||||
}
|
@ -596,6 +596,7 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
|
||||
#endif
|
||||
|
||||
#include "t00091/test_case.h"
|
||||
#include "t00092/test_case.h"
|
||||
|
||||
///
|
||||
/// Sequence diagram tests
|
||||
|
@ -270,6 +270,9 @@ test_cases:
|
||||
- name: t00091
|
||||
title: Declaration forwarding test case
|
||||
description:
|
||||
- name: t00092
|
||||
title: Test case for relationship_hints config option
|
||||
description:
|
||||
Sequence diagrams:
|
||||
- name: t20001
|
||||
title: Basic sequence diagram test case
|
||||
|
Loading…
x
Reference in New Issue
Block a user