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)
|
||||
{
|
||||
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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)"));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user