Added combine_free_functions_into_file_participants sequence diagram option
This commit is contained in:
@@ -101,6 +101,8 @@ void inheritable_diagram_options::inherit(
|
||||
base_directory.override(parent.base_directory);
|
||||
relative_to.override(parent.relative_to);
|
||||
comment_parser.override(parent.comment_parser);
|
||||
combine_free_functions_into_file_participants.override(
|
||||
combine_free_functions_into_file_participants);
|
||||
}
|
||||
|
||||
std::string inheritable_diagram_options::simplify_template_type(
|
||||
@@ -589,6 +591,8 @@ template <> struct convert<sequence_diagram> {
|
||||
return false;
|
||||
|
||||
get_option(node, rhs.start_from);
|
||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||
get_option(node, rhs.relative_to);
|
||||
|
||||
rhs.initialize_type_aliases();
|
||||
|
||||
|
||||
@@ -142,6 +142,8 @@ struct inheritable_diagram_options {
|
||||
option<type_aliases_t> type_aliases{"type_aliases"};
|
||||
option<comment_parser_t> comment_parser{
|
||||
"comment_parser", comment_parser_t::plain};
|
||||
option<bool> combine_free_functions_into_file_participants{
|
||||
"combine_free_functions_into_file_participants", false};
|
||||
|
||||
void inherit(const inheritable_diagram_options &parent);
|
||||
|
||||
|
||||
@@ -62,10 +62,25 @@ void generator::generate_call(const message &m, std::ostream &ostr) const
|
||||
message = dynamic_cast<const model::function &>(to.value())
|
||||
.message_name(model::function::message_render_mode::full);
|
||||
}
|
||||
else if (m_config.combine_free_functions_into_file_participants()) {
|
||||
if (to.value().type_name() == "function") {
|
||||
message =
|
||||
dynamic_cast<const model::function &>(to.value())
|
||||
.message_name(model::function::message_render_mode::full);
|
||||
}
|
||||
else if (to.value().type_name() == "function_template") {
|
||||
message =
|
||||
dynamic_cast<const model::function_template &>(to.value())
|
||||
.message_name(model::function::message_render_mode::full);
|
||||
}
|
||||
}
|
||||
|
||||
ostr << from.value().alias() << " "
|
||||
const std::string from_alias = generate_alias(from.value());
|
||||
const std::string to_alias = generate_alias(to.value());
|
||||
|
||||
ostr << from_alias << " "
|
||||
<< common::generators::plantuml::to_plantuml(message_t::kCall) << " "
|
||||
<< to.value().alias() << " : " << message << std::endl;
|
||||
<< to_alias << " : " << message << std::endl;
|
||||
|
||||
LOG_DBG("Generated call '{}' from {} [{}] to {} [{}]", message, from,
|
||||
m.from, to, m.to);
|
||||
@@ -79,9 +94,13 @@ void generator::generate_return(const message &m, std::ostream &ostr) const
|
||||
const auto &from = m_model.get_participant<model::participant>(m.from);
|
||||
const auto &to = m_model.get_participant<model::participant>(m.to);
|
||||
|
||||
ostr << to.value().alias() << " "
|
||||
const std::string from_alias = generate_alias(from.value());
|
||||
|
||||
const std::string to_alias = generate_alias(to.value());
|
||||
|
||||
ostr << to_alias << " "
|
||||
<< common::generators::plantuml::to_plantuml(message_t::kReturn)
|
||||
<< " " << from.value().alias() << '\n';
|
||||
<< " " << from_alias << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +118,9 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
||||
|
||||
generate_call(m, ostr);
|
||||
|
||||
ostr << "activate " << to.value().alias() << std::endl;
|
||||
std::string to_alias = generate_alias(to.value());
|
||||
|
||||
ostr << "activate " << to_alias << std::endl;
|
||||
|
||||
if (m_model.sequences.find(m.to) != m_model.sequences.end()) {
|
||||
if (std::find(visited.begin(), visited.end(), m.to) ==
|
||||
@@ -117,7 +138,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
||||
|
||||
visited.pop_back();
|
||||
|
||||
ostr << "deactivate " << to.value().alias() << std::endl;
|
||||
ostr << "deactivate " << to_alias << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +173,35 @@ void generator::generate_participant(std::ostream &ostr, common::id_t id) const
|
||||
|
||||
generated_participants_.emplace(class_id);
|
||||
}
|
||||
else if ((participant.type_name() == "function" ||
|
||||
participant.type_name() == "function_template") &&
|
||||
m_config.combine_free_functions_into_file_participants()) {
|
||||
// Create a single participant for all functions declared in a
|
||||
// single file
|
||||
const auto &file_path =
|
||||
m_model.get_participant<model::function>(participant_id)
|
||||
.value()
|
||||
.file();
|
||||
|
||||
assert(!file_path.empty());
|
||||
|
||||
const auto file_id = common::to_id(file_path);
|
||||
|
||||
if (is_participant_generated(file_id))
|
||||
return;
|
||||
|
||||
[[maybe_unused]] const auto &relative_to =
|
||||
std::filesystem::canonical(m_config.relative_to());
|
||||
|
||||
auto participant_name = std::filesystem::relative(
|
||||
std::filesystem::path{file_path}, relative_to)
|
||||
.string();
|
||||
|
||||
ostr << "participant \"" << render_name(participant_name)
|
||||
<< "\" as " << fmt::format("C_{:022}", file_id) << '\n';
|
||||
|
||||
generated_participants_.emplace(file_id);
|
||||
}
|
||||
else {
|
||||
ostr << "participant \""
|
||||
<< m_config.using_namespace().relative(
|
||||
@@ -192,6 +242,8 @@ void generator::generate(std::ostream &ostr) const
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Use this to break out of recurrent loops
|
||||
std::vector<common::model::diagram_element::id_t>
|
||||
visited_participants;
|
||||
|
||||
@@ -199,20 +251,22 @@ void generator::generate(std::ostream &ostr) const
|
||||
m_model.get_participant<model::participant>(start_from);
|
||||
|
||||
if (!from.has_value()) {
|
||||
LOG_WARN(
|
||||
"Failed to find participant {} for start_from condition",
|
||||
LOG_WARN("Failed to find participant {} for start_from "
|
||||
"condition",
|
||||
sf.location);
|
||||
continue;
|
||||
}
|
||||
|
||||
generate_participant(ostr, start_from);
|
||||
|
||||
ostr << "activate " << from.value().alias() << std::endl;
|
||||
std::string from_alias = generate_alias(from.value());
|
||||
|
||||
ostr << "activate " << from_alias << std::endl;
|
||||
|
||||
generate_activity(
|
||||
m_model.sequences[start_from], ostr, visited_participants);
|
||||
|
||||
ostr << "deactivate " << from.value().alias() << std::endl;
|
||||
ostr << "deactivate " << from_alias << std::endl;
|
||||
}
|
||||
else {
|
||||
// TODO: Add support for other sequence start location types
|
||||
@@ -225,4 +279,17 @@ void generator::generate(std::ostream &ostr) const
|
||||
ostr << "@enduml" << std::endl;
|
||||
}
|
||||
|
||||
std::string generator::generate_alias(
|
||||
const model::participant &participant) const
|
||||
{
|
||||
if ((participant.type_name() == "function" ||
|
||||
participant.type_name() == "function_template") &&
|
||||
m_config.combine_free_functions_into_file_participants()) {
|
||||
const auto file_id = common::to_id(participant.file());
|
||||
|
||||
return fmt::format("C_{:022}", file_id);
|
||||
}
|
||||
|
||||
return participant.alias();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ private:
|
||||
std::string render_name(std::string name) const;
|
||||
|
||||
mutable std::set<common::id_t> generated_participants_;
|
||||
std::string generate_alias(const model::participant &participant) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -246,6 +246,21 @@ struct function_template : public function, public template_trait {
|
||||
std::string full_name(bool relative = true) const override;
|
||||
|
||||
std::string full_name_no_ns() const override;
|
||||
|
||||
std::string message_name(message_render_mode mode) const override
|
||||
{
|
||||
std::ostringstream s;
|
||||
render_template_params(s, using_namespace(), true);
|
||||
std::string template_params = s.str();
|
||||
|
||||
if (mode == message_render_mode::no_arguments) {
|
||||
return fmt::format("{}{}(){}", name(), template_params,
|
||||
is_const() ? " const" : "");
|
||||
}
|
||||
|
||||
return fmt::format("{}{}({}){}", name(), template_params,
|
||||
fmt::join(parameters(), ","), is_const() ? " const" : "");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -376,6 +376,8 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
|
||||
|
||||
set_unique_id(f->getID(), f_ptr->id());
|
||||
|
||||
set_source_location(*f, *f_ptr);
|
||||
|
||||
// TODO: Handle overloaded functions with different arguments
|
||||
diagram().add_participant(std::move(f_ptr));
|
||||
}
|
||||
@@ -401,6 +403,8 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
|
||||
|
||||
set_unique_id(f->getID(), f_ptr->id());
|
||||
|
||||
set_source_location(*f, *f_ptr);
|
||||
|
||||
// TODO: Handle overloaded functions with different arguments
|
||||
diagram().add_participant(std::move(f_ptr));
|
||||
}
|
||||
@@ -437,6 +441,8 @@ bool translation_unit_visitor::VisitFunctionTemplateDecl(
|
||||
|
||||
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
|
||||
|
||||
set_source_location(*function_template, *f_ptr);
|
||||
|
||||
context().update(function_template);
|
||||
|
||||
context().set_caller_id(f_ptr->id());
|
||||
@@ -784,8 +790,9 @@ bool translation_unit_visitor::process_class_template_method_call_expression(
|
||||
get_unique_id(template_declaration->getID()).value());
|
||||
}
|
||||
else {
|
||||
LOG_WARN("Cannot generate call due to unresolvable "
|
||||
"CXXDependentScopeMemberExpr");
|
||||
LOG_DBG("Skipping call due to unresolvable "
|
||||
"CXXDependentScopeMemberExpr at {}",
|
||||
expr->getBeginLoc().printToString(source_manager()));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
16
tests/t20017/.clang-uml
Normal file
16
tests/t20017/.clang-uml
Normal file
@@ -0,0 +1,16 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t20017_sequence:
|
||||
type: sequence
|
||||
combine_free_functions_into_file_participants: true
|
||||
relative_to: ../../tests/t20017
|
||||
glob:
|
||||
- ../../tests/t20017/t20017.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20017
|
||||
using_namespace:
|
||||
- clanguml::t20017
|
||||
start_from:
|
||||
- function: "clanguml::t20017::tmain()"
|
||||
9
tests/t20017/include/t20017_a.h
Normal file
9
tests/t20017/include/t20017_a.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20017 {
|
||||
int a1(int x, int y) { return x + y; }
|
||||
int a2(int x, int y) { return x - y; }
|
||||
int a3(int x, int y) { return x * y; }
|
||||
}
|
||||
}
|
||||
9
tests/t20017/include/t20017_b.h
Normal file
9
tests/t20017/include/t20017_b.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20017 {
|
||||
int b1(int x, int y);
|
||||
|
||||
template <typename T> T b2(T x, T y) { return x / y; }
|
||||
}
|
||||
}
|
||||
8
tests/t20017/t20017.cc
Normal file
8
tests/t20017/t20017.cc
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "include/t20017_a.h"
|
||||
#include "include/t20017_b.h"
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20017 {
|
||||
int tmain() { return b2(a1(a2(a3(1, 2), b1(3, 4)), 5), 6); }
|
||||
}
|
||||
}
|
||||
7
tests/t20017/t20017_b.cc
Normal file
7
tests/t20017/t20017_b.cc
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "include/t20017_b.h"
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20017 {
|
||||
int b1(int x, int y) { return x - y; }
|
||||
}
|
||||
}
|
||||
51
tests/t20017/test_case.h
Normal file
51
tests/t20017/test_case.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* tests/t20017/test_case.h
|
||||
*
|
||||
* 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("t20017", "[test-case][sequence]")
|
||||
{
|
||||
auto [config, db] = load_config("t20017");
|
||||
|
||||
auto diagram = config.diagrams["t20017_sequence"];
|
||||
|
||||
REQUIRE(diagram->name == "t20017_sequence");
|
||||
|
||||
auto model = generate_sequence_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t20017_sequence");
|
||||
|
||||
auto puml = generate_sequence_puml(diagram, *model);
|
||||
AliasMatcher _A(puml);
|
||||
|
||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
// Check if all calls exist
|
||||
REQUIRE_THAT(puml,
|
||||
HasCall(_A("t20017.cc"), _A("include/t20017_a.h"), "a1(int,int)"));
|
||||
REQUIRE_THAT(
|
||||
puml, HasCall(_A("t20017.cc"), _A("include/t20017_a.h"), "a2(int,int)"));
|
||||
REQUIRE_THAT(puml,
|
||||
HasCall(_A("t20017.cc"), _A("include/t20017_a.h"), "a3(int,int)"));
|
||||
REQUIRE_THAT(puml,
|
||||
HasCall(_A("t20017.cc"), _A("include/t20017_b.h"), "b1(int,int)"));
|
||||
REQUIRE_THAT(puml,
|
||||
HasCall(_A("t20017.cc"), _A("include/t20017_b.h"), "b2<int>(int,int)"));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
@@ -263,6 +263,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20014/test_case.h"
|
||||
#include "t20015/test_case.h"
|
||||
#include "t20016/test_case.h"
|
||||
#include "t20017/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
@@ -196,6 +196,9 @@ test_cases:
|
||||
- name: t20016
|
||||
title: Template method specialization sequence diagram test case
|
||||
description:
|
||||
- name: t20017
|
||||
title: Test case for combine_free_functions_into_file_participants option
|
||||
description:
|
||||
Package diagrams:
|
||||
- name: t30001
|
||||
title: Basic package diagram test case
|
||||
|
||||
Reference in New Issue
Block a user