Fixed handling of multiple dimension arrays in class fields (#278)

This commit is contained in:
Bartek Kryza
2024-06-01 18:47:27 +02:00
parent d599bb6715
commit ff7edef585
10 changed files with 139 additions and 13 deletions

View File

@@ -34,7 +34,7 @@ void set_module(nlohmann::json &j, const common::model::element &e)
void to_json(nlohmann::json &j, const class_element &c)
{
j["name"] = c.name();
j["type"] = c.type();
j["type"] = common::generators::json::render_name(c.type());
if (c.access() != common::model::access_t::kNone)
j["access"] = to_string(c.access());
if (!c.file().empty())

View File

@@ -30,4 +30,14 @@ bool class_member::is_static() const { return is_static_; }
void class_member::is_static(bool is_static) { is_static_ = is_static; }
void class_member::set_destination_multiplicity(std::optional<size_t> m)
{
destination_multiplicity_ = m;
}
std::optional<size_t> class_member::destination_multiplicity() const
{
return destination_multiplicity_;
}
} // namespace clanguml::class_diagram::model

View File

@@ -54,8 +54,23 @@ public:
*/
void is_static(bool is_static);
/**
* @brief Set members destination multiplicity.
*
* @param m Optional multiplicity value
*/
void set_destination_multiplicity(std::optional<size_t> m);
/**
* @brief Get members destination multiplicity.
*
* @return Optional multiplicity value
*/
std::optional<size_t> destination_multiplicity() const;
private:
bool is_static_{false};
std::optional<size_t> destination_multiplicity_{};
};
} // namespace clanguml::class_diagram::model

View File

@@ -906,14 +906,21 @@ void translation_unit_visitor::process_record_parent(
if (cls_name.empty()) {
// Nested structs can be anonymous
if (anonymous_struct_relationships_.count(cls->getID()) > 0) {
const auto &[label, hint, access] =
const auto &[label, hint, access, destination_multiplicity] =
anonymous_struct_relationships_[cls->getID()];
c.set_name(parent_class.value().name() + "##" +
fmt::format("({})", label));
std::string destination_multiplicity_str{};
if (destination_multiplicity.has_value()) {
destination_multiplicity_str =
std::to_string(*destination_multiplicity); // NOLINT
}
parent_class.value().add_relationship(
{hint, common::to_id(c.full_name(false)), access, label});
{hint, common::to_id(c.full_name(false)), access, label, "",
destination_multiplicity_str});
}
else
c.set_name(parent_class.value().name() + "##" +
@@ -1649,14 +1656,22 @@ void translation_unit_visitor::add_relationships(class_ &c,
relationship r{relationship_type, target};
r.set_label(field.name());
r.set_access(field.access());
bool mulitplicity_provided_in_comment{false};
if (decorator_rtype != relationship_t::kNone) {
r.set_type(decorator_rtype);
auto mult = util::split(decorator_rmult, ":", false);
if (mult.size() == 2) {
mulitplicity_provided_in_comment = true;
r.set_multiplicity_source(mult[0]);
r.set_multiplicity_destination(mult[1]);
}
}
if (!mulitplicity_provided_in_comment &&
field.destination_multiplicity().has_value()) {
r.set_multiplicity_destination(std::to_string(
*field.destination_multiplicity())); // NOLINT
}
r.set_style(field.style_spec());
LOG_DBG("Adding relationship from {} to {} with label {}",
@@ -1797,8 +1812,24 @@ void translation_unit_visitor::process_field(
}
else if (field_type->isArrayType()) {
relationship_hint = relationship_t::kAggregation;
while (field_type->isArrayType()) {
auto current_multiplicity = field.destination_multiplicity();
if (!current_multiplicity)
field.set_destination_multiplicity(common::get_array_size(
*field_type->getAsArrayTypeUnsafe()));
else {
auto maybe_array_size =
common::get_array_size(*field_type->getAsArrayTypeUnsafe());
if (maybe_array_size.has_value()) {
field.set_destination_multiplicity(
current_multiplicity.value() *
maybe_array_size.value());
}
}
field_type = field_type->getAsArrayTypeUnsafe()->getElementType();
}
}
else if (field_type->isRValueReferenceType()) {
field_type = field_type.getNonReferenceType();
}
@@ -1917,8 +1948,8 @@ void translation_unit_visitor::process_field(
// struct have to be handled separately here
anonymous_struct_relationships_[field_type->getAsRecordDecl()
->getID()] =
std::make_tuple(
field.name(), relationship_hint, field.access());
std::make_tuple(field.name(), relationship_hint,
field.access(), field.destination_multiplicity());
}
else
find_relationships(
@@ -1928,6 +1959,17 @@ void translation_unit_visitor::process_field(
add_relationships(c, field, relationships);
}
// If this is an anonymous struct - replace the anonymous_XYZ part with
// field name
if ((field_type->getAsRecordDecl() != nullptr) &&
field_type->getAsRecordDecl()->getNameAsString().empty()) {
if (util::contains(field.type(), "(anonymous_")) {
std::regex anonymous_re("anonymous_(\\d*)");
field.set_type(
std::regex_replace(field.type(), anonymous_re, field_name));
}
}
c.add_member(std::move(field));
}

View File

@@ -438,7 +438,8 @@ private:
std::map<int64_t /* local anonymous struct id */,
std::tuple<std::string /* field name */, common::model::relationship_t,
common::model::access_t>>
common::model::access_t,
std::optional<size_t> /* destination_multiplicity */>>
anonymous_struct_relationships_;
/**

View File

@@ -113,13 +113,36 @@ std::string get_tag_name(const clang::TagDecl &declaration)
return base_name;
}
std::string to_string(const clang::ArrayType &array_type,
const clang::ASTContext &ctx, bool try_canonical,
std::vector<std::string> &dimensions)
{
auto maybe_size = get_array_size(array_type);
std::string array_size =
maybe_size.has_value() ? std::to_string(maybe_size.value()) : "";
dimensions.emplace_back(std::move(array_size));
const auto underlying_type = array_type.getElementType();
if (underlying_type->isArrayType())
return to_string(*underlying_type->getAsArrayTypeUnsafe(), ctx,
try_canonical, dimensions);
std::string dimensions_str;
for (const auto &d : dimensions) {
dimensions_str += fmt::format("[{}]", d);
}
return fmt::format(
"{}{}", to_string(underlying_type, ctx, try_canonical), dimensions_str);
}
std::string to_string(const clang::QualType &type, const clang::ASTContext &ctx,
bool try_canonical)
{
if (type->isArrayType()) {
return fmt::format("{}[]",
to_string(type->getAsArrayTypeUnsafe()->getElementType(), ctx,
try_canonical));
std::vector<std::string> dimensions;
return to_string(
*type->getAsArrayTypeUnsafe(), ctx, try_canonical, dimensions);
}
clang::PrintingPolicy print_policy(ctx.getLangOpts());
@@ -962,4 +985,15 @@ bool has_attr(const clang::FunctionDecl *decl, clang::attr::Kind function_attr)
[function_attr](
auto &&attr) { return attr->getKind() == function_attr; });
}
std::optional<size_t> get_array_size(const clang::ArrayType &type)
{
if (const auto *constant_array =
clang::dyn_cast<clang::ConstantArrayType>(&type);
constant_array != nullptr) {
return {constant_array->getSize().getZExtValue()};
}
return {};
}
} // namespace clanguml::common

View File

@@ -324,4 +324,12 @@ bool is_struct(const clang::NamedDecl *decl);
*/
bool has_attr(const clang::FunctionDecl *decl, clang::attr::Kind function_attr);
/**
* If `type` is a constant array, return it's number of elements. Otherwise
* nothing.
*
* @param type
* @return Number of elements in the array.
*/
std::optional<size_t> get_array_size(const clang::ArrayType &type);
} // namespace clanguml::common

View File

@@ -53,8 +53,8 @@ TEST_CASE("t00006")
REQUIRE(IsAssociation<Public>(src, "R", "G", "g"));
REQUIRE(IsAggregation<Public>(src, "R", "H", "h"));
REQUIRE(IsAssociation<Public>(src, "R", "I", "i"));
REQUIRE(IsAggregation<Public>(src, "R", "J", "j"));
REQUIRE(IsAssociation<Public>(src, "R", "K", "k"));
REQUIRE(IsAggregation<Public>(src, "R", "J", "j", "", "10"));
REQUIRE(IsAssociation<Public>(src, "R", "K", "k", "", "20"));
REQUIRE(IsAggregation<Public>(src, "R", "L", "lm"));
REQUIRE(IsAggregation<Public>(src, "R", "M", "lm"));
REQUIRE(IsAggregation<Public>(src, "R", "N", "ns"));

View File

@@ -3,6 +3,11 @@ namespace t00037 {
constexpr auto LENGTH{10ULL};
struct S {
double x;
double y;
};
class ST {
public:
struct {
@@ -22,6 +27,8 @@ private:
double c{1.0};
double h{1.0};
} units;
S s[4][3][2];
};
struct A {

View File

@@ -35,6 +35,15 @@ TEST_CASE("t00037")
REQUIRE(
IsAggregation<Public>(src, "ST", "ST::(dimensions)", "dimensions"));
REQUIRE(IsAggregation<Private>(src, "ST", "ST::(units)", "units"));
REQUIRE(IsAggregation<Public>(src, "ST", "ST::(bars)", "bars"));
REQUIRE(
IsAggregation<Public>(src, "ST", "ST::(bars)", "bars", "", "10"));
REQUIRE(IsAggregation<Private>(
src, "ST", "S", "s", "", std::to_string(4 * 3 * 2)));
REQUIRE(IsField<Private>(src, "ST", "s", "S[4][3][2]"));
REQUIRE(IsField<Public>(src, "ST", "bars", "ST::(bars)[10]"));
REQUIRE(IsField<Public>(src, "ST", "dimensions", "ST::(dimensions)"));
REQUIRE(IsField<Private>(src, "ST", "units", "ST::(units)"));
});
}