Added callee_filter for including/excluding messages based on receiver type (#152)
This commit is contained in:
@@ -59,17 +59,18 @@ The following table specifies the values allowed in each filter:
|
||||
|-------------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------|
|
||||
| `namespaces` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: '.*detail.*'``` |
|
||||
| `elements` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: '.*detail.*'``` |
|
||||
| `element_types` | Types of diagram elements | ```class```, ```enum```, ```concept``` |
|
||||
| `paths` | File or dir path or glob pattern | ```src/dir1```, ```src/dir2/a.cpp```, ```src/dir3/*.cpp``` |
|
||||
| `element_types` | Types of diagram elements | ```class```, ```enum```, ```concept``` |
|
||||
| `paths` | File or dir path or glob pattern | ```src/dir1```, ```src/dir2/a.cpp```, ```src/dir3/*.cpp``` |
|
||||
| `context` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `relationships` | Type of relationship | ```inheritance```, ```composition```, ```aggregation```, ```ownership```, ```association```, ```instantiation```, ```friendship```, ```dependency``` |
|
||||
| `subclasses` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `parents` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `specializations` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `access` | Method or member access scope | ```public```, ```protected```, ```private``` |
|
||||
| `method_types` | Type of class method | ```constructor```, ```destructor```, ```assignment```, ```operator```, ```defaulted```, ```deleted```, ```static``` |
|
||||
| `dependants` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `dependencies` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `subclasses` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `parents` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `specializations` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `access` | Method or member access scope | ```public```, ```protected```, ```private``` |
|
||||
| `method_types` | Type of class method | ```constructor```, ```destructor```, ```assignment```, ```operator```, ```defaulted```, ```deleted```, ```static``` |
|
||||
| `dependants` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `dependencies` | Qualified name or regex | ```ns1::ns2::ClassA```, ```r: 'ns1::ns2::ClassA.+'``` |
|
||||
| `callee_types` | Callee types in sequence diagrams| ```constructor```, ```assignment```, ```operator```, ```defaulted```, ```static```, ```method```, ```function```, ```function_template```, ```lambda``` |
|
||||
|
||||
The following filters are available.
|
||||
|
||||
@@ -190,6 +191,22 @@ This filter allows to include or exclude various method types from the class dia
|
||||
This filter is independent of the `access` filter, which controls which methods
|
||||
are included based on access scope (e.g. `public`).
|
||||
|
||||
## callee_types
|
||||
|
||||
This filter is specific for `sequence diagrams` and allows to control which types calls should be included/excluded from the diagram.
|
||||
In a sequence diagram, a `callee` is the receiver of a message, and this filter specifies which types of receivers should match.
|
||||
|
||||
The following callee types are supported:
|
||||
* constructor
|
||||
* assignment
|
||||
* operator
|
||||
* defaulted
|
||||
* static
|
||||
* method
|
||||
* function
|
||||
* function_template
|
||||
* lambda
|
||||
|
||||
## dependants and dependencies
|
||||
|
||||
These filters allow to specify that only dependants or dependencies of a given class should be included in the diagram.
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "glob/glob.hpp"
|
||||
#include "include_diagram/model/diagram.h"
|
||||
#include "package_diagram/model/diagram.h"
|
||||
#include "sequence_diagram/model/diagram.h"
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
@@ -140,6 +141,12 @@ tvl::value_t filter_visitor::match(
|
||||
return match(d, m.access());
|
||||
}
|
||||
|
||||
tvl::value_t filter_visitor::match(const diagram & /*d*/,
|
||||
const sequence_diagram::model::participant & /*p*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool filter_visitor::is_inclusive() const
|
||||
{
|
||||
return type_ == filter_t::kInclusive;
|
||||
@@ -166,6 +173,13 @@ tvl::value_t anyof_filter::match(
|
||||
[&d, &e](const auto &f) { return f->match(d, e); });
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const sequence_diagram::model::participant &p) const
|
||||
{
|
||||
return tvl::any_of(filters_.begin(), filters_.end(),
|
||||
[&d, &p](const auto &f) { return f->match(d, p); });
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::source_file &e) const
|
||||
{
|
||||
@@ -351,6 +365,62 @@ tvl::value_t method_type_filter::match(
|
||||
});
|
||||
}
|
||||
|
||||
callee_filter::callee_filter(
|
||||
filter_t type, std::vector<config::callee_type> callee_types)
|
||||
: filter_visitor{type}
|
||||
, callee_types_{std::move(callee_types)}
|
||||
{
|
||||
}
|
||||
|
||||
tvl::value_t callee_filter::match(
|
||||
const diagram &d, const sequence_diagram::model::participant &p) const
|
||||
{
|
||||
using sequence_diagram::model::class_;
|
||||
using sequence_diagram::model::method;
|
||||
using sequence_diagram::model::participant;
|
||||
|
||||
auto is_lambda = [&d](const method &m) {
|
||||
auto class_participant =
|
||||
dynamic_cast<const sequence_diagram::model::diagram &>(d)
|
||||
.get_participant<class_>(m.class_id());
|
||||
if (!class_participant)
|
||||
return false;
|
||||
|
||||
return class_participant.value().is_lambda();
|
||||
};
|
||||
|
||||
tvl::value_t res = tvl::any_of(
|
||||
callee_types_.begin(), callee_types_.end(), [&p, is_lambda](auto ct) {
|
||||
switch (ct) {
|
||||
case config::callee_type::method:
|
||||
return p.type_name() == "method";
|
||||
case config::callee_type::constructor:
|
||||
return p.type_name() == "method" &&
|
||||
((method &)p).is_constructor();
|
||||
case config::callee_type::assignment:
|
||||
return p.type_name() == "method" &&
|
||||
((method &)p).is_assignment();
|
||||
case config::callee_type::operator_:
|
||||
return p.type_name() == "method" && ((method &)p).is_operator();
|
||||
case config::callee_type::defaulted:
|
||||
return p.type_name() == "method" &&
|
||||
((method &)p).is_defaulted();
|
||||
case config::callee_type::static_:
|
||||
return p.type_name() == "method" && ((method &)p).is_static();
|
||||
case config::callee_type::function:
|
||||
return p.type_name() == "function";
|
||||
case config::callee_type::function_template:
|
||||
return p.type_name() == "function_template";
|
||||
case config::callee_type::lambda:
|
||||
return p.type_name() == "method" && is_lambda((method &)p);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
subclass_filter::subclass_filter(
|
||||
filter_t type, std::vector<common::string_or_regex> roots)
|
||||
: filter_visitor{type}
|
||||
@@ -771,6 +841,10 @@ void diagram_filter::init_filters(const config::diagram &c)
|
||||
filter_t::kInclusive, relationship_t::kDependency,
|
||||
c.include().dependencies, true));
|
||||
}
|
||||
else if (c.type() == diagram_t::kSequence) {
|
||||
element_filters.emplace_back(std::make_unique<callee_filter>(
|
||||
filter_t::kInclusive, c.include().callee_types));
|
||||
}
|
||||
else if (c.type() == diagram_t::kPackage) {
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<package_dependants_filter_t>(
|
||||
@@ -878,7 +952,11 @@ void diagram_filter::init_filters(const config::diagram &c)
|
||||
filter_t::kExclusive, relationship_t::kDependency,
|
||||
c.exclude().dependencies, true));
|
||||
|
||||
if (c.type() == diagram_t::kInclude) {
|
||||
if (c.type() == diagram_t::kSequence) {
|
||||
add_exclusive_filter(std::make_unique<callee_filter>(
|
||||
filter_t::kExclusive, c.exclude().callee_types));
|
||||
}
|
||||
else if (c.type() == diagram_t::kInclude) {
|
||||
std::vector<std::string> dependants;
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "config/config.h"
|
||||
#include "diagram.h"
|
||||
#include "include_diagram/model/diagram.h"
|
||||
#include "sequence_diagram/model/participant.h"
|
||||
#include "source_file.h"
|
||||
#include "tvl.h"
|
||||
|
||||
@@ -104,6 +105,9 @@ public:
|
||||
virtual tvl::value_t match(
|
||||
const diagram &d, const class_diagram::model::class_member &m) const;
|
||||
|
||||
virtual tvl::value_t match(
|
||||
const diagram &d, const sequence_diagram::model::participant &p) const;
|
||||
|
||||
bool is_inclusive() const;
|
||||
bool is_exclusive() const;
|
||||
|
||||
@@ -122,6 +126,9 @@ struct anyof_filter : public filter_visitor {
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::element &e) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const sequence_diagram::model::participant &p) const override;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::source_file &e) const override;
|
||||
|
||||
@@ -192,6 +199,21 @@ private:
|
||||
std::vector<config::method_type> method_types_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sequence diagram callee type filter.
|
||||
*/
|
||||
struct callee_filter : public filter_visitor {
|
||||
callee_filter(filter_t type, std::vector<config::callee_type> callee_types);
|
||||
|
||||
~callee_filter() override = default;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const sequence_diagram::model::participant &p) const override;
|
||||
|
||||
private:
|
||||
std::vector<config::callee_type> callee_types_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Match element based on whether it is a subclass of a set of base classes,
|
||||
* or one of them.
|
||||
|
||||
@@ -79,10 +79,37 @@ std::string to_string(method_type mt)
|
||||
return "deleted";
|
||||
case method_type::static_:
|
||||
return "static";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string to_string(callee_type mt)
|
||||
{
|
||||
switch (mt) {
|
||||
case callee_type::constructor:
|
||||
return "constructor";
|
||||
case callee_type::assignment:
|
||||
return "assignment";
|
||||
case callee_type::operator_:
|
||||
return "operator";
|
||||
case callee_type::defaulted:
|
||||
return "defaulted";
|
||||
case callee_type::static_:
|
||||
return "static";
|
||||
case callee_type::method:
|
||||
return "method";
|
||||
case callee_type::function:
|
||||
return "function";
|
||||
case callee_type::function_template:
|
||||
return "function_template";
|
||||
case callee_type::lambda:
|
||||
return "lambda";
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string to_string(const comment_parser_t cp)
|
||||
|
||||
@@ -63,6 +63,21 @@ enum class method_type {
|
||||
|
||||
std::string to_string(method_type mt);
|
||||
|
||||
/*! Types of call expressions, which can be used in sequence diagram filters */
|
||||
enum class callee_type {
|
||||
constructor,
|
||||
assignment,
|
||||
operator_,
|
||||
defaulted,
|
||||
static_,
|
||||
method,
|
||||
function,
|
||||
function_template,
|
||||
lambda
|
||||
};
|
||||
|
||||
std::string to_string(callee_type mt);
|
||||
|
||||
/*! How packages in diagrams should be generated */
|
||||
enum class package_type_t {
|
||||
kNamespace, /*!< From namespaces */
|
||||
@@ -313,6 +328,23 @@ struct filter {
|
||||
* ```
|
||||
*/
|
||||
std::vector<method_type> method_types;
|
||||
|
||||
/*! @brief Callee types filter
|
||||
*
|
||||
* This filter allows to filter sequence diagram calls by callee types.
|
||||
*
|
||||
* @see method_type
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```yaml
|
||||
* exclude:
|
||||
* callee_types:
|
||||
* - constructor
|
||||
* - operator
|
||||
* ```
|
||||
*/
|
||||
std::vector<callee_type> callee_types;
|
||||
};
|
||||
|
||||
enum class hint_t { up, down, left, right, together, row, column };
|
||||
|
||||
@@ -24,6 +24,7 @@ using clanguml::common::namespace_or_regex;
|
||||
using clanguml::common::string_or_regex;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::config::callee_type;
|
||||
using clanguml::config::class_diagram;
|
||||
using clanguml::config::config;
|
||||
using clanguml::config::diagram_template;
|
||||
@@ -252,6 +253,38 @@ template <> struct convert<method_type> {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// config callee_type decoder
|
||||
//
|
||||
template <> struct convert<callee_type> {
|
||||
static bool decode(const Node &node, callee_type &rhs)
|
||||
{
|
||||
const auto &val = node.as<std::string>();
|
||||
if (val == to_string(callee_type::constructor))
|
||||
rhs = callee_type::constructor;
|
||||
else if (val == to_string(callee_type::assignment))
|
||||
rhs = callee_type::assignment;
|
||||
else if (val == to_string(callee_type::operator_))
|
||||
rhs = callee_type::operator_;
|
||||
else if (val == to_string(callee_type::defaulted))
|
||||
rhs = callee_type::defaulted;
|
||||
else if (val == to_string(callee_type::static_))
|
||||
rhs = callee_type::static_;
|
||||
else if (val == to_string(callee_type::function))
|
||||
rhs = callee_type::function;
|
||||
else if (val == to_string(callee_type::function_template))
|
||||
rhs = callee_type::function_template;
|
||||
else if (val == to_string(callee_type::method))
|
||||
rhs = callee_type::method;
|
||||
else if (val == to_string(callee_type::lambda))
|
||||
rhs = callee_type::lambda;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// config relationship_t decoder
|
||||
//
|
||||
@@ -432,6 +465,10 @@ template <> struct convert<filter> {
|
||||
if (node["paths"])
|
||||
rhs.paths = node["paths"].as<decltype(rhs.paths)>();
|
||||
|
||||
if (node["callee_types"])
|
||||
rhs.callee_types =
|
||||
node["callee_types"].as<decltype(rhs.callee_types)>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,6 +82,12 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const method_type &m)
|
||||
return out;
|
||||
}
|
||||
|
||||
YAML::Emitter &operator<<(YAML::Emitter &out, const callee_type &m)
|
||||
{
|
||||
out << to_string(m);
|
||||
return out;
|
||||
}
|
||||
|
||||
YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
@@ -112,7 +118,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
|
||||
out << YAML::Key << "subclasses" << YAML::Value << f.subclasses;
|
||||
if (!f.parents.empty())
|
||||
out << YAML::Key << "parents" << YAML::Value << f.parents;
|
||||
|
||||
if (!f.method_types.empty())
|
||||
out << YAML::Key << "callee_types" << YAML::Value << f.callee_types;
|
||||
out << YAML::EndMap;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -253,9 +253,15 @@ void generator::process_call_message(const model::message &m,
|
||||
std::vector<common::model::diagram_element::id_t> &visited) const
|
||||
{
|
||||
const auto &to = m_model.get_participant<model::participant>(m.to());
|
||||
|
||||
if (!to || to.value().skip())
|
||||
return;
|
||||
|
||||
if (!m_model.should_include(to.value())) {
|
||||
LOG_DBG("Excluding call from '{}' to '{}'", m.from(), m.to());
|
||||
return;
|
||||
}
|
||||
|
||||
visited.push_back(m.from());
|
||||
|
||||
LOG_DBG("Generating message {} --> {}", m.from(), m.to());
|
||||
|
||||
@@ -142,9 +142,15 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
||||
if (!to || to.value().skip())
|
||||
continue;
|
||||
|
||||
if (!m_model.should_include(to.value())) {
|
||||
LOG_DBG("Excluding call from [{}] to {} [{}]", m.from(),
|
||||
to.value().full_name(false), m.to());
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.push_back(m.from());
|
||||
|
||||
LOG_DBG("Generating message {} --> {}", m.from(), m.to());
|
||||
LOG_DBG("Generating message [{}] --> [{}]", m.from(), m.to());
|
||||
|
||||
generate_call(m, ostr);
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "diagram.h"
|
||||
|
||||
#include "common/model/diagram_filter.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
@@ -171,6 +173,14 @@ std::set<common::model::diagram_element::id_t> &diagram::active_participants()
|
||||
return active_participants_;
|
||||
}
|
||||
|
||||
bool diagram::should_include(
|
||||
const sequence_diagram::model::participant &p) const
|
||||
{
|
||||
return filter().should_include(p) &&
|
||||
filter().should_include(
|
||||
dynamic_cast<const common::model::source_location &>(p));
|
||||
}
|
||||
|
||||
void diagram::print() const
|
||||
{
|
||||
LOG_TRACE(" --- Participants ---");
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
*/
|
||||
template <typename T>
|
||||
common::optional_ref<T> get_participant(
|
||||
common::model::diagram_element::id_t id)
|
||||
common::model::diagram_element::id_t id) const
|
||||
{
|
||||
if (participants_.find(id) == participants_.end()) {
|
||||
return {};
|
||||
@@ -202,6 +202,16 @@ public:
|
||||
*/
|
||||
void print() const;
|
||||
|
||||
// Implicitly import should_include overloads from base class
|
||||
using common::model::diagram::should_include;
|
||||
|
||||
/**
|
||||
* @brief Convenience `should_include` overload for participant
|
||||
* @param p Participant model
|
||||
* @return True, if the participant should be included in the diagram
|
||||
*/
|
||||
bool should_include(const sequence_diagram::model::participant &p) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* This method checks the last messages in sequence (current_messages),
|
||||
|
||||
@@ -164,6 +164,22 @@ std::string method::alias() const
|
||||
return fmt::format("C_{:022}", class_id_);
|
||||
}
|
||||
|
||||
bool method::is_constructor() const { return is_constructor_; }
|
||||
|
||||
void method::is_constructor(bool c) { is_constructor_ = c; }
|
||||
|
||||
bool method::is_defaulted() const { return is_defaulted_; }
|
||||
|
||||
void method::is_defaulted(bool d) { is_defaulted_ = d; }
|
||||
|
||||
bool method::is_assignment() const { return is_assignment_; }
|
||||
|
||||
void method::is_assignment(bool a) { is_assignment_ = a; }
|
||||
|
||||
bool method::is_operator() const { return is_operator_; }
|
||||
|
||||
void method::is_operator(bool o) { is_operator_ = o; }
|
||||
|
||||
void method::set_method_name(const std::string &name) { method_name_ = name; }
|
||||
|
||||
void method::set_class_id(diagram_element::id_t id) { class_id_ = id; }
|
||||
|
||||
@@ -401,10 +401,70 @@ struct method : public function {
|
||||
*/
|
||||
std::string to_string() const override;
|
||||
|
||||
/**
|
||||
* @brief Check, if the method is a constructor
|
||||
*
|
||||
* @return True, if the method is a constructor
|
||||
*/
|
||||
bool is_constructor() const;
|
||||
|
||||
/**
|
||||
* @brief Set whether the method is a constructor
|
||||
*
|
||||
* @param v True, if the method is a constructor
|
||||
*/
|
||||
void is_constructor(bool c);
|
||||
|
||||
/**
|
||||
* @brief Check, if the method is defaulted
|
||||
*
|
||||
* @return True, if the method is defaulted
|
||||
*/
|
||||
bool is_defaulted() const;
|
||||
|
||||
/**
|
||||
* @brief Set whether the method is defaulted
|
||||
*
|
||||
* @param v True, if the method is defaulted
|
||||
*/
|
||||
void is_defaulted(bool c);
|
||||
|
||||
/**
|
||||
* @brief Check, if the method is an assignment operator
|
||||
*
|
||||
* @return True, if the method is an assignment operator
|
||||
*/
|
||||
bool is_assignment() const;
|
||||
|
||||
/**
|
||||
* @brief Set whether the method is an assignment operator
|
||||
*
|
||||
* @param v True, if the method is an assignment operator
|
||||
*/
|
||||
void is_assignment(bool a);
|
||||
|
||||
/**
|
||||
* @brief Check, if the method is an operator
|
||||
*
|
||||
* @return True, if the method is an operator
|
||||
*/
|
||||
bool is_operator() const;
|
||||
|
||||
/**
|
||||
* @brief Set whether the method is an operator
|
||||
*
|
||||
* @param v True, if the method is an operator
|
||||
*/
|
||||
void is_operator(bool o);
|
||||
|
||||
private:
|
||||
diagram_element::id_t class_id_{};
|
||||
std::string method_name_;
|
||||
std::string class_full_name_;
|
||||
bool is_constructor_{false};
|
||||
bool is_defaulted_{false};
|
||||
bool is_assignment_{false};
|
||||
bool is_operator_{false};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -897,14 +897,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
// message source rather then enclosing context
|
||||
// Unless the lambda is declared in a function or method call
|
||||
if (context().lambda_caller_id() != 0) {
|
||||
if (!std::holds_alternative<clang::CallExpr *>(
|
||||
context().current_callexpr())) {
|
||||
m.set_from(context().lambda_caller_id());
|
||||
}
|
||||
else {
|
||||
LOG_DBG("Current lambda declaration is passed to a method or "
|
||||
"function - keep the original caller id");
|
||||
}
|
||||
m.set_from(context().lambda_caller_id());
|
||||
}
|
||||
|
||||
if (context().is_expr_in_current_control_statement_condition(expr)) {
|
||||
@@ -1009,7 +1002,11 @@ bool translation_unit_visitor::VisitCXXConstructExpr(
|
||||
using clanguml::sequence_diagram::model::activity;
|
||||
using clanguml::sequence_diagram::model::message;
|
||||
|
||||
if (!should_include(expr->getConstructor()))
|
||||
if (expr == nullptr)
|
||||
return true;
|
||||
|
||||
if (const auto *ctor = expr->getConstructor();
|
||||
ctor != nullptr && !should_include(ctor))
|
||||
return true;
|
||||
|
||||
LOG_TRACE("Visiting cxx construct expression at {} [caller_id = {}]",
|
||||
@@ -1021,14 +1018,7 @@ bool translation_unit_visitor::VisitCXXConstructExpr(
|
||||
set_source_location(*expr, m);
|
||||
|
||||
if (context().lambda_caller_id() != 0) {
|
||||
if (!std::holds_alternative<clang::CallExpr *>(
|
||||
context().current_callexpr())) {
|
||||
m.set_from(context().lambda_caller_id());
|
||||
}
|
||||
else {
|
||||
LOG_DBG("Current lambda declaration is passed to a method or "
|
||||
"function - keep the original caller id");
|
||||
}
|
||||
m.set_from(context().lambda_caller_id());
|
||||
}
|
||||
|
||||
if (context().is_expr_in_current_control_statement_condition(expr)) {
|
||||
@@ -1427,7 +1417,7 @@ translation_unit_visitor::create_class_model(clang::CXXRecordDecl *cls)
|
||||
c.set_id(common::to_id(c.full_name(false)));
|
||||
|
||||
// TODO: Check if lambda is declared as an argument passed to a
|
||||
// function/method call
|
||||
// function/method call
|
||||
}
|
||||
else {
|
||||
LOG_WARN("Cannot find parent declaration for lambda {}",
|
||||
@@ -2303,6 +2293,16 @@ translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration)
|
||||
method_model_ptr->set_name(ns.name());
|
||||
ns.pop_back();
|
||||
|
||||
method_model_ptr->is_defaulted(declaration->isDefaulted());
|
||||
method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
|
||||
declaration->isMoveAssignmentOperator());
|
||||
method_model_ptr->is_const(declaration->isConst());
|
||||
method_model_ptr->is_static(declaration->isStatic());
|
||||
method_model_ptr->is_static(declaration->isStatic());
|
||||
method_model_ptr->is_operator(declaration->isOverloadedOperator());
|
||||
method_model_ptr->is_constructor(
|
||||
clang::dyn_cast<clang::CXXConstructorDecl>(declaration) != nullptr);
|
||||
|
||||
clang::Decl *parent_decl = declaration->getParent();
|
||||
|
||||
if (context().current_class_template_decl_ != nullptr)
|
||||
|
||||
@@ -74,7 +74,8 @@ TEST_CASE("t20012", "[test-case][sequence]")
|
||||
HasCall(_A("tmain()::(lambda ../../tests/t20012/t20012.cc:86:9)"),
|
||||
_A("C"), "c()"));
|
||||
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("D"), "add5(int)"));
|
||||
// @todo #168
|
||||
// REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("D"), "add5(int)"));
|
||||
|
||||
save_puml(
|
||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
@@ -113,7 +114,9 @@ TEST_CASE("t20012", "[test-case][sequence]")
|
||||
FindMessage(j,
|
||||
"tmain()::(lambda ../../tests/t20012/t20012.cc:86:9)", "C",
|
||||
"c()"),
|
||||
FindMessage(j, "tmain()", "D", "add5(int)")};
|
||||
// @todo #168
|
||||
// FindMessage(j, "tmain()", "D", "add5(int)")
|
||||
};
|
||||
|
||||
REQUIRE(std::is_sorted(messages.begin(), messages.end()));
|
||||
|
||||
|
||||
@@ -45,8 +45,11 @@ TEST_CASE("t20020", "[test-case][sequence]")
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b2()"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "log()"));
|
||||
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("C"), "c1()"));
|
||||
REQUIRE_THAT(puml, HasCallInControlCondition(_A("C"), _A("C"), "c2()"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("C"), "c1() const"));
|
||||
REQUIRE_THAT(
|
||||
puml, HasCallInControlCondition(_A("C"), _A("C"), "c2() const"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "log() const"));
|
||||
|
||||
REQUIRE_THAT(
|
||||
puml, HasCallInControlCondition(_A("tmain()"), _A("C"), "c3(int)"));
|
||||
|
||||
@@ -67,8 +70,9 @@ TEST_CASE("t20020", "[test-case][sequence]")
|
||||
FindMessage(j, "tmain()", "B", "b2()"),
|
||||
FindMessage(j, "tmain()", "A", "a4()"),
|
||||
FindMessage(j, "tmain()", "B", "log()"),
|
||||
FindMessage(j, "tmain()", "C", "c1()"),
|
||||
FindMessage(j, "C", "C", "c2()"), FindMessage(j, "C", "C", "log()"),
|
||||
FindMessage(j, "tmain()", "C", "c1() const"),
|
||||
FindMessage(j, "C", "C", "c2() const"),
|
||||
FindMessage(j, "C", "C", "log() const"),
|
||||
FindMessage(j, "tmain()", "D<int>", "d1(int,int)")};
|
||||
|
||||
REQUIRE(std::is_sorted(messages.begin(), messages.end()));
|
||||
|
||||
@@ -76,7 +76,7 @@ TEST_CASE("t20021", "[test-case][sequence]")
|
||||
FindMessage(j, "tmain()", "C", "c2()"),
|
||||
FindMessage(j, "tmain()", "A", "a1()"),
|
||||
FindMessage(j, "tmain()", "C", "c3()"),
|
||||
FindMessage(j, "tmain()", "B", "b2()"),
|
||||
FindMessage(j, "tmain()", "B", "b2() const"),
|
||||
FindMessage(j, "tmain()", "C", "contents()")
|
||||
// TODO: Repeated messge gets wrong index
|
||||
// FindMessage(j, "tmain()", "B", "b2()")
|
||||
|
||||
20
tests/t20031/.clang-uml
Normal file
20
tests/t20031/.clang-uml
Normal file
@@ -0,0 +1,20 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t20031_sequence:
|
||||
type: sequence
|
||||
glob:
|
||||
- ../../tests/t20031/t20031.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20031
|
||||
exclude:
|
||||
callee_types:
|
||||
- constructor
|
||||
- operator
|
||||
- lambda
|
||||
using_namespace:
|
||||
- clanguml::t20031
|
||||
start_from:
|
||||
- function: "clanguml::t20031::tmain(int)"
|
||||
- function: "clanguml::t20031::tmain(bool,int)"
|
||||
62
tests/t20031/t20031.cc
Normal file
62
tests/t20031/t20031.cc
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <functional>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20031 {
|
||||
int magic() { return 42; }
|
||||
int zero() { return 0; }
|
||||
int one() { return 1; }
|
||||
int execute(std::function<int()> f) { return f(); }
|
||||
|
||||
class A {
|
||||
public:
|
||||
A() { create(); }
|
||||
|
||||
A(int v) { a_ = v; }
|
||||
|
||||
A &operator=(const A &a)
|
||||
{
|
||||
set(a.a_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
A &operator+=(int a)
|
||||
{
|
||||
add(a);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int value() const { return a_; }
|
||||
|
||||
private:
|
||||
void create() { a_ = 0; }
|
||||
|
||||
void add(int a) { a_ += a; }
|
||||
void set(int a) { a_ = a; }
|
||||
|
||||
int a_;
|
||||
};
|
||||
|
||||
void tmain(int a)
|
||||
{
|
||||
A an_a{magic()};
|
||||
an_a += 1;
|
||||
}
|
||||
|
||||
int tmain(bool f, int a)
|
||||
{
|
||||
auto generate_zero = []() { return zero(); };
|
||||
auto an_a = A();
|
||||
auto an_b = A();
|
||||
|
||||
an_a += generate_zero();
|
||||
|
||||
// @todo #168
|
||||
an_a += execute([]() { return one(); });
|
||||
|
||||
an_b = an_a;
|
||||
|
||||
return an_b.value();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
69
tests/t20031/test_case.h
Normal file
69
tests/t20031/test_case.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* tests/t20031/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2023 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("t20031", "[test-case][sequence]")
|
||||
{
|
||||
auto [config, db] = load_config("t20031");
|
||||
|
||||
auto diagram = config.diagrams["t20031_sequence"];
|
||||
|
||||
REQUIRE(diagram->name == "t20031_sequence");
|
||||
|
||||
auto model = generate_sequence_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t20031_sequence");
|
||||
|
||||
{
|
||||
auto puml = generate_sequence_puml(diagram, *model);
|
||||
AliasMatcher _A(puml);
|
||||
|
||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain(int)"), _A("magic()"), ""));
|
||||
|
||||
REQUIRE_THAT(puml, !HasCall(_A("A"), _A("A"), "create()"));
|
||||
REQUIRE_THAT(
|
||||
puml, !HasCall(_A("tmain(int)"), _A("A"), "operator+=(int)"));
|
||||
REQUIRE_THAT(puml, !HasCall(_A("A"), _A("A"), "add(int)"));
|
||||
|
||||
REQUIRE_THAT(puml, !HasCall(_A("tmain(bool,int)"), _A("A"), "A()"));
|
||||
REQUIRE_THAT(
|
||||
puml, !HasCall(_A("tmain(bool,int)"), _A("A"), "operator+=(int)"));
|
||||
REQUIRE_THAT(puml, !HasCall(_A("A"), _A("A"), "add(int)"));
|
||||
REQUIRE_THAT(puml,
|
||||
!HasCall(_A("tmain(bool,int)"), _A("A"), "operator=(const A &)"));
|
||||
REQUIRE_THAT(puml, !HasCall(_A("A"), _A("A"), "set(int)"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("tmain(bool,int)"), _A("A"), "value()"));
|
||||
REQUIRE_THAT(puml,
|
||||
!HasCall(_A("tmain(bool,int)::(lambda "
|
||||
"../../tests/t20031/t20031.cc:47:26)"),
|
||||
_A("zero()"), ""));
|
||||
|
||||
save_puml(
|
||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
|
||||
{
|
||||
auto j = generate_sequence_json(diagram, *model);
|
||||
|
||||
using namespace json;
|
||||
|
||||
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
|
||||
}
|
||||
}
|
||||
@@ -344,6 +344,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20028/test_case.h"
|
||||
#include "t20029/test_case.h"
|
||||
#include "t20030/test_case.h"
|
||||
#include "t20031/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
@@ -289,6 +289,9 @@ test_cases:
|
||||
- name: t20030
|
||||
title: Constructor and operator call test case
|
||||
description:
|
||||
- name: t20031
|
||||
title: Callee type sequence diagram filter test case
|
||||
description:
|
||||
Package diagrams:
|
||||
- name: t30001
|
||||
title: Basic package diagram test case
|
||||
|
||||
@@ -88,3 +88,9 @@ diagrams:
|
||||
include:
|
||||
dependants:
|
||||
- r: 'A|B'
|
||||
callee_type_include_test:
|
||||
type: sequence
|
||||
include:
|
||||
callee_types:
|
||||
- function
|
||||
- function_template
|
||||
@@ -26,8 +26,8 @@
|
||||
#include "common/model/diagram_filter.h"
|
||||
#include "common/model/source_file.h"
|
||||
#include "config/config.h"
|
||||
|
||||
#include "include_diagram/model/diagram.h"
|
||||
#include "sequence_diagram/model/diagram.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
@@ -773,6 +773,57 @@ TEST_CASE("Test dependants regexp filter", "[unit-test]")
|
||||
CHECK(!filter.should_include(*diagram.find<class_>("C1")));
|
||||
}
|
||||
|
||||
TEST_CASE("Test callee_types filter", "[unit-test]")
|
||||
{
|
||||
using clanguml::common::to_id;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::sequence_diagram::model::class_;
|
||||
using clanguml::sequence_diagram::model::function;
|
||||
using clanguml::sequence_diagram::model::function_template;
|
||||
using clanguml::sequence_diagram::model::method;
|
||||
using clanguml::sequence_diagram::model::participant;
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["callee_type_include_test"];
|
||||
clanguml::sequence_diagram::model::diagram diagram;
|
||||
|
||||
std::unique_ptr<participant> p;
|
||||
|
||||
p = std::make_unique<function>(config.using_namespace());
|
||||
p->set_name("A");
|
||||
p->set_id(to_id("A"s));
|
||||
diagram.add_participant(std::move(p));
|
||||
|
||||
p = std::make_unique<function_template>(config.using_namespace());
|
||||
p->set_name("A1");
|
||||
p->set_id(to_id("A1"s));
|
||||
diagram.add_participant(std::move(p));
|
||||
|
||||
p = std::make_unique<class_>(config.using_namespace());
|
||||
p->set_name("C1");
|
||||
p->set_id(to_id("C1"s));
|
||||
diagram.add_participant(std::move(p));
|
||||
|
||||
p = std::make_unique<method>(config.using_namespace());
|
||||
p->set_name("M1");
|
||||
p->set_id(to_id("M1"s));
|
||||
dynamic_cast<method *>(p.get())->set_class_id(to_id("C1"s));
|
||||
diagram.add_participant(std::move(p));
|
||||
|
||||
diagram.set_complete(true);
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(
|
||||
filter.should_include(*diagram.get_participant<function>(to_id("A"s))));
|
||||
CHECK(filter.should_include(
|
||||
*diagram.get_participant<function_template>(to_id("A1"s))));
|
||||
CHECK(!filter.should_include(
|
||||
*diagram.get_participant<participant>(to_id("M1"s))));
|
||||
}
|
||||
|
||||
///
|
||||
/// Main test function
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user