mirror of
https://github.com/bkryza/clang-uml.git
synced 2025-05-17 11:12:20 +08:00
Added sequence diagram model cleanup step to remove empty block statements
This commit is contained in:
parent
9ada158828
commit
ed88fcd39d
@ -359,6 +359,8 @@ std::unique_ptr<DiagramModel> generate(const common::compilation_database &db,
|
||||
|
||||
diagram->set_complete(true);
|
||||
|
||||
diagram->finalize();
|
||||
|
||||
return diagram;
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,8 @@ void diagram::set_complete(bool complete) { complete_ = complete; }
|
||||
|
||||
bool diagram::complete() const { return complete_; }
|
||||
|
||||
void diagram::finalize() { }
|
||||
|
||||
bool diagram::should_include(const element &e) const
|
||||
{
|
||||
if (filter_.get() == nullptr)
|
||||
|
@ -124,12 +124,24 @@ public:
|
||||
void set_complete(bool complete);
|
||||
|
||||
/**
|
||||
* Whether the diagram is complete.
|
||||
* @brief Whether the diagram is complete.
|
||||
*
|
||||
* This flag is set to true, when all translation units for this diagram
|
||||
* have been visited.
|
||||
*
|
||||
* @return Diagram completion status.
|
||||
*/
|
||||
bool complete() const;
|
||||
|
||||
/**
|
||||
* @brief Once the diagram is complete, run any final processing.
|
||||
*
|
||||
* This method should be overriden by specific diagram models to do some
|
||||
* final tasks like cleaning up the model (e.g. some filters only work
|
||||
* on completed diagrams).
|
||||
*/
|
||||
virtual void finalize();
|
||||
|
||||
// TODO: refactor to a template method
|
||||
bool should_include(const element &e) const;
|
||||
bool should_include(const namespace_ &ns) const;
|
||||
|
@ -376,6 +376,7 @@ 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::function;
|
||||
using sequence_diagram::model::method;
|
||||
using sequence_diagram::model::participant;
|
||||
|
||||
@ -391,6 +392,10 @@ tvl::value_t callee_filter::match(
|
||||
|
||||
tvl::value_t res = tvl::any_of(
|
||||
callee_types_.begin(), callee_types_.end(), [&p, is_lambda](auto ct) {
|
||||
auto is_function = [](const participant *p) {
|
||||
return dynamic_cast<const function *>(p) != nullptr;
|
||||
};
|
||||
|
||||
switch (ct) {
|
||||
case config::callee_type::method:
|
||||
return p.type_name() == "method";
|
||||
@ -401,12 +406,12 @@ tvl::value_t callee_filter::match(
|
||||
return p.type_name() == "method" &&
|
||||
((method &)p).is_assignment();
|
||||
case config::callee_type::operator_:
|
||||
return p.type_name() == "method" && ((method &)p).is_operator();
|
||||
return is_function(&p) && ((function &)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();
|
||||
return is_function(&p) && ((function &)p).is_static();
|
||||
case config::callee_type::function:
|
||||
return p.type_name() == "function";
|
||||
case config::callee_type::function_template:
|
||||
|
@ -252,16 +252,6 @@ nlohmann::json &generator::current_block_statement() const
|
||||
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());
|
||||
|
@ -139,14 +139,6 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
||||
if (m.type() == message_t::kCall) {
|
||||
const auto &to =
|
||||
m_model.get_participant<model::participant>(m.to());
|
||||
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());
|
||||
|
||||
|
@ -248,6 +248,81 @@ void diagram::fold_or_end_block_statement(message &&m,
|
||||
current_messages.emplace_back(std::move(m));
|
||||
}
|
||||
}
|
||||
|
||||
void diagram::finalize()
|
||||
{
|
||||
// Apply diagram filters and remove any empty block statements
|
||||
using common::model::message_t;
|
||||
|
||||
// First in each sequence (activity) filter out any remaining
|
||||
// uninteresting calls
|
||||
for (auto &[id, act] : sequences_) {
|
||||
util::erase_if(act.messages(), [this](auto &m) {
|
||||
if (m.type() != message_t::kCall)
|
||||
return false;
|
||||
|
||||
const auto &to = get_participant<model::participant>(m.to());
|
||||
if (!to || to.value().skip())
|
||||
return true;
|
||||
|
||||
if (!should_include(to.value())) {
|
||||
LOG_DBG("Excluding call from [{}] to {} [{}]", m.from(),
|
||||
to.value().full_name(false), m.to());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Now remove any empty block statements, e.g. if/endif
|
||||
for (auto &[id, act] : sequences_) {
|
||||
int64_t block_nest_level{0};
|
||||
std::vector<std::vector<message>> block_message_stack;
|
||||
// Add first stack level - this level will contain the filtered
|
||||
// message sequence
|
||||
block_message_stack.emplace_back();
|
||||
|
||||
// First create a recursive stack from the messages and
|
||||
// message blocks (e.g. if statements)
|
||||
for (auto &m : act.messages()) {
|
||||
if (is_begin_block_message(m.type())) {
|
||||
block_nest_level++;
|
||||
block_message_stack.push_back({m});
|
||||
}
|
||||
else if (is_end_block_message(m.type())) {
|
||||
block_nest_level--;
|
||||
|
||||
block_message_stack.back().push_back(m);
|
||||
|
||||
// Check the last stack for any calls, if yes, collapse it
|
||||
// on the previous stack
|
||||
if (std::count_if(block_message_stack.back().begin(),
|
||||
block_message_stack.back().end(), [](auto &m) {
|
||||
return m.type() == message_t::kCall;
|
||||
}) > 0) {
|
||||
std::copy(block_message_stack.back().begin(),
|
||||
block_message_stack.back().end(),
|
||||
std::back_inserter(
|
||||
block_message_stack.at(block_nest_level)));
|
||||
}
|
||||
|
||||
block_message_stack.pop_back();
|
||||
|
||||
assert(block_nest_level >= 0);
|
||||
}
|
||||
else {
|
||||
block_message_stack.back().push_back(m);
|
||||
}
|
||||
}
|
||||
|
||||
act.messages().clear();
|
||||
|
||||
for (auto &m : block_message_stack[0]) {
|
||||
act.add_message(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace clanguml::sequence_diagram::model
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
@ -212,6 +212,15 @@ public:
|
||||
*/
|
||||
bool should_include(const sequence_diagram::model::participant &p) const;
|
||||
|
||||
/**
|
||||
* @brief Once the diagram is complete, run any final processing.
|
||||
*
|
||||
* This method should be overriden by specific diagram models to do some
|
||||
* final tasks like cleaning up the model (e.g. some filters only work
|
||||
* on completed diagrams).
|
||||
*/
|
||||
void finalize() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* This method checks the last messages in sequence (current_messages),
|
||||
@ -233,6 +242,27 @@ private:
|
||||
common::model::message_t statement_begin,
|
||||
std::vector<message> ¤t_messages) const;
|
||||
|
||||
bool is_begin_block_message(common::model::message_t mt)
|
||||
{
|
||||
using common::model::message_t;
|
||||
static std::set<message_t> block_begin_types{message_t::kIf,
|
||||
message_t::kWhile, message_t::kDo, message_t::kFor, message_t::kTry,
|
||||
message_t::kSwitch, message_t::kConditional};
|
||||
|
||||
return block_begin_types.count(mt) > 0;
|
||||
};
|
||||
|
||||
bool is_end_block_message(common::model::message_t mt)
|
||||
{
|
||||
using common::model::message_t;
|
||||
static std::set<message_t> block_end_types{message_t::kIfEnd,
|
||||
message_t::kWhileEnd, message_t::kDoEnd, message_t::kForEnd,
|
||||
message_t::kTryEnd, message_t::kSwitchEnd,
|
||||
message_t::kConditionalEnd};
|
||||
|
||||
return block_end_types.count(mt) > 0;
|
||||
};
|
||||
|
||||
bool started_{false};
|
||||
|
||||
std::map<common::model::diagram_element::id_t, activity> sequences_;
|
||||
|
@ -143,6 +143,10 @@ bool function::is_static() const { return is_static_; }
|
||||
|
||||
void function::is_static(bool s) { is_static_ = s; }
|
||||
|
||||
bool function::is_operator() const { return is_operator_; }
|
||||
|
||||
void function::is_operator(bool o) { is_operator_ = o; }
|
||||
|
||||
void function::add_parameter(const std::string &a) { parameters_.push_back(a); }
|
||||
|
||||
const std::vector<std::string> &function::parameters() const
|
||||
@ -176,10 +180,6 @@ 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; }
|
||||
|
@ -277,6 +277,20 @@ struct function : public participant {
|
||||
*/
|
||||
void is_static(bool s);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Add a function parameter
|
||||
*
|
||||
@ -298,6 +312,7 @@ private:
|
||||
bool is_const_{false};
|
||||
bool is_void_{false};
|
||||
bool is_static_{false};
|
||||
bool is_operator_{false};
|
||||
std::vector<std::string> parameters_;
|
||||
};
|
||||
|
||||
@ -429,20 +444,6 @@ struct method : public function {
|
||||
*/
|
||||
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_;
|
||||
@ -450,7 +451,6 @@ private:
|
||||
bool is_constructor_{false};
|
||||
bool is_defaulted_{false};
|
||||
bool is_assignment_{false};
|
||||
bool is_operator_{false};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -330,6 +330,8 @@ bool translation_unit_visitor::VisitFunctionDecl(
|
||||
|
||||
function_model_ptr->is_void(declaration->getReturnType()->isVoidType());
|
||||
|
||||
function_model_ptr->is_operator(declaration->isOverloadedOperator());
|
||||
|
||||
context().update(declaration);
|
||||
|
||||
context().set_caller_id(function_model_ptr->id());
|
||||
@ -373,6 +375,9 @@ bool translation_unit_visitor::VisitFunctionTemplateDecl(
|
||||
function_template_model->set_id(
|
||||
common::to_id(function_template_model->full_name(false)));
|
||||
|
||||
function_template_model->is_operator(
|
||||
declaration->getAsFunction()->isOverloadedOperator());
|
||||
|
||||
context().update(declaration);
|
||||
|
||||
context().set_caller_id(function_template_model->id());
|
||||
@ -2260,6 +2265,7 @@ void translation_unit_visitor::finalize()
|
||||
{
|
||||
std::set<common::model::diagram_element::id_t> active_participants_unique;
|
||||
|
||||
// Change all active participants AST local ids to diagram global ids
|
||||
for (auto id : diagram().active_participants()) {
|
||||
if (local_ast_id_map_.find(id) != local_ast_id_map_.end()) {
|
||||
active_participants_unique.emplace(local_ast_id_map_.at(id));
|
||||
@ -2271,6 +2277,7 @@ void translation_unit_visitor::finalize()
|
||||
|
||||
diagram().active_participants() = std::move(active_participants_unique);
|
||||
|
||||
// Change all message callees AST local ids to diagram global ids
|
||||
for (auto &[id, activity] : diagram().sequences()) {
|
||||
for (auto &m : activity.messages()) {
|
||||
if (local_ast_id_map_.find(m.to()) != local_ast_id_map_.end()) {
|
||||
|
@ -252,7 +252,7 @@ public:
|
||||
int64_t local_id) const;
|
||||
|
||||
/**
|
||||
* @brief Finalize diagram model
|
||||
* @brief Finalize diagram model for this translation unit
|
||||
*/
|
||||
void finalize();
|
||||
|
||||
|
@ -10,4 +10,4 @@ plantuml:
|
||||
before:
|
||||
- 'title clang-uml clanguml::class_diagram::generators::plantuml::generator sequence diagram'
|
||||
start_from:
|
||||
- function: "clanguml::class_diagram::generators::plantuml::generator::generate(std::ostream &)"
|
||||
- function: "clanguml::class_diagram::generators::plantuml::generator::generate(std::ostream &) const"
|
@ -1,6 +1,7 @@
|
||||
type: sequence
|
||||
combine_free_functions_into_file_participants: true
|
||||
generate_method_arguments: none
|
||||
debug_mode: true
|
||||
glob:
|
||||
- src/cli/cli_handler.cc
|
||||
- src/config/config.cc
|
||||
@ -14,6 +15,8 @@ exclude:
|
||||
- r: "clanguml::config::option.*"
|
||||
paths:
|
||||
- src/util/util.h
|
||||
callee_types:
|
||||
- operator
|
||||
using_namespace:
|
||||
- clanguml
|
||||
start_from:
|
||||
|
@ -7,12 +7,8 @@ using_namespace:
|
||||
- clanguml
|
||||
include:
|
||||
namespaces:
|
||||
- clang
|
||||
- clanguml::common::generators
|
||||
exclude:
|
||||
namespaces:
|
||||
- clanguml::model::tvl
|
||||
- clanguml::decorators
|
||||
paths:
|
||||
- src/common/model/source_location.h
|
||||
start_from:
|
||||
|
Loading…
x
Reference in New Issue
Block a user