Fixed unexposed template parameters namespace resolution
This commit is contained in:
@@ -41,9 +41,11 @@ std::string enum_::full_name(bool relative) const
|
|||||||
|
|
||||||
std::ostringstream ostr;
|
std::ostringstream ostr;
|
||||||
if (relative)
|
if (relative)
|
||||||
ostr << namespace_{name()}.relative_to(using_namespace()).to_string();
|
ostr << namespace_{name_and_ns()}
|
||||||
|
.relative_to(using_namespace())
|
||||||
|
.to_string();
|
||||||
else
|
else
|
||||||
ostr << name();
|
ostr << name_and_ns();
|
||||||
|
|
||||||
return ostr.str();
|
return ostr.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,4 +209,59 @@ translation_unit_context::using_namespace_directive(
|
|||||||
return using_ns_declarations_.at(ns.to_string());
|
return using_ns_declarations_.at(ns.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type_safe::optional<common::model::namespace_>
|
||||||
|
translation_unit_context::get_name_with_namespace(const std::string &name) const
|
||||||
|
{
|
||||||
|
using common::model::namespace_;
|
||||||
|
|
||||||
|
std::set<namespace_> possible_matches;
|
||||||
|
possible_matches.emplace(name);
|
||||||
|
|
||||||
|
possible_matches.emplace(get_namespace() | namespace_{name});
|
||||||
|
auto parent = get_namespace().parent();
|
||||||
|
while (parent.has_value()) {
|
||||||
|
possible_matches.emplace(parent.value() | namespace_{name});
|
||||||
|
parent = parent.value().parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (using_ns_declarations_.find(get_namespace().to_string()) !=
|
||||||
|
using_ns_declarations_.end()) {
|
||||||
|
for (const auto &ns :
|
||||||
|
using_ns_declarations_.at(get_namespace().to_string())) {
|
||||||
|
possible_matches.emplace(ns | namespace_{name});
|
||||||
|
auto parent = ns.parent();
|
||||||
|
while (parent.has_value()) {
|
||||||
|
possible_matches.emplace(parent.value() | namespace_{name});
|
||||||
|
parent = parent.value().parent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search classes
|
||||||
|
for (const auto &c : diagram_.classes()) {
|
||||||
|
auto c_ns = namespace_{c->name_and_ns()};
|
||||||
|
for (const auto &possible_match : possible_matches) {
|
||||||
|
if (c_ns == possible_match) {
|
||||||
|
return possible_match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search enums
|
||||||
|
for (const auto &e : diagram_.enums()) {
|
||||||
|
auto e_ns = namespace_{e->name_and_ns()};
|
||||||
|
for (const auto &possible_match : possible_matches) {
|
||||||
|
if (e_ns == possible_match) {
|
||||||
|
return possible_match;
|
||||||
|
}
|
||||||
|
// Try to also match possible references to enum values
|
||||||
|
else if (possible_match.starts_with(e_ns)) {
|
||||||
|
return possible_match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ public:
|
|||||||
const std::set<common::model::namespace_> &using_namespace_directive(
|
const std::set<common::model::namespace_> &using_namespace_directive(
|
||||||
const common::model::namespace_ &ns) const;
|
const common::model::namespace_ &ns) const;
|
||||||
|
|
||||||
|
type_safe::optional<common::model::namespace_> get_name_with_namespace(
|
||||||
|
const std::string &name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Current visitor namespace
|
// Current visitor namespace
|
||||||
common::model::namespace_ ns_;
|
common::model::namespace_ ns_;
|
||||||
@@ -90,6 +93,8 @@ private:
|
|||||||
// A map of 'using namespace' declared within a given namespace scope
|
// A map of 'using namespace' declared within a given namespace scope
|
||||||
// This is necessary to properly establish the namespace of a given entity
|
// This is necessary to properly establish the namespace of a given entity
|
||||||
// for instance in unexposed template parameters
|
// for instance in unexposed template parameters
|
||||||
|
// - key - namespace
|
||||||
|
// - value - set of namespaces 'imported' within this namespace scope
|
||||||
std::map<std::string, std::set<common::model::namespace_>>
|
std::map<std::string, std::set<common::model::namespace_>>
|
||||||
using_ns_declarations_;
|
using_ns_declarations_;
|
||||||
|
|
||||||
|
|||||||
@@ -179,14 +179,15 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
|||||||
process_type_alias_template(at);
|
process_type_alias_template(at);
|
||||||
}
|
}
|
||||||
else if (e.kind() == cppast::cpp_entity_kind::using_directive_t) {
|
else if (e.kind() == cppast::cpp_entity_kind::using_directive_t) {
|
||||||
|
using common::model::namespace_;
|
||||||
|
|
||||||
const auto &using_directive =
|
const auto &using_directive =
|
||||||
static_cast<const cppast::cpp_using_directive &>(e);
|
static_cast<const cppast::cpp_using_directive &>(e);
|
||||||
|
|
||||||
const auto ns_ref = using_directive.target();
|
const auto ns_ref = using_directive.target();
|
||||||
|
const auto &ns = ns_ref.get(ctx.entity_index()).at(0).get();
|
||||||
if (ns_ref.get(ctx.entity_index()).size() > 0) {
|
if (ns_ref.get(ctx.entity_index()).size() > 0) {
|
||||||
auto full_ns = cx::util::full_name(ctx.get_namespace(),
|
auto full_ns = namespace_{cx::util::ns(ns)} | ns.name();
|
||||||
ns_ref.get(ctx.entity_index()).at(0).get());
|
|
||||||
|
|
||||||
ctx.add_using_namespace_directive(full_ns);
|
ctx.add_using_namespace_directive(full_ns);
|
||||||
}
|
}
|
||||||
@@ -503,10 +504,16 @@ void translation_unit_visitor::
|
|||||||
{
|
{
|
||||||
auto ua = tspec.value().unexposed_arguments().as_string();
|
auto ua = tspec.value().unexposed_arguments().as_string();
|
||||||
|
|
||||||
auto template_params = cx::util::parse_unexposed_template_params(ua);
|
auto template_params = cx::util::parse_unexposed_template_params(
|
||||||
|
ua, [this](const std::string &t) {
|
||||||
|
auto full_type = ctx.get_name_with_namespace(t);
|
||||||
|
if (full_type.has_value())
|
||||||
|
return full_type.value().to_string();
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
|
||||||
found_relationships_t relationships;
|
found_relationships_t relationships;
|
||||||
for (const auto ¶m : template_params) {
|
for (auto ¶m : template_params) {
|
||||||
find_relationships_in_unexposed_template_params(param, relationships);
|
find_relationships_in_unexposed_template_params(param, relationships);
|
||||||
c.add_template(param);
|
c.add_template(param);
|
||||||
}
|
}
|
||||||
@@ -1414,31 +1421,17 @@ bool translation_unit_visitor::find_relationships_in_unexposed_template_params(
|
|||||||
LOG_DBG("Finding relationships in user defined type: {}",
|
LOG_DBG("Finding relationships in user defined type: {}",
|
||||||
ct.to_string(ctx.config().using_namespace()));
|
ct.to_string(ctx.config().using_namespace()));
|
||||||
|
|
||||||
if (!util::contains(ct.type(), "::")) {
|
auto type_with_namespace = ctx.get_name_with_namespace(ct.type());
|
||||||
// The type name has no namespace - assume it is in the current
|
|
||||||
// namespace
|
|
||||||
// TODO: try also all namespaces declared through 'using namespace'
|
|
||||||
// directive in the current scope
|
|
||||||
if (ctx.config().should_include(ctx.get_namespace(), ct.type())) {
|
|
||||||
relationships.emplace_back(ct.type(), relationship_t::kDependency);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Calculate the complete namespace of the type
|
|
||||||
auto type_ns = common::model::namespace_{ct.type()};
|
|
||||||
auto type_name = type_ns.name();
|
|
||||||
type_ns.pop_back();
|
|
||||||
|
|
||||||
auto current_ns = ctx.get_namespace();
|
if (!type_with_namespace.has_value()) {
|
||||||
if (current_ns.ends_with(type_ns)) {
|
// Couldn't find declaration of this type
|
||||||
if (ctx.config().should_include(current_ns, type_name)) {
|
type_with_namespace = common::model::namespace_{ct.type()};
|
||||||
auto full_name = (current_ns | type_name).to_string();
|
}
|
||||||
relationships.emplace_back(
|
|
||||||
full_name, relationship_t::kDependency);
|
if (ctx.config().should_include(type_with_namespace.value())) {
|
||||||
found = true;
|
relationships.emplace_back(type_with_namespace.value().to_string(),
|
||||||
}
|
relationship_t::kDependency);
|
||||||
}
|
found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &nested_template_params : ct.template_params_) {
|
for (const auto &nested_template_params : ct.template_params_) {
|
||||||
|
|||||||
@@ -99,6 +99,17 @@ void namespace_::append(const namespace_ &ns)
|
|||||||
|
|
||||||
void namespace_::pop_back() { namespace_path_.pop_back(); }
|
void namespace_::pop_back() { namespace_path_.pop_back(); }
|
||||||
|
|
||||||
|
type_safe::optional<namespace_> namespace_::parent() const
|
||||||
|
{
|
||||||
|
if (size() <= 1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace_ res{*this};
|
||||||
|
res.pop_back();
|
||||||
|
return {std::move(res)};
|
||||||
|
}
|
||||||
|
|
||||||
namespace_ namespace_::operator|(const namespace_ &right) const
|
namespace_ namespace_::operator|(const namespace_ &right) const
|
||||||
{
|
{
|
||||||
namespace_ res{*this};
|
namespace_ res{*this};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_safe/optional.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace clanguml::common::model {
|
namespace clanguml::common::model {
|
||||||
@@ -68,6 +69,8 @@ public:
|
|||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
|
|
||||||
|
type_safe::optional<namespace_> parent() const;
|
||||||
|
|
||||||
bool starts_with(const namespace_ &right) const;
|
bool starts_with(const namespace_ &right) const;
|
||||||
bool ends_with(const namespace_ &right) const;
|
bool ends_with(const namespace_ &right) const;
|
||||||
namespace_ common_path(const namespace_ &right) const;
|
namespace_ common_path(const namespace_ &right) const;
|
||||||
|
|||||||
@@ -297,7 +297,8 @@ const cppast::cpp_type &unreferenced(const cppast::cpp_type &t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<class_diagram::model::class_template>
|
std::vector<class_diagram::model::class_template>
|
||||||
parse_unexposed_template_params(const std::string ¶ms)
|
parse_unexposed_template_params(const std::string ¶ms,
|
||||||
|
std::function<std::string(const std::string &)> ns_resolve)
|
||||||
{
|
{
|
||||||
using class_diagram::model::class_template;
|
using class_diagram::model::class_template;
|
||||||
|
|
||||||
@@ -331,7 +332,8 @@ parse_unexposed_template_params(const std::string ¶ms)
|
|||||||
}
|
}
|
||||||
std::string nested_params_str(
|
std::string nested_params_str(
|
||||||
bracket_match_begin, bracket_match_end);
|
bracket_match_begin, bracket_match_end);
|
||||||
nested_params = parse_unexposed_template_params(nested_params_str);
|
nested_params =
|
||||||
|
parse_unexposed_template_params(nested_params_str, ns_resolve);
|
||||||
if (nested_params.empty())
|
if (nested_params.empty())
|
||||||
nested_params.emplace_back(class_template{nested_params_str});
|
nested_params.emplace_back(class_template{nested_params_str});
|
||||||
it = bracket_match_end - 1;
|
it = bracket_match_end - 1;
|
||||||
@@ -347,7 +349,7 @@ parse_unexposed_template_params(const std::string ¶ms)
|
|||||||
}
|
}
|
||||||
if (complete_class_template) {
|
if (complete_class_template) {
|
||||||
class_template t;
|
class_template t;
|
||||||
t.set_type(clanguml::util::trim(type));
|
t.set_type(ns_resolve(clanguml::util::trim(type)));
|
||||||
type = "";
|
type = "";
|
||||||
t.template_params_ = std::move(nested_params);
|
t.template_params_ = std::move(nested_params);
|
||||||
|
|
||||||
@@ -359,7 +361,7 @@ parse_unexposed_template_params(const std::string ¶ms)
|
|||||||
|
|
||||||
if (!type.empty()) {
|
if (!type.empty()) {
|
||||||
class_template t;
|
class_template t;
|
||||||
t.set_type(clanguml::util::trim(type));
|
t.set_type(ns_resolve(clanguml::util::trim(type)));
|
||||||
type = "";
|
type = "";
|
||||||
t.template_params_ = std::move(nested_params);
|
t.template_params_ = std::move(nested_params);
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ std::pair<common::model::namespace_, std::string> split_ns(
|
|||||||
bool is_inside_class(const cppast::cpp_entity &e);
|
bool is_inside_class(const cppast::cpp_entity &e);
|
||||||
|
|
||||||
std::vector<class_diagram::model::class_template>
|
std::vector<class_diagram::model::class_template>
|
||||||
parse_unexposed_template_params(const std::string ¶ms);
|
parse_unexposed_template_params(const std::string ¶ms,
|
||||||
|
std::function<std::string(const std::string &)> ns_resolve);
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace cx
|
} // namespace cx
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ output_directory: puml
|
|||||||
diagrams:
|
diagrams:
|
||||||
t00038_class:
|
t00038_class:
|
||||||
type: class
|
type: class
|
||||||
generate_packages: true
|
generate_packages: false
|
||||||
glob:
|
glob:
|
||||||
- ../../tests/t00038/t00038.cc
|
- ../../tests/t00038/t00038.cc
|
||||||
using_namespace:
|
using_namespace:
|
||||||
- clanguml::t00038
|
- clanguml::t00038
|
||||||
include:
|
include:
|
||||||
namespaces:
|
namespaces:
|
||||||
- clanguml::t00038
|
- clanguml::t00038
|
||||||
|
- thirdparty::ns1
|
||||||
@@ -3,6 +3,19 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
namespace thirdparty {
|
||||||
|
namespace ns1 {
|
||||||
|
enum class color_t { red, green, blue };
|
||||||
|
|
||||||
|
struct E {
|
||||||
|
};
|
||||||
|
} // namespace ns1
|
||||||
|
namespace ns2 {
|
||||||
|
struct F {
|
||||||
|
};
|
||||||
|
} // namespace ns2
|
||||||
|
} // namespace thirdparty
|
||||||
|
|
||||||
namespace clanguml {
|
namespace clanguml {
|
||||||
namespace t00038 {
|
namespace t00038 {
|
||||||
|
|
||||||
@@ -21,6 +34,11 @@ struct key_t {
|
|||||||
|
|
||||||
template <typename T> struct map;
|
template <typename T> struct map;
|
||||||
|
|
||||||
|
using namespace thirdparty::ns1;
|
||||||
|
|
||||||
|
template <> struct map<std::integral_constant<color_t, color_t::red>> : E {
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct map<std::integral_constant<clanguml::t00038::property_t,
|
struct map<std::integral_constant<clanguml::t00038::property_t,
|
||||||
clanguml::t00038::property_t::property_a>> : A {
|
clanguml::t00038::property_t::property_a>> : A {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ TEST_CASE("t00038", "[test-case][class]")
|
|||||||
auto diagram = config.diagrams["t00038_class"];
|
auto diagram = config.diagrams["t00038_class"];
|
||||||
|
|
||||||
REQUIRE(diagram->name == "t00038_class");
|
REQUIRE(diagram->name == "t00038_class");
|
||||||
REQUIRE(diagram->generate_packages() == true);
|
REQUIRE(diagram->generate_packages() == false);
|
||||||
|
|
||||||
auto model = generate_class_diagram(db, diagram);
|
auto model = generate_class_diagram(db, diagram);
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ TEST_CASE("t00038", "[test-case][class]")
|
|||||||
REQUIRE_THAT(puml, IsClass(_A("A")));
|
REQUIRE_THAT(puml, IsClass(_A("A")));
|
||||||
REQUIRE_THAT(puml, IsClass(_A("B")));
|
REQUIRE_THAT(puml, IsClass(_A("B")));
|
||||||
REQUIRE_THAT(puml, IsClass(_A("C")));
|
REQUIRE_THAT(puml, IsClass(_A("C")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("thirdparty::ns1::E")));
|
||||||
REQUIRE_THAT(puml, IsClass(_A("key_t")));
|
REQUIRE_THAT(puml, IsClass(_A("key_t")));
|
||||||
REQUIRE_THAT(puml, IsClassTemplate("map", "T"));
|
REQUIRE_THAT(puml, IsClassTemplate("map", "T"));
|
||||||
REQUIRE_THAT(puml,
|
REQUIRE_THAT(puml,
|
||||||
@@ -45,8 +46,7 @@ TEST_CASE("t00038", "[test-case][class]")
|
|||||||
"std::integral_constant<property_t,property_t::property_a>"));
|
"std::integral_constant<property_t,property_t::property_a>"));
|
||||||
REQUIRE_THAT(puml,
|
REQUIRE_THAT(puml,
|
||||||
IsClassTemplate("map",
|
IsClassTemplate("map",
|
||||||
"std::vector<std::integral_constant<t00038::property_t,t00038::"
|
"std::vector<std::integral_constant<property_t,property_t::"
|
||||||
"property_t::"
|
|
||||||
"property_b>>"));
|
"property_b>>"));
|
||||||
REQUIRE_THAT(puml,
|
REQUIRE_THAT(puml,
|
||||||
IsClassTemplate("map",
|
IsClassTemplate("map",
|
||||||
@@ -70,9 +70,8 @@ TEST_CASE("t00038", "[test-case][class]")
|
|||||||
|
|
||||||
REQUIRE_THAT(puml,
|
REQUIRE_THAT(puml,
|
||||||
IsDependency(_A("map<"
|
IsDependency(_A("map<"
|
||||||
"std::vector<std::integral_constant<t00038::property_t,"
|
"std::vector<std::integral_constant<property_t,"
|
||||||
"t00038::property_t::"
|
"property_t::property_b>>>"),
|
||||||
"property_b>>>"),
|
|
||||||
_A("property_t")));
|
_A("property_t")));
|
||||||
|
|
||||||
REQUIRE_THAT(puml,
|
REQUIRE_THAT(puml,
|
||||||
@@ -85,6 +84,11 @@ TEST_CASE("t00038", "[test-case][class]")
|
|||||||
"property_t,property_t::property_c>>>>"),
|
"property_t,property_t::property_c>>>>"),
|
||||||
_A("key_t")));
|
_A("key_t")));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsDependency(_A("map<std::integral_constant<thirdparty::ns1::color_t,"
|
||||||
|
"thirdparty::ns1::color_t::red>>"),
|
||||||
|
_A("thirdparty::ns1::color_t")));
|
||||||
|
|
||||||
save_puml(
|
save_puml(
|
||||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
#define CATCH_CONFIG_RUNNER
|
#define CATCH_CONFIG_RUNNER
|
||||||
|
#define CATCH_CONFIG_CONSOLE_WIDTH 512
|
||||||
|
|
||||||
#include "catch.h"
|
#include "catch.h"
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]")
|
|||||||
|
|
||||||
const std::string int_template_str{"ns1::ns2::class1<int>"};
|
const std::string int_template_str{"ns1::ns2::class1<int>"};
|
||||||
|
|
||||||
auto int_template = parse_unexposed_template_params(int_template_str);
|
auto int_template = parse_unexposed_template_params(
|
||||||
|
int_template_str, [](const auto &n) { return n; });
|
||||||
|
|
||||||
CHECK(int_template.size() == 1);
|
CHECK(int_template.size() == 1);
|
||||||
CHECK(int_template[0].template_params_.size() == 1);
|
CHECK(int_template[0].template_params_.size() == 1);
|
||||||
@@ -84,8 +85,8 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]")
|
|||||||
|
|
||||||
const std::string int_int_template_str{"ns1::ns2::class1<int, int>"};
|
const std::string int_int_template_str{"ns1::ns2::class1<int, int>"};
|
||||||
|
|
||||||
auto int_int_template =
|
auto int_int_template = parse_unexposed_template_params(
|
||||||
parse_unexposed_template_params(int_int_template_str);
|
int_int_template_str, [](const auto &n) { return n; });
|
||||||
|
|
||||||
CHECK(int_int_template.size() == 1);
|
CHECK(int_int_template.size() == 1);
|
||||||
CHECK(int_int_template[0].template_params_.size() == 2);
|
CHECK(int_int_template[0].template_params_.size() == 2);
|
||||||
@@ -96,7 +97,8 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]")
|
|||||||
const std::string nested_template_str{
|
const std::string nested_template_str{
|
||||||
"class1<int, ns1::class2<int, std::vector<std::string>>>"};
|
"class1<int, ns1::class2<int, std::vector<std::string>>>"};
|
||||||
|
|
||||||
auto nested_template = parse_unexposed_template_params(nested_template_str);
|
auto nested_template = parse_unexposed_template_params(
|
||||||
|
nested_template_str, [](const auto &n) { return n; });
|
||||||
|
|
||||||
CHECK(nested_template.size() == 1);
|
CHECK(nested_template.size() == 1);
|
||||||
CHECK(nested_template[0].template_params_.size() == 2);
|
CHECK(nested_template[0].template_params_.size() == 2);
|
||||||
|
|||||||
Reference in New Issue
Block a user