Added package diagram namespace alias test case
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <cppast/cpp_class.hpp>
|
#include <cppast/cpp_class.hpp>
|
||||||
#include <cppast/cpp_entity_kind.hpp>
|
#include <cppast/cpp_entity_kind.hpp>
|
||||||
|
#include <cppast/cpp_namespace.hpp>
|
||||||
#include <cppast/cpp_template.hpp>
|
#include <cppast/cpp_template.hpp>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
@@ -92,6 +93,27 @@ std::string ns(const cppast::cpp_entity &e)
|
|||||||
return fmt::format("{}", fmt::join(res, "::"));
|
return fmt::format("{}", fmt::join(res, "::"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type_safe::optional_ref<const cppast::cpp_namespace> entity_ns(
|
||||||
|
const cppast::cpp_entity &e)
|
||||||
|
{
|
||||||
|
std::vector<std::string> res{};
|
||||||
|
|
||||||
|
if (e.kind() == cppast::cpp_entity_kind::namespace_t)
|
||||||
|
return type_safe::optional_ref<const cppast::cpp_namespace>(
|
||||||
|
static_cast<const cppast::cpp_namespace &>(e));
|
||||||
|
|
||||||
|
auto it = e.parent();
|
||||||
|
while (it) {
|
||||||
|
if (it.value().kind() == cppast::cpp_entity_kind::namespace_t) {
|
||||||
|
return type_safe::optional_ref<const cppast::cpp_namespace>(
|
||||||
|
static_cast<const cppast::cpp_namespace &>(it.value()));
|
||||||
|
}
|
||||||
|
it = it.value().parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
bool is_inside_class(const cppast::cpp_entity &e)
|
bool is_inside_class(const cppast::cpp_entity &e)
|
||||||
{
|
{
|
||||||
auto it = e.parent();
|
auto it = e.parent();
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ const cppast::cpp_type &unreferenced(const cppast::cpp_type &t);
|
|||||||
|
|
||||||
std::string ns(const cppast::cpp_entity &e);
|
std::string ns(const cppast::cpp_entity &e);
|
||||||
|
|
||||||
|
type_safe::optional_ref<const cppast::cpp_namespace> entity_ns(
|
||||||
|
const cppast::cpp_entity &e);
|
||||||
|
|
||||||
std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx);
|
std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx);
|
||||||
|
|
||||||
bool is_inside_class(const cppast::cpp_entity &e);
|
bool is_inside_class(const cppast::cpp_entity &e);
|
||||||
|
|||||||
@@ -32,6 +32,52 @@ translation_unit_context::translation_unit_context(
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool translation_unit_context::has_namespace_alias(
|
||||||
|
const std::string &full_name) const
|
||||||
|
{
|
||||||
|
bool res =
|
||||||
|
namespace_alias_index_.find(full_name) != namespace_alias_index_.end();
|
||||||
|
|
||||||
|
LOG_DBG("Alias {} {} found in index", full_name, res ? "" : "not");
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_context::add_namespace_alias(const std::string &full_name,
|
||||||
|
type_safe::object_ref<const cppast::cpp_namespace> ref)
|
||||||
|
{
|
||||||
|
if (!has_namespace_alias(full_name)) {
|
||||||
|
LOG_DBG("Stored type alias: {} -> {} ", full_name, ref.get().name());
|
||||||
|
|
||||||
|
namespace_alias_index_.emplace(full_name, std::move(ref));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_namespace>
|
||||||
|
translation_unit_context::get_namespace_alias(
|
||||||
|
const std::string &full_name) const
|
||||||
|
{
|
||||||
|
assert(has_namespace_alias(full_name));
|
||||||
|
|
||||||
|
return namespace_alias_index_.at(full_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_namespace>
|
||||||
|
translation_unit_context::get_namespace_alias_final(
|
||||||
|
const cppast::cpp_namespace &ns) const
|
||||||
|
{
|
||||||
|
auto ns_full_name = cx::util::full_name({}, ns);
|
||||||
|
|
||||||
|
ns_full_name = cx::util::ns(ns) + "::" + ns_full_name;
|
||||||
|
|
||||||
|
if (has_namespace_alias(ns_full_name)) {
|
||||||
|
return get_namespace_alias_final(
|
||||||
|
namespace_alias_index_.at(ns_full_name).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return type_safe::ref(ns);
|
||||||
|
}
|
||||||
|
|
||||||
bool translation_unit_context::has_type_alias(
|
bool translation_unit_context::has_type_alias(
|
||||||
const std::string &full_name) const
|
const std::string &full_name) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "package_diagram/model/diagram.h"
|
#include "package_diagram/model/diagram.h"
|
||||||
|
|
||||||
#include <cppast/cpp_entity_index.hpp>
|
#include <cppast/cpp_entity_index.hpp>
|
||||||
|
#include <cppast/cpp_namespace.hpp>
|
||||||
#include <cppast/cpp_type.hpp>
|
#include <cppast/cpp_type.hpp>
|
||||||
#include <type_safe/reference.hpp>
|
#include <type_safe/reference.hpp>
|
||||||
|
|
||||||
@@ -32,6 +33,17 @@ public:
|
|||||||
clanguml::package_diagram::model::diagram &diagram,
|
clanguml::package_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::package_diagram &config);
|
const clanguml::config::package_diagram &config);
|
||||||
|
|
||||||
|
bool has_namespace_alias(const std::string &full_name) const;
|
||||||
|
|
||||||
|
void add_namespace_alias(const std::string &full_name,
|
||||||
|
type_safe::object_ref<const cppast::cpp_namespace> ref);
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_namespace> get_namespace_alias(
|
||||||
|
const std::string &full_name) const;
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_namespace>
|
||||||
|
get_namespace_alias_final(const cppast::cpp_namespace &t) const;
|
||||||
|
|
||||||
bool has_type_alias(const std::string &full_name) const;
|
bool has_type_alias(const std::string &full_name) const;
|
||||||
|
|
||||||
void add_type_alias(const std::string &full_name,
|
void add_type_alias(const std::string &full_name,
|
||||||
@@ -80,7 +92,11 @@ private:
|
|||||||
// Reference to class diagram config
|
// Reference to class diagram config
|
||||||
const clanguml::config::package_diagram &config_;
|
const clanguml::config::package_diagram &config_;
|
||||||
|
|
||||||
// Map of discovered aliases (declared with 'using' keyword)
|
// Map of discovered aliases (declared with 'namespace' keyword)
|
||||||
|
std::map<std::string, type_safe::object_ref<const cppast::cpp_namespace>>
|
||||||
|
namespace_alias_index_;
|
||||||
|
|
||||||
|
// Map of discovered type aliases (declared with 'using' keyword)
|
||||||
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
||||||
alias_index_;
|
alias_index_;
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,15 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
|||||||
ctx.pop_namespace();
|
ctx.pop_namespace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (e.kind() == cppast::cpp_entity_kind::namespace_alias_t) {
|
||||||
|
auto &na = static_cast<const cppast::cpp_namespace_alias &>(e);
|
||||||
|
|
||||||
|
for (const auto &alias_target :
|
||||||
|
na.target().get(ctx.entity_index())) {
|
||||||
|
auto full_ns = cx::util::full_name(ctx.get_namespace(), na);
|
||||||
|
ctx.add_namespace_alias(full_ns, alias_target);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (e.kind() ==
|
else if (e.kind() ==
|
||||||
cppast::cpp_entity_kind::class_template_specialization_t) {
|
cppast::cpp_entity_kind::class_template_specialization_t) {
|
||||||
LOG_DBG("========== Visiting '{}' - {}",
|
LOG_DBG("========== Visiting '{}' - {}",
|
||||||
@@ -207,8 +216,6 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
|||||||
|
|
||||||
ctx.add_type_alias(cx::util::full_name(ctx.get_namespace(), ta),
|
ctx.add_type_alias(cx::util::full_name(ctx.get_namespace(), ta),
|
||||||
type_safe::ref(ta.underlying_type()));
|
type_safe::ref(ta.underlying_type()));
|
||||||
|
|
||||||
// ctx.diagram().add_type_alias(std::move(t));
|
|
||||||
}
|
}
|
||||||
else if (e.kind() == cppast::cpp_entity_kind::alias_template_t) {
|
else if (e.kind() == cppast::cpp_entity_kind::alias_template_t) {
|
||||||
LOG_DBG("========== Visiting '{}' - {}",
|
LOG_DBG("========== Visiting '{}' - {}",
|
||||||
@@ -294,21 +301,15 @@ void translation_unit_visitor::process_class_declaration(
|
|||||||
for (auto &base : cls.bases()) {
|
for (auto &base : cls.bases()) {
|
||||||
find_relationships(
|
find_relationships(
|
||||||
base.type(), relationships, relationship_t::kDependency);
|
base.type(), relationships, relationship_t::kDependency);
|
||||||
|
|
||||||
clanguml::cx::util::fully_prefixed(ctx.get_namespace(), base);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &dependency : relationships) {
|
for (const auto &dependency : relationships) {
|
||||||
auto type_ns = util::split(std::get<0>(dependency), "::");
|
auto destination = util::split(std::get<0>(dependency), "::");
|
||||||
type_ns.pop_back();
|
|
||||||
|
|
||||||
relationship r{relationship_t::kDependency,
|
if (!starts_with(ctx.get_namespace(), destination) &&
|
||||||
fmt::format("{}", fmt::join(type_ns, "::"))};
|
!starts_with(destination, ctx.get_namespace())) {
|
||||||
|
relationship r{
|
||||||
if (!starts_with(ctx.get_namespace(), type_ns) &&
|
relationship_t::kDependency, std::get<0>(dependency)};
|
||||||
!starts_with(type_ns, ctx.get_namespace())) {
|
|
||||||
relationship r{relationship_t::kDependency,
|
|
||||||
fmt::format("{}", fmt::join(type_ns, "::"))};
|
|
||||||
current_package.value().add_relationship(std::move(r));
|
current_package.value().add_relationship(std::move(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,16 +331,12 @@ void translation_unit_visitor::process_function(const cppast::cpp_function &f)
|
|||||||
f.return_type(), relationships, relationship_t::kDependency);
|
f.return_type(), relationships, relationship_t::kDependency);
|
||||||
|
|
||||||
for (const auto &dependency : relationships) {
|
for (const auto &dependency : relationships) {
|
||||||
auto type_ns = util::split(std::get<0>(dependency), "::");
|
auto destination = util::split(std::get<0>(dependency), "::");
|
||||||
type_ns.pop_back();
|
|
||||||
|
|
||||||
relationship r{relationship_t::kDependency,
|
if (!starts_with(ctx.get_namespace(), destination) &&
|
||||||
fmt::format("{}", fmt::join(type_ns, "::"))};
|
!starts_with(destination, ctx.get_namespace())) {
|
||||||
|
relationship r{
|
||||||
if (!starts_with(ctx.get_namespace(), type_ns) &&
|
relationship_t::kDependency, std::get<0>(dependency)};
|
||||||
!starts_with(type_ns, ctx.get_namespace())) {
|
|
||||||
relationship r{relationship_t::kDependency,
|
|
||||||
fmt::format("{}", fmt::join(type_ns, "::"))};
|
|
||||||
current_package.value().add_relationship(std::move(r));
|
current_package.value().add_relationship(std::move(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,8 +349,37 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
|
|||||||
{
|
{
|
||||||
bool found{false};
|
bool found{false};
|
||||||
|
|
||||||
const auto fn =
|
const auto fn = cx::util::full_name(
|
||||||
cx::util::full_name(cppast::remove_cv(t_), ctx.entity_index(), false);
|
resolve_alias(cppast::remove_cv(t_)), ctx.entity_index(), false);
|
||||||
|
auto t_ns = util::split(fn, "::");
|
||||||
|
t_ns.pop_back();
|
||||||
|
|
||||||
|
const auto &t_raw = resolve_alias(cppast::remove_cv(t_));
|
||||||
|
|
||||||
|
if (t_raw.kind() == cppast::cpp_type_kind::user_defined_t) {
|
||||||
|
|
||||||
|
auto t_raw_ns = cx::util::ns(t_raw, ctx.entity_index());
|
||||||
|
|
||||||
|
const auto &type_entities =
|
||||||
|
static_cast<const cppast::cpp_user_defined_type &>(t_raw)
|
||||||
|
.entity()
|
||||||
|
.get(ctx.entity_index());
|
||||||
|
if (type_entities.size() > 0) {
|
||||||
|
const auto &type_entity = type_entities[0];
|
||||||
|
|
||||||
|
const auto &t_raw_ns = cx::util::entity_ns(type_entity.get());
|
||||||
|
|
||||||
|
const auto &t_raw_ns_final = cx::util::ns(t_raw_ns.value()) +
|
||||||
|
"::" + cx::util::full_name({}, t_raw_ns.value());
|
||||||
|
t_ns = util::split(t_raw_ns_final, "::");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> possible_matches;
|
||||||
|
|
||||||
|
possible_matches.push_back(util::join(t_ns, "::"));
|
||||||
|
|
||||||
|
const auto fn_ns = cx::util::ns(cppast::remove_cv(t_), ctx.entity_index());
|
||||||
|
|
||||||
LOG_DBG("Finding relationships for type {}, {}, {}", cppast::to_string(t_),
|
LOG_DBG("Finding relationships for type {}, {}, {}", cppast::to_string(t_),
|
||||||
t_.kind(), fn);
|
t_.kind(), fn);
|
||||||
@@ -391,21 +417,19 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
|
|||||||
LOG_DBG("User defined type: {} | {}", cppast::to_string(t_),
|
LOG_DBG("User defined type: {} | {}", cppast::to_string(t_),
|
||||||
cppast::to_string(t_.canonical()));
|
cppast::to_string(t_.canonical()));
|
||||||
|
|
||||||
if (relationship_type != relationship_t::kNone)
|
|
||||||
relationships.emplace_back(cppast::to_string(t), relationship_type);
|
|
||||||
else
|
|
||||||
relationships.emplace_back(
|
|
||||||
cppast::to_string(t), relationship_t::kDependency);
|
|
||||||
|
|
||||||
// Check if t_ has an alias in the alias index
|
// Check if t_ has an alias in the alias index
|
||||||
if (ctx.has_type_alias(fn)) {
|
if (ctx.has_type_alias(fn)) {
|
||||||
LOG_DBG("Find relationship in alias of {} | {}", fn,
|
LOG_DBG("Found relationship in alias of {} | {}", fn,
|
||||||
cppast::to_string(ctx.get_type_alias(fn).get()));
|
cppast::to_string(ctx.get_type_alias(fn).get()));
|
||||||
found = find_relationships(
|
found = find_relationships(
|
||||||
ctx.get_type_alias(fn).get(), relationships, relationship_type);
|
ctx.get_type_alias(fn).get(), relationships, relationship_type);
|
||||||
if (found)
|
if (found)
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto &pm : possible_matches) {
|
||||||
|
relationships.emplace_back(pm, relationship_t::kDependency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
else if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||||
auto &tinst =
|
auto &tinst =
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ std::vector<std::string> split(std::string str, std::string delimiter)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string join(const std::vector<std::string> &toks, std::string delimiter)
|
||||||
|
{
|
||||||
|
return fmt::format("{}", fmt::join(toks, delimiter));
|
||||||
|
}
|
||||||
|
|
||||||
std::string ns_relative(
|
std::string ns_relative(
|
||||||
const std::vector<std::string> &namespaces, const std::string &n)
|
const std::vector<std::string> &namespaces, const std::string &n)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ std::string trim(const std::string &s);
|
|||||||
*/
|
*/
|
||||||
std::vector<std::string> split(std::string str, std::string delimiter);
|
std::vector<std::string> split(std::string str, std::string delimiter);
|
||||||
|
|
||||||
|
std::string join(const std::vector<std::string> &toks, std::string delimiter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get name of the identifier relative to a set of namespaces
|
* @brief Get name of the identifier relative to a set of namespaces
|
||||||
*
|
*
|
||||||
|
|||||||
15
tests/t30005/.clang-uml
Normal file
15
tests/t30005/.clang-uml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t30005_package:
|
||||||
|
type: package
|
||||||
|
glob:
|
||||||
|
- ../../tests/t30005/t30005.cc
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t30005
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t30005
|
||||||
|
plantuml:
|
||||||
|
before:
|
||||||
|
- "' t30005 test package diagram"
|
||||||
27
tests/t30005/t30005.cc
Normal file
27
tests/t30005/t30005.cc
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
namespace clanguml {
|
||||||
|
namespace t30005 {
|
||||||
|
|
||||||
|
namespace A::AA::AAA {
|
||||||
|
struct C1 {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace B::BB::BBB {
|
||||||
|
namespace A6 = A::AA::AAA;
|
||||||
|
namespace ASix = A6;
|
||||||
|
struct C2 {
|
||||||
|
ASix::C1 *cb;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace C::CC::CCC {
|
||||||
|
namespace A6 = A::AA::AAA;
|
||||||
|
namespace ASix = A6;
|
||||||
|
using ADSix = ASix::C1;
|
||||||
|
struct C2 {
|
||||||
|
ADSix *cc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
54
tests/t30005/test_case.h
Normal file
54
tests/t30005/test_case.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* tests/t30005/test_case.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2022 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("t30005", "[test-case][package]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t30005");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t30005_package"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t30005"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t30005::A"));
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t30005::C"));
|
||||||
|
REQUIRE(!diagram->should_include("std::vector"));
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t30005_package");
|
||||||
|
|
||||||
|
auto model = generate_package_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name() == "t30005_package");
|
||||||
|
|
||||||
|
auto puml = generate_package_puml(diagram, model);
|
||||||
|
AliasMatcher _A(puml);
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||||
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsPackage("AAA"));
|
||||||
|
REQUIRE_THAT(puml, IsPackage("BBB"));
|
||||||
|
REQUIRE_THAT(puml, IsPackage("CCC"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("AAA")));
|
||||||
|
REQUIRE_THAT(puml, IsDependency(_A("CCC"), _A("AAA")));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
@@ -180,6 +180,7 @@ using namespace clanguml::test::matchers;
|
|||||||
#include "t30002/test_case.h"
|
#include "t30002/test_case.h"
|
||||||
#include "t30003/test_case.h"
|
#include "t30003/test_case.h"
|
||||||
#include "t30004/test_case.h"
|
#include "t30004/test_case.h"
|
||||||
|
#include "t30005/test_case.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Other tests (e.g. configuration file)
|
// Other tests (e.g. configuration file)
|
||||||
|
|||||||
Reference in New Issue
Block a user