Added option to enable rendering return types in sequence diagrams (fixes #93)
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
* [Grouping free functions by file](#grouping-free-functions-by-file)
|
||||
* [Lambda expressions in sequence diagrams](#lambda-expressions-in-sequence-diagrams)
|
||||
* [Customizing participants order](#customizing-participants-order)
|
||||
* [Generating return types](#generating-return-types)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
@@ -247,3 +248,18 @@ diagrams:
|
||||
- clanguml::t20029::encode_b64(std::string &&)
|
||||
```
|
||||
|
||||
## Generating return types
|
||||
By default, return messages do not contain the return type information from
|
||||
the function or method. Instead, if the result is void there is no return
|
||||
arrow from the activity representing the function body.
|
||||
|
||||
It is however possible to enable rendering of return types, by adding the
|
||||
following configuration option:
|
||||
|
||||
```yaml
|
||||
generate_return_types: true
|
||||
```
|
||||
|
||||
This option only affects the `plantuml` generation, in `json` generator
|
||||
`return_type` property is always present in the message nodes.
|
||||
|
||||
|
||||
@@ -166,7 +166,8 @@ void inheritable_diagram_options::inherit(
|
||||
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);
|
||||
parent.combine_free_functions_into_file_participants);
|
||||
generate_return_types.override(parent.generate_return_types);
|
||||
debug_mode.override(parent.debug_mode);
|
||||
generate_metadata.override(parent.generate_metadata);
|
||||
}
|
||||
|
||||
@@ -445,6 +445,7 @@ struct inheritable_diagram_options {
|
||||
"comment_parser", comment_parser_t::plain};
|
||||
option<bool> combine_free_functions_into_file_participants{
|
||||
"combine_free_functions_into_file_participants", false};
|
||||
option<bool> generate_return_types{"generate_return_types", false};
|
||||
option<std::vector<std::string>> participants_order{"participants_order"};
|
||||
option<bool> debug_mode{"debug_mode", false};
|
||||
option<bool> generate_metadata{"generate_metadata", true};
|
||||
|
||||
@@ -513,6 +513,7 @@ template <> struct convert<git_config> {
|
||||
|
||||
template <typename T> bool decode_diagram(const Node &node, T &rhs)
|
||||
{
|
||||
// Decode options common for all diagrams
|
||||
get_option(node, rhs.glob);
|
||||
get_option(node, rhs.using_namespace);
|
||||
get_option(node, rhs.include);
|
||||
@@ -566,6 +567,7 @@ template <> struct convert<sequence_diagram> {
|
||||
|
||||
get_option(node, rhs.start_from);
|
||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||
get_option(node, rhs.generate_return_types);
|
||||
get_option(node, rhs.relative_to);
|
||||
get_option(node, rhs.participants_order);
|
||||
get_option(node, rhs.generate_method_arguments);
|
||||
@@ -752,6 +754,9 @@ template <> struct convert<config> {
|
||||
get_option(node, rhs.comment_parser);
|
||||
get_option(node, rhs.debug_mode);
|
||||
get_option(node, rhs.generate_metadata);
|
||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||
get_option(node, rhs.generate_return_types);
|
||||
|
||||
rhs.base_directory.set(node["__parent_path"].as<std::string>());
|
||||
get_option(node, rhs.relative_to);
|
||||
|
||||
|
||||
@@ -289,6 +289,7 @@ YAML::Emitter &operator<<(
|
||||
}
|
||||
out << c.comment_parser;
|
||||
out << c.combine_free_functions_into_file_participants;
|
||||
out << c.generate_return_types;
|
||||
out << c.participants_order;
|
||||
out << c.debug_mode;
|
||||
|
||||
|
||||
@@ -645,16 +645,15 @@ void generator::generate(std::ostream &ostr) const
|
||||
generate_activity(
|
||||
m_model.get_activity(start_from), visited_participants);
|
||||
|
||||
json_["sequences"].push_back(std::move(sequence));
|
||||
|
||||
block_statements_stack_.pop_back();
|
||||
|
||||
if (from.value().type_name() == "method" ||
|
||||
m_config.combine_free_functions_into_file_participants()) {
|
||||
|
||||
// TODO
|
||||
// sequence["return_type"] = from.value().id()
|
||||
sequence["return_type"] = from.value().return_type();
|
||||
}
|
||||
|
||||
json_["sequences"].push_back(std::move(sequence));
|
||||
}
|
||||
else {
|
||||
// TODO: Add support for other sequence start location types
|
||||
|
||||
@@ -128,7 +128,13 @@ void generator::generate_return(const message &m, std::ostream &ostr) const
|
||||
|
||||
ostr << to_alias << " "
|
||||
<< common::generators::plantuml::to_plantuml(message_t::kReturn)
|
||||
<< " " << from_alias << '\n';
|
||||
<< " " << from_alias;
|
||||
|
||||
if (m_config.generate_return_types()) {
|
||||
ostr << " : //" << m.return_type() << "//";
|
||||
}
|
||||
|
||||
ostr << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +457,12 @@ void generator::generate(std::ostream &ostr) const
|
||||
|
||||
if (!from.value().is_void()) {
|
||||
ostr << "[<--"
|
||||
<< " " << from_alias << std::endl;
|
||||
<< " " << from_alias;
|
||||
|
||||
if (m_config.generate_return_types())
|
||||
ostr << " : //" << from.value().return_type() << "//";
|
||||
|
||||
ostr << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -312,6 +312,16 @@ void diagram::finalize()
|
||||
assert(block_nest_level >= 0);
|
||||
}
|
||||
else {
|
||||
if (m.type() == message_t::kCall) {
|
||||
// Set the message return type based on the callee return
|
||||
// type
|
||||
auto to_participant =
|
||||
get_participant<sequence_diagram::model::function>(
|
||||
m.to());
|
||||
if (to_participant.has_value()) {
|
||||
m.set_return_type(to_participant.value().return_type());
|
||||
}
|
||||
}
|
||||
block_message_stack.back().push_back(m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,10 @@ bool function::is_operator() const { return is_operator_; }
|
||||
|
||||
void function::is_operator(bool o) { is_operator_ = o; }
|
||||
|
||||
void function::return_type(const std::string &rt) { return_type_ = rt; }
|
||||
|
||||
const std::string &function::return_type() const { return return_type_; }
|
||||
|
||||
void function::add_parameter(const std::string &a) { parameters_.push_back(a); }
|
||||
|
||||
const std::vector<std::string> &function::parameters() const
|
||||
|
||||
@@ -291,6 +291,20 @@ struct function : public participant {
|
||||
*/
|
||||
void is_operator(bool o);
|
||||
|
||||
/**
|
||||
* @brief Set functions return type
|
||||
*
|
||||
* @param rt Return type
|
||||
*/
|
||||
void return_type(const std::string &rt);
|
||||
|
||||
/**
|
||||
* @brief Get function return type
|
||||
*
|
||||
* @return Return type
|
||||
*/
|
||||
const std::string &return_type() const;
|
||||
|
||||
/**
|
||||
* @brief Add a function parameter
|
||||
*
|
||||
@@ -313,6 +327,7 @@ private:
|
||||
bool is_void_{false};
|
||||
bool is_static_{false};
|
||||
bool is_operator_{false};
|
||||
std::string return_type_;
|
||||
std::vector<std::string> parameters_;
|
||||
};
|
||||
|
||||
|
||||
@@ -972,13 +972,6 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This crashes on LLVM <= 12, for now just return empty type
|
||||
//
|
||||
// const auto &return_type =
|
||||
// function_call_expr->getCallReturnType(current_ast_context);
|
||||
// m.return_type = return_type.getAsString();
|
||||
|
||||
if (m.from() > 0 && m.to() > 0) {
|
||||
if (diagram().sequences().find(m.from()) ==
|
||||
diagram().sequences().end()) {
|
||||
@@ -1550,6 +1543,10 @@ translation_unit_visitor::build_function_template(
|
||||
|
||||
process_template_parameters(declaration, *function_template_model_ptr);
|
||||
|
||||
function_template_model_ptr->return_type(
|
||||
common::to_string(declaration.getAsFunction()->getReturnType(),
|
||||
declaration.getASTContext()));
|
||||
|
||||
for (const auto *param : declaration.getTemplatedDecl()->parameters()) {
|
||||
function_template_model_ptr->add_parameter(
|
||||
simplify_system_template(common::to_string(
|
||||
@@ -1612,6 +1609,9 @@ std::unique_ptr<model::function> translation_unit_visitor::build_function_model(
|
||||
ns.pop_back();
|
||||
function_model_ptr->set_namespace(ns);
|
||||
|
||||
function_model_ptr->return_type(common::to_string(
|
||||
declaration.getReturnType(), declaration.getASTContext()));
|
||||
|
||||
for (const auto *param : declaration.parameters()) {
|
||||
function_model_ptr->add_parameter(
|
||||
simplify_system_template(common::to_string(
|
||||
@@ -2337,6 +2337,9 @@ translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration)
|
||||
"::" + declaration->getNameAsString());
|
||||
method_model_ptr->is_static(declaration->isStatic());
|
||||
|
||||
method_model_ptr->return_type(common::to_string(
|
||||
declaration->getReturnType(), declaration->getASTContext()));
|
||||
|
||||
for (const auto *param : declaration->parameters()) {
|
||||
method_model_ptr->add_parameter(config().using_namespace().relative(
|
||||
simplify_system_template(common::to_string(
|
||||
|
||||
15
tests/t20032/.clang-uml
Normal file
15
tests/t20032/.clang-uml
Normal file
@@ -0,0 +1,15 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t20032_sequence:
|
||||
type: sequence
|
||||
glob:
|
||||
- ../../tests/t20032/t20032.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20032
|
||||
using_namespace:
|
||||
- clanguml::t20032
|
||||
generate_return_types: true
|
||||
start_from:
|
||||
- function: "clanguml::t20032::tmain(int,char **)"
|
||||
27
tests/t20032/t20032.cc
Normal file
27
tests/t20032/t20032.cc
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace clanguml {
|
||||
namespace t20032 {
|
||||
|
||||
struct A {
|
||||
int a1(int i) { return i; }
|
||||
double a2(double d) { return d; }
|
||||
const char *a3(const char *s) { return s; }
|
||||
};
|
||||
|
||||
struct B {
|
||||
int b(int i) { return a.a1(i); }
|
||||
double b(double d) { return a.a2(d); }
|
||||
const char *b(const char *s) { return a.a3(s); }
|
||||
|
||||
A a;
|
||||
};
|
||||
|
||||
void tmain(int argc, char **argv)
|
||||
{
|
||||
B b;
|
||||
|
||||
b.b(1);
|
||||
b.b(2.0);
|
||||
b.b("three");
|
||||
}
|
||||
}
|
||||
}
|
||||
79
tests/t20032/test_case.h
Normal file
79
tests/t20032/test_case.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* tests/t20032/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("t20032", "[test-case][sequence]")
|
||||
{
|
||||
auto [config, db] = load_config("t20032");
|
||||
|
||||
auto diagram = config.diagrams["t20032_sequence"];
|
||||
|
||||
REQUIRE(diagram->name == "t20032_sequence");
|
||||
|
||||
auto model = generate_sequence_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t20032_sequence");
|
||||
|
||||
REQUIRE(model->name() == "t20032_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("tmain(int,char **)"), _A("B"), "b(int)"));
|
||||
REQUIRE_THAT(
|
||||
puml, HasResponse(_A("tmain(int,char **)"), _A("B"), "int"));
|
||||
|
||||
REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a1(int)"));
|
||||
REQUIRE_THAT(puml, HasResponse(_A("B"), _A("A"), "int"));
|
||||
|
||||
REQUIRE_THAT(
|
||||
puml, HasCall(_A("tmain(int,char **)"), _A("B"), "b(double)"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a2(double)"));
|
||||
REQUIRE_THAT(puml, HasResponse(_A("B"), _A("A"), "double"));
|
||||
|
||||
REQUIRE_THAT(puml,
|
||||
HasCall(_A("tmain(int,char **)"), _A("B"), "b(const char *)"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a3(const char *)"));
|
||||
REQUIRE_THAT(puml, HasResponse(_A("B"), _A("A"), "const char *"));
|
||||
|
||||
save_puml(
|
||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
|
||||
{
|
||||
auto j = generate_sequence_json(diagram, *model);
|
||||
|
||||
using namespace json;
|
||||
|
||||
std::vector<int> messages = {
|
||||
FindMessage(j, "tmain(int,char **)", "B", "b(int)", "int"),
|
||||
FindMessage(j, "B", "A", "a1(int)", "int"),
|
||||
FindMessage(j, "tmain(int,char **)", "B", "b(double)"),
|
||||
FindMessage(j, "B", "A", "a2(double)"),
|
||||
FindMessage(j, "tmain(int,char **)", "B", "b(const char *)"),
|
||||
FindMessage(j, "B", "A", "a3(const char *)")};
|
||||
|
||||
REQUIRE(std::is_sorted(messages.begin(), messages.end()));
|
||||
|
||||
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
|
||||
}
|
||||
}
|
||||
@@ -345,6 +345,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20029/test_case.h"
|
||||
#include "t20030/test_case.h"
|
||||
#include "t20031/test_case.h"
|
||||
#include "t20032/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
@@ -134,12 +134,15 @@ struct HasCallWithResultMatcher : ContainsMatcher {
|
||||
|
||||
template <typename T> class HasCallMatcher : public Catch::MatcherBase<T> {
|
||||
T m_from, m_to, m_message;
|
||||
bool m_is_response;
|
||||
std::string call_pattern, response_pattern;
|
||||
|
||||
public:
|
||||
HasCallMatcher(T from, T to, T message)
|
||||
HasCallMatcher(T from, T to, T message, bool is_response = false)
|
||||
: m_from(from)
|
||||
, m_to{to}
|
||||
, m_message{message}
|
||||
, m_is_response{is_response}
|
||||
{
|
||||
util::replace_all(m_message, "(", "\\(");
|
||||
util::replace_all(m_message, ")", "\\)");
|
||||
@@ -147,15 +150,22 @@ public:
|
||||
util::replace_all(m_message, "[", "\\[");
|
||||
util::replace_all(m_message, "]", "\\]");
|
||||
util::replace_all(m_message, "+", "\\+");
|
||||
|
||||
call_pattern = fmt::format("{} -> {} "
|
||||
"(\\[\\[.*\\]\\] )?: {}",
|
||||
m_from, m_to, m_message);
|
||||
|
||||
response_pattern =
|
||||
fmt::format("{} --> {} : //{}//", m_from, m_to, m_message);
|
||||
}
|
||||
|
||||
bool match(T const &in) const override
|
||||
{
|
||||
std::istringstream fin(in);
|
||||
std::string line;
|
||||
std::regex r{fmt::format("{} -> {} "
|
||||
"(\\[\\[.*\\]\\] )?: {}",
|
||||
m_from, m_to, m_message)};
|
||||
|
||||
std::regex r{m_is_response ? response_pattern : call_pattern};
|
||||
|
||||
while (std::getline(fin, line)) {
|
||||
std::smatch base_match;
|
||||
std::regex_search(in, base_match, r);
|
||||
@@ -182,6 +192,13 @@ auto HasCall(std::string const &from, std::string const &to,
|
||||
return HasCallMatcher(from, to, message);
|
||||
}
|
||||
|
||||
auto HasResponse(std::string const &from, std::string const &to,
|
||||
std::string const &message,
|
||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||
{
|
||||
return HasCallMatcher(to, from, message, true);
|
||||
}
|
||||
|
||||
auto HasCallInControlCondition(std::string const &from, std::string const &to,
|
||||
std::string const &message,
|
||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||
@@ -955,7 +972,8 @@ bool IsFileParticipant(const nlohmann::json &j, const std::string &name)
|
||||
|
||||
namespace detail {
|
||||
int find_message_nested(const nlohmann::json &j, const std::string &from,
|
||||
const std::string &to, const std::string &msg, const nlohmann::json &from_p,
|
||||
const std::string &to, const std::string &msg,
|
||||
std::optional<std::string> return_type, const nlohmann::json &from_p,
|
||||
const nlohmann::json &to_p, int &count)
|
||||
{
|
||||
const auto &messages = j["messages"];
|
||||
@@ -965,23 +983,25 @@ int find_message_nested(const nlohmann::json &j, const std::string &from,
|
||||
for (const auto &m : messages) {
|
||||
if (m.contains("branches")) {
|
||||
for (const auto &b : m["branches"]) {
|
||||
auto nested_res =
|
||||
find_message_nested(b, from, to, msg, from_p, to_p, count);
|
||||
auto nested_res = find_message_nested(
|
||||
b, from, to, msg, return_type, from_p, to_p, count);
|
||||
|
||||
if (nested_res >= 0)
|
||||
return nested_res;
|
||||
}
|
||||
}
|
||||
else if (m.contains("messages")) {
|
||||
auto nested_res =
|
||||
find_message_nested(m, from, to, msg, from_p, to_p, count);
|
||||
auto nested_res = find_message_nested(
|
||||
m, from, to, msg, return_type, from_p, to_p, count);
|
||||
|
||||
if (nested_res >= 0)
|
||||
return nested_res;
|
||||
}
|
||||
else {
|
||||
if ((m["from"]["participant_id"] == from_p["id"]) &&
|
||||
(m["to"]["participant_id"] == to_p["id"]) && (m["name"] == msg))
|
||||
(m["to"]["participant_id"] == to_p["id"]) &&
|
||||
(m["name"] == msg) &&
|
||||
(!return_type || m["return_type"] == *return_type))
|
||||
return count;
|
||||
|
||||
count++;
|
||||
@@ -992,7 +1012,8 @@ int find_message_nested(const nlohmann::json &j, const std::string &from,
|
||||
}
|
||||
|
||||
int find_message_impl(const nlohmann::json &j, const std::string &from,
|
||||
const std::string &to, const std::string &msg)
|
||||
const std::string &to, const std::string &msg,
|
||||
std::optional<std::string> return_type)
|
||||
{
|
||||
|
||||
auto from_p = get_participant(j, from);
|
||||
@@ -1004,7 +1025,7 @@ int find_message_impl(const nlohmann::json &j, const std::string &from,
|
||||
int count{0};
|
||||
|
||||
auto res = detail::find_message_nested(
|
||||
sequence_0, from, to, msg, *from_p, *to_p, count);
|
||||
sequence_0, from, to, msg, return_type, *from_p, *to_p, count);
|
||||
|
||||
if (res >= 0)
|
||||
return res;
|
||||
@@ -1018,14 +1039,15 @@ int find_message_impl(const nlohmann::json &j, const std::string &from,
|
||||
int FindMessage(const nlohmann::json &j, const File &from, const File &to,
|
||||
const std::string &msg)
|
||||
{
|
||||
return detail::find_message_impl(j, from.file, to.file, msg);
|
||||
return detail::find_message_impl(j, from.file, to.file, msg, {});
|
||||
}
|
||||
|
||||
int FindMessage(const nlohmann::json &j, const std::string &from,
|
||||
const std::string &to, const std::string &msg)
|
||||
const std::string &to, const std::string &msg,
|
||||
std::optional<std::string> return_type = {})
|
||||
{
|
||||
return detail::find_message_impl(
|
||||
j, expand_name(j, from), expand_name(j, to), msg);
|
||||
j, expand_name(j, from), expand_name(j, to), msg, return_type);
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
|
||||
@@ -292,6 +292,9 @@ test_cases:
|
||||
- name: t20031
|
||||
title: Callee type sequence diagram filter test case
|
||||
description:
|
||||
- name: t20032
|
||||
title: Return type generation option sequence diagram test case
|
||||
description:
|
||||
Package diagrams:
|
||||
- name: t30001
|
||||
title: Basic package diagram test case
|
||||
|
||||
@@ -323,3 +323,20 @@ TEST_CASE("Test config diagram_templates", "[unit-test]")
|
||||
REQUIRE(cfg.diagram_templates()["main_sequence_tmpl"].type ==
|
||||
clanguml::common::model::diagram_t::kSequence);
|
||||
}
|
||||
|
||||
TEST_CASE("Test config sequence inherited", "[unit-test]")
|
||||
{
|
||||
auto cfg = clanguml::config::load(
|
||||
"./test_config_data/sequence_inheritable_options.yml");
|
||||
|
||||
CHECK(cfg.diagrams.size() == 2);
|
||||
auto &def = *cfg.diagrams["sequence_default"];
|
||||
CHECK(def.type() == clanguml::common::model::diagram_t::kSequence);
|
||||
CHECK(def.combine_free_functions_into_file_participants() == true);
|
||||
CHECK(def.generate_return_types() == true);
|
||||
|
||||
def = *cfg.diagrams["sequence_default2"];
|
||||
CHECK(def.type() == clanguml::common::model::diagram_t::kSequence);
|
||||
CHECK(def.combine_free_functions_into_file_participants() == false);
|
||||
CHECK(def.generate_return_types() == false);
|
||||
}
|
||||
15
tests/test_config_data/sequence_inheritable_options.yml
Normal file
15
tests/test_config_data/sequence_inheritable_options.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
compilation_database_dir: debug
|
||||
output_directory: output
|
||||
combine_free_functions_into_file_participants: true
|
||||
generate_return_types: true
|
||||
diagrams:
|
||||
sequence_default:
|
||||
type: sequence
|
||||
glob:
|
||||
- src/*.cc
|
||||
sequence_default2:
|
||||
type: sequence
|
||||
glob:
|
||||
- src/*.cc
|
||||
combine_free_functions_into_file_participants: false
|
||||
generate_return_types: false
|
||||
Reference in New Issue
Block a user