Fixed handling of multiple dimension arrays in class fields (#278)
This commit is contained in:
@@ -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)
|
void to_json(nlohmann::json &j, const class_element &c)
|
||||||
{
|
{
|
||||||
j["name"] = c.name();
|
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)
|
if (c.access() != common::model::access_t::kNone)
|
||||||
j["access"] = to_string(c.access());
|
j["access"] = to_string(c.access());
|
||||||
if (!c.file().empty())
|
if (!c.file().empty())
|
||||||
|
|||||||
@@ -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::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
|
} // namespace clanguml::class_diagram::model
|
||||||
|
|||||||
@@ -54,8 +54,23 @@ public:
|
|||||||
*/
|
*/
|
||||||
void is_static(bool is_static);
|
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:
|
private:
|
||||||
bool is_static_{false};
|
bool is_static_{false};
|
||||||
|
std::optional<size_t> destination_multiplicity_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace clanguml::class_diagram::model
|
} // namespace clanguml::class_diagram::model
|
||||||
|
|||||||
@@ -906,14 +906,21 @@ void translation_unit_visitor::process_record_parent(
|
|||||||
if (cls_name.empty()) {
|
if (cls_name.empty()) {
|
||||||
// Nested structs can be anonymous
|
// Nested structs can be anonymous
|
||||||
if (anonymous_struct_relationships_.count(cls->getID()) > 0) {
|
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()];
|
anonymous_struct_relationships_[cls->getID()];
|
||||||
|
|
||||||
c.set_name(parent_class.value().name() + "##" +
|
c.set_name(parent_class.value().name() + "##" +
|
||||||
fmt::format("({})", label));
|
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(
|
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
|
else
|
||||||
c.set_name(parent_class.value().name() + "##" +
|
c.set_name(parent_class.value().name() + "##" +
|
||||||
@@ -1649,14 +1656,22 @@ void translation_unit_visitor::add_relationships(class_ &c,
|
|||||||
relationship r{relationship_type, target};
|
relationship r{relationship_type, target};
|
||||||
r.set_label(field.name());
|
r.set_label(field.name());
|
||||||
r.set_access(field.access());
|
r.set_access(field.access());
|
||||||
|
bool mulitplicity_provided_in_comment{false};
|
||||||
if (decorator_rtype != relationship_t::kNone) {
|
if (decorator_rtype != relationship_t::kNone) {
|
||||||
r.set_type(decorator_rtype);
|
r.set_type(decorator_rtype);
|
||||||
auto mult = util::split(decorator_rmult, ":", false);
|
auto mult = util::split(decorator_rmult, ":", false);
|
||||||
if (mult.size() == 2) {
|
if (mult.size() == 2) {
|
||||||
|
mulitplicity_provided_in_comment = true;
|
||||||
r.set_multiplicity_source(mult[0]);
|
r.set_multiplicity_source(mult[0]);
|
||||||
r.set_multiplicity_destination(mult[1]);
|
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());
|
r.set_style(field.style_spec());
|
||||||
|
|
||||||
LOG_DBG("Adding relationship from {} to {} with label {}",
|
LOG_DBG("Adding relationship from {} to {} with label {}",
|
||||||
@@ -1797,8 +1812,24 @@ void translation_unit_visitor::process_field(
|
|||||||
}
|
}
|
||||||
else if (field_type->isArrayType()) {
|
else if (field_type->isArrayType()) {
|
||||||
relationship_hint = relationship_t::kAggregation;
|
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();
|
field_type = field_type->getAsArrayTypeUnsafe()->getElementType();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (field_type->isRValueReferenceType()) {
|
else if (field_type->isRValueReferenceType()) {
|
||||||
field_type = field_type.getNonReferenceType();
|
field_type = field_type.getNonReferenceType();
|
||||||
}
|
}
|
||||||
@@ -1917,8 +1948,8 @@ void translation_unit_visitor::process_field(
|
|||||||
// struct have to be handled separately here
|
// struct have to be handled separately here
|
||||||
anonymous_struct_relationships_[field_type->getAsRecordDecl()
|
anonymous_struct_relationships_[field_type->getAsRecordDecl()
|
||||||
->getID()] =
|
->getID()] =
|
||||||
std::make_tuple(
|
std::make_tuple(field.name(), relationship_hint,
|
||||||
field.name(), relationship_hint, field.access());
|
field.access(), field.destination_multiplicity());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
find_relationships(
|
find_relationships(
|
||||||
@@ -1928,6 +1959,17 @@ void translation_unit_visitor::process_field(
|
|||||||
add_relationships(c, field, relationships);
|
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));
|
c.add_member(std::move(field));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -438,7 +438,8 @@ private:
|
|||||||
|
|
||||||
std::map<int64_t /* local anonymous struct id */,
|
std::map<int64_t /* local anonymous struct id */,
|
||||||
std::tuple<std::string /* field name */, common::model::relationship_t,
|
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_;
|
anonymous_struct_relationships_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -113,13 +113,36 @@ std::string get_tag_name(const clang::TagDecl &declaration)
|
|||||||
return base_name;
|
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,
|
std::string to_string(const clang::QualType &type, const clang::ASTContext &ctx,
|
||||||
bool try_canonical)
|
bool try_canonical)
|
||||||
{
|
{
|
||||||
if (type->isArrayType()) {
|
if (type->isArrayType()) {
|
||||||
return fmt::format("{}[]",
|
std::vector<std::string> dimensions;
|
||||||
to_string(type->getAsArrayTypeUnsafe()->getElementType(), ctx,
|
return to_string(
|
||||||
try_canonical));
|
*type->getAsArrayTypeUnsafe(), ctx, try_canonical, dimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
clang::PrintingPolicy print_policy(ctx.getLangOpts());
|
clang::PrintingPolicy print_policy(ctx.getLangOpts());
|
||||||
@@ -962,4 +985,15 @@ bool has_attr(const clang::FunctionDecl *decl, clang::attr::Kind function_attr)
|
|||||||
[function_attr](
|
[function_attr](
|
||||||
auto &&attr) { return attr->getKind() == 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
|
} // namespace clanguml::common
|
||||||
|
|||||||
@@ -324,4 +324,12 @@ bool is_struct(const clang::NamedDecl *decl);
|
|||||||
*/
|
*/
|
||||||
bool has_attr(const clang::FunctionDecl *decl, clang::attr::Kind function_attr);
|
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
|
} // namespace clanguml::common
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ TEST_CASE("t00006")
|
|||||||
REQUIRE(IsAssociation<Public>(src, "R", "G", "g"));
|
REQUIRE(IsAssociation<Public>(src, "R", "G", "g"));
|
||||||
REQUIRE(IsAggregation<Public>(src, "R", "H", "h"));
|
REQUIRE(IsAggregation<Public>(src, "R", "H", "h"));
|
||||||
REQUIRE(IsAssociation<Public>(src, "R", "I", "i"));
|
REQUIRE(IsAssociation<Public>(src, "R", "I", "i"));
|
||||||
REQUIRE(IsAggregation<Public>(src, "R", "J", "j"));
|
REQUIRE(IsAggregation<Public>(src, "R", "J", "j", "", "10"));
|
||||||
REQUIRE(IsAssociation<Public>(src, "R", "K", "k"));
|
REQUIRE(IsAssociation<Public>(src, "R", "K", "k", "", "20"));
|
||||||
REQUIRE(IsAggregation<Public>(src, "R", "L", "lm"));
|
REQUIRE(IsAggregation<Public>(src, "R", "L", "lm"));
|
||||||
REQUIRE(IsAggregation<Public>(src, "R", "M", "lm"));
|
REQUIRE(IsAggregation<Public>(src, "R", "M", "lm"));
|
||||||
REQUIRE(IsAggregation<Public>(src, "R", "N", "ns"));
|
REQUIRE(IsAggregation<Public>(src, "R", "N", "ns"));
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ namespace t00037 {
|
|||||||
|
|
||||||
constexpr auto LENGTH{10ULL};
|
constexpr auto LENGTH{10ULL};
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
};
|
||||||
|
|
||||||
class ST {
|
class ST {
|
||||||
public:
|
public:
|
||||||
struct {
|
struct {
|
||||||
@@ -22,6 +27,8 @@ private:
|
|||||||
double c{1.0};
|
double c{1.0};
|
||||||
double h{1.0};
|
double h{1.0};
|
||||||
} units;
|
} units;
|
||||||
|
|
||||||
|
S s[4][3][2];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct A {
|
struct A {
|
||||||
|
|||||||
@@ -35,6 +35,15 @@ TEST_CASE("t00037")
|
|||||||
REQUIRE(
|
REQUIRE(
|
||||||
IsAggregation<Public>(src, "ST", "ST::(dimensions)", "dimensions"));
|
IsAggregation<Public>(src, "ST", "ST::(dimensions)", "dimensions"));
|
||||||
REQUIRE(IsAggregation<Private>(src, "ST", "ST::(units)", "units"));
|
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)"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user