diff --git a/docs/sequence_diagrams.md b/docs/sequence_diagrams.md index 057957c3..cb1222e9 100644 --- a/docs/sequence_diagrams.md +++ b/docs/sequence_diagrams.md @@ -45,71 +45,106 @@ Consider the following diagram: ![extension](test_cases/t20029_sequence.svg) -`clang-uml` generated sequence diagrams are not strictly speaking conforming to the UML specification. In order to -make them more useful for documenting modern C++ code, the following assumptions were made: - * Free functions are included in the sequence diagrams as standalone participants (in fact `clang-uml` can be used - to generate sequence diagrams from plain old C code). Functions can also be aggregated into file participants, - based on their place of declaration - * Call expressions in conditional expressions in block statements (e.g. `if` or `while`) are rendered inside the - PlantUML `alt` or `loop` blocks but wrapped in `[`, `]` brackets - * Lambda expressions are generated as standalone participants, whose name comprises the parent context where they - are defined and the exact source code location +`clang-uml` generated sequence diagrams are not strictly speaking conforming to +the UML specification. In order to make them more useful for documenting modern +C++ code, the following assumptions were made: + * Free functions are included in the sequence diagrams as standalone + participants (in fact `clang-uml` can be used to generate sequence diagrams + from plain old C code). Functions can also be aggregated into file + participants, based on their place of declaration + * Call expressions in conditional expressions in block statements (e.g. `if` + or `while`) are rendered inside the PlantUML `alt` or `loop` blocks but + wrapped in `[`, `]` brackets + * Lambda expressions are generated as standalone participants, whose name + comprises the parent context where they are defined and the exact source code + location + +## Specifying diagram location constraints +Sequence diagrams require a specification of location constraints in order to +determine, which call chains should be included in the diagram. Currently, +there are 3 types of constraints: +* `from` - will include all message call chains which start at the + locations specified in this constraint (this was previously named + `start_from`) +* `to` - will include all message call chains which end at the specified + locations +* `from_to` - will include all call chains which start and end at the specified + location constraints + +Currently, the constraints can be a method or a free function, both specified +using the full signature of the function, e.g. -## Specifying diagram entry point -Sequence diagrams require an entry point for the diagram in order to determine, at which point in the code the sequence -diagram should start. Currently, the entry point can only be a method or a free function, both specified using `start_from` -configuration property, for instance: ```yaml - start_from: + from: - function: "main(int,const char**)" ``` or ```yaml -start_from: - - function: "clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *)" + to: + - function: "clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *)" ``` -The entrypoints must be fully qualified and they must match exactly the string -representation of given function or method in the `clang-uml` model. +The locations must be fully qualified, and they must match exactly the string +representation of a given function or method in the `clang-uml` model. -To find the exact function signature run `clang-uml` as follows: +In case of the `from_to` constraint, it is necessary to provide both `from` +and `to` locations as follows: +```yaml + from_to: + - [function: "clanguml::t20034::D::d2()", + function: "clanguml::t20034::A::a2()"] +``` + +To find the exact function signature which can be used as a `from` location, +run `clang-uml` as follows: ```bash -clang-uml --print-start-from -n main_sequence | grep main +clang-uml --print-from -n main_sequence | grep main ``` -Command line flag `--print-start-from` will print on stdout all functions -and methods available in the diagram model, and each line of this output -can be directly used as a value of `start_from` option in the config file. +or to get all possible `to` locations, run: + +```bash +clang-uml --print-to -n main_sequence | grep main +``` + +Command line flags `--print-from` and `--print-to` will print on stdout all +functions and methods available in the diagram model, and each line of this +output can be directly used as a value of `start_from`, `from_to` or `to` +properties in the config file. Since that list can be quite large, it's best to filter the output to limit the number of lines to a subset of possible candidates. ## Grouping free functions by file -By default, `clang-uml` will generate a new participant for each call to a free function (not method), which can lead -to a very large number of participants in the diagram. If it's an issue, an option can be provided in the diagram +By default, `clang-uml` will generate a new participant for each call to a free +function (not method), which can lead to a very large number of participants in +the diagram. If it's an issue, an option can be provided in the diagram definition: ```yaml combine_free_functions_into_file_participants: true ``` -which will aggregate free functions per source file where they were declared thus minimizing the -diagram size. An example of such diagram is presented below: +which will aggregate free functions per source file where they were declared +thus minimizing the diagram size. An example of such diagram is presented below: ![extension](test_cases/t20017_sequence.svg) ## Lambda expressions in sequence diagrams -Lambda expressions in sequence diagrams are... tricky. There is currently tentative support, which follows the -following rules: - * If lambda expression is called within the scope of the diagram, the calls from the lambda will be placed - at the lambda invocation and not declaration - * If lambda expression is passed to some function or method, which is outside the scope of the diagram - (e.g. used in `std::transform` call) the call will not be generated - * If the lambda is passed as template parameter in instantiation it will not be generated +Lambda expressions in sequence diagrams are... tricky. There is currently +tentative support, which follows the following rules: + * If lambda expression is called within the scope of the diagram, the calls + from the lambda will be placed at the lambda invocation and not declaration + * If lambda expression is passed to some function or method, which is outside + the scope of the diagram (e.g. used in `std::transform` call) the call will + not be generated + * If the lambda is passed as template parameter in instantiation it will not + be generated -Another issue is the naming of lambda participants. Currently, each lambda is rendered in the diagram as a separate -class whose name is composed of the lambda location in the code (the only unique way of identifying lambdas I was able +Another issue is the naming of lambda participants. Currently, each lambda is +rendered in the diagram as a separate class whose name is composed of the lambda +location in the code (the only unique way of identifying lambdas I was able to find). For example the following code: ```cpp @@ -216,10 +251,11 @@ results in the following diagram: ![extension](test_cases/t20012_sequence.svg) ## Customizing participants order -The default participant order in the sequence diagram can be suboptimal in the sense that consecutive calls -can go right, then left, then right again depending on the specific call chain in the code. It is however -possible to override this order in the diagram definition using `participants_order` property, -for instance like this test case: +The default participant order in the sequence diagram can be suboptimal in the +sense that consecutive calls can go right, then left, then right again +depending on the specific call chain in the code. It is however +possible to override this order in the diagram definition using +`participants_order` property, for instance like this test case: ```yaml compilation_database_dir: .. diff --git a/src/config/config.h b/src/config/config.h index 4e25d8ac..8a634c39 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -541,7 +541,8 @@ struct sequence_diagram : public diagram { common::model::diagram_t type() const override; - option> start_from{"start_from"}; + option> from{ + option_with_alt_names_tag{}, "from", {"start_from"}}; option>> from_to{"from_to"}; option> to{"to"}; }; diff --git a/src/config/option.h b/src/config/option.h index ab6df843..35495d7f 100644 --- a/src/config/option.h +++ b/src/config/option.h @@ -32,6 +32,8 @@ enum class option_inherit_mode { kAppend /*!< Append to list options */ }; +struct option_with_alt_names_tag { }; + /** * @brief Generic configuration option type * @@ -62,6 +64,15 @@ template struct option { , inheritance_mode{im} { } + option(option_with_alt_names_tag /*unused*/, std::string name_, + std::vector alternate_names_, + option_inherit_mode im = option_inherit_mode::kOverride) + : name{std::move(name_)} + , alternate_names{std::move(alternate_names_)} + , value{} + , inheritance_mode{im} + { + } /** * @brief Set the option value @@ -107,6 +118,9 @@ template struct option { /*! Option name, it is also the YAML key in the configuration file */ std::string name; + /*! Alternate option names */ + std::vector alternate_names; + /*! Option value */ T value; diff --git a/src/config/schema.h b/src/config/schema.h index a4d77dc6..58577e74 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -194,7 +194,8 @@ types: generate_return_types: !optional bool generate_condition_statements: !optional bool participants_order: !optional [string] - start_from: !optional [source_location_t] + start_from: !optional [source_location_t] # deprecated -> 'from' + from: !optional [source_location_t] from_to: !optional [[source_location_t]] to: !optional [source_location_t] package_diagram_t: diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index 0ba49e8f..1c0a9f45 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -65,6 +65,12 @@ void get_option(const Node &node, clanguml::config::option &option) { if (node[option.name]) option.set(node[option.name].template as()); + + for (const auto &alt_name : option.alternate_names) + if (node[alt_name]) { + option.set(node[alt_name].template as()); + break; + } } template <> @@ -573,7 +579,7 @@ template <> struct convert { if (!decode_diagram(node, rhs)) return false; - get_option(node, rhs.start_from); + get_option(node, rhs.from); get_option(node, rhs.from_to); get_option(node, rhs.to); get_option(node, rhs.combine_free_functions_into_file_participants); diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index 893f0747..1fd80ec0 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -347,7 +347,7 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const sequence_diagram &c) { out << YAML::BeginMap; out << YAML::Key << "type" << YAML::Value << c.type(); - out << c.start_from; + out << c.from; out << c.from_to; out << c.to; out << dynamic_cast(c); diff --git a/src/sequence_diagram/generators/json/sequence_diagram_generator.cc b/src/sequence_diagram/generators/json/sequence_diagram_generator.cc index 2fb34ff6..e0fdf0c1 100644 --- a/src/sequence_diagram/generators/json/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/json/sequence_diagram_generator.cc @@ -691,7 +691,7 @@ void generator::generate_diagram(nlohmann::json &parent) const json_["sequences"].push_back(std::move(sequence)); } - for (const auto &sf : config().start_from()) { + for (const auto &sf : config().from()) { if (sf.location_type == location_t::function) { common::model::diagram_element::id_t start_from{0}; std::string start_from_str; diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index bcbed236..8ba07087 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc @@ -488,7 +488,7 @@ void generator::generate_diagram(std::ostream &ostr) const } } - for (const auto &sf : config().start_from()) { + for (const auto &sf : config().from()) { if (sf.location_type == location_t::function) { common::model::diagram_element::id_t start_from{0}; for (const auto &[k, v] : model().sequences()) { diff --git a/tests/t20001/.clang-uml b/tests/t20001/.clang-uml index 508c5de1..8cf49fdc 100644 --- a/tests/t20001/.clang-uml +++ b/tests/t20001/.clang-uml @@ -13,7 +13,7 @@ diagrams: - clanguml::t20001::detail using_namespace: - clanguml::t20001 - start_from: + from: - function: "clanguml::t20001::tmain()" plantuml: before: diff --git a/tests/t20002/.clang-uml b/tests/t20002/.clang-uml index 29d74b71..8dfb0825 100644 --- a/tests/t20002/.clang-uml +++ b/tests/t20002/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20002 using_namespace: - clanguml::t20002 - start_from: + from: - function: "clanguml::t20002::m1()" diff --git a/tests/t20003/.clang-uml b/tests/t20003/.clang-uml index 9aae6b72..aee8374b 100644 --- a/tests/t20003/.clang-uml +++ b/tests/t20003/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20003 using_namespace: - clanguml::t20003 - start_from: + from: - function: "clanguml::t20003::m1(T)" diff --git a/tests/t20004/.clang-uml b/tests/t20004/.clang-uml index de348abf..0e1618e9 100644 --- a/tests/t20004/.clang-uml +++ b/tests/t20004/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20004 using_namespace: - clanguml::t20004 - start_from: + from: - function: "clanguml::t20004::main()" \ No newline at end of file diff --git a/tests/t20005/.clang-uml b/tests/t20005/.clang-uml index 7fbd937a..5b298510 100644 --- a/tests/t20005/.clang-uml +++ b/tests/t20005/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20005 using_namespace: - clanguml::t20005 - start_from: + from: - function: "clanguml::t20005::C::c(T)" \ No newline at end of file diff --git a/tests/t20006/.clang-uml b/tests/t20006/.clang-uml index dedfb8f7..47360f32 100644 --- a/tests/t20006/.clang-uml +++ b/tests/t20006/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20006 using_namespace: - clanguml::t20006 - start_from: + from: - function: "clanguml::t20006::tmain()" \ No newline at end of file diff --git a/tests/t20007/.clang-uml b/tests/t20007/.clang-uml index 40a6a2f7..e0324ed3 100644 --- a/tests/t20007/.clang-uml +++ b/tests/t20007/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20007 using_namespace: - clanguml::t20007 - start_from: + from: - function: "clanguml::t20007::tmain()" \ No newline at end of file diff --git a/tests/t20008/.clang-uml b/tests/t20008/.clang-uml index f266305c..5aa1c79d 100644 --- a/tests/t20008/.clang-uml +++ b/tests/t20008/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20008 using_namespace: - clanguml::t20008 - start_from: + from: - function: "clanguml::t20008::tmain()" \ No newline at end of file diff --git a/tests/t20009/.clang-uml b/tests/t20009/.clang-uml index 6f8b97ea..3bc94e92 100644 --- a/tests/t20009/.clang-uml +++ b/tests/t20009/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20009 using_namespace: - clanguml::t20009 - start_from: + from: - function: "clanguml::t20009::tmain()" \ No newline at end of file diff --git a/tests/t20010/.clang-uml b/tests/t20010/.clang-uml index 55f2dd81..0af678b5 100644 --- a/tests/t20010/.clang-uml +++ b/tests/t20010/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20010 using_namespace: - clanguml::t20010 - start_from: + from: - function: "clanguml::t20010::tmain()" \ No newline at end of file diff --git a/tests/t20011/.clang-uml b/tests/t20011/.clang-uml index 9a2a29be..8b1586e5 100644 --- a/tests/t20011/.clang-uml +++ b/tests/t20011/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20011 using_namespace: - clanguml::t20011 - start_from: + from: - function: "clanguml::t20011::tmain()" \ No newline at end of file diff --git a/tests/t20012/.clang-uml b/tests/t20012/.clang-uml index bd288238..a4d1882c 100644 --- a/tests/t20012/.clang-uml +++ b/tests/t20012/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20012 using_namespace: - clanguml::t20012 - start_from: + from: - function: "clanguml::t20012::tmain()" \ No newline at end of file diff --git a/tests/t20013/.clang-uml b/tests/t20013/.clang-uml index e37d69ee..650e6e87 100644 --- a/tests/t20013/.clang-uml +++ b/tests/t20013/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20013 using_namespace: - clanguml::t20013 - start_from: + from: - function: "clanguml::t20013::tmain(int,char **)" \ No newline at end of file diff --git a/tests/t20014/.clang-uml b/tests/t20014/.clang-uml index 9de444e7..464dba5d 100644 --- a/tests/t20014/.clang-uml +++ b/tests/t20014/.clang-uml @@ -13,5 +13,5 @@ diagrams: - clanguml::t20014 using_namespace: - clanguml::t20014 - start_from: + from: - function: "clanguml::t20014::tmain()" \ No newline at end of file diff --git a/tests/t20015/.clang-uml b/tests/t20015/.clang-uml index 231e9543..d858dba3 100644 --- a/tests/t20015/.clang-uml +++ b/tests/t20015/.clang-uml @@ -13,5 +13,5 @@ diagrams: - clanguml::t20015::detail using_namespace: - clanguml::t20015 - start_from: + from: - function: "clanguml::t20015::tmain()" \ No newline at end of file diff --git a/tests/t20016/.clang-uml b/tests/t20016/.clang-uml index 1cf1f97f..ef2b94d6 100644 --- a/tests/t20016/.clang-uml +++ b/tests/t20016/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20016 using_namespace: - clanguml::t20016 - start_from: + from: - function: "clanguml::t20016::tmain()" \ No newline at end of file diff --git a/tests/t20017/.clang-uml b/tests/t20017/.clang-uml index 0d9947d3..23fc3539 100644 --- a/tests/t20017/.clang-uml +++ b/tests/t20017/.clang-uml @@ -14,5 +14,5 @@ diagrams: - . using_namespace: - clanguml::t20017 - start_from: + from: - function: "clanguml::t20017::tmain()" \ No newline at end of file diff --git a/tests/t20018/.clang-uml b/tests/t20018/.clang-uml index a3f634da..0babfeb5 100644 --- a/tests/t20018/.clang-uml +++ b/tests/t20018/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20018 using_namespace: - clanguml::t20018 - start_from: + from: - function: "clanguml::t20018::tmain()" \ No newline at end of file diff --git a/tests/t20019/.clang-uml b/tests/t20019/.clang-uml index 77631b8c..3804e9cc 100644 --- a/tests/t20019/.clang-uml +++ b/tests/t20019/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20019 using_namespace: - clanguml::t20019 - start_from: + from: - function: "clanguml::t20019::tmain()" \ No newline at end of file diff --git a/tests/t20020/.clang-uml b/tests/t20020/.clang-uml index 9f58d838..a7679e65 100644 --- a/tests/t20020/.clang-uml +++ b/tests/t20020/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20020 using_namespace: - clanguml::t20020 - start_from: + from: - function: "clanguml::t20020::tmain()" \ No newline at end of file diff --git a/tests/t20021/.clang-uml b/tests/t20021/.clang-uml index 4c80b1bf..938b7a7e 100644 --- a/tests/t20021/.clang-uml +++ b/tests/t20021/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20021 using_namespace: - clanguml::t20021 - start_from: + from: - function: "clanguml::t20021::tmain()" \ No newline at end of file diff --git a/tests/t20022/.clang-uml b/tests/t20022/.clang-uml index 0fa68fa9..08f05219 100644 --- a/tests/t20022/.clang-uml +++ b/tests/t20022/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20022 using_namespace: - clanguml::t20022 - start_from: + from: - function: "clanguml::t20022::tmain()" \ No newline at end of file diff --git a/tests/t20024/.clang-uml b/tests/t20024/.clang-uml index fde4258a..1420bf1f 100644 --- a/tests/t20024/.clang-uml +++ b/tests/t20024/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20024 using_namespace: - clanguml::t20024 - start_from: + from: - function: "clanguml::t20024::tmain()" \ No newline at end of file diff --git a/tests/t20025/.clang-uml b/tests/t20025/.clang-uml index 1598fcf3..14e8ed22 100644 --- a/tests/t20025/.clang-uml +++ b/tests/t20025/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20025 using_namespace: - clanguml::t20025 - start_from: + from: - function: "clanguml::t20025::tmain()" \ No newline at end of file diff --git a/tests/t20026/.clang-uml b/tests/t20026/.clang-uml index 083b47ec..fc5e972e 100644 --- a/tests/t20026/.clang-uml +++ b/tests/t20026/.clang-uml @@ -10,5 +10,5 @@ diagrams: - clanguml::t20026 using_namespace: - clanguml::t20026 - start_from: + from: - function: "clanguml::t20026::tmain()" \ No newline at end of file diff --git a/tests/t20027/.clang-uml b/tests/t20027/.clang-uml index a107bba2..10737381 100644 --- a/tests/t20027/.clang-uml +++ b/tests/t20027/.clang-uml @@ -12,5 +12,5 @@ diagrams: - public using_namespace: - clanguml::t20027 - start_from: + from: - function: "clanguml::t20027::tmain()" \ No newline at end of file diff --git a/tests/t20028/.clang-uml b/tests/t20028/.clang-uml index c20559fc..24d8ee97 100644 --- a/tests/t20028/.clang-uml +++ b/tests/t20028/.clang-uml @@ -13,5 +13,5 @@ diagrams: - clanguml::t20028::detail using_namespace: - clanguml::t20028 - start_from: + from: - function: "clanguml::t20028::tmain()" \ No newline at end of file diff --git a/tests/t20029/.clang-uml b/tests/t20029/.clang-uml index 8308648f..ca543ed2 100644 --- a/tests/t20029/.clang-uml +++ b/tests/t20029/.clang-uml @@ -13,7 +13,7 @@ diagrams: - private using_namespace: - clanguml::t20029 - start_from: + from: - function: clanguml::t20029::tmain() participants_order: - clanguml::t20029::tmain() diff --git a/tests/t20030/.clang-uml b/tests/t20030/.clang-uml index a8e29397..a4d9f94a 100644 --- a/tests/t20030/.clang-uml +++ b/tests/t20030/.clang-uml @@ -10,6 +10,6 @@ diagrams: - clanguml::t20030 using_namespace: - clanguml::t20030 - start_from: + from: - function: "clanguml::t20030::tmain(int)" - function: "clanguml::t20030::tmain(bool,int)" \ No newline at end of file diff --git a/tests/t20031/.clang-uml b/tests/t20031/.clang-uml index fc1a5533..da8b7038 100644 --- a/tests/t20031/.clang-uml +++ b/tests/t20031/.clang-uml @@ -15,6 +15,6 @@ diagrams: - lambda using_namespace: - clanguml::t20031 - start_from: + from: - function: "clanguml::t20031::tmain(int)" - function: "clanguml::t20031::tmain(bool,int)" \ No newline at end of file diff --git a/tests/t20032/.clang-uml b/tests/t20032/.clang-uml index 48ccf1d2..5c88e05a 100644 --- a/tests/t20032/.clang-uml +++ b/tests/t20032/.clang-uml @@ -11,5 +11,5 @@ diagrams: using_namespace: - clanguml::t20032 generate_return_types: true - start_from: + from: - function: "clanguml::t20032::tmain(int,char **)" \ No newline at end of file diff --git a/tests/t20033/.clang-uml b/tests/t20033/.clang-uml index 390e2d3a..f081bdbc 100644 --- a/tests/t20033/.clang-uml +++ b/tests/t20033/.clang-uml @@ -11,5 +11,5 @@ diagrams: using_namespace: - clanguml::t20033 generate_condition_statements: true - start_from: + from: - function: "clanguml::t20033::tmain()" \ No newline at end of file