diff --git a/.clang-uml b/.clang-uml new file mode 100644 index 00000000..192bfc80 --- /dev/null +++ b/.clang-uml @@ -0,0 +1,123 @@ +compilation_database_dir: debug +output_directory: docs/diagrams +diagrams: + main_package: + type: package + glob: + - src/**/*.h + - src/**/*.cc + include: + namespaces: + - clanguml + using_namespace: + - clanguml + plantuml: + before: + - 'title clang-uml namespaces' + config_class: + type: class + include_relations_also_as_members: false + glob: + - src/config/config.h + - src/config/config.cc + include: + namespaces: + - clanguml::config + using_namespace: + - clanguml::config + plantuml: + before: + - 'title clang-uml configuration model' + decorators_class: + type: class + include_relations_also_as_members: false + glob: + - src/decorators/decorators.h + - src/decorators/decorators.cc + include: + namespaces: + - clanguml::decorators + using_namespace: + - clanguml::decorators + plantuml: + before: + - 'title clang-uml decorators model' + common_model_class: + type: class + include_relations_also_as_members: false + glob: + - src/common/model/*.h + - src/common/model/*.cc + include: + namespaces: + - clanguml::common::model + using_namespace: + - clanguml::common::model + plantuml: + before: + - 'title clang-uml common diagram model' + class_model_class: + type: class + include_relations_also_as_members: false + glob: + - src/class_diagram/model/*.h + - src/class_diagram/model/*.cc + include: + namespaces: + - clanguml::class_diagram::model + using_namespace: + - clanguml::class_diagram::model + plantuml: + before: + - 'title clang-uml class diagram model' + sequence_model_class: + type: class + include_relations_also_as_members: false + glob: + - src/sequence_diagram/model/*.h + - src/sequence_diagram/model/*.cc + include: + namespaces: + - clanguml::sequence_diagram::model + using_namespace: + - clanguml::sequence_diagram::model + plantuml: + before: + - 'title clang-uml sequence diagram model' + package_model_class: + type: class + include_relations_also_as_members: false + glob: + - src/package_diagram/model/*.h + - src/package_diagram/model/*.cc + include: + namespaces: + - clanguml::package_diagram::model + using_namespace: + - clanguml::package_diagram::model + plantuml: + before: + - 'title clang-uml package diagram model' + diagram_model_class: + type: class + include_relations_also_as_members: false + glob: + - src/common/model/*.h + - src/common/model/*.cc + - src/class_diagram/model/*.h + - src/class_diagram/model/*.cc + - src/sequence_diagram/model/*.h + - src/sequence_diagram/model/*.cc + - src/package_diagram/model/*.h + - src/package_diagram/model/*.cc + include: + namespaces: + - clanguml::common::model + - clanguml::class_diagram::model + - clanguml::sequence_diagram::model + - clanguml::package_diagram::model + using_namespace: + - clanguml + plantuml: + before: + - 'title clang-uml diagram model' \ No newline at end of file diff --git a/.gitignore b/.gitignore index 077bb710..b474bd93 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,10 @@ bin/ /debug/ /release/ /.cache +docs/diagrams + + +# CLion + +.idea/ +cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1840e13d..47b8262c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ add_library(clang-umllib OBJECT ${SOURCES}) add_executable(clang-uml ${MAIN_SOURCE_FILE}) install(TARGETS clang-uml DESTINATION ${CLANG_UML_INSTALL_BIN_DIR}) target_link_libraries(clang-uml ${LIBCLANG_LIBRARIES} ${YAML_CPP_LIBRARIES} spdlog::spdlog cppast clang-umllib) +target_compile_features(clang-uml PRIVATE cxx_std_17) install( FILES diff --git a/Makefile b/Makefile index 516b97d8..3ed817cb 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile # -# Copyright (c) 2021 Bartek Kryza +# Copyright (c) 2021-2022 Bartek Kryza # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -52,6 +52,11 @@ test_plantuml: test document_test_cases: test_plantuml python3 util/generate_test_cases_docs.py +clanguml_diagrams: debug + mkdir -p docs/diagrams + debug/clang-uml + plantuml docs/diagrams/*.puml + .PHONY: submodules submodules: git submodule update --init --recursive diff --git a/README.md b/README.md index 5f206cbd..6885a898 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Main features supported so far include: * Namespace based content filtering * Sequence diagram generation * Generation of sequence diagram from one code location to another +* Package diagram generation + * Generation of package diagram based on C++ namespaces ## Installation @@ -87,8 +89,8 @@ diagrams: myproject_class: type: class glob: - - src/**.h - - src/**.cc + - src/*.h + - src/*.cc using_namespace: - myproject include: @@ -104,6 +106,18 @@ diagrams: See ![here](docs/configuration_file.md) for detailed configuration file reference guide. +### Examples +To see what `clang-uml` can do, checkout the test cases documentation [here](./docs/test_cases.md). + +In order to see diagrams for the `clang-uml` itself, based on its own [config](.clang-uml) run +the following: + +```bash +make clanguml_diagrams +``` + +and checkout the PNG diagrams in `docs/diagrams` folder. + ### Class diagrams #### Example @@ -257,6 +271,51 @@ generates the following diagram (via PlantUML): ![sequence_diagram_example](docs/test_cases/t20001_sequence.png) +### Package diagrams + +#### Example + +The following C++ code: + +```cpp +namespace clanguml { +namespace t30003 { + +namespace ns1 { +namespace ns2_v1_0_0 { +class A { +}; +} + +namespace [[deprecated]] ns2_v0_9_0 { +class A { +}; +} + +namespace { +class Anon final { +}; +} +} + +namespace [[deprecated]] ns3 { + +namespace ns1::ns2 { +class Anon : public t30003::ns1::ns2_v1_0_0::A { +}; +} + +class B : public ns1::ns2::Anon { +}; +} +} +} +``` + +generates the following diagram (via PlantUML): + +![package_diagram_example](docs/test_cases/t30003_package.png) + ### Test cases The build-in test cases used for unit testing of the `clang-uml`, can be browsed [here](./docs/test_cases.md). diff --git a/docs/test_cases.md b/docs/test_cases.md index a02ab059..1f0e4c0d 100644 --- a/docs/test_cases.md +++ b/docs/test_cases.md @@ -36,5 +36,12 @@ ## Sequence diagrams * [t20001](./test_cases/t20001.md) - Basic sequence diagram test case * [t20002](./test_cases/t20002.md) - Free function sequence diagram test case +## Package diagrams + * [t30001](./test_cases/t30001.md) - Basic package diagram test case + * [t30002](./test_cases/t30002.md) - Package dependency test case + * [t30003](./test_cases/t30003.md) - Package deprecated attribute test case + * [t30004](./test_cases/t30004.md) - PlantUML package decorators test case + * [t30005](./test_cases/t30005.md) - Package namespace alias test case + * [t30006](./test_cases/t30006.md) - Package split namespace test case ## Configuration diagrams * [t90000](./test_cases/t90000.md) - Basic config test diff --git a/docs/test_cases/t00002_class.png b/docs/test_cases/t00002_class.png index c7cabb63..dd2ad759 100644 Binary files a/docs/test_cases/t00002_class.png and b/docs/test_cases/t00002_class.png differ diff --git a/docs/test_cases/t00003_class.png b/docs/test_cases/t00003_class.png index b6c50039..dc414087 100644 Binary files a/docs/test_cases/t00003_class.png and b/docs/test_cases/t00003_class.png differ diff --git a/docs/test_cases/t00004_class.png b/docs/test_cases/t00004_class.png index 5d31b5c1..132275f7 100644 Binary files a/docs/test_cases/t00004_class.png and b/docs/test_cases/t00004_class.png differ diff --git a/docs/test_cases/t00005_class.png b/docs/test_cases/t00005_class.png index 86b95ac4..785beba3 100644 Binary files a/docs/test_cases/t00005_class.png and b/docs/test_cases/t00005_class.png differ diff --git a/docs/test_cases/t00006_class.png b/docs/test_cases/t00006_class.png index 23133f84..aab0c532 100644 Binary files a/docs/test_cases/t00006_class.png and b/docs/test_cases/t00006_class.png differ diff --git a/docs/test_cases/t00007_class.png b/docs/test_cases/t00007_class.png index a7081e0f..e59b437e 100644 Binary files a/docs/test_cases/t00007_class.png and b/docs/test_cases/t00007_class.png differ diff --git a/docs/test_cases/t00008_class.png b/docs/test_cases/t00008_class.png index c77d374e..3390a88d 100644 Binary files a/docs/test_cases/t00008_class.png and b/docs/test_cases/t00008_class.png differ diff --git a/docs/test_cases/t00009_class.png b/docs/test_cases/t00009_class.png index 3024da07..1a918d19 100644 Binary files a/docs/test_cases/t00009_class.png and b/docs/test_cases/t00009_class.png differ diff --git a/docs/test_cases/t00010_class.png b/docs/test_cases/t00010_class.png index 920fd9a5..d0891517 100644 Binary files a/docs/test_cases/t00010_class.png and b/docs/test_cases/t00010_class.png differ diff --git a/docs/test_cases/t00011_class.png b/docs/test_cases/t00011_class.png index 0e648478..13058526 100644 Binary files a/docs/test_cases/t00011_class.png and b/docs/test_cases/t00011_class.png differ diff --git a/docs/test_cases/t00012_class.png b/docs/test_cases/t00012_class.png index ad78b4ba..c4215544 100644 Binary files a/docs/test_cases/t00012_class.png and b/docs/test_cases/t00012_class.png differ diff --git a/docs/test_cases/t00013_class.png b/docs/test_cases/t00013_class.png index 6e24d861..0ba20b69 100644 Binary files a/docs/test_cases/t00013_class.png and b/docs/test_cases/t00013_class.png differ diff --git a/docs/test_cases/t00014_class.png b/docs/test_cases/t00014_class.png index fcf58d9e..f69b2167 100644 Binary files a/docs/test_cases/t00014_class.png and b/docs/test_cases/t00014_class.png differ diff --git a/docs/test_cases/t00015_class.png b/docs/test_cases/t00015_class.png index 254b39c9..b1752e0b 100644 Binary files a/docs/test_cases/t00015_class.png and b/docs/test_cases/t00015_class.png differ diff --git a/docs/test_cases/t00016_class.png b/docs/test_cases/t00016_class.png index 4b443082..d7effe54 100644 Binary files a/docs/test_cases/t00016_class.png and b/docs/test_cases/t00016_class.png differ diff --git a/docs/test_cases/t00017_class.png b/docs/test_cases/t00017_class.png index 521b89a4..cea9349a 100644 Binary files a/docs/test_cases/t00017_class.png and b/docs/test_cases/t00017_class.png differ diff --git a/docs/test_cases/t00018_class.png b/docs/test_cases/t00018_class.png index 1962786d..90783472 100644 Binary files a/docs/test_cases/t00018_class.png and b/docs/test_cases/t00018_class.png differ diff --git a/docs/test_cases/t00019_class.png b/docs/test_cases/t00019_class.png index 04cdc046..5ee2f31e 100644 Binary files a/docs/test_cases/t00019_class.png and b/docs/test_cases/t00019_class.png differ diff --git a/docs/test_cases/t00020_class.png b/docs/test_cases/t00020_class.png index acc44a80..6189d478 100644 Binary files a/docs/test_cases/t00020_class.png and b/docs/test_cases/t00020_class.png differ diff --git a/docs/test_cases/t00021_class.png b/docs/test_cases/t00021_class.png index 8beb9d59..59df8ab1 100644 Binary files a/docs/test_cases/t00021_class.png and b/docs/test_cases/t00021_class.png differ diff --git a/docs/test_cases/t00022_class.png b/docs/test_cases/t00022_class.png index 101b46c9..9e8810f4 100644 Binary files a/docs/test_cases/t00022_class.png and b/docs/test_cases/t00022_class.png differ diff --git a/docs/test_cases/t00023_class.png b/docs/test_cases/t00023_class.png index 3e7c4112..ed477c67 100644 Binary files a/docs/test_cases/t00023_class.png and b/docs/test_cases/t00023_class.png differ diff --git a/docs/test_cases/t00024_class.png b/docs/test_cases/t00024_class.png index f66f0bb9..5bc279cd 100644 Binary files a/docs/test_cases/t00024_class.png and b/docs/test_cases/t00024_class.png differ diff --git a/docs/test_cases/t00025_class.png b/docs/test_cases/t00025_class.png index e14192c1..e37053e1 100644 Binary files a/docs/test_cases/t00025_class.png and b/docs/test_cases/t00025_class.png differ diff --git a/docs/test_cases/t00026_class.png b/docs/test_cases/t00026_class.png index 34205bc5..1abc51be 100644 Binary files a/docs/test_cases/t00026_class.png and b/docs/test_cases/t00026_class.png differ diff --git a/docs/test_cases/t00027_class.png b/docs/test_cases/t00027_class.png index 0a68d68e..8515edf9 100644 Binary files a/docs/test_cases/t00027_class.png and b/docs/test_cases/t00027_class.png differ diff --git a/docs/test_cases/t00028_class.png b/docs/test_cases/t00028_class.png index 88458f63..b0fc2c7b 100644 Binary files a/docs/test_cases/t00028_class.png and b/docs/test_cases/t00028_class.png differ diff --git a/docs/test_cases/t00029_class.png b/docs/test_cases/t00029_class.png index 46c0fd08..8a186767 100644 Binary files a/docs/test_cases/t00029_class.png and b/docs/test_cases/t00029_class.png differ diff --git a/docs/test_cases/t00030_class.png b/docs/test_cases/t00030_class.png index c3eab966..6ab171f1 100644 Binary files a/docs/test_cases/t00030_class.png and b/docs/test_cases/t00030_class.png differ diff --git a/docs/test_cases/t00031_class.png b/docs/test_cases/t00031_class.png index 2ad5ff2d..5fa47576 100644 Binary files a/docs/test_cases/t00031_class.png and b/docs/test_cases/t00031_class.png differ diff --git a/docs/test_cases/t00032_class.png b/docs/test_cases/t00032_class.png index 45a7d7e3..c9fe322c 100644 Binary files a/docs/test_cases/t00032_class.png and b/docs/test_cases/t00032_class.png differ diff --git a/docs/test_cases/t00033_class.png b/docs/test_cases/t00033_class.png index 0d6f7678..d3d6200e 100644 Binary files a/docs/test_cases/t00033_class.png and b/docs/test_cases/t00033_class.png differ diff --git a/docs/test_cases/t00034_class.png b/docs/test_cases/t00034_class.png index 25ce0b36..96d584a1 100644 Binary files a/docs/test_cases/t00034_class.png and b/docs/test_cases/t00034_class.png differ diff --git a/docs/test_cases/t20001_sequence.png b/docs/test_cases/t20001_sequence.png index e972c3f5..e9595008 100644 Binary files a/docs/test_cases/t20001_sequence.png and b/docs/test_cases/t20001_sequence.png differ diff --git a/docs/test_cases/t20002_sequence.png b/docs/test_cases/t20002_sequence.png index 74f0e2cb..cc5f2e5d 100644 Binary files a/docs/test_cases/t20002_sequence.png and b/docs/test_cases/t20002_sequence.png differ diff --git a/docs/test_cases/t30001.md b/docs/test_cases/t30001.md new file mode 100644 index 00000000..87bf01a4 --- /dev/null +++ b/docs/test_cases/t30001.md @@ -0,0 +1,55 @@ +# t30001 - Basic package diagram test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t30001_package: + type: package + glob: + - ../../tests/t30001/t30001.cc + include: + namespaces: + - clanguml::t30001 + exclude: + namespaces: + - clanguml::t30001::detail + using_namespace: + - clanguml::t30001 + plantuml: + before: + - "' t30001 test package diagram" + after: + - "note right of @A(A::AA::AAA): A AAA note..." +``` +## Source code +File t30001.cc +```cpp +namespace clanguml { +namespace t30001 { +namespace A { +namespace AA { +namespace AAA { +} // namespace AAA +namespace BBB { +} // namespace BBB +} // namespace AA +namespace BB { +} // namespace BB +} // namespace A +namespace B { +namespace AA { +namespace AAA { +} // namespace AAA +namespace BBB { +} // namespace BBB +} // namespace AA +namespace BB { +} // namespace BB +} // namespace B +} // namespace t30001 +} // namespace clanguml + +``` +## Generated UML diagrams +![t30001_package](./t30001_package.png "Basic package diagram test case") diff --git a/docs/test_cases/t30001_package.png b/docs/test_cases/t30001_package.png new file mode 100644 index 00000000..ccd8830e Binary files /dev/null and b/docs/test_cases/t30001_package.png differ diff --git a/docs/test_cases/t30002.md b/docs/test_cases/t30002.md new file mode 100644 index 00000000..74bf0e6c --- /dev/null +++ b/docs/test_cases/t30002.md @@ -0,0 +1,127 @@ +# t30002 - Package dependency test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t30002_package: + type: package + glob: + - ../../tests/t30002/t30002.cc + include: + namespaces: + - clanguml::t30002 + exclude: + namespaces: + - clanguml::t30002::detail + using_namespace: + - clanguml::t30002 + plantuml: + before: + - "' t30002 test package diagram" +``` +## Source code +File t30002.cc +```cpp +#include +#include +#include + +namespace clanguml { +namespace t30002 { +namespace A::AA { +namespace A1 { +struct CA { +}; +} +namespace A2 { +struct CB { +}; +} +namespace A3 { +struct CC { +}; +} +namespace A4 { +struct CD { +}; +} +namespace A5 { +struct CE { +}; +} +namespace A6 { +struct CF { +}; +} +namespace A7 { +struct CG { +}; +} +namespace A8 { +struct CH { +}; +} +namespace A9 { +struct CI { +}; +} +namespace A10 { +struct CJ { +}; +} +namespace A11 { +struct CK { +}; +} +namespace A12 { +struct CL { +}; +} +namespace A13 { +struct CM { +}; +} +} +namespace B::BB::BBB { +struct CBA : public A::AA::A6::CF { + A::AA::A1::CA *ca_; + A::AA::A2::CB cb_; + std::shared_ptr cc_; + std::map> cd_; + + void ce(const std::vector /*ce_*/) { } + + std::shared_ptr cg() { return {}; } + + template + void ch(std::map> & /*ch_*/) + { + } + + template std::map> ci() + { + return {}; + } +}; + +void cj(std::unique_ptr /*cj_*/) { } + +std::unique_ptr ck() { return {}; } + +template +void cl(std::map> & /*ch_*/) +{ +} + +template std::map> cm() +{ + return {}; +} +} +} // namespace t30002 +} // namespace clanguml + +``` +## Generated UML diagrams +![t30002_package](./t30002_package.png "Package dependency test case") diff --git a/docs/test_cases/t30002_package.png b/docs/test_cases/t30002_package.png new file mode 100644 index 00000000..ce3d6e66 Binary files /dev/null and b/docs/test_cases/t30002_package.png differ diff --git a/docs/test_cases/t30003.md b/docs/test_cases/t30003.md new file mode 100644 index 00000000..6503e30e --- /dev/null +++ b/docs/test_cases/t30003.md @@ -0,0 +1,57 @@ +# t30003 - Package deprecated attribute test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t30003_package: + type: package + glob: + - ../../tests/t30003/t30003.cc + include: + namespaces: + - clanguml::t30003 + using_namespace: + - clanguml::t30003 + plantuml: + before: + - "' t30003 test package diagram" +``` +## Source code +File t30003.cc +```cpp +namespace clanguml { +namespace t30003 { + +namespace ns1 { +namespace ns2_v1_0_0 { +class A { +}; +} + +namespace [[deprecated]] ns2_v0_9_0 { +class A { +}; +} + +namespace { +class Anon final { +}; +} +} + +namespace [[deprecated]] ns3 { + +namespace ns1::ns2 { +class Anon : public t30003::ns1::ns2_v1_0_0::A { +}; +} + +class B : public ns1::ns2::Anon { +}; +} +} +} +``` +## Generated UML diagrams +![t30003_package](./t30003_package.png "Package deprecated attribute test case") diff --git a/docs/test_cases/t30003_package.png b/docs/test_cases/t30003_package.png new file mode 100644 index 00000000..33e2682a Binary files /dev/null and b/docs/test_cases/t30003_package.png differ diff --git a/docs/test_cases/t30004.md b/docs/test_cases/t30004.md new file mode 100644 index 00000000..a4d84685 --- /dev/null +++ b/docs/test_cases/t30004.md @@ -0,0 +1,60 @@ +# t30004 - PlantUML package decorators test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t30004_package: + type: package + glob: + - ../../tests/t30004/t30004.cc + include: + namespaces: + - clanguml::t30004 + using_namespace: + - clanguml::t30004 + plantuml: + before: + - "' t30004 test package diagram" +``` +## Source code +File t30004.cc +```cpp +namespace clanguml { +namespace t30004 { + +/// @uml{style[#green]} +namespace A { + +/// @uml{note[ bottom ] Package AAA.} +namespace AAA { +} + +/// \uml{note[right] Package BBB.} +namespace BBB { +} + +/// +/// @uml{note:t30004_package[bottom] CCCC package note.} +/// This is package CCC. +namespace CCC { +} + +/// \uml{skip} +namespace DDD { +} + +/// @uml{style[#pink;line:red;line.bold;text:red]} +/// \uml{note[top] We skipped DDD.} +namespace EEE { +} + +/// \uml{note[top] Another CCC note.} +namespace CCC { +} +} +} +} +``` +## Generated UML diagrams +![t30004_package](./t30004_package.png "PlantUML package decorators test case") diff --git a/docs/test_cases/t30004_package.png b/docs/test_cases/t30004_package.png new file mode 100644 index 00000000..661b6253 Binary files /dev/null and b/docs/test_cases/t30004_package.png differ diff --git a/docs/test_cases/t30005.md b/docs/test_cases/t30005.md new file mode 100644 index 00000000..61b41d4e --- /dev/null +++ b/docs/test_cases/t30005.md @@ -0,0 +1,53 @@ +# t30005 - Package namespace alias test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t30005_package: + type: package + glob: + - ../../tests/t30005/t30005.cc + include: + namespaces: + - clanguml::t30005 + using_namespace: + - clanguml::t30005 + plantuml: + before: + - "' t30005 test package diagram" +``` +## Source code +File t30005.cc +```cpp +namespace clanguml { +namespace t30005 { + +namespace A::AA::AAA { +struct C1 { +}; +} + +namespace B::BB::BBB { +namespace A6 = A::AA::AAA; +namespace ASix = A6; +struct C2 { + ASix::C1 *cb; +}; +} + +namespace C::CC::CCC { +namespace A6 = A::AA::AAA; +namespace ASix = A6; +using ADSix = ASix::C1; +struct C2 { + ADSix *cc; +}; +} +} + +} + +``` +## Generated UML diagrams +![t30005_package](./t30005_package.png "Package namespace alias test case") diff --git a/docs/test_cases/t30005_package.png b/docs/test_cases/t30005_package.png new file mode 100644 index 00000000..b10a0efd Binary files /dev/null and b/docs/test_cases/t30005_package.png differ diff --git a/docs/test_cases/t30006.md b/docs/test_cases/t30006.md new file mode 100644 index 00000000..46d9d432 --- /dev/null +++ b/docs/test_cases/t30006.md @@ -0,0 +1,54 @@ +# t30006 - Package split namespace test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t30006_package: + type: package + glob: + - ../../tests/t30006/t30006.cc + include: + namespaces: + - clanguml::t30006 + using_namespace: + - clanguml::t30006 + plantuml: + before: + - "' t30006 test package diagram" +``` +## Source code +File t30006.cc +```cpp +namespace clanguml { +namespace t30006 { + +namespace B { +struct BB { +}; +} + +/// \uml{note[top] Top A note.} +namespace A { +struct A1 { + B::BB *b; +}; +} + +namespace C { +struct CC { +}; +} + +/// \uml{note[bottom] Bottom A note.} +namespace A { +struct A2 { + C::CC *c; +}; +} + +} +} +``` +## Generated UML diagrams +![t30006_package](./t30006_package.png "Package split namespace test case") diff --git a/docs/test_cases/t30006_package.png b/docs/test_cases/t30006_package.png new file mode 100644 index 00000000..21376db7 Binary files /dev/null and b/docs/test_cases/t30006_package.png differ diff --git a/docs/test_cases/t90000_class.png b/docs/test_cases/t90000_class.png index 3a516c01..a4703d4b 100644 Binary files a/docs/test_cases/t90000_class.png and b/docs/test_cases/t90000_class.png differ diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index afa2486f..e3b7ee00 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/generators/plantuml/class_diagram_generator.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -119,7 +119,7 @@ void generator::generate_alias(const enum_ &e, std::ostream &ostr) const void generator::generate(const class_ &c, std::ostream &ostr) const { - const auto uns = m_config.using_namespace; + const auto &uns = m_config.using_namespace; std::string class_type{"class"}; if (c.is_abstract()) @@ -360,8 +360,7 @@ void generator::generate(std::ostream &ostr) const if (m_config.should_include_entities("classes")) { for (const auto &c : m_model.classes()) { - if (!c.is_template_instantiation() && - !m_config.should_include(c.name())) + if (!m_config.should_include(c.name())) continue; generate_alias(c, ostr); ostr << '\n'; @@ -375,8 +374,7 @@ void generator::generate(std::ostream &ostr) const } for (const auto &c : m_model.classes()) { - if (!c.is_template_instantiation() && - !m_config.should_include(c.name())) + if (!m_config.should_include(c.name())) continue; generate(c, ostr); ostr << '\n'; @@ -414,7 +412,7 @@ clanguml::class_diagram::model::diagram generate( cppast::libclang_compilation_database &db, const std::string &name, clanguml::config::class_diagram &diagram) { - spdlog::info("Generating diagram {}.puml", name); + LOG_DBG("Generating diagram {}.puml", name); clanguml::class_diagram::model::diagram d; d.set_name(name); @@ -422,7 +420,7 @@ clanguml::class_diagram::model::diagram generate( // configuration std::vector translation_units{}; for (const auto &g : diagram.glob) { - spdlog::debug("Processing glob: {}", g); + LOG_DBG("Processing glob: {}", g); const auto matches = glob::rglob(g); std::copy(matches.begin(), matches.end(), std::back_inserter(translation_units)); diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.h b/src/class_diagram/generators/plantuml/class_diagram_generator.h index 52d5b565..2f8388c0 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.h +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.h @@ -1,7 +1,7 @@ /** * src/class_diagram/generators/plantuml/class_diagram_generator.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ #pragma once #include "class_diagram/model/class.h" -#include "class_diagram/model/class_relationship.h" #include "class_diagram/model/diagram.h" #include "class_diagram/model/enum.h" #include "class_diagram/visitor/translation_unit_visitor.h" +#include "common/model/relationship.h" #include "config/config.h" #include "cx/compilation_database.h" #include "util/util.h" @@ -44,8 +44,8 @@ using diagram_config = clanguml::class_diagram::model::diagram; using diagram_model = clanguml::class_diagram::model::diagram; using clanguml::class_diagram::model::class_; using clanguml::class_diagram::model::enum_; -using clanguml::class_diagram::model::relationship_t; -using clanguml::class_diagram::model::scope_t; +using clanguml::common::model::relationship_t; +using clanguml::common::model::scope_t; using namespace clanguml::util; std::string relative_to(std::string n, std::string c); diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 3d851e06..fc886186 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index e25d83b5..e84fba7d 100644 --- a/src/class_diagram/model/class.h +++ b/src/class_diagram/model/class.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,9 @@ #include "class_method.h" #include "class_parent.h" #include "class_template.h" -#include "element.h" -#include "enums.h" -#include "stylable_element.h" +#include "common/model/element.h" +#include "common/model/enums.h" +#include "common/model/stylable_element.h" #include "type_alias.h" #include @@ -31,7 +31,8 @@ namespace clanguml::class_diagram::model { -class class_ : public element, public stylable_element { +class class_ : public common::model::element, + public common::model::stylable_element { public: class_(const std::vector &using_namespaces); diff --git a/src/class_diagram/model/class_element.cc b/src/class_diagram/model/class_element.cc index 02157ad8..de30ae5f 100644 --- a/src/class_diagram/model/class_element.cc +++ b/src/class_diagram/model/class_element.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_element.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,15 @@ namespace clanguml::class_diagram::model { -class_element::class_element( - scope_t scope, const std::string &name, const std::string &type) +class_element::class_element(common::model::scope_t scope, + const std::string &name, const std::string &type) : scope_{scope} , name_{name} , type_{type} { } -scope_t class_element::scope() const { return scope_; } +common::model::scope_t class_element::scope() const { return scope_; } std::string class_element::name() const { return name_; } diff --git a/src/class_diagram/model/class_element.h b/src/class_diagram/model/class_element.h index 7f741f56..a53fdeab 100644 --- a/src/class_diagram/model/class_element.h +++ b/src/class_diagram/model/class_element.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_element.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,23 @@ */ #pragma once -#include "decorated_element.h" +#include "common/model/decorated_element.h" #include namespace clanguml::class_diagram::model { -class class_element : public decorated_element { +class class_element : public common::model::decorated_element { public: - class_element( - scope_t scope, const std::string &name, const std::string &type); + class_element(common::model::scope_t scope, const std::string &name, + const std::string &type); - scope_t scope() const; + common::model::scope_t scope() const; std::string name() const; std::string type() const; private: - scope_t scope_; + common::model::scope_t scope_; std::string name_; std::string type_; }; diff --git a/src/class_diagram/model/class_member.cc b/src/class_diagram/model/class_member.cc index 50f37160..360f76b0 100644 --- a/src/class_diagram/model/class_member.cc +++ b/src/class_diagram/model/class_member.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_member.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ namespace clanguml::class_diagram::model { -class_member::class_member( - scope_t scope, const std::string &name, const std::string &type) +class_member::class_member(common::model::scope_t scope, + const std::string &name, const std::string &type) : class_element{scope, name, type} { } diff --git a/src/class_diagram/model/class_member.h b/src/class_diagram/model/class_member.h index f04cbdf3..f339d4cb 100644 --- a/src/class_diagram/model/class_member.h +++ b/src/class_diagram/model/class_member.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_member.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,8 @@ namespace clanguml::class_diagram::model { class class_member : public class_element { public: - class_member( - scope_t scope, const std::string &name, const std::string &type); + class_member(common::model::scope_t scope, const std::string &name, + const std::string &type); bool is_relationship() const; void is_relationship(bool is_relationship); diff --git a/src/class_diagram/model/class_method.cc b/src/class_diagram/model/class_method.cc index 745a3b55..38f44bfe 100644 --- a/src/class_diagram/model/class_method.cc +++ b/src/class_diagram/model/class_method.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_method.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ namespace clanguml::class_diagram::model { -class_method::class_method( - scope_t scope, const std::string &name, const std::string &type) +class_method::class_method(common::model::scope_t scope, + const std::string &name, const std::string &type) : class_element{scope, name, type} { } diff --git a/src/class_diagram/model/class_method.h b/src/class_diagram/model/class_method.h index 98a829ce..024fbac7 100644 --- a/src/class_diagram/model/class_method.h +++ b/src/class_diagram/model/class_method.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_method.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ namespace clanguml::class_diagram::model { class class_method : public class_element { public: - class_method( - scope_t scope, const std::string &name, const std::string &type); + class_method(common::model::scope_t scope, const std::string &name, + const std::string &type); bool is_pure_virtual() const; void is_pure_virtual(bool is_pure_virtual); diff --git a/src/class_diagram/model/class_parent.cc b/src/class_diagram/model/class_parent.cc index ede58319..83669fef 100644 --- a/src/class_diagram/model/class_parent.cc +++ b/src/class_diagram/model/class_parent.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_parent.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,11 @@ void class_parent::is_virtual(bool is_virtual) { is_virtual_ = is_virtual; } bool class_parent::is_virtual() const { return is_virtual_; } -void class_parent::set_access(access_t access) { access_ = access; } +void class_parent::set_access(common::model::access_t access) +{ + access_ = access; +} -access_t class_parent::access() const { return access_; } +common::model::access_t class_parent::access() const { return access_; } } diff --git a/src/class_diagram/model/class_parent.h b/src/class_diagram/model/class_parent.h index 56f2f6a7..f51953d2 100644 --- a/src/class_diagram/model/class_parent.h +++ b/src/class_diagram/model/class_parent.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_parent.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ */ #pragma once -#include "enums.h" +#include "common/model/enums.h" #include @@ -31,12 +31,12 @@ public: void is_virtual(bool is_virtual); bool is_virtual() const; - void set_access(access_t access); - access_t access() const; + void set_access(common::model::access_t access); + common::model::access_t access() const; private: std::string name_; bool is_virtual_{false}; - access_t access_; + common::model::access_t access_; }; } diff --git a/src/class_diagram/model/class_template.cc b/src/class_diagram/model/class_template.cc index d4036317..94a95e41 100644 --- a/src/class_diagram/model/class_template.cc +++ b/src/class_diagram/model/class_template.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_template.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/class_template.h b/src/class_diagram/model/class_template.h index 4fe0cbc1..54f2f8b6 100644 --- a/src/class_diagram/model/class_template.h +++ b/src/class_diagram/model/class_template.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class_template.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 1fa636ca..9eb6664b 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/diagram.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 1408d793..0dffe194 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/diagram.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/enum.cc b/src/class_diagram/model/enum.cc index 6b9004c0..4b8fb341 100644 --- a/src/class_diagram/model/enum.cc +++ b/src/class_diagram/model/enum.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/enum.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/enum.h b/src/class_diagram/model/enum.h index 19692097..cd2070fa 100644 --- a/src/class_diagram/model/enum.h +++ b/src/class_diagram/model/enum.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/enum.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,8 @@ namespace clanguml::class_diagram::model { -class enum_ : public element, public stylable_element { +class enum_ : public common::model::element, + public common::model::stylable_element { public: enum_(const std::vector &using_namespaces); diff --git a/src/class_diagram/model/method_parameter.cc b/src/class_diagram/model/method_parameter.cc index f64b5e1f..f17b9094 100644 --- a/src/class_diagram/model/method_parameter.cc +++ b/src/class_diagram/model/method_parameter.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/method_parameter.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/method_parameter.h b/src/class_diagram/model/method_parameter.h index 5b5cab0d..5eceadb6 100644 --- a/src/class_diagram/model/method_parameter.h +++ b/src/class_diagram/model/method_parameter.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/method_parameter.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,14 @@ */ #pragma once -#include "decorated_element.h" +#include "common/model/decorated_element.h" #include #include namespace clanguml::class_diagram::model { -class method_parameter : public decorated_element { +class method_parameter : public common::model::decorated_element { public: void set_type(const std::string &type); std::string type() const; diff --git a/src/class_diagram/model/type_alias.cc b/src/class_diagram/model/type_alias.cc index cde5f50d..8a693d7e 100644 --- a/src/class_diagram/model/type_alias.cc +++ b/src/class_diagram/model/type_alias.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/type_alias.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/model/type_alias.h b/src/class_diagram/model/type_alias.h index d94ed579..68908ffd 100644 --- a/src/class_diagram/model/type_alias.h +++ b/src/class_diagram/model/type_alias.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/type_alias.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/visitor/element_visitor_context.cc b/src/class_diagram/visitor/element_visitor_context.cc index 4dc41aef..ed81432d 100644 --- a/src/class_diagram/visitor/element_visitor_context.cc +++ b/src/class_diagram/visitor/element_visitor_context.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/visitor/element_visitor_context.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/visitor/element_visitor_context.h b/src/class_diagram/visitor/element_visitor_context.h index 89cc32c6..cf6bd31f 100644 --- a/src/class_diagram/visitor/element_visitor_context.h +++ b/src/class_diagram/visitor/element_visitor_context.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/visitor/element_visitor_context.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/visitor/translation_unit_context.cc b/src/class_diagram/visitor/translation_unit_context.cc index 3fcc6eef..77e9c50e 100644 --- a/src/class_diagram/visitor/translation_unit_context.cc +++ b/src/class_diagram/visitor/translation_unit_context.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/visitor/translation_unit_context.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/visitor/translation_unit_context.h b/src/class_diagram/visitor/translation_unit_context.h index 90ef0b9e..a5dff73c 100644 --- a/src/class_diagram/visitor/translation_unit_context.h +++ b/src/class_diagram/visitor/translation_unit_context.h @@ -1,7 +1,7 @@ /** * src/class_diagram/visitor/translation_unit_context.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index b71d8497..b7687f10 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/visitor/translation_unit_visitor.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,19 +36,19 @@ namespace clanguml::class_diagram::visitor { -using clanguml::class_diagram::model::access_t; using clanguml::class_diagram::model::class_; using clanguml::class_diagram::model::class_member; using clanguml::class_diagram::model::class_method; using clanguml::class_diagram::model::class_parent; -using clanguml::class_diagram::model::class_relationship; using clanguml::class_diagram::model::class_template; using clanguml::class_diagram::model::diagram; using clanguml::class_diagram::model::enum_; using clanguml::class_diagram::model::method_parameter; -using clanguml::class_diagram::model::relationship_t; -using clanguml::class_diagram::model::scope_t; using clanguml::class_diagram::model::type_alias; +using clanguml::common::model::access_t; +using clanguml::common::model::relationship; +using clanguml::common::model::relationship_t; +using clanguml::common::model::scope_t; namespace detail { scope_t cpp_access_specifier_to_scope( @@ -551,12 +551,12 @@ bool translation_unit_visitor::process_field_with_template_instantiation( else relationship_type = relationship_t::kAggregation; - class_relationship rr{relationship_type, tinst.full_name(), + relationship rr{relationship_type, tinst.full_name(), detail::cpp_access_specifier_to_scope(as), mv.name()}; rr.set_style(m.style_spec()); // Process field decorators - auto [decorator_rtype, decorator_rmult] = m.relationship(); + auto [decorator_rtype, decorator_rmult] = m.get_relationship(); if (decorator_rtype != relationship_t::kNone) { rr.set_type(decorator_rtype); auto mult = util::split(decorator_rmult, ":"); @@ -568,9 +568,8 @@ bool translation_unit_visitor::process_field_with_template_instantiation( if (ctx.config().should_include(tinst.name())) { LOG_DBG("Adding field instantiation relationship {} {} {} : {}", - rr.destination(), - clanguml::class_diagram::model::to_string(rr.type()), c.full_name(), - rr.label()); + rr.destination(), clanguml::common::model::to_string(rr.type()), + c.full_name(), rr.label()); c.add_relationship(std::move(rr)); @@ -632,11 +631,10 @@ void translation_unit_visitor::process_field( for (const auto &[type, relationship_type] : relationships) { if (relationship_type != relationship_t::kNone) { - class_relationship r{ - relationship_type, type, m.scope(), m.name()}; + relationship r{relationship_type, type, m.scope(), m.name()}; r.set_style(m.style_spec()); - auto [decorator_rtype, decorator_rmult] = m.relationship(); + auto [decorator_rtype, decorator_rmult] = m.get_relationship(); if (decorator_rtype != relationship_t::kNone) { r.set_type(decorator_rtype); auto mult = util::split(decorator_rmult, ":"); @@ -648,8 +646,8 @@ void translation_unit_visitor::process_field( LOG_DBG("Adding field relationship {} {} {} : {}", r.destination(), - clanguml::class_diagram::model::to_string(r.type()), - c.full_name(), r.label()); + clanguml::common::model::to_string(r.type()), c.full_name(), + r.label()); c.add_relationship(std::move(r)); } @@ -874,12 +872,12 @@ void translation_unit_visitor::process_function_parameter( for (const auto &[type, relationship_type] : relationships) { if ((relationship_type != relationship_t::kNone) && (type != c.name())) { - class_relationship r{relationship_t::kDependency, type}; + relationship r{relationship_t::kDependency, type}; LOG_DBG("Adding field relationship {} {} {} : {}", r.destination(), - clanguml::class_diagram::model::to_string(r.type()), - c.full_name(), r.label()); + clanguml::common::model::to_string(r.type()), c.full_name(), + r.label()); c.add_relationship(std::move(r)); } @@ -940,15 +938,14 @@ void translation_unit_visitor::process_function_parameter( "only adding reference to template {}", cx::util::full_name(cppast::remove_cv(t), ctx.entity_index(), false)); - class_relationship rr{relationship_t::kDependency, + relationship rr{relationship_t::kDependency, cx::util::full_name(cppast::remove_cv(t), ctx.entity_index(), false)}; LOG_DBG("Adding field template dependency relationship " "{} {} {} " ": {}", rr.destination(), - clanguml::class_diagram::model::to_string( - rr.type()), + clanguml::common::model::to_string(rr.type()), c.full_name(), rr.label()); c.add_relationship(std::move(rr)); } @@ -957,14 +954,13 @@ void translation_unit_visitor::process_function_parameter( class_ tinst = build_template_instantiation( template_instantiation_type); - class_relationship rr{ + relationship rr{ relationship_t::kDependency, tinst.full_name()}; LOG_DBG("Adding field dependency relationship {} {} {} " ": {}", rr.destination(), - clanguml::class_diagram::model::to_string( - rr.type()), + clanguml::common::model::to_string(rr.type()), c.full_name(), rr.label()); c.add_relationship(std::move(rr)); @@ -1008,7 +1004,7 @@ void translation_unit_visitor::process_friend( cppast::cpp_entity_kind::class_template_t)) return; - class_relationship r{ + relationship r{ relationship_t::kFriendship, "", scope_t::kNone, "<>"}; if (f.comment().has_value()) @@ -1355,7 +1351,7 @@ class_ translation_unit_visitor::build_template_instantiation( ? std::make_optional(&tinst) : parent); - class_relationship tinst_dependency{ + relationship tinst_dependency{ relationship_t::kDependency, nested_tinst.full_name()}; auto nested_tinst_full_name = nested_tinst.full_name(); @@ -1392,8 +1388,7 @@ class_ translation_unit_visitor::build_template_instantiation( } else if (targ.type().value().kind() == cppast::cpp_type_kind::user_defined_t) { - class_relationship tinst_dependency{ - relationship_t::kDependency, + relationship tinst_dependency{relationship_t::kDependency, cx::util::full_name( cppast::remove_cv( cx::util::unreferenced(targ.type().value())), @@ -1478,7 +1473,7 @@ class_ translation_unit_visitor::build_template_instantiation( // Otherwise point to the base template destination = tinst.base_template(); } - class_relationship r{relationship_t::kInstantiation, destination}; + relationship r{relationship_t::kInstantiation, destination}; tinst.add_relationship(std::move(r)); return tinst; diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index e1c8c6ad..508866f8 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -1,7 +1,7 @@ /** * src/class_diagram/visitor/translation_unit_visitor.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -100,9 +100,9 @@ public: bool find_relationships(const cppast::cpp_type &t, std::vector> &relationships, - clanguml::class_diagram::model::relationship_t relationship_hint = - clanguml::class_diagram::model::relationship_t::kNone); + clanguml::common::model::relationship_t>> &relationships, + clanguml::common::model::relationship_t relationship_hint = + clanguml::common::model::relationship_t::kNone); void process_template_type_parameter( const cppast::cpp_template_type_parameter &t, diff --git a/src/class_diagram/model/decorated_element.cc b/src/common/model/decorated_element.cc similarity index 87% rename from src/class_diagram/model/decorated_element.cc rename to src/common/model/decorated_element.cc index a7b59492..b828fec1 100644 --- a/src/class_diagram/model/decorated_element.cc +++ b/src/common/model/decorated_element.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/decorated_element.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ #include "decorated_element.h" -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { bool decorated_element::skip() const { @@ -38,7 +38,8 @@ bool decorated_element::skip_relationship() const return false; } -std::pair decorated_element::relationship() const +std::pair +decorated_element::get_relationship() const { for (auto &d : decorators_) if (std::dynamic_pointer_cast(d)) @@ -79,4 +80,11 @@ void decorated_element::add_decorators( decorators_.push_back(d); } } + +void decorated_element::append(const decorated_element &de) +{ + for (auto d : de.decorators()) { + decorators_.push_back(d); + } +} } diff --git a/src/class_diagram/model/decorated_element.h b/src/common/model/decorated_element.h similarity index 84% rename from src/class_diagram/model/decorated_element.h rename to src/common/model/decorated_element.h index a63803d4..fad15119 100644 --- a/src/class_diagram/model/decorated_element.h +++ b/src/common/model/decorated_element.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/decorated_element.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ #include #include -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { class decorated_element { public: @@ -33,7 +33,7 @@ public: bool skip_relationship() const; - std::pair relationship() const; + std::pair get_relationship() const; std::string style_spec(); @@ -43,6 +43,8 @@ public: void add_decorators( const std::vector> &decorators); + void append(const decorated_element &de); + private: std::vector> decorators_; }; diff --git a/src/class_diagram/model/element.cc b/src/common/model/element.cc similarity index 73% rename from src/class_diagram/model/element.cc rename to src/common/model/element.cc index 9f523f4d..b5c7fe25 100644 --- a/src/class_diagram/model/element.cc +++ b/src/common/model/element.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/element.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ #include "util/util.h" -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { std::atomic_uint64_t element::m_nextId = 1; @@ -32,7 +32,7 @@ element::element(const std::vector &using_namespaces) std::string element::alias() const { return fmt::format("C_{:010}", m_id); } -void element::add_relationship(class_relationship &&cr) +void element::add_relationship(relationship &&cr) { if (cr.destination().empty()) { LOG_WARN("Skipping relationship '{}' - {} - '{}' due empty " @@ -41,8 +41,10 @@ void element::add_relationship(class_relationship &&cr) return; } - auto it = std::find(relationships_.begin(), relationships_.end(), cr); - if (it == relationships_.end()) + LOG_DBG("Adding relationship: '{}' - {} - '{}'", cr.destination(), + to_string(cr.type()), full_name(true)); + + if (!util::contains(relationships_, cr)) relationships_.emplace_back(std::move(cr)); } @@ -56,13 +58,12 @@ const std::vector &element::using_namespaces() const return using_namespaces_; } -std::vector &element::relationships() +std::vector &element::relationships() { return relationships_; } + +const std::vector &element::relationships() const { return relationships_; } -const std::vector &element::relationships() const -{ - return relationships_; -} +void element::append(const element &e) { decorated_element::append(e); } } diff --git a/src/class_diagram/model/element.h b/src/common/model/element.h similarity index 81% rename from src/class_diagram/model/element.h rename to src/common/model/element.h index 092feed2..1fad1f0f 100644 --- a/src/class_diagram/model/element.h +++ b/src/common/model/element.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/element.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,14 @@ */ #pragma once -#include "class_relationship.h" #include "decorated_element.h" +#include "relationship.h" #include #include #include -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { class element : public decorated_element { public: @@ -46,11 +46,13 @@ public: const std::vector &using_namespaces() const; - std::vector &relationships(); + std::vector &relationships(); - const std::vector &relationships() const; + const std::vector &relationships() const; - void add_relationship(class_relationship &&cr); + void add_relationship(relationship &&cr); + + void append(const element &e); protected: const uint64_t m_id{0}; @@ -59,7 +61,7 @@ private: std::string name_; std::vector namespace_; std::vector using_namespaces_; - std::vector relationships_; + std::vector relationships_; static std::atomic_uint64_t m_nextId; }; diff --git a/src/class_diagram/model/enums.h b/src/common/model/enums.h similarity index 90% rename from src/class_diagram/model/enums.h rename to src/common/model/enums.h index ee658bfa..08c78d45 100644 --- a/src/class_diagram/model/enums.h +++ b/src/common/model/enums.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/enums.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ */ #pragma once -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { enum class access_t { kPublic, kProtected, kPrivate }; diff --git a/src/class_diagram/model/class_relationship.cc b/src/common/model/relationship.cc similarity index 62% rename from src/class_diagram/model/class_relationship.cc rename to src/common/model/relationship.cc index 7bb1ae8d..96c92fd6 100644 --- a/src/class_diagram/model/class_relationship.cc +++ b/src/common/model/relationship.cc @@ -1,7 +1,7 @@ /** - * src/class_diagram/model/class_relationship.cc + * src/common/model/class_relationship.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,9 @@ * limitations under the License. */ -#include "class_relationship.h" +#include "relationship.h" -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { std::string to_string(relationship_t r) { @@ -48,8 +48,8 @@ std::string to_string(relationship_t r) } } -class_relationship::class_relationship(relationship_t type, - const std::string &destination, scope_t scope, const std::string &label, +relationship::relationship(relationship_t type, const std::string &destination, + scope_t scope, const std::string &label, const std::string &multiplicity_source, const std::string &multiplicity_destination) : type_{type} @@ -61,51 +61,48 @@ class_relationship::class_relationship(relationship_t type, { } -void class_relationship::set_type(relationship_t type) noexcept -{ - type_ = type; -} +void relationship::set_type(relationship_t type) noexcept { type_ = type; } -relationship_t class_relationship::type() const noexcept { return type_; } +relationship_t relationship::type() const noexcept { return type_; } -void class_relationship::set_destination(const std::string &destination) +void relationship::set_destination(const std::string &destination) { destination_ = destination; } -std::string class_relationship::destination() const { return destination_; } +std::string relationship::destination() const { return destination_; } -void class_relationship::set_multiplicity_source( +void relationship::set_multiplicity_source( const std::string &multiplicity_source) { multiplicity_source_ = multiplicity_source; } -std::string class_relationship::multiplicity_source() const +std::string relationship::multiplicity_source() const { return multiplicity_source_; } -void class_relationship::set_multiplicity_destination( +void relationship::set_multiplicity_destination( const std::string &multiplicity_destination) { multiplicity_destination_ = multiplicity_destination; } -std::string class_relationship::multiplicity_destination() const +std::string relationship::multiplicity_destination() const { return multiplicity_destination_; } -void class_relationship::set_label(const std::string &label) { label_ = label; } +void relationship::set_label(const std::string &label) { label_ = label; } -std::string class_relationship::label() const { return label_; } +std::string relationship::label() const { return label_; } -void class_relationship::set_scope(scope_t scope) noexcept { scope_ = scope; } +void relationship::set_scope(scope_t scope) noexcept { scope_ = scope; } -scope_t class_relationship::scope() const noexcept { return scope_; } +scope_t relationship::scope() const noexcept { return scope_; } -bool operator==(const class_relationship &l, const class_relationship &r) +bool operator==(const relationship &l, const relationship &r) { return l.type() == r.type() && l.destination() == r.destination() && l.label() == r.label(); diff --git a/src/class_diagram/model/class_relationship.h b/src/common/model/relationship.h similarity index 76% rename from src/class_diagram/model/class_relationship.h rename to src/common/model/relationship.h index dd364c85..0bb66390 100644 --- a/src/class_diagram/model/class_relationship.h +++ b/src/common/model/relationship.h @@ -1,7 +1,7 @@ /** - * src/class_diagram/model/class_relationship.h + * src/common/model/relationship.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,24 @@ */ #pragma once -#include "decorated_element.h" -#include "stylable_element.h" +#include "common/model/decorated_element.h" +#include "common/model/stylable_element.h" #include -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { std::string to_string(relationship_t r); -class class_relationship : public decorated_element, public stylable_element { +class relationship : public common::model::decorated_element, + public common::model::stylable_element { public: - class_relationship(relationship_t type, const std::string &destination, + relationship(relationship_t type, const std::string &destination, scope_t scope = scope_t::kNone, const std::string &label = "", const std::string &multiplicity_source = "", const std::string &multiplicity_destination = ""); - virtual ~class_relationship() = default; + virtual ~relationship() = default; void set_type(relationship_t type) noexcept; relationship_t type() const noexcept; @@ -54,8 +55,7 @@ public: void set_scope(scope_t scope) noexcept; scope_t scope() const noexcept; - friend bool operator==( - const class_relationship &l, const class_relationship &r); + friend bool operator==(const relationship &l, const relationship &r); private: relationship_t type_{relationship_t::kAssociation}; diff --git a/src/class_diagram/model/stylable_element.cc b/src/common/model/stylable_element.cc similarity index 89% rename from src/class_diagram/model/stylable_element.cc rename to src/common/model/stylable_element.cc index 090e9b5e..e1919fb7 100644 --- a/src/class_diagram/model/stylable_element.cc +++ b/src/common/model/stylable_element.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/stylable_element.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ #include "stylable_element.h" -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { void stylable_element::set_style(const std::string &style) { style_ = style; } diff --git a/src/class_diagram/model/stylable_element.h b/src/common/model/stylable_element.h similarity index 89% rename from src/class_diagram/model/stylable_element.h rename to src/common/model/stylable_element.h index e1aa3929..ab0059ae 100644 --- a/src/class_diagram/model/stylable_element.h +++ b/src/common/model/stylable_element.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/stylable_element.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ #include -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { class stylable_element { public: diff --git a/src/config/config.cc b/src/config/config.cc index a005b1cc..ee3c71fe 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -1,7 +1,7 @@ /** * src/config/config.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,7 +69,7 @@ bool diagram::should_include(const std::string &name_) const for (const auto &ex : exclude.namespaces) { if (name.find(ex) == 0) { - spdlog::debug("Skipping from diagram: {}", name); + LOG_DBG("Skipping from diagram: {}", name); return false; } } @@ -89,8 +89,7 @@ bool diagram::should_include(const std::string &name_) const return false; } -bool diagram::should_include( - const clanguml::class_diagram::model::scope_t scope) const +bool diagram::should_include(const clanguml::common::model::scope_t scope) const { for (const auto &s : exclude.scopes) { if (s == scope) @@ -128,10 +127,11 @@ bool class_diagram::has_class(std::string clazz) } namespace YAML { -using clanguml::class_diagram::model::scope_t; +using clanguml::common::model::scope_t; using clanguml::config::class_diagram; using clanguml::config::config; using clanguml::config::filter; +using clanguml::config::package_diagram; using clanguml::config::plantuml; using clanguml::config::sequence_diagram; using clanguml::config::source_location; @@ -282,6 +282,19 @@ template <> struct convert { } }; +// +// class_diagram Yaml decoder +// +template <> struct convert { + static bool decode(const Node &node, package_diagram &rhs) + { + if (!decode_diagram(node, rhs)) + return false; + + return true; + } +}; + // // config Yaml decoder // @@ -313,8 +326,12 @@ template <> struct convert { rhs.diagrams[name] = std::make_shared( d.second.as()); } + else if (diagram_type == "package") { + rhs.diagrams[name] = std::make_shared( + d.second.as()); + } else { - spdlog::warn( + LOG_WARN( "Diagrams of type {} are not supported at the moment... ", diagram_type); } diff --git a/src/config/config.h b/src/config/config.h index 8516ce1a..aae0d1d1 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -1,7 +1,7 @@ /** * src/config/config.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ #pragma once #include "class_diagram/model/diagram.h" -#include "class_diagram/model/enums.h" +#include "common/model/enums.h" #include "util/util.h" #include @@ -56,7 +56,7 @@ struct filter { // E.g.: // - public // - private - std::vector scopes; + std::vector scopes; }; struct diagram { @@ -77,7 +77,7 @@ struct diagram { bool should_include(const std::string &name_) const; - bool should_include(const class_diagram::model::scope_t scope) const; + bool should_include(const common::model::scope_t scope) const; }; struct source_location { @@ -101,6 +101,10 @@ struct sequence_diagram : public diagram { std::vector start_from; }; +struct package_diagram : public diagram { + virtual ~package_diagram() = default; +}; + struct config { // the glob list is additive and relative to the current // directory diff --git a/src/cx/compilation_database.cc b/src/cx/compilation_database.cc index 3b797fd7..c2842364 100644 --- a/src/cx/compilation_database.cc +++ b/src/cx/compilation_database.cc @@ -1,7 +1,7 @@ /** * src/cx/compilation_database.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ * limitations under the License. */ #include "compilation_database.h" +#include "util/util.h" #include #include @@ -73,7 +74,7 @@ CXTranslationUnit compilation_database::parse_translation_unit( clang_CompileCommands_getCommand(compile_commands, 0); auto cc_filename = clang_CompileCommand_getFilename(compile_command); - spdlog::debug( + LOG_DBG( "Processing compile command file: {}", clang_getCString(cc_filename)); auto num_args = clang_CompileCommand_getNumArgs(compile_command); @@ -84,7 +85,7 @@ CXTranslationUnit compilation_database::parse_translation_unit( arguments = (char **)malloc(sizeof(char *) * num_args); for (j = 0; j < num_args; ++j) { CXString arg = clang_CompileCommand_getArg(compile_command, j); - spdlog::debug("Processing argument: {}", clang_getCString(arg)); + LOG_DBG("Processing argument: {}", clang_getCString(arg)); arguments[j] = strdup(clang_getCString(arg)); clang_disposeString(arg); } diff --git a/src/cx/compilation_database.h b/src/cx/compilation_database.h index 0a20efcb..fc027a04 100644 --- a/src/cx/compilation_database.h +++ b/src/cx/compilation_database.h @@ -1,7 +1,7 @@ /** * src/cx/compilation_database.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/cx/cursor.cc b/src/cx/cursor.cc index f84c20b0..8dee8b4a 100644 --- a/src/cx/cursor.cc +++ b/src/cx/cursor.cc @@ -1,7 +1,7 @@ /** * src/cx/cursor.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/cx/cursor.h b/src/cx/cursor.h index 5f74f448..31657bf7 100644 --- a/src/cx/cursor.h +++ b/src/cx/cursor.h @@ -1,7 +1,7 @@ /** * src/cx/cursor.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/cx/type.cc b/src/cx/type.cc index 2ed28838..32b071ea 100644 --- a/src/cx/type.cc +++ b/src/cx/type.cc @@ -1,7 +1,7 @@ /** * src/cx/type.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/cx/type.h b/src/cx/type.h index 2856e47a..283608e5 100644 --- a/src/cx/type.h +++ b/src/cx/type.h @@ -1,7 +1,7 @@ /** * src/cx/type.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/cx/util.cc b/src/cx/util.cc index b3d16d6e..721acc09 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -1,7 +1,7 @@ /** * src/cx/util.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -92,6 +93,27 @@ std::string ns(const cppast::cpp_entity &e) return fmt::format("{}", fmt::join(res, "::")); } +type_safe::optional_ref entity_ns( + const cppast::cpp_entity &e) +{ + std::vector res{}; + + if (e.kind() == cppast::cpp_entity_kind::namespace_t) + return type_safe::optional_ref( + static_cast(e)); + + auto it = e.parent(); + while (it) { + if (it.value().kind() == cppast::cpp_entity_kind::namespace_t) { + return type_safe::optional_ref( + static_cast(it.value())); + } + it = it.value().parent(); + } + + return {}; +} + bool is_inside_class(const cppast::cpp_entity &e) { auto it = e.parent(); diff --git a/src/cx/util.h b/src/cx/util.h index a523f617..9a0392e6 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -1,7 +1,7 @@ /** * src/cx/util.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,10 +53,12 @@ const cppast::cpp_type &unreferenced(const cppast::cpp_type &t); std::string ns(const cppast::cpp_entity &e); +type_safe::optional_ref entity_ns( + const cppast::cpp_entity &e); + std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx); bool is_inside_class(const cppast::cpp_entity &e); - } // namespace util } // namespace cx } // namespace clanguml diff --git a/src/decorators/decorators.cc b/src/decorators/decorators.cc index 97921469..a83d862d 100644 --- a/src/decorators/decorators.cc +++ b/src/decorators/decorators.cc @@ -1,7 +1,7 @@ /** * src/decorators/decorators.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/decorators/decorators.h b/src/decorators/decorators.h index edb25d1e..554759c0 100644 --- a/src/decorators/decorators.h +++ b/src/decorators/decorators.h @@ -1,7 +1,7 @@ /** * src/decorators/decorators.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ #include #include #include +#include +#include namespace clanguml { namespace decorators { diff --git a/src/main.cc b/src/main.cc index 5a1fef33..b7691ad1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,7 +1,7 @@ /** * src/main.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,28 +19,21 @@ #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #include "class_diagram/generators/plantuml/class_diagram_generator.h" -#include "class_diagram/model/diagram.h" -#include "class_diagram/visitor/translation_unit_visitor.h" #include "config/config.h" #include "cx/compilation_database.h" +#include "package_diagram/generators/plantuml/package_diagram_generator.h" #include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h" -#include "sequence_diagram/visitor/translation_unit_context.h" + #include "util/util.h" #include #include -#include #include #include #include #include -#include -#include -#include -#include #include -#include using namespace clanguml; using config::config; @@ -48,11 +41,12 @@ using cx::compilation_database; int main(int argc, const char *argv[]) { - CLI::App app{"Clang-based PlantUML generator from C++ sources"}; + CLI::App app{"Clang-based PlantUML diagram generator for C++"}; std::string config_path{".clang-uml"}; std::string compilation_database_dir{'.'}; std::vector diagram_names{}; + std::optional output_directory; bool verbose{false}; app.add_option( @@ -61,6 +55,8 @@ int main(int argc, const char *argv[]) "Location of compilation database directory"); app.add_option("-n,--diagram-name", diagram_names, "List of diagram names to generate"); + app.add_option("-o,--output-directory", output_directory, + "Override output directory specified in config file"); app.add_flag("-v,--verbose", verbose, "Verbose logging"); CLI11_PARSE(app, argc, argv); @@ -79,18 +75,21 @@ int main(int argc, const char *argv[]) cppast::libclang_compilation_database db(config.compilation_database_dir); + std::string od = config.output_directory; + if (output_directory) + od = output_directory.value(); + for (const auto &[name, diagram] : config.diagrams) { // If there are any specific diagram names provided on the command line, // and this diagram is not in that list - skip it - if (!diagram_names.empty() && - std::find(diagram_names.begin(), diagram_names.end(), name) == - diagram_names.end()) + if (!diagram_names.empty() && !util::contains(diagram_names, name)) continue; using clanguml::config::class_diagram; + using clanguml::config::package_diagram; using clanguml::config::sequence_diagram; - std::filesystem::path path{"puml/" + name + ".puml"}; + auto path = std::filesystem::path{od} / fmt::format("{}.puml", name); std::ofstream ofs; ofs.open(path, std::ofstream::out | std::ofstream::trunc); @@ -112,6 +111,17 @@ int main(int argc, const char *argv[]) dynamic_cast(*diagram), model); } + else if (std::dynamic_pointer_cast(diagram)) { + auto model = + clanguml::package_diagram::generators::plantuml::generate( + db, name, dynamic_cast(*diagram)); + + ofs << clanguml::package_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), + model); + } + + LOG_INFO("Written {} diagram to {}", name, path.string()); ofs.close(); } diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.cc b/src/package_diagram/generators/plantuml/package_diagram_generator.cc new file mode 100644 index 00000000..016375e4 --- /dev/null +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.cc @@ -0,0 +1,232 @@ +/** + * src/package_diagram/generators/plantuml/package_diagram_generator.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ + +#include "package_diagram_generator.h" + +#include "util/error.h" + +namespace clanguml::package_diagram::generators::plantuml { + +std::string relative_to(std::string n, std::string c) +{ + if (c.rfind(n) == std::string::npos) + return c; + + return c.substr(n.size() + 2); +} + +generator::generator( + clanguml::config::package_diagram &config, diagram_model &model) + : m_config(config) + , m_model(model) +{ +} + +std::string generator::to_string(relationship_t r, std::string style) const +{ + switch (r) { + case relationship_t::kOwnership: + case relationship_t::kComposition: + return style.empty() ? "*--" : fmt::format("*-[{}]-", style); + case relationship_t::kAggregation: + return style.empty() ? "o--" : fmt::format("o-[{}]-", style); + case relationship_t::kContainment: + return style.empty() ? "--+" : fmt::format("-[{}]-+", style); + case relationship_t::kAssociation: + return style.empty() ? "-->" : fmt::format("-[{}]->", style); + case relationship_t::kInstantiation: + return style.empty() ? "..|>" : fmt::format(".[{}].|>", style); + case relationship_t::kFriendship: + return style.empty() ? "<.." : fmt::format("<.[{}].", style); + case relationship_t::kDependency: + return style.empty() ? "..>" : fmt::format(".[{}].>", style); + default: + return ""; + } +} + +std::string generator::name(relationship_t r) const +{ + switch (r) { + case relationship_t::kOwnership: + case relationship_t::kComposition: + return "composition"; + case relationship_t::kAggregation: + return "aggregation"; + case relationship_t::kContainment: + return "containment"; + case relationship_t::kAssociation: + return "association"; + case relationship_t::kDependency: + return "dependency"; + default: + return "unknown"; + } +} + +void generator::generate_relationships( + const package &p, std::ostream &ostr) const +{ + const auto &uns = m_config.using_namespace; + + // Generate this packages relationship + if (m_config.should_include_relationship("dependency")) { + for (const auto &r : p.relationships()) { + std::stringstream relstr; + try { + relstr << m_model.to_alias(ns_relative(uns, p.full_name(false))) + << " ..> " + << m_model.to_alias(ns_relative(uns, r.destination())) + << '\n'; + ostr << relstr.str(); + } + catch (error::uml_alias_missing &e) { + LOG_ERROR("=== Skipping dependency relation from {} to {} due " + "to: {}", + p.full_name(false), r.destination(), e.what()); + } + } + } + + // Process it's subpackages relationships + for (auto subpackage = p.cbegin(); subpackage != p.cend(); subpackage++) { + generate_relationships(**subpackage, ostr); + } +} + +void generator::generate(const package &p, std::ostream &ostr) const +{ + const auto uns = m_config.using_namespace; + + ostr << "package [" << p.name() << "] "; + ostr << "as " << p.alias(); + + if (p.is_deprecated()) + ostr << " <>"; + + if (!p.style().empty()) + ostr << " " << p.style(); + + ostr << " {" << '\n'; + + // C++17 cannot figure out to use cbegin/cend in a for-range loop on const + // collection... ¯\_(ツ)_/¯ + for (auto subpackage = p.cbegin(); subpackage != p.cend(); subpackage++) { + generate(**subpackage, ostr); + } + + ostr << "}" << '\n'; + + // + // Process notes + // + for (auto decorator : p.decorators()) { + auto note = std::dynamic_pointer_cast(decorator); + if (note && note->applies_to_diagram(m_config.name)) { + ostr << "note " << note->position << " of " << p.alias() << '\n' + << note->text << '\n' + << "end note\n"; + } + } +} + +void generator::generate(std::ostream &ostr) const +{ + ostr << "@startuml" << '\n'; + + // Process aliases in any of the puml directives + for (const auto &b : m_config.puml.before) { + std::string note{b}; + std::tuple alias_match; + while (util::find_element_alias(note, alias_match)) { + auto alias = m_model.to_alias(ns_relative( + m_config.using_namespace, std::get<0>(alias_match))); + note.replace( + std::get<1>(alias_match), std::get<2>(alias_match), alias); + } + ostr << note << '\n'; + } + + if (m_config.should_include_entities("packages")) { + for (const auto &p : m_model) { + generate(*p, ostr); + ostr << '\n'; + } + } + + // Process package relationships + for (const auto &p : m_model) { + generate_relationships(*p, ostr); + ostr << '\n'; + } + + // Process aliases in any of the puml directives + for (const auto &b : m_config.puml.after) { + std::string note{b}; + std::tuple alias_match; + while (util::find_element_alias(note, alias_match)) { + auto alias = m_model.to_alias(ns_relative( + m_config.using_namespace, std::get<0>(alias_match))); + note.replace( + std::get<1>(alias_match), std::get<2>(alias_match), alias); + } + ostr << note << '\n'; + } + + ostr << "@enduml" << '\n'; +} + +std::ostream &operator<<(std::ostream &os, const generator &g) +{ + g.generate(os); + return os; +} + +clanguml::package_diagram::model::diagram generate( + cppast::libclang_compilation_database &db, const std::string &name, + clanguml::config::package_diagram &diagram) +{ + LOG_INFO("Generating package diagram {}.puml", name); + clanguml::package_diagram::model::diagram d; + d.set_name(name); + + // Get all translation units matching the glob from diagram + // configuration + std::vector translation_units{}; + for (const auto &g : diagram.glob) { + LOG_DBG("Processing glob: {}", g); + const auto matches = glob::rglob(g); + std::copy(matches.begin(), matches.end(), + std::back_inserter(translation_units)); + } + + cppast::cpp_entity_index idx; + cppast::simple_file_parser parser{ + type_safe::ref(idx)}; + + // Process all matching translation units + clanguml::package_diagram::visitor::translation_unit_visitor ctx( + idx, d, diagram); + cppast::parse_files(parser, translation_units, db); + for (auto &file : parser.files()) + ctx(file); + + return d; +} + +} diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.h b/src/package_diagram/generators/plantuml/package_diagram_generator.h new file mode 100644 index 00000000..ed5b8fcf --- /dev/null +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.h @@ -0,0 +1,81 @@ +/** + * src/package_diagram/generators/plantuml/package_diagram_generator.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ +#pragma once + +#include "common/model/relationship.h" +#include "config/config.h" +#include "cx/compilation_database.h" +#include "package_diagram/model/diagram.h" +#include "package_diagram/model/package.h" +#include "package_diagram/visitor/translation_unit_visitor.h" +#include "util/util.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace clanguml { +namespace package_diagram { +namespace generators { +namespace plantuml { + +using diagram_config = clanguml::package_diagram::model::diagram; +using diagram_model = clanguml::package_diagram::model::diagram; +using clanguml::common::model::relationship_t; +using clanguml::common::model::scope_t; +using clanguml::package_diagram::model::package; +using namespace clanguml::util; + +std::string relative_to(std::string n, std::string c); + +class generator { +public: + generator(clanguml::config::package_diagram &config, diagram_model &model); + + std::string to_string(relationship_t r, std::string style = "") const; + + std::string name(relationship_t r) const; + + void generate_alias(const package &c, std::ostream &ostr) const; + + void generate_relationships(const package &p, std::ostream &ostr) const; + + void generate(const package &e, std::ostream &ostr) const; + + void generate(std::ostream &ostr) const; + + friend std::ostream &operator<<(std::ostream &os, const generator &g); + +private: + clanguml::config::package_diagram &m_config; + diagram_model &m_model; +}; + +clanguml::package_diagram::model::diagram generate( + cppast::libclang_compilation_database &db, const std::string &name, + clanguml::config::package_diagram &diagram); + +} +} +} +} diff --git a/src/package_diagram/model/diagram.cc b/src/package_diagram/model/diagram.cc new file mode 100644 index 00000000..1ec49861 --- /dev/null +++ b/src/package_diagram/model/diagram.cc @@ -0,0 +1,48 @@ +/** + * src/package_diagram/model/diagram.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ + +#include "diagram.h" + +#include "util/error.h" +#include "util/util.h" + +namespace clanguml::package_diagram::model { + +std::string diagram::name() const { return name_; } + +void diagram::set_name(const std::string &name) { name_ = name; } + +std::string diagram::to_alias(const std::string &full_name) const +{ + LOG_DBG("Looking for alias for {}", full_name); + + auto fn = util::split(full_name, "::"); + + if (fn.empty()) + throw error::uml_alias_missing( + fmt::format("Missing alias for '{}'", full_name)); + + auto package = get_package(fn); + + if (!package) + throw error::uml_alias_missing( + fmt::format("Missing alias for '{}'", full_name)); + + return package.value().alias(); +} +} diff --git a/src/package_diagram/model/diagram.h b/src/package_diagram/model/diagram.h new file mode 100644 index 00000000..7a18f8ea --- /dev/null +++ b/src/package_diagram/model/diagram.h @@ -0,0 +1,42 @@ +/** + * src/package_diagram/model/diagram.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ +#pragma once + +#include "package.h" + +#include + +#include +#include + +namespace clanguml::package_diagram::model { + +class diagram : public detail::package_trait { +public: + std::string name() const; + + void set_name(const std::string &name); + + std::string to_alias(const std::string &full_name) const; + +private: + std::string name_; + + std::vector> packages_; +}; +} diff --git a/src/package_diagram/model/package.cc b/src/package_diagram/model/package.cc new file mode 100644 index 00000000..719689ae --- /dev/null +++ b/src/package_diagram/model/package.cc @@ -0,0 +1,54 @@ +/** + * src/package_diagram/model/class.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ + +#include "package.h" + +#include "util/util.h" + +#include + +namespace clanguml::package_diagram::model { +package::package(const std::vector &using_namespaces) + : element{using_namespaces} +{ +} + +std::string package::full_name(bool relative) const +{ + auto fn = get_namespace(); + auto ns = using_namespaces(); + + if (relative && (fn.size() >= ns.size())) { + if (util::starts_with(fn, ns)) + fn = std::vector(fn.begin() + ns.size(), fn.end()); + } + + fn.push_back(name()); + + return fmt::format("{}", fmt::join(fn, "::")); +} + +bool operator==(const package &l, const package &r) +{ + return l.full_name(false) == r.full_name(false); +} + +bool package::is_deprecated() const { return is_deprecated_; } + +void package::set_deprecated(bool deprecated) { is_deprecated_ = deprecated; } +} \ No newline at end of file diff --git a/src/package_diagram/model/package.h b/src/package_diagram/model/package.h new file mode 100644 index 00000000..451827a4 --- /dev/null +++ b/src/package_diagram/model/package.h @@ -0,0 +1,137 @@ +/** + * src/package_diagram/model/class.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ +#pragma once + +#include "common/model/element.h" +#include "common/model/stylable_element.h" +#include "util/util.h" + +#include +#include + +#include +#include +#include + +namespace clanguml::package_diagram::model { + +namespace detail { +template class Container, + typename Ptr = std::unique_ptr> +class package_trait { +public: + void add_package(std::unique_ptr p) + { + auto it = std::find_if(packages_.begin(), packages_.end(), + [&p](const auto &e) { return *e == *p; }); + + if (it != packages_.end()) { + (*it)->append(*p); + } + else { + packages_.emplace_back(std::move(p)); + } + } + + void add_package(std::vector path, std::unique_ptr p) + { + assert(p); + + LOG_DBG( + "Adding package {} at path '{}'", p->name(), fmt::join(path, "::")); + + if (path.empty()) { + add_package(std::move(p)); + return; + } + + auto parent = get_package(path); + + if (parent) + parent.value().add_package(std::move(p)); + else + spdlog::error( + "No parent package found at: {}", fmt::join(path, "::")); + } + + type_safe::optional_ref get_package(std::vector path) const + { + LOG_DBG("Getting package at path: {}", fmt::join(path, "::")); + + if (path.empty() || !has_package(path.at(0))) + return {}; + + auto p = get_package(path.at(0)); + if (path.size() == 1) + return p; + + return p.value().get_package( + std::vector(path.begin() + 1, path.end())); + } + + type_safe::optional_ref get_package(const std::string &name) const + { + auto it = std::find_if(packages_.cbegin(), packages_.cend(), + [&](const auto &p) { return name == p->name(); }); + + if (it == packages_.end()) + return {}; + + assert(it->get() != nullptr); + + return type_safe::ref(*(it->get())); + } + + bool has_package(const std::string &name) const + { + return std::find_if(packages_.cbegin(), packages_.cend(), + [&](const auto &p) { return name == p->name(); }) != + packages_.end(); + } + + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + inline iterator begin() noexcept { return packages_.begin(); } + inline const_iterator cbegin() const noexcept { return packages_.cbegin(); } + inline iterator end() noexcept { return packages_.end(); } + inline const_iterator cend() const noexcept { return packages_.cend(); } + +protected: + Container> packages_; +}; +} + +class package : public common::model::element, + public common::model::stylable_element, + public detail::package_trait { +public: + package(const std::vector &using_namespaces); + + std::string full_name(bool relative) const override; + + friend bool operator==(const package &l, const package &r); + + bool is_deprecated() const; + + void set_deprecated(bool deprecated); + +private: + bool is_deprecated_{false}; +}; +} diff --git a/src/package_diagram/visitor/element_visitor_context.cc b/src/package_diagram/visitor/element_visitor_context.cc new file mode 100644 index 00000000..56f3a2ac --- /dev/null +++ b/src/package_diagram/visitor/element_visitor_context.cc @@ -0,0 +1,43 @@ +/** + * src/package_diagram/model/visitor/element_visitor_context.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ + +#include "element_visitor_context.h" + +#include "translation_unit_context.h" + +namespace clanguml::package_diagram::visitor { + +template +element_visitor_context::element_visitor_context( + clanguml::package_diagram::model::diagram &diagram, T &element) + : element_{element} + , diagram_{diagram} +{ +} + +template T &element_visitor_context::element() +{ + return element_; +} + +template +clanguml::package_diagram::model::diagram &element_visitor_context::diagram() +{ + return diagram_; +} +} diff --git a/src/package_diagram/visitor/element_visitor_context.h b/src/package_diagram/visitor/element_visitor_context.h new file mode 100644 index 00000000..bcf7650e --- /dev/null +++ b/src/package_diagram/visitor/element_visitor_context.h @@ -0,0 +1,42 @@ +/** + * src/package_diagram/model/visitor/element_visitor_context.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ +#pragma once + +#include "package_diagram/model/diagram.h" + +namespace clanguml::package_diagram::visitor { + +class translation_unit_context; + +template class element_visitor_context { +public: + element_visitor_context( + clanguml::package_diagram::model::diagram &diagram, T &element); + + T &element(); + + clanguml::package_diagram::model::diagram &diagram(); + +private: + translation_unit_context *ctx_; + + T &element_; + clanguml::package_diagram::model::diagram &diagram_; +}; + +} diff --git a/src/package_diagram/visitor/translation_unit_context.cc b/src/package_diagram/visitor/translation_unit_context.cc new file mode 100644 index 00000000..c95355c2 --- /dev/null +++ b/src/package_diagram/visitor/translation_unit_context.cc @@ -0,0 +1,194 @@ +/** + * src/package_diagram/visitor/translation_unit_context.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ + +#include "translation_unit_context.h" + +#include "cx/util.h" + +namespace clanguml::package_diagram::visitor { + +translation_unit_context::translation_unit_context( + cppast::cpp_entity_index &idx, + clanguml::package_diagram::model::diagram &diagram, + const clanguml::config::package_diagram &config) + : entity_index_{idx} + , diagram_{diagram} + , config_{config} +{ +} + +bool translation_unit_context::has_namespace_alias( + const std::string &full_name) const +{ + bool res = + namespace_alias_index_.find(full_name) != namespace_alias_index_.end(); + + LOG_DBG("Alias {} {} found in index", full_name, res ? "" : "not"); + + return res; +} + +void translation_unit_context::add_namespace_alias(const std::string &full_name, + type_safe::object_ref ref) +{ + if (!has_namespace_alias(full_name)) { + LOG_DBG("Stored type alias: {} -> {} ", full_name, ref.get().name()); + + namespace_alias_index_.emplace(full_name, std::move(ref)); + } +} + +type_safe::object_ref +translation_unit_context::get_namespace_alias( + const std::string &full_name) const +{ + assert(has_namespace_alias(full_name)); + + return namespace_alias_index_.at(full_name); +} + +type_safe::object_ref +translation_unit_context::get_namespace_alias_final( + const cppast::cpp_namespace &ns) const +{ + auto ns_full_name = cx::util::full_name({}, ns); + + ns_full_name = cx::util::ns(ns) + "::" + ns_full_name; + + if (has_namespace_alias(ns_full_name)) { + return get_namespace_alias_final( + namespace_alias_index_.at(ns_full_name).get()); + } + + return type_safe::ref(ns); +} + +bool translation_unit_context::has_type_alias( + const std::string &full_name) const +{ + bool res = alias_index_.find(full_name) != alias_index_.end(); + + LOG_DBG("Alias {} {} found in index", full_name, res ? "" : "not"); + + return res; +} + +void translation_unit_context::add_type_alias(const std::string &full_name, + type_safe::object_ref &&ref) +{ + if (!has_type_alias(full_name)) { + LOG_DBG("Stored type alias: {} -> {} ", full_name, + cppast::to_string(ref.get())); + + alias_index_.emplace(full_name, std::move(ref)); + } +} + +type_safe::object_ref +translation_unit_context::get_type_alias(const std::string &full_name) const +{ + assert(has_type_alias(full_name)); + + return alias_index_.at(full_name); +} + +type_safe::object_ref +translation_unit_context::get_type_alias_final(const cppast::cpp_type &t) const +{ + const auto type_full_name = + cx::util::full_name(cppast::remove_cv(t), entity_index_, false); + + if (has_type_alias(type_full_name)) { + return get_type_alias_final(alias_index_.at(type_full_name).get()); + } + + return type_safe::ref(t); +} + +bool translation_unit_context::has_type_alias_template( + const std::string &full_name) const +{ + bool res = + alias_template_index_.find(full_name) != alias_template_index_.end(); + + LOG_DBG("Alias template {} {} found in index", full_name, res ? "" : "not"); + + return res; +} + +void translation_unit_context::add_type_alias_template( + const std::string &full_name, + type_safe::object_ref &&ref) +{ + if (!has_type_alias_template(full_name)) { + LOG_DBG("Stored type alias template for: {} ", full_name); + + alias_template_index_.emplace(full_name, std::move(ref)); + } +} + +type_safe::object_ref +translation_unit_context::get_type_alias_template( + const std::string &full_name) const +{ + assert(has_type_alias_template(full_name)); + + return alias_template_index_.at(full_name); +} + +void translation_unit_context::push_namespace(const std::string &ns) +{ + namespace_.push_back(ns); +} + +void translation_unit_context::pop_namespace() { namespace_.pop_back(); } + +const std::vector &translation_unit_context::get_namespace() const +{ + return namespace_; +} + +const cppast::cpp_entity_index &translation_unit_context::entity_index() const +{ + return entity_index_; +} + +const clanguml::config::package_diagram & +translation_unit_context::config() const +{ + return config_; +} + +clanguml::package_diagram::model::diagram &translation_unit_context::diagram() +{ + return diagram_; +} + +void translation_unit_context::set_current_package( + type_safe::optional_ref p) +{ + current_package_ = p; +} + +type_safe::optional_ref +translation_unit_context::get_current_package() const +{ + return current_package_; +} + +} diff --git a/src/package_diagram/visitor/translation_unit_context.h b/src/package_diagram/visitor/translation_unit_context.h new file mode 100644 index 00000000..10c71a4f --- /dev/null +++ b/src/package_diagram/visitor/translation_unit_context.h @@ -0,0 +1,110 @@ +/** + * src/package_diagram/visitor/translation_unit_context.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ +#pragma once + +#include "config/config.h" +#include "package_diagram/model/diagram.h" + +#include +#include +#include +#include + +namespace clanguml::package_diagram::visitor { + +class translation_unit_context { +public: + translation_unit_context(cppast::cpp_entity_index &idx, + clanguml::package_diagram::model::diagram &diagram, + const clanguml::config::package_diagram &config); + + bool has_namespace_alias(const std::string &full_name) const; + + void add_namespace_alias(const std::string &full_name, + type_safe::object_ref ref); + + type_safe::object_ref get_namespace_alias( + const std::string &full_name) const; + + type_safe::object_ref + get_namespace_alias_final(const cppast::cpp_namespace &t) const; + + bool has_type_alias(const std::string &full_name) const; + + void add_type_alias(const std::string &full_name, + type_safe::object_ref &&ref); + + type_safe::object_ref get_type_alias( + const std::string &full_name) const; + + type_safe::object_ref get_type_alias_final( + const cppast::cpp_type &t) const; + + bool has_type_alias_template(const std::string &full_name) const; + + void add_type_alias_template(const std::string &full_name, + type_safe::object_ref &&ref); + + type_safe::object_ref get_type_alias_template( + const std::string &full_name) const; + + void push_namespace(const std::string &ns); + + void pop_namespace(); + + const std::vector &get_namespace() const; + + const cppast::cpp_entity_index &entity_index() const; + + const clanguml::config::package_diagram &config() const; + + clanguml::package_diagram::model::diagram &diagram(); + + void set_current_package(type_safe::optional_ref p); + + type_safe::optional_ref get_current_package() const; + +private: + // Current visitor namespace + std::vector namespace_; + + // Reference to the cppast entity index + cppast::cpp_entity_index &entity_index_; + + // Reference to the output diagram model + clanguml::package_diagram::model::diagram &diagram_; + + // Reference to class diagram config + const clanguml::config::package_diagram &config_; + + // Map of discovered aliases (declared with 'namespace' keyword) + std::map> + namespace_alias_index_; + + // Map of discovered type aliases (declared with 'using' keyword) + std::map> + alias_index_; + + // Map of discovered template aliases (declared with 'using' keyword) + std::map> + alias_template_index_; + + type_safe::optional_ref current_package_; +}; + +} diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc new file mode 100644 index 00000000..88086760 --- /dev/null +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -0,0 +1,509 @@ +/** + * src/package_diagram/visitor/translation_unit_visitor.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ + +#include "translation_unit_visitor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace clanguml::package_diagram::visitor { + +using clanguml::class_diagram::model::type_alias; +using clanguml::common::model::access_t; +using clanguml::common::model::relationship; +using clanguml::common::model::relationship_t; +using clanguml::common::model::scope_t; +using clanguml::package_diagram::model::diagram; +using clanguml::package_diagram::model::package; + +namespace detail { +scope_t cpp_access_specifier_to_scope( + cppast::cpp_access_specifier_kind access_specifier) +{ + scope_t scope = scope_t::kPublic; + switch (access_specifier) { + case cppast::cpp_access_specifier_kind::cpp_public: + scope = scope_t::kPublic; + break; + case cppast::cpp_access_specifier_kind::cpp_private: + scope = scope_t::kPrivate; + break; + case cppast::cpp_access_specifier_kind::cpp_protected: + scope = scope_t::kProtected; + break; + default: + break; + } + + return scope; +} +} + +translation_unit_visitor::translation_unit_visitor( + cppast::cpp_entity_index &idx, + clanguml::package_diagram::model::diagram &diagram, + const clanguml::config::package_diagram &config) + : ctx{idx, diagram, config} +{ +} + +void translation_unit_visitor::operator()(const cppast::cpp_entity &file) +{ + cppast::visit(file, + [&, this](const cppast::cpp_entity &e, cppast::visitor_info info) { + if (e.kind() == cppast::cpp_entity_kind::namespace_t) { + if (info.event == + cppast::visitor_info::container_entity_enter) { + LOG_DBG("========== Visiting '{}' - {}", e.name(), + cppast::to_string(e.kind())); + + const auto &ns_declaration = + static_cast(e); + if (!ns_declaration.is_anonymous() && + !ns_declaration.is_inline()) { + + std::vector package_parent = + ctx.get_namespace(); + auto package_path = package_parent; + package_path.push_back(e.name()); + + auto usn = + util::split(ctx.config().using_namespace[0], "::"); + + if (!util::starts_with(usn, package_path)) { + auto p = std::make_unique( + ctx.config().using_namespace); + util::remove_prefix(package_path, usn); + util::remove_prefix(package_parent, usn); + + p->set_name(e.name()); + p->set_namespace(package_parent); + + if (ns_declaration.comment().has_value()) + p->add_decorators(decorators::parse( + ns_declaration.comment().value())); + + p->set_style(p->style_spec()); + + for (const auto &attr : + ns_declaration.attributes()) { + if (attr.kind() == + cppast::cpp_attribute_kind::deprecated) { + p->set_deprecated(true); + break; + } + } + + if (!p->skip()) { + ctx.diagram().add_package( + package_parent, std::move(p)); + ctx.set_current_package( + ctx.diagram().get_package(package_path)); + } + } + + ctx.push_namespace(e.name()); + } + } + else { + LOG_DBG("========== Leaving '{}' - {}", e.name(), + cppast::to_string(e.kind())); + + const auto &ns_declaration = + static_cast(e); + if (!ns_declaration.is_anonymous() && + !ns_declaration.is_inline()) + ctx.pop_namespace(); + } + } + else if (e.kind() == cppast::cpp_entity_kind::namespace_alias_t) { + auto &na = static_cast(e); + + for (const auto &alias_target : + na.target().get(ctx.entity_index())) { + auto full_ns = cx::util::full_name(ctx.get_namespace(), na); + ctx.add_namespace_alias(full_ns, alias_target); + } + } + else if (e.kind() == + cppast::cpp_entity_kind::class_template_specialization_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &tspec = static_cast< + const cppast::cpp_class_template_specialization &>(e); + + process_class_declaration( + tspec.class_(), type_safe::ref(tspec)); + } + else if (e.kind() == cppast::cpp_entity_kind::class_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &cls = static_cast(e); + if (cppast::get_definition(ctx.entity_index(), cls)) { + auto &clsdef = static_cast( + cppast::get_definition(ctx.entity_index(), cls) + .value()); + if (&cls != &clsdef) { + LOG_DBG("Forward declaration of class {} - skipping...", + cls.name()); + return; + } + } + + process_class_declaration(cls); + } + else if (e.kind() == cppast::cpp_entity_kind::function_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &f = static_cast(e); + + process_function(f); + } + else if (e.kind() == cppast::cpp_entity_kind::function_template_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &f = static_cast( + static_cast(e) + .function()); + + process_function(f); + } + else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &ta = static_cast(e); + type_alias t; + t.set_alias(cx::util::full_name(ctx.get_namespace(), ta)); + t.set_underlying_type(cx::util::full_name(ta.underlying_type(), + ctx.entity_index(), cx::util::is_inside_class(e))); + + ctx.add_type_alias(cx::util::full_name(ctx.get_namespace(), ta), + type_safe::ref(ta.underlying_type())); + } + else if (e.kind() == cppast::cpp_entity_kind::alias_template_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &at = static_cast(e); + } + }); +} + +void translation_unit_visitor::process_class_declaration( + const cppast::cpp_class &cls, + type_safe::optional_ref tspec) +{ + auto current_package = ctx.get_current_package(); + + if (!current_package) + return; + + cppast::cpp_access_specifier_kind last_access_specifier = + cppast::cpp_access_specifier_kind::cpp_private; + + std::vector> relationships; + + // Process class elements + for (auto &child : cls) { + if (child.kind() == cppast::cpp_entity_kind::access_specifier_t) { + auto &as = static_cast(child); + last_access_specifier = as.access_specifier(); + } + else if (child.kind() == cppast::cpp_entity_kind::member_variable_t) { + auto &mv = static_cast(child); + find_relationships( + mv.type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::variable_t) { + auto &mv = static_cast(child); + find_relationships( + mv.type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::member_function_t) { + auto &mf = static_cast(child); + for (const auto ¶m : mf.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + + find_relationships( + mf.return_type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::function_t) { + auto &mf = static_cast(child); + for (const auto ¶m : mf.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::function_template_t) { + auto &tm = static_cast(child) + .function(); + for (const auto ¶m : tm.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + + if (tm.kind() == cppast::cpp_entity_kind::member_function_t) + find_relationships( + static_cast(tm) + .return_type(), + relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::constructor_t) { + auto &mc = static_cast(child); + for (const auto ¶m : mc.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + } + else { + LOG_DBG("Found some other class child: {} ({})", child.name(), + cppast::to_string(child.kind())); + } + } + + // Process class bases + for (auto &base : cls.bases()) { + find_relationships( + base.type(), relationships, relationship_t::kDependency); + } + + for (const auto &dependency : relationships) { + auto destination = util::split(std::get<0>(dependency), "::"); + + if (!util::starts_with(ctx.get_namespace(), destination) && + !util::starts_with(destination, ctx.get_namespace())) { + relationship r{ + relationship_t::kDependency, std::get<0>(dependency)}; + current_package.value().add_relationship(std::move(r)); + } + } +} + +void translation_unit_visitor::process_function(const cppast::cpp_function &f) +{ + std::vector> relationships; + auto current_package = ctx.get_current_package(); + + if (!current_package) + return; + + for (const auto ¶m : f.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + + find_relationships( + f.return_type(), relationships, relationship_t::kDependency); + + for (const auto &dependency : relationships) { + auto destination = util::split(std::get<0>(dependency), "::"); + + if (!util::starts_with(ctx.get_namespace(), destination) && + !util::starts_with(destination, ctx.get_namespace())) { + relationship r{ + relationship_t::kDependency, std::get<0>(dependency)}; + current_package.value().add_relationship(std::move(r)); + } + } +} + +bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_, + std::vector> + &relationships, + relationship_t relationship_hint) +{ + bool found{false}; + + const auto fn = cx::util::full_name( + resolve_alias(cppast::remove_cv(t_)), ctx.entity_index(), false); + auto t_ns = util::split(fn, "::"); + t_ns.pop_back(); + + const auto &t_raw = resolve_alias(cppast::remove_cv(t_)); + + if (t_raw.kind() == cppast::cpp_type_kind::user_defined_t) { + + auto t_raw_ns = cx::util::ns(t_raw, ctx.entity_index()); + + const auto &type_entities = + static_cast(t_raw) + .entity() + .get(ctx.entity_index()); + if (type_entities.size() > 0) { + const auto &type_entity = type_entities[0]; + + const auto &t_raw_ns = cx::util::entity_ns(type_entity.get()); + + const auto &t_raw_ns_final = cx::util::ns(t_raw_ns.value()) + + "::" + cx::util::full_name({}, t_raw_ns.value()); + t_ns = util::split(t_raw_ns_final, "::"); + } + } + + std::vector possible_matches; + + possible_matches.push_back(util::join(t_ns, "::")); + + const auto fn_ns = cx::util::ns(cppast::remove_cv(t_), ctx.entity_index()); + + LOG_DBG("Finding relationships for type {}, {}, {}", cppast::to_string(t_), + t_.kind(), fn); + + relationship_t relationship_type = relationship_hint; + const auto &t = cppast::remove_cv(cx::util::unreferenced(t_)); + + if (t.kind() == cppast::cpp_type_kind::array_t) { + auto &a = static_cast(t); + found = find_relationships( + a.value_type(), relationships, relationship_t::kDependency); + return found; + } + + auto name = cppast::to_string(t); + + if (t_.kind() == cppast::cpp_type_kind::pointer_t) { + auto &p = static_cast(t_); + auto rt = relationship_t::kAssociation; + if (relationship_hint == relationship_t::kDependency) + rt = relationship_hint; + found = find_relationships(p.pointee(), relationships, rt); + } + else if (t_.kind() == cppast::cpp_type_kind::reference_t) { + auto &r = static_cast(t_); + auto rt = relationship_t::kAssociation; + if (r.reference_kind() == cppast::cpp_reference::cpp_ref_rvalue) { + rt = relationship_t::kAggregation; + } + if (relationship_hint == relationship_t::kDependency) + rt = relationship_hint; + found = find_relationships(r.referee(), relationships, rt); + } + if (cppast::remove_cv(t_).kind() == cppast::cpp_type_kind::user_defined_t) { + LOG_DBG("User defined type: {} | {}", cppast::to_string(t_), + cppast::to_string(t_.canonical())); + + // Check if t_ has an alias in the alias index + if (ctx.has_type_alias(fn)) { + LOG_DBG("Found relationship in alias of {} | {}", fn, + cppast::to_string(ctx.get_type_alias(fn).get())); + found = find_relationships( + ctx.get_type_alias(fn).get(), relationships, relationship_type); + if (found) + return found; + } + + for (const auto &pm : possible_matches) { + relationships.emplace_back(pm, relationship_t::kDependency); + } + } + else if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) { + auto &tinst = + static_cast(t); + + if (!tinst.arguments_exposed()) { + LOG_DBG("Template instantiation {} has no exposed arguments", name); + + return found; + } + + const auto &args = tinst.arguments().value(); + + // Try to match common containers + // TODO: Refactor to a separate class with configurable + // container list + if (name.find("std::unique_ptr") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (name.find("std::shared_ptr") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (name.find("std::weak_ptr") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (name.find("std::vector") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (ctx.config().should_include(fn)) { + LOG_DBG("User defined template instantiation: {} | {}", + cppast::to_string(t_), cppast::to_string(t_.canonical())); + + relationships.emplace_back( + cppast::to_string(t), relationship_t::kDependency); + + // Check if t_ has an alias in the alias index + if (ctx.has_type_alias(fn)) { + LOG_DBG("Find relationship in alias of {} | {}", fn, + cppast::to_string(ctx.get_type_alias(fn).get())); + found = find_relationships(ctx.get_type_alias(fn).get(), + relationships, relationship_type); + if (found) + return found; + } + + return found; + } + else { + for (const auto &arg : args) { + if (arg.type()) { + found = find_relationships( + arg.type().value(), relationships, relationship_type); + } + } + } + } + + return found; +} + +const cppast::cpp_type &translation_unit_visitor::resolve_alias( + const cppast::cpp_type &type) +{ + const auto &raw_type = cppast::remove_cv(cx::util::unreferenced(type)); + const auto type_full_name = + cx::util::full_name(raw_type, ctx.entity_index(), false); + if (ctx.has_type_alias(type_full_name)) { + return ctx.get_type_alias_final(raw_type).get(); + } + + return type; +} +} diff --git a/src/package_diagram/visitor/translation_unit_visitor.h b/src/package_diagram/visitor/translation_unit_visitor.h new file mode 100644 index 00000000..ca237e9c --- /dev/null +++ b/src/package_diagram/visitor/translation_unit_visitor.h @@ -0,0 +1,75 @@ +/** + * src/package_diagram/visitor/translation_unit_visitor.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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. + */ +#pragma once + +#include "config/config.h" +#include "cx/cursor.h" +#include "package_diagram/model/diagram.h" +#include "package_diagram/visitor/translation_unit_context.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace clanguml::package_diagram::visitor { + +class translation_unit_visitor { +public: + translation_unit_visitor(cppast::cpp_entity_index &idx, + clanguml::package_diagram::model::diagram &diagram, + const clanguml::config::package_diagram &config); + + void operator()(const cppast::cpp_entity &file); + + void process_class_declaration(const cppast::cpp_class &cls, + type_safe::optional_ref + tspec = nullptr); + + void process_function(const cppast::cpp_function &f); + + bool find_relationships(const cppast::cpp_type &t_, + std::vector> + &relationships, + common::model::relationship_t relationship_hint); + +private: + /** + * Try to resolve a type instance into a type referenced through an alias. + * If t does not represent an alias, returns t. + */ + const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t); + + // ctx allows to track current visitor context, e.g. current namespace + translation_unit_context ctx; +}; +} diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index 22ac7469..47549a74 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h index 076b8912..f54fc42a 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/model/activity.cc b/src/sequence_diagram/model/activity.cc index aa5dc7a8..c9e3d226 100644 --- a/src/sequence_diagram/model/activity.cc +++ b/src/sequence_diagram/model/activity.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/activity.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/model/activity.h b/src/sequence_diagram/model/activity.h index cf054965..d60cb225 100644 --- a/src/sequence_diagram/model/activity.h +++ b/src/sequence_diagram/model/activity.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/activity.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/model/diagram.cc b/src/sequence_diagram/model/diagram.cc index 17ebd3fa..6204153b 100644 --- a/src/sequence_diagram/model/diagram.cc +++ b/src/sequence_diagram/model/diagram.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/diagram.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 6b23c644..e6ddf2cf 100644 --- a/src/sequence_diagram/model/diagram.h +++ b/src/sequence_diagram/model/diagram.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/diagram.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/model/enums.h b/src/sequence_diagram/model/enums.h index d64bf49b..05a808e7 100644 --- a/src/sequence_diagram/model/enums.h +++ b/src/sequence_diagram/model/enums.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/enums.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/model/message.cc b/src/sequence_diagram/model/message.cc index 1b694806..ef92071b 100644 --- a/src/sequence_diagram/model/message.cc +++ b/src/sequence_diagram/model/message.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/message.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/model/message.h b/src/sequence_diagram/model/message.h index 872d5411..8c790eab 100644 --- a/src/sequence_diagram/model/message.h +++ b/src/sequence_diagram/model/message.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/message.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/visitor/translation_unit_context.cc b/src/sequence_diagram/visitor/translation_unit_context.cc index ffe490da..b8d6fcec 100644 --- a/src/sequence_diagram/visitor/translation_unit_context.cc +++ b/src/sequence_diagram/visitor/translation_unit_context.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/visitor/translation_unit_context.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/visitor/translation_unit_context.h b/src/sequence_diagram/visitor/translation_unit_context.h index e4c48a4d..739bf5d7 100644 --- a/src/sequence_diagram/visitor/translation_unit_context.h +++ b/src/sequence_diagram/visitor/translation_unit_context.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/visitor/translation_unit_context.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index b5bfbdef..7091dafd 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/visitor/translation_unit_visitor.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 2ff32d63..064ac399 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/visitor/translation_unit_visitor.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/util/error.h b/src/util/error.h index 21ff6773..46bf94e3 100644 --- a/src/util/error.h +++ b/src/util/error.h @@ -1,7 +1,7 @@ /** * src/util/error.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/util/util.cc b/src/util/util.cc index 656e4040..37dc8699 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -1,7 +1,7 @@ /** * src/util/util.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,11 @@ std::vector split(std::string str, std::string delimiter) return result; } +std::string join(const std::vector &toks, std::string delimiter) +{ + return fmt::format("{}", fmt::join(toks, delimiter)); +} + std::string ns_relative( const std::vector &namespaces, const std::string &n) { @@ -141,5 +146,6 @@ bool replace_all( return replaced; } + } } diff --git a/src/util/util.h b/src/util/util.h index 3d87acc8..dd65930d 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -1,7 +1,7 @@ /** * src/util/util.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,8 @@ std::string trim(const std::string &s); */ std::vector split(std::string str, std::string delimiter); +std::string join(const std::vector &toks, std::string delimiter); + /** * @brief Get name of the identifier relative to a set of namespaces * @@ -114,5 +116,74 @@ bool find_element_alias( */ bool replace_all( std::string &input, std::string pattern, std::string replace_with); + +/** + * @brief Appends a vector to a vector. + * + * @tparam T + * @param l + * @param r + */ +template void append(std::vector &l, const std::vector &r) +{ + l.insert(l.end(), r.begin(), r.end()); } + +/** + * @brief Checks if vector starts with a prefix. + * + * @tparam T + * @param col + * @param prefix + * @return + */ +template +bool starts_with(const std::vector &col, const std::vector &prefix) +{ + if (prefix.size() > col.size()) + return false; + + return std::vector(prefix.begin(), prefix.end()) == + std::vector(col.begin(), col.begin() + prefix.size()); } + +/** + * @brief Removes prefix sequence of elements from the beggining of col. + * + * @tparam T + * @param col + * @param prefix + */ +template +void remove_prefix(std::vector &col, const std::vector &prefix) +{ + if (!starts_with(col, prefix)) + return; + + col = std::vector(col.begin() + prefix.size(), col.end()); +} + +/** + * Returns true if element exists in container. + * + * @tparam T + * @tparam E + * @param container + * @param element + * @return + */ +template +bool contains(const T &container, const E &element) +{ + if constexpr (std::is_pointer_v) { + return std::find_if(container.begin(), container.end(), + [&element](const auto &e) { return *e == *element; }) != + container.end(); + } + else { + return std::find(container.cbegin(), container.cend(), element) != + container.cend(); + } +} +} // namespace util +} // namespace clanguml \ No newline at end of file diff --git a/tests/catch.h b/tests/catch.h index f531df86..90d8fe9a 100644 --- a/tests/catch.h +++ b/tests/catch.h @@ -1,9 +1,9 @@ /* - * Catch v2.11.3 - * Generated: 2020-03-19 13:44:21.042491 + * Catch v2.13.8 + * Generated: 2022-01-03 21:20:09.589503 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it - * directly Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. + * directly Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,8 +13,8 @@ // start catch.hpp #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 11 -#define CATCH_VERSION_PATCH 3 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 8 #ifdef __clang__ #pragma clang system_header @@ -65,11 +65,14 @@ #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ #include -#if TARGET_OS_OSX == 1 +#if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) #define CATCH_PLATFORM_MAC -#elif TARGET_OS_IPHONE == 1 +#elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) #define CATCH_PLATFORM_IPHONE #endif @@ -132,13 +135,10 @@ unsigned int rngSeed(); #endif -#if defined(CATCH_CPP17_OR_GREATER) -#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - -// We have to avoid both ICC and Clang, because they try to mask themselves -// as gcc, and we want only GCC in this block -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && \ + !defined(__CUDACC__) && !defined(__LCC__) #define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic push") #define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic pop") @@ -153,8 +153,23 @@ unsigned int rngSeed(); _Pragma("clang diagnostic push") #define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("clang diagnostic pop") +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +#if !defined(__ibmxl__) && !defined(__CUDACC__) #define CATCH_INTERNAL_IGNORE_BUT_WARN(...) \ - (void)__builtin_constant_p(__VA_ARGS__) + (void)__builtin_constant_p( \ + __VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, \ + hicpp-vararg) */ +#endif #define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ @@ -236,13 +251,6 @@ unsigned int rngSeed(); // Visual C++ #if defined(_MSC_VER) -#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma(warning(push)) -#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma(warning(pop)) - -#if _MSC_VER >= 1900 // Visual Studio 2015 or newer -#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - // Universal Windows platform does not support SEH // Or console colours (or console at all...) #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) @@ -251,14 +259,20 @@ unsigned int rngSeed(); #define CATCH_INTERNAL_CONFIG_WINDOWS_SEH #endif +#if !defined(__clang__) // Handle Clang masquerading for msvc + // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -#if !defined(__clang__) // Handle Clang masquerading for msvc #if !defined(_MSVC_TRADITIONAL) || \ (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) #define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic +// push` & `diagnostic pop` +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma(warning(push)) +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma(warning(pop)) #endif // __clang__ #endif // _MSC_VER @@ -327,7 +341,10 @@ unsigned int rngSeed(); // Check if byte is available and usable #if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#include +#if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) #define CATCH_INTERNAL_CONFIG_CPP17_BYTE +#endif #endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if variant is available and usable @@ -386,12 +403,6 @@ unsigned int rngSeed(); #define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && \ - !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && \ - !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -#define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && \ !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && \ !defined(CATCH_CONFIG_CPP17_STRING_VIEW) @@ -638,6 +649,7 @@ bool matchTest( std::vector filterTests(std::vector const &testCases, TestSpec const &testSpec, IConfig const &config); std::vector const &getAllTestCasesSorted(IConfig const &config); + } // end catch_interfaces_testcase.h @@ -880,7 +892,7 @@ constexpr auto operator"" _catch_sr( INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) #define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) \ INTERNAL_CATCH_REMOVE_PARENS(_0), \ - INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) + INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) \ INTERNAL_CATCH_REMOVE_PARENS(_0), \ INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) @@ -1292,16 +1304,16 @@ struct is_callable #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is -// replaced with std::invoke_result here. Also *_t format is preferred over -// typename *::type format. -template +// replaced with std::invoke_result here. +template using FunctionReturnType = - std::remove_reference_t>>; + std::remove_reference_t>>; #else -template +// Keep ::type here because we still support C++11 +template using FunctionReturnType = typename std::remove_reference::type>::type>::type; + typename std::result_of::type>::type>::type; #endif } // namespace Catch @@ -1383,19 +1395,17 @@ struct AutoReg : NonCopyable { #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, typename TestType, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ INTERNAL_CATCH_EXPAND_VARGS( \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, typename TestType, __VA_ARGS__)) #endif @@ -1403,20 +1413,18 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION( \ Name, Tags, Signature, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, Signature, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION( \ Name, Tags, Signature, ...) \ INTERNAL_CATCH_EXPAND_VARGS( \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, Signature, __VA_ARGS__)) #endif @@ -1425,9 +1433,8 @@ struct AutoReg : NonCopyable { ClassName, Name, Tags, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, typename T, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( \ @@ -1435,9 +1442,8 @@ struct AutoReg : NonCopyable { INTERNAL_CATCH_EXPAND_VARGS( \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, typename T, __VA_ARGS__)) #endif @@ -1446,9 +1452,8 @@ struct AutoReg : NonCopyable { ClassName, Name, Tags, Signature, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, Signature, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( \ @@ -1456,9 +1461,8 @@ struct AutoReg : NonCopyable { INTERNAL_CATCH_EXPAND_VARGS( \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, Signature, __VA_ARGS__)) #endif #endif @@ -1477,7 +1481,7 @@ struct AutoReg : NonCopyable { static void TestName() #define INTERNAL_CATCH_TESTCASE(...) \ INTERNAL_CATCH_TESTCASE2( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), __VA_ARGS__) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), __VA_ARGS__) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, ...) \ @@ -1506,7 +1510,7 @@ struct AutoReg : NonCopyable { void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, ...) \ INTERNAL_CATCH_TEST_CASE_METHOD2( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), ClassName, \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), ClassName, \ __VA_ARGS__) /////////////////////////////////////////////////////////////////////////////// @@ -1545,7 +1549,7 @@ struct AutoReg : NonCopyable { Catch::NameAndTags{Name " - " + \ std::string(tmpl_types[index]), \ Tags}), \ - index++, 0)...}; /* NOLINT */ \ + index++)...}; /* NOLINT */ \ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ @@ -1562,36 +1566,32 @@ struct AutoReg : NonCopyable { #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, typename TestType, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, typename TestType, __VA_ARGS__)) #endif #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, Signature, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, Signature, __VA_ARGS__)) #endif @@ -1630,7 +1630,7 @@ struct AutoReg : NonCopyable { std::string(types_list[index % num_types]) + \ ">", \ Tags}), \ - index++, 0)...}; /* NOLINT */ \ + index++)...}; /* NOLINT */ \ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ @@ -1651,18 +1651,16 @@ struct AutoReg : NonCopyable { #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...) \ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, typename T, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...) \ INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, typename T, __VA_ARGS__)) #endif @@ -1670,19 +1668,17 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( \ Name, Tags, Signature, ...) \ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, Signature, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( \ Name, Tags, Signature, ...) \ INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, Signature, __VA_ARGS__)) #endif @@ -1709,7 +1705,7 @@ struct AutoReg : NonCopyable { INTERNAL_CATCH_STRINGIZE(TmplList)) + \ " - " + std::to_string(index), \ Tags}), \ - index++, 0)...}; /* NOLINT */ \ + index++)...}; /* NOLINT */ \ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ @@ -1725,10 +1721,9 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ Name, Tags, TmplList) #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ @@ -1757,7 +1752,7 @@ struct AutoReg : NonCopyable { Catch::NameAndTags{Name " - " + \ std::string(tmpl_types[index]), \ Tags}), \ - index++, 0)...}; /* NOLINT */ \ + index++)...}; /* NOLINT */ \ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ @@ -1775,17 +1770,15 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, typename T, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, typename T, __VA_ARGS__)) #endif @@ -1794,18 +1787,16 @@ struct AutoReg : NonCopyable { ClassName, Name, Tags, Signature, ...) \ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, Signature, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( \ ClassName, Name, Tags, Signature, ...) \ INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_), \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ ClassName, Name, Tags, Signature, __VA_ARGS__)) #endif @@ -1847,7 +1838,7 @@ struct AutoReg : NonCopyable { std::string(types_list[index % num_types]) + \ ">", \ Tags}), \ - index++, 0)...}; /* NOLINT */ \ + index++)...}; /* NOLINT */ \ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ @@ -1869,20 +1860,18 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( \ ClassName, Name, Tags, ...) \ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ ClassName, Name, Tags, typename T, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( \ ClassName, Name, Tags, ...) \ INTERNAL_CATCH_EXPAND_VARGS( \ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ ClassName, Name, Tags, typename T, __VA_ARGS__)) #endif @@ -1890,20 +1879,18 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( \ ClassName, Name, Tags, Signature, ...) \ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ ClassName, Name, Tags, Signature, __VA_ARGS__) #else #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( \ ClassName, Name, Tags, Signature, ...) \ INTERNAL_CATCH_EXPAND_VARGS( \ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ ClassName, Name, Tags, Signature, __VA_ARGS__)) #endif @@ -1934,7 +1921,7 @@ struct AutoReg : NonCopyable { INTERNAL_CATCH_STRINGIZE(TmplList)) + \ " - " + std::to_string(index), \ Tags}), \ - index++, 0)...}; /* NOLINT */ \ + index++)...}; /* NOLINT */ \ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ @@ -1951,10 +1938,9 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( \ ClassName, Name, Tags, TmplList) \ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( \ + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_), \ INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ - INTERNAL_CATCH_UNIQUE_NAME( \ - ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_), \ ClassName, Name, Tags, TmplList) // end catch_test_registry.h @@ -2444,8 +2430,8 @@ template struct StringMaker { #endif namespace Detail { -template -std::string rangeToString(InputIterator first, InputIterator last) +template +std::string rangeToString(InputIterator first, Sentinel last) { ReusableStringStream rss; rss << "{ "; @@ -2553,6 +2539,7 @@ template struct TupleElementPrinter { static void print(const Tuple &, std::ostream &) { } }; + } template struct StringMaker> { @@ -2596,21 +2583,26 @@ template struct StringMaker> { #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER namespace Catch { -struct not_this_one { -}; // Tag type for detecting which begin/ end are being selected - -// Import begin/ end from std here so they are considered alongside the fallback -// (...) overloads in this namespace +// Import begin/ end from std here using std::begin; using std::end; -not_this_one begin(...); -not_this_one end(...); +namespace detail { +template struct void_type { + using type = void; +}; -template struct is_range { - static const bool value = !std::is_same())), - not_this_one>::value && - !std::is_same())), not_this_one>::value; +template struct is_range_impl : std::false_type { +}; + +template +struct is_range_impl()))>::type> + : std::true_type { +}; +} // namespace detail + +template struct is_range : detail::is_range_impl { }; #if defined(_MANAGED) // Managed types are never ranges @@ -3040,6 +3032,21 @@ public: { return {static_cast(m_lhs <= rhs), m_lhs, "<=", rhs}; } + template + auto operator|(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs | rhs), m_lhs, "|", rhs}; + } + template + auto operator&(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs & rhs), m_lhs, "&", rhs}; + } + template + auto operator^(RhsT const &rhs) -> BinaryExpr const + { + return {static_cast(m_lhs ^ rhs), m_lhs, "^", rhs}; + } template auto operator&&(RhsT const &) -> BinaryExpr const @@ -3124,8 +3131,8 @@ struct IResultCapture { virtual void sectionEnded(SectionEndInfo const &endInfo) = 0; virtual void sectionEndedEarly(SectionEndInfo const &endInfo) = 0; - virtual auto acquireGeneratorTracker(SourceLineInfo const &lineInfo) - -> IGeneratorTracker & = 0; + virtual auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker & = 0; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) virtual void benchmarkPreparing(std::string const &name) = 0; @@ -3686,6 +3693,7 @@ IRegistryHub const &getRegistryHub(); IMutableRegistryHub &getMutableRegistryHub(); void cleanUp(); std::string translateActiveException(); + } // end catch_interfaces_registry_hub.h @@ -3729,6 +3737,9 @@ class ExceptionTranslatorRegistrar { std::string translate(ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd) const override { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return ""; +#else try { if (it == itEnd) std::rethrow_exception(std::current_exception()); @@ -3738,6 +3749,7 @@ class ExceptionTranslatorRegistrar { catch (T &ex) { return m_translateFunction(ex); } +#endif } protected: @@ -3799,7 +3811,7 @@ public: template ::value>::type> - Approx operator()(T const &value) + Approx operator()(T const &value) const { Approx approx(static_cast(value)); approx.m_epsilon = m_epsilon; @@ -4368,15 +4380,15 @@ namespace Catch { namespace Matchers { namespace Vector { -template -struct ContainsElementMatcher : MatcherBase> { +template +struct ContainsElementMatcher : MatcherBase> { ContainsElementMatcher(T const &comparator) : m_comparator(comparator) { } - bool match(std::vector const &v) const override + bool match(std::vector const &v) const override { for (auto const &el : v) { if (el == m_comparator) { @@ -4394,14 +4406,15 @@ struct ContainsElementMatcher : MatcherBase> { T const &m_comparator; }; -template struct ContainsMatcher : MatcherBase> { +template +struct ContainsMatcher : MatcherBase> { - ContainsMatcher(std::vector const &comparator) + ContainsMatcher(std::vector const &comparator) : m_comparator(comparator) { } - bool match(std::vector const &v) const override + bool match(std::vector const &v) const override { // !TBD: see note in EqualsMatcher if (m_comparator.size() > v.size()) @@ -4425,22 +4438,23 @@ template struct ContainsMatcher : MatcherBase> { return "Contains: " + ::Catch::Detail::stringify(m_comparator); } - std::vector const &m_comparator; + std::vector const &m_comparator; }; -template struct EqualsMatcher : MatcherBase> { +template +struct EqualsMatcher : MatcherBase> { - EqualsMatcher(std::vector const &comparator) + EqualsMatcher(std::vector const &comparator) : m_comparator(comparator) { } - bool match(std::vector const &v) const override + bool match(std::vector const &v) const override { // !TBD: This currently works if all elements can be compared using != // - a more general approach would be via a compare template that // defaults to using !=. but could be specialised for, e.g. - // std::vector etc + // std::vector etc // - then just call that directly if (m_comparator.size() != v.size()) return false; @@ -4453,17 +4467,18 @@ template struct EqualsMatcher : MatcherBase> { { return "Equals: " + ::Catch::Detail::stringify(m_comparator); } - std::vector const &m_comparator; + std::vector const &m_comparator; }; -template struct ApproxMatcher : MatcherBase> { +template +struct ApproxMatcher : MatcherBase> { - ApproxMatcher(std::vector const &comparator) + ApproxMatcher(std::vector const &comparator) : m_comparator(comparator) { } - bool match(std::vector const &v) const override + bool match(std::vector const &v) const override { if (m_comparator.size() != v.size()) return false; @@ -4498,21 +4513,18 @@ template struct ApproxMatcher : MatcherBase> { return *this; } - std::vector const &m_comparator; + std::vector const &m_comparator; mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); }; -template -struct UnorderedEqualsMatcher : MatcherBase> { - UnorderedEqualsMatcher(std::vector const &target) +template +struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const &target) : m_target(target) { } - bool match(std::vector const &vec) const override + bool match(std::vector const &vec) const override { - // Note: This is a reimplementation of std::is_permutation, - // because I don't want to include inside the common - // path if (m_target.size() != vec.size()) { return false; } @@ -4526,7 +4538,7 @@ struct UnorderedEqualsMatcher : MatcherBase> { } private: - std::vector const &m_target; + std::vector const &m_target; }; } // namespace Vector @@ -4534,34 +4546,42 @@ private: // The following functions create the actual matcher objects. // This allows the types to be inferred -template -Vector::ContainsMatcher Contains(std::vector const &comparator) +template , + typename AllocMatch = AllocComp> +Vector::ContainsMatcher Contains( + std::vector const &comparator) { - return Vector::ContainsMatcher(comparator); + return Vector::ContainsMatcher(comparator); } -template -Vector::ContainsElementMatcher VectorContains(T const &comparator) +template > +Vector::ContainsElementMatcher VectorContains(T const &comparator) { - return Vector::ContainsElementMatcher(comparator); + return Vector::ContainsElementMatcher(comparator); } -template -Vector::EqualsMatcher Equals(std::vector const &comparator) +template , + typename AllocMatch = AllocComp> +Vector::EqualsMatcher Equals( + std::vector const &comparator) { - return Vector::EqualsMatcher(comparator); + return Vector::EqualsMatcher(comparator); } -template -Vector::ApproxMatcher Approx(std::vector const &comparator) +template , + typename AllocMatch = AllocComp> +Vector::ApproxMatcher Approx( + std::vector const &comparator) { - return Vector::ApproxMatcher(comparator); + return Vector::ApproxMatcher(comparator); } -template -Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const &target) +template , + typename AllocMatch = AllocComp> +Vector::UnorderedEqualsMatcher UnorderedEquals( + std::vector const &target) { - return Vector::UnorderedEqualsMatcher(target); + return Vector::UnorderedEqualsMatcher(target); } } // namespace Matchers @@ -4916,19 +4936,21 @@ auto makeGenerators(as, U &&val, Gs &&...moreGenerators) -> Generators value(T(std::forward(val))), std::forward(moreGenerators)...); } -auto acquireGeneratorTracker(SourceLineInfo const &lineInfo) - -> IGeneratorTracker &; +auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker &; template // Note: The type after -> is weird, because VS2015 cannot parse // the expression used in the typedef inside, when it is in // return type. Yeah. -auto generate(SourceLineInfo const &lineInfo, L const &generatorExpression) +auto generate(StringRef generatorName, SourceLineInfo const &lineInfo, + L const &generatorExpression) -> decltype(std::declval().get()) { using UnderlyingType = typename decltype(generatorExpression())::type; - IGeneratorTracker &tracker = acquireGeneratorTracker(lineInfo); + IGeneratorTracker &tracker = + acquireGeneratorTracker(generatorName, lineInfo); if (!tracker.hasGenerator()) { tracker.setGenerator( pf::make_unique>(generatorExpression())); @@ -4943,20 +4965,26 @@ auto generate(SourceLineInfo const &lineInfo, L const &generatorExpression) } // namespace Catch #define GENERATE(...) \ - Catch::Generators::generate(CATCH_INTERNAL_LINEINFO, [] { \ - using namespace Catch::Generators; \ - return makeGenerators(__VA_ARGS__); \ - }) // NOLINT(google-build-using-namespace) + Catch::Generators::generate( \ + INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, [] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) // NOLINT(google-build-using-namespace) #define GENERATE_COPY(...) \ - Catch::Generators::generate(CATCH_INTERNAL_LINEINFO, [=] { \ - using namespace Catch::Generators; \ - return makeGenerators(__VA_ARGS__); \ - }) // NOLINT(google-build-using-namespace) + Catch::Generators::generate( \ + INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, [=] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) // NOLINT(google-build-using-namespace) #define GENERATE_REF(...) \ - Catch::Generators::generate(CATCH_INTERNAL_LINEINFO, [&] { \ - using namespace Catch::Generators; \ - return makeGenerators(__VA_ARGS__); \ - }) // NOLINT(google-build-using-namespace) + Catch::Generators::generate( \ + INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, [&] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) // NOLINT(google-build-using-namespace) // end catch_generators.hpp // start catch_generators_generic.hpp @@ -5015,7 +5043,7 @@ public: if (!m_predicate(m_generator.get())) { // It might happen that there are no values that pass the // filter. In that case we throw an exception. - auto has_initial_value = next(); + auto has_initial_value = nextImpl(); if (!has_initial_value) { Catch::throw_exception(GeneratorException( "No valid value found in filtered generator")); @@ -5025,7 +5053,10 @@ public: T const &get() const override { return m_generator.get(); } - bool next() override + bool next() override { return nextImpl(); } + +private: + bool nextImpl() { bool success = m_generator.next(); if (!success) { @@ -5378,6 +5409,7 @@ struct IConfig : NonCopyable { virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; + virtual double minDuration() const = 0; virtual TestSpec const &testSpec() const = 0; virtual bool hasTestFilters() const = 0; virtual std::vector const &getTestsOrTags() const = 0; @@ -6212,6 +6244,7 @@ struct ConfigData { Verbosity verbosity = Verbosity::Normal; WarnAbout::What warnings = WarnAbout::Nothing; ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; UseColour::YesOrNo useColour = UseColour::Auto; WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; @@ -6261,6 +6294,7 @@ public: bool warnAboutMissingAssertions() const override; bool warnAboutNoTests() const override; ShowDurations::OrNot showDurations() const override; + double minDuration() const override; RunTests::InWhatOrder runOrder() const override; unsigned int rngSeed() const override; UseColour::YesOrNo useColour() const override; @@ -6376,6 +6410,8 @@ struct OutlierClassification { } // namespace Catch // end catch_outlier_classification.hpp + +#include #endif // CATCH_CONFIG_ENABLE_BENCHMARKING #include @@ -6631,6 +6667,9 @@ void prepareExpandedExpression(AssertionResult &result); // Returns double formatted as %.3f (format expected on output) std::string getFormattedDuration(double duration); +//! Should the reporter show +bool shouldShowDuration(IConfig const &config, double duration); + std::string serializeFilters(std::vector const &container); template struct StreamingReporterBase : IStreamingReporter { @@ -7064,8 +7103,6 @@ struct CompactReporter : StreamingReporterBase { static std::string getDescription(); - ReporterPreferences getPreferences() const override; - void noMatchingTestCases(std::string const &spec) override; void assertionStarting(AssertionInfo const &) override; @@ -7270,6 +7307,7 @@ private: std::string m_indent; std::ostream &m_os; }; + } // end catch_xmlwriter.h @@ -7303,7 +7341,7 @@ public: void writeTestCase(TestCaseNode const &testCaseNode); void writeSection(std::string const &className, std::string const &rootName, - SectionNode const §ionNode); + SectionNode const §ionNode, bool testOkToFail); void writeAssertions(SectionNode const §ionNode); void writeAssertion(AssertionStats const &stats); @@ -7515,14 +7553,13 @@ template <> struct CompleteInvoker { return {}; } }; -template using ResultOf_t = typename std::result_of::type; // invoke and not return void :( template -CompleteType_t> complete_invoke( +CompleteType_t> complete_invoke( Fun &&fun, Args &&...args) { - return CompleteInvoker>::invoke( + return CompleteInvoker>::invoke( std::forward(fun), std::forward(args)...); } @@ -7530,7 +7567,7 @@ const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; } // namespace Detail template -Detail::CompleteType_t> user_code(Fun &&fun) +Detail::CompleteType_t> user_code(Fun &&fun) { CATCH_TRY { return Detail::complete_invoke(std::forward(fun)); } CATCH_CATCH_ALL @@ -7793,9 +7830,9 @@ template struct Timing { Result result; int iterations; }; -template +template using TimingOf = Timing, - Detail::CompleteType_t>>; + Detail::CompleteType_t>>; } // namespace Benchmark } // namespace Catch @@ -7806,7 +7843,7 @@ namespace Catch { namespace Benchmark { namespace Detail { template -TimingOf measure(Fun &&fun, Args &&...args) +TimingOf measure(Fun &&fun, Args &&...args) { auto start = Clock::now(); auto &&r = Detail::complete_invoke(fun, std::forward(args)...); @@ -7826,12 +7863,12 @@ namespace Catch { namespace Benchmark { namespace Detail { template -TimingOf measure_one(Fun &&fun, int iters, std::false_type) +TimingOf measure_one(Fun &&fun, int iters, std::false_type) { return Detail::measure(fun, iters); } template -TimingOf measure_one( +TimingOf measure_one( Fun &&fun, int iters, std::true_type) { Detail::ChronometerModel meter; @@ -7853,7 +7890,7 @@ struct optimized_away_error : std::exception { }; template -TimingOf)> run_for_at_least( +TimingOf> run_for_at_least( ClockDuration how_long, int seed, Fun &&fun) { auto iters = seed; @@ -7866,7 +7903,7 @@ TimingOf)> run_for_at_least( } iters *= 2; } - throw optimized_away_error{}; + Catch::throw_exception(optimized_away_error{}); } } // namespace Detail } // namespace Benchmark @@ -7874,6 +7911,7 @@ TimingOf)> run_for_at_least( // end catch_run_for_at_least.hpp #include +#include namespace Catch { namespace Benchmark { @@ -8068,8 +8106,8 @@ Estimate bootstrap(double confidence_level, Iterator first, double b2 = bias - z1; double a1 = a(b1); double a2 = a(b2); - auto lo = std::max(cumn(a1), 0); - auto hi = std::min(cumn(a2), n - 1); + auto lo = (std::max)(cumn(a1), 0); + auto hi = (std::min)(cumn(a2), n - 1); return {point, resample[lo], resample[hi], confidence_level}; } @@ -8149,7 +8187,7 @@ template EnvironmentEstimate> estimate_clock_cost( FloatDuration resolution) { - auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, + auto time_limit = (std::min)(resolution * clock_cost_estimation_tick_limit, FloatDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure([k] { @@ -8516,23 +8554,37 @@ struct NameAndLocation { SourceLineInfo location; NameAndLocation(std::string const &_name, SourceLineInfo const &_location); + friend bool operator==( + NameAndLocation const &lhs, NameAndLocation const &rhs) + { + return lhs.name == rhs.name && lhs.location == rhs.location; + } }; -struct ITracker; +class ITracker; using ITrackerPtr = std::shared_ptr; -struct ITracker { - virtual ~ITracker(); +class ITracker { + NameAndLocation m_nameAndLocation; + +public: + ITracker(NameAndLocation const &nameAndLoc) + : m_nameAndLocation(nameAndLoc) + { + } // static queries - virtual NameAndLocation const &nameAndLocation() const = 0; + NameAndLocation const &nameAndLocation() const { return m_nameAndLocation; } + + virtual ~ITracker(); // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed virtual bool isSuccessfullyCompleted() const = 0; virtual bool isOpen() const = 0; // Started but not complete virtual bool hasChildren() const = 0; + virtual bool hasStarted() const = 0; virtual ITracker &parent() = 0; @@ -8582,7 +8634,6 @@ protected: }; using Children = std::vector; - NameAndLocation m_nameAndLocation; TrackerContext &m_ctx; ITracker *m_parent; Children m_children; @@ -8592,11 +8643,11 @@ public: TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent); - NameAndLocation const &nameAndLocation() const override; bool isComplete() const override; bool isSuccessfullyCompleted() const override; bool isOpen() const override; bool hasChildren() const override; + bool hasStarted() const override { return m_runState != NotStarted; } void addChild(ITrackerPtr const &child) override; @@ -8638,6 +8689,10 @@ public: void addInitialFilters(std::vector const &filters); void addNextFilters(std::vector const &filters); + //! Returns filters active in this tracker + std::vector const &getFilters() const; + //! Returns whitespace-trimmed name of the tracked section + std::string const &trimmedName() const; }; } // namespace TestCaseTracking @@ -8658,6 +8713,7 @@ struct LeakDetector { LeakDetector(); ~LeakDetector(); }; + } // end catch_leak_detector.h // Cpp files will be included in the single-header file here @@ -8764,6 +8820,7 @@ double standard_deviation( }) / (last - first); return std::sqrt(variance); } + } namespace Catch { @@ -8810,7 +8867,7 @@ double outlier_variance(Estimate mean, Estimate stddev, int n) double sb = stddev.point; double mn = mean.point / n; double mg_min = mn / 2.; - double sg = std::min(mg_min / 4., sb / std::sqrt(n)); + double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); double sg2 = sg * sg; double sb2 = sb * sb; @@ -8829,7 +8886,8 @@ double outlier_variance(Estimate mean, Estimate stddev, int n) return (nc / n) * (sb2 - nc * sg2); }; - return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / + return (std::min)(var_out(1), + var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; } @@ -8900,6 +8958,7 @@ bool marginComparison(double lhs, double rhs, double margin) { return (lhs + margin >= rhs) && (rhs + margin >= lhs); } + } namespace Catch { @@ -8984,7 +9043,11 @@ bool isDebuggerActive(); #ifdef CATCH_PLATFORM_MAC +#if defined(__i386__) || defined(__x86_64__) #define CATCH_TRAP() __asm__("int $3\n" : :) /* NOLINT */ +#elif defined(__aarch64__) +#define CATCH_TRAP() __asm__(".inst 0xd4200000") +#endif #elif defined(CATCH_PLATFORM_IPHONE) @@ -9035,87 +9098,62 @@ extern "C" __declspec(dllimport) void __stdcall DebugBreak(); // start catch_fatal_condition.h -// start catch_windows_h_proxy.h - -#if defined(CATCH_PLATFORM_WINDOWS) - -#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -#define CATCH_DEFINED_NOMINMAX -#define NOMINMAX -#endif -#if !defined(WIN32_LEAN_AND_MEAN) && \ - !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -#define CATCH_DEFINED_WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - -#ifdef CATCH_DEFINED_NOMINMAX -#undef NOMINMAX -#endif -#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif - -#endif // defined(CATCH_PLATFORM_WINDOWS) - -// end catch_windows_h_proxy.h -#if defined(CATCH_CONFIG_WINDOWS_SEH) +#include namespace Catch { -struct FatalConditionHandler { +// Wrapper for platform-specific fatal error (signals/SEH) handlers +// +// Tries to be cooperative with other handlers, and not step over +// other handlers. This means that unknown structured exceptions +// are passed on, previous signal handlers are called, and so on. +// +// Can only be instantiated once, and assumes that once a signal +// is caught, the binary will end up terminating. Thus, there +class FatalConditionHandler { + bool m_started = false; - static LONG CALLBACK handleVectoredException( - PEXCEPTION_POINTERS ExceptionInfo); - FatalConditionHandler(); - static void reset(); - ~FatalConditionHandler(); - -private: - static bool isSet; - static ULONG guaranteeSize; - static PVOID exceptionHandlerHandle; -}; - -} // namespace Catch - -#elif defined(CATCH_CONFIG_POSIX_SIGNALS) - -#include - -namespace Catch { - -struct FatalConditionHandler { - - static bool isSet; - static struct sigaction oldSigActions[]; - static stack_t oldSigStack; - static char altStackMem[]; - - static void handleSignal(int sig); + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform(); + void disengage_platform(); +public: + // Should also have platform-specific implementations as needed FatalConditionHandler(); ~FatalConditionHandler(); - static void reset(); + + void engage() + { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() + { + assert(m_started && + "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } }; -} // namespace Catch +//! Simple RAII guard for (dis)engaging the FatalConditionHandler +class FatalConditionHandlerGuard { + FatalConditionHandler *m_handler; -#else - -namespace Catch { -struct FatalConditionHandler { - void reset(); +public: + FatalConditionHandlerGuard(FatalConditionHandler *handler) + : m_handler(handler) + { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { m_handler->disengage(); } }; -} -#endif +} // end namespace Catch // end catch_fatal_condition.h #include @@ -9167,8 +9205,8 @@ public: // IResultCapture void sectionEnded(SectionEndInfo const &endInfo) override; void sectionEndedEarly(SectionEndInfo const &endInfo) override; - auto acquireGeneratorTracker(SourceLineInfo const &lineInfo) - -> IGeneratorTracker & override; + auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker & override; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) void benchmarkPreparing(std::string const &name) override; @@ -9231,6 +9269,7 @@ private: std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; bool m_lastAssertionPassed = false; bool m_shouldReportUnexpected = true; bool m_includeSuccessfulResults; @@ -9930,6 +9969,7 @@ inline auto Column::operator+(Column const &other) -> Columns return cols; } } + } } @@ -10281,7 +10321,7 @@ inline auto convertInto(std::string const &source, bool &target) -> ParserResult { std::string srcLC = source; std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), - [](char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return static_cast(std::tolower(c)); }); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; @@ -10982,6 +11022,7 @@ using detail::ParseResultType; // Result type for parser operation using detail::ParserResult; + } } // namespace Catch::clara @@ -11160,6 +11201,9 @@ clara::Parser makeCommandLineParser(ConfigData &config) flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no")["-d"]["--durations"]("show test durations") | + Opt(config.minDuration, "seconds")["-D"]["--min-duration"]( + "show test durations for tests taking at least the given number of " + "seconds") | Opt(loadTestNamesFromFile, "filename")["-f"]["--input-file"]( "load test names to run from a file") | Opt(config.filenamesAsTags)["-#"]["--filenames-as-tags"]( @@ -11239,6 +11283,7 @@ std::string StreamEndStop::operator+() const { return std::string(); } NonCopyable::NonCopyable() = default; NonCopyable::~NonCopyable() = default; + } // end catch_common.cpp // start catch_config.cpp @@ -11320,6 +11365,7 @@ ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } +double Config::minDuration() const { return m_data.minDuration; } RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } unsigned int Config::rngSeed() const { return m_data.rngSeed; } UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } @@ -11369,9 +11415,40 @@ public: private: int m_oldErrno; }; + } // end catch_errno_guard.h +// start catch_windows_h_proxy.h + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +#define CATCH_DEFINED_NOMINMAX +#define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && \ + !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +#define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +#undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h #include namespace Catch { @@ -11688,6 +11765,7 @@ SimplePcg32 &rng() static SimplePcg32 s_rng; return s_rng; } + } // end catch_context.cpp // start catch_debug_console.cpp @@ -11736,10 +11814,9 @@ void writeToDebugConsole(std::string const &text) #if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) -#include +#include #include #include -#include #include #include @@ -11940,7 +12017,7 @@ namespace { // In other words, it returns the Blue part of Bikeshed::Colour::Blue StringRef extractInstanceName(StringRef enumInstance) { - // Find last occurence of ":" + // Find last occurrence of ":" size_t name_start = enumInstance.size(); while (name_start > 0 && enumInstance[name_start - 1] != ':') { --name_start; @@ -12116,31 +12193,55 @@ std::string ExceptionTranslatorRegistry::tryTranslators() const "CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } #endif + } // end catch_exception_translator_registry.cpp // start catch_fatal_condition.cpp -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif +#include + +#if !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + +// If neither SEH nor signal handling is required, the handler impls +// do not have to do anything, and can be empty. +void FatalConditionHandler::engage_platform() { } +void FatalConditionHandler::disengage_platform() { } +FatalConditionHandler::FatalConditionHandler() = default; +FatalConditionHandler::~FatalConditionHandler() = default; + +} // end namespace Catch + +#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS + +#if defined(CATCH_CONFIG_WINDOWS_SEH) && defined(CATCH_CONFIG_POSIX_SIGNALS) +#error \ + "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" +#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS #if defined(CATCH_CONFIG_WINDOWS_SEH) || defined(CATCH_CONFIG_POSIX_SIGNALS) namespace { -// Report the error condition +//! Signals fatal error message to the run context void reportFatal(char const *const message) { Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message); } -} -#endif // signals/SEH handling +//! Minimal size Catch2 needs for its own fatal error handling. +//! Picked anecdotally, so it might not be sufficient on all +//! platforms, and for all configurations. +constexpr std::size_t minStackSizeForErrors = 32 * 1024; +} // end unnamed namespace + +#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS #if defined(CATCH_CONFIG_WINDOWS_SEH) namespace Catch { + struct SignalDefs { DWORD id; const char *name; @@ -12158,8 +12259,7 @@ static SignalDefs signalDefs[] = { {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, }; -LONG CALLBACK FatalConditionHandler::handleVectoredException( - PEXCEPTION_POINTERS ExceptionInfo) +static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { for (auto const &def : signalDefs) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { @@ -12171,39 +12271,53 @@ LONG CALLBACK FatalConditionHandler::handleVectoredException( return EXCEPTION_CONTINUE_SEARCH; } +// Since we do not support multiple instantiations, we put these +// into global variables and rely on cleaning them up in outlined +// constructors/destructors +static PVOID exceptionHandlerHandle = nullptr; + +// For MSVC, we reserve part of the stack memory for handling +// memory overflow structured exception. FatalConditionHandler::FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = nullptr; - // Register as first handler in current chain - exceptionHandlerHandle = - AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); -} - -void FatalConditionHandler::reset() -{ - if (isSet) { - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = nullptr; - isSet = false; + ULONG guaranteeSize = static_cast(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; } } -FatalConditionHandler::~FatalConditionHandler() { reset(); } +// We do not attempt to unset the stack guarantee, because +// Windows does not support lowering the stack size guarantee. +FatalConditionHandler::~FatalConditionHandler() = default; -bool FatalConditionHandler::isSet = false; -ULONG FatalConditionHandler::guaranteeSize = 0; -PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; +void FatalConditionHandler::engage_platform() +{ + // Register as first handler in current chain + exceptionHandlerHandle = + AddVectoredExceptionHandler(1, handleVectoredException); + if (!exceptionHandlerHandle) { + CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); + } +} -} // namespace Catch +void FatalConditionHandler::disengage_platform() +{ + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); + } + exceptionHandlerHandle = nullptr; +} -#elif defined(CATCH_CONFIG_POSIX_SIGNALS) +} // end namespace Catch + +#endif // CATCH_CONFIG_WINDOWS_SEH + +#if defined(CATCH_CONFIG_POSIX_SIGNALS) + +#include namespace Catch { @@ -12212,11 +12326,6 @@ struct SignalDefs { const char *name; }; -// 32kb for the alternate stack seems to be sufficient. However, this value -// is experimentally determined, so that's not guaranteed. -static constexpr std::size_t sigStackSize = - 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; - static SignalDefs signalDefs[] = { {SIGINT, "SIGINT - Terminal interrupt signal"}, {SIGILL, "SIGILL - Illegal instruction signal"}, @@ -12225,7 +12334,34 @@ static SignalDefs signalDefs[] = { {SIGTERM, "SIGTERM - Termination request signal"}, {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; -void FatalConditionHandler::handleSignal(int sig) +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +static char *altStackMem = nullptr; +static std::size_t altStackSize = 0; +static stack_t oldSigStack{}; +static struct sigaction + oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + +static void restorePreviousSignalHandlers() +{ + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); +} + +static void handleSignal(int sig) { char const *name = ""; for (auto const &def : signalDefs) { @@ -12234,17 +12370,38 @@ void FatalConditionHandler::handleSignal(int sig) break; } } - reset(); + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); reportFatal(name); raise(sig); } FatalConditionHandler::FatalConditionHandler() { - isSet = true; + assert(!altStackMem && + "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = + std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + } + altStackMem = new char[altStackSize](); +} + +FatalConditionHandler::~FatalConditionHandler() +{ + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; +} + +void FatalConditionHandler::engage_platform() +{ stack_t sigStack; sigStack.ss_sp = altStackMem; - sigStack.ss_size = sigStackSize; + sigStack.ss_size = altStackSize; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = {}; @@ -12256,42 +12413,18 @@ FatalConditionHandler::FatalConditionHandler() } } -FatalConditionHandler::~FatalConditionHandler() { reset(); } - -void FatalConditionHandler::reset() -{ - if (isSet) { - // Set signals back to previous values -- hopefully nobody overwrote - // them in the meantime - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); - ++i) { - sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); - } - // Return the old stack - sigaltstack(&oldSigStack, nullptr); - isSet = false; - } -} - -bool FatalConditionHandler::isSet = false; -struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs) / - sizeof(SignalDefs)] = {}; -stack_t FatalConditionHandler::oldSigStack = {}; -char FatalConditionHandler::altStackMem[sigStackSize] = {}; - -} // namespace Catch - -#else - -namespace Catch { -void FatalConditionHandler::reset() { } -} - -#endif // signals/SEH handling - #if defined(__GNUC__) #pragma GCC diagnostic pop #endif + +void FatalConditionHandler::disengage_platform() +{ + restorePreviousSignalHandlers(); +} + +} // end namespace Catch + +#endif // CATCH_CONFIG_POSIX_SIGNALS // end catch_fatal_condition.cpp // start catch_generators.cpp @@ -12308,10 +12441,10 @@ namespace Generators { GeneratorUntypedBase::~GeneratorUntypedBase() { } -auto acquireGeneratorTracker(SourceLineInfo const &lineInfo) - -> IGeneratorTracker & +auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker & { - return getResultCapture().acquireGeneratorTracker(lineInfo); + return getResultCapture().acquireGeneratorTracker(generatorName, lineInfo); } } // namespace Generators @@ -12796,6 +12929,7 @@ std::string ExceptionMessageMatcher::describe() const { return "exception message matches \"" + m_message + "\""; } + } Exception::ExceptionMessageMatcher Message(std::string const &message) { @@ -12881,7 +13015,8 @@ template bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) return lhs == rhs; } - auto ulpDiff = std::abs(lc - rc); + // static cast as a workaround for IBM XLC + auto ulpDiff = std::abs(static_cast(lc - rc)); return static_cast(ulpDiff) <= maxUlpDiff; } @@ -13088,7 +13223,6 @@ Floating::WithinRelMatcher WithinRel(float target) } // namespace Matchers } // namespace Catch - // end catch_matchers_floating.cpp // start catch_matchers_generic.cpp @@ -13322,10 +13456,12 @@ Capturer::Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, StringRef names) { auto trimmed = [&](size_t start, size_t end) { - while (names[start] == ',' || isspace(names[start])) { + while (names[start] == ',' || + isspace(static_cast(names[start]))) { ++start; } - while (names[end] == ',' || isspace(names[end])) { + while (names[end] == ',' || + isspace(static_cast(names[end]))) { --end; } return names.substr(start, end - start + 1); @@ -13575,7 +13711,7 @@ TempFile::TempFile() if (tmpnam_s(m_buffer)) { CATCH_RUNTIME_ERROR("Could not get a temp filename"); } - if (fopen_s(&m_file, m_buffer, "w")) { + if (fopen_s(&m_file, m_buffer, "w+")) { char buffer[100]; if (strerror_s(buffer, errno)) { CATCH_RUNTIME_ERROR("Could not translate errno to a string"); @@ -13697,6 +13833,7 @@ uint32_t rotate_right(uint32_t val, uint32_t count) #if defined(_MSC_VER) #pragma warning(pop) #endif + } SimplePcg32::SimplePcg32(result_type seed_) { seed(seed_); } @@ -13880,12 +14017,14 @@ private: namespace Catch { class StartupExceptionRegistry { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) public: void add(std::exception_ptr const &exception) noexcept; std::vector const &getExceptions() const noexcept; private: std::vector m_exceptions; +#endif }; } // end namespace Catch @@ -13981,7 +14120,12 @@ public: // IMutableRegistryHub } void registerStartupException() noexcept override { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) m_exceptionRegistry.add(std::current_exception()); +#else + CATCH_INTERNAL_ERROR("Attempted to register active exception under " + "CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif } IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() override { @@ -14053,6 +14197,7 @@ IReporterRegistry::Listeners const &ReporterRegistry::getListeners() const { return m_listeners; } + } // end catch_reporter_registry.cpp // start catch_result_type.cpp @@ -14108,8 +14253,26 @@ struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { std::shared_ptr tracker; ITracker ¤tTracker = ctx.currentTracker(); - if (TestCaseTracking::ITrackerPtr childTracker = - currentTracker.findChild(nameAndLocation)) { + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if (currentTracker.nameAndLocation() == nameAndLocation) { + auto thisTracker = + currentTracker.parent().findChild(nameAndLocation); + assert(thisTracker); + assert(thisTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast(thisTracker); + } + else if (TestCaseTracking::ITrackerPtr childTracker = + currentTracker.findChild(nameAndLocation)) { assert(childTracker); assert(childTracker->isGeneratorTracker()); tracker = std::static_pointer_cast(childTracker); @@ -14120,7 +14283,7 @@ struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { currentTracker.addChild(tracker); } - if (!ctx.completedCycle() && !tracker->isComplete()) { + if (!tracker->isComplete()) { tracker->open(); } @@ -14133,9 +14296,61 @@ struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { void close() override { TrackerBase::close(); - // Generator interface only finds out if it has another item on atual - // move - if (m_runState == CompletedSuccessfully && m_generator->next()) { + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if (m_children.empty()) { + return false; + } + // If at least one child started executing, don't wait + if (std::find_if(m_children.begin(), m_children.end(), + [](TestCaseTracking::ITrackerPtr tracker) { + return tracker->hasStarted(); + }) != m_children.end()) { + return false; + } + + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + auto *parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while (!parent->isSectionTracker()) { + parent = &(parent->parent()); + } + assert(parent && "Missing root (test case) level section"); + + auto const &parentSection = static_cast(*parent); + auto const &filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if (filters.empty()) { + return true; + } + + for (auto const &child : m_children) { + if (child->isSectionTracker() && + std::find(filters.begin(), filters.end(), + static_cast(*child).trimmedName()) != + filters.end()) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if (should_wait_for_child || + (m_runState == CompletedSuccessfully && m_generator->next())) { m_children.clear(); m_runState = Executing; } @@ -14289,13 +14504,13 @@ bool RunContext::sectionStarted( return true; } -auto RunContext::acquireGeneratorTracker(SourceLineInfo const &lineInfo) - -> IGeneratorTracker & +auto RunContext::acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker & { using namespace Generators; GeneratorTracker &tracker = GeneratorTracker::acquire(m_trackerContext, - TestCaseTracking::NameAndLocation("generator", lineInfo)); - assert(tracker.isOpen()); + TestCaseTracking::NameAndLocation( + static_cast(generatorName), lineInfo)); m_lastAssertionInfo.lineInfo = lineInfo; return tracker; } @@ -14505,9 +14720,8 @@ void RunContext::runCurrentTest( void RunContext::invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals + FatalConditionHandlerGuard _(&m_fatalConditionhandler); m_activeTestCase->invoke(); - fatalConditionHandler.reset(); } void RunContext::handleUnfinishedSections() @@ -14636,6 +14850,7 @@ void seedRng(IConfig const &config) } unsigned int rngSeed() { return getCurrentContext().getConfig()->rngSeed(); } + } // end catch_run_context.cpp // start catch_section.cpp @@ -15111,6 +15326,7 @@ void cleanupSingletons() // end catch_singletons.cpp // start catch_startup_exception_registry.cpp +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) namespace Catch { void StartupExceptionRegistry::add(std::exception_ptr const &exception) noexcept { @@ -15130,6 +15346,7 @@ StartupExceptionRegistry::getExceptions() const noexcept } } // end namespace Catch +#endif // end catch_startup_exception_registry.cpp // start catch_stream.cpp @@ -15242,6 +15459,7 @@ public: public: // IStream std::ostream &stream() const override { return m_os; } }; + } } // namespace anon::detail @@ -15329,7 +15547,10 @@ std::ostream &clog() { return std::clog; } namespace Catch { namespace { -char toLowerCh(char c) { return static_cast(std::tolower(c)); } +char toLowerCh(char c) +{ + return static_cast(std::tolower(static_cast(c))); +} } bool startsWith(std::string const &s, std::string const &prefix) @@ -15436,6 +15657,7 @@ std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser) os << 's'; return os; } + } // end catch_string_manip.cpp // start catch_stringref.cpp @@ -15517,6 +15739,7 @@ RegistrarForTagAliases::RegistrarForTagAliases( getMutableRegistryHub().registerStartupException(); } } + } // end catch_tag_alias_autoregistrar.cpp // start catch_tag_alias_registry.cpp @@ -15757,29 +15980,86 @@ TestCaseInfo const &TestCase::getTestCaseInfo() const { return *this; } // end catch_test_case_info.cpp // start catch_test_case_registry_impl.cpp +#include #include namespace Catch { +namespace { +struct TestHasher { + using hash_t = uint64_t; + + explicit TestHasher(hash_t hashSuffix) + : m_hashSuffix{hashSuffix} + { + } + + uint32_t operator()(TestCase const &t) const + { + // FNV-1a hash with multiplication fold. + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for (const char c : t.name) { + hash ^= c; + hash *= prime; + } + hash ^= m_hashSuffix; + hash *= prime; + const uint32_t low{static_cast(hash)}; + const uint32_t high{static_cast(hash >> 32)}; + return low * high; + } + +private: + hash_t m_hashSuffix; +}; +} // end unnamed namespace + std::vector sortTests( IConfig const &config, std::vector const &unsortedTestCases) { - - std::vector sorted = unsortedTestCases; - switch (config.runOrder()) { - case RunTests::InLexicographicalOrder: - std::sort(sorted.begin(), sorted.end()); - break; - case RunTests::InRandomOrder: - seedRng(config); - std::shuffle(sorted.begin(), sorted.end(), rng()); - break; case RunTests::InDeclarationOrder: // already in declaration order break; + + case RunTests::InLexicographicalOrder: { + std::vector sorted = unsortedTestCases; + std::sort(sorted.begin(), sorted.end()); + return sorted; } - return sorted; + + case RunTests::InRandomOrder: { + seedRng(config); + TestHasher h{config.rngSeed()}; + + using hashedTest = std::pair; + std::vector indexed_tests; + indexed_tests.reserve(unsortedTestCases.size()); + + for (auto const &testCase : unsortedTestCases) { + indexed_tests.emplace_back(h(testCase), &testCase); + } + + std::sort(indexed_tests.begin(), indexed_tests.end(), + [](hashedTest const &lhs, hashedTest const &rhs) { + if (lhs.first == rhs.first) { + return lhs.second->name < rhs.second->name; + } + return lhs.first < rhs.first; + }); + + std::vector sorted; + sorted.reserve(indexed_tests.size()); + + for (auto const &hashed : indexed_tests) { + sorted.emplace_back(*hashed.second); + } + + return sorted; + } + } + return unsortedTestCases; } bool isThrowSafe(TestCase const &testCase, IConfig const &config) @@ -15937,16 +16217,12 @@ void TrackerContext::setCurrentTracker(ITracker *tracker) TrackerBase::TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) - : m_nameAndLocation(nameAndLocation) + : ITracker(nameAndLocation) , m_ctx(ctx) , m_parent(parent) { } -NameAndLocation const &TrackerBase::nameAndLocation() const -{ - return m_nameAndLocation; -} bool TrackerBase::isComplete() const { return m_runState == CompletedSuccessfully || m_runState == Failed; @@ -16068,7 +16344,7 @@ bool SectionTracker::isComplete() const { bool complete = true; - if ((m_filters.empty() || m_filters[0] == "") || + if (m_filters.empty() || m_filters[0] == "" || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { complete = TrackerBase::isComplete(); @@ -16120,6 +16396,16 @@ void SectionTracker::addNextFilters(std::vector const &filters) m_filters.insert(m_filters.end(), filters.begin() + 1, filters.end()); } +std::vector const &SectionTracker::getFilters() const +{ + return m_filters; +} + +std::string const &SectionTracker::trimmedName() const +{ + return m_trimmed_name; +} + } // namespace TestCaseTracking using TestCaseTracking::ITracker; @@ -16262,6 +16548,7 @@ const TestSpec::vectorStrings &TestSpec::getInvalidArgs() const { return (m_invalidArgs); } + } // end catch_test_spec.cpp // start catch_test_spec_parser.cpp @@ -16435,6 +16722,7 @@ bool TestSpecParser::separate() m_pos = m_arg.size(); m_substring.clear(); m_patternName.clear(); + m_realPatternPos = 0; return false; } endMode(); @@ -16455,6 +16743,7 @@ std::string TestSpecParser::preprocessPattern() } m_patternName.clear(); + m_realPatternPos = 0; return token; } @@ -16918,16 +17207,54 @@ Totals Totals::delta(Totals const &prevTotals) const ++diff.testCases.passed; return diff; } + } // end catch_totals.cpp // start catch_uncaught_exceptions.cpp +// start catch_config_uncaught_exceptions.hpp + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP + +#if defined(_MSC_VER) +#if _MSC_VER >= 1900 // Visual Studio 2015 or newer +#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif +#endif + +#include + +#if defined(__cpp_lib_uncaught_exceptions) && \ + !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif // __cpp_lib_uncaught_exceptions + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && \ + !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && \ + !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +// end catch_config_uncaught_exceptions.hpp #include namespace Catch { bool uncaught_exceptions() { -#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) return std::uncaught_exceptions() > 0; #else return std::uncaught_exception(); @@ -16965,9 +17292,10 @@ std::ostream &operator<<(std::ostream &os, Version const &version) Version const &libraryVersion() { - static Version version(2, 11, 3, "", 0); + static Version version(2, 13, 8, "", 0); return version; } + } // end catch_version.cpp // start catch_wildcard_pattern.cpp @@ -17139,10 +17467,10 @@ void XmlEncode::encodeTo(std::ostream &os) const // UTF-8 territory // Check if the encoding is valid and if it is not, hex escape - // bytes. Important: We do not check the exact decoded values - // for validity, only the encoding format First check that this - // bytes is a valid lead byte: This means that it is not encoded - // as 1111 1XXX Or as 10XX XXXX + // bytes. Important: We do not check the exact decoded values for + // validity, only the encoding format First check that this bytes is + // a valid lead byte: This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX if (c < 0xC0 || c >= 0xF8) { hexEscapeChar(os, c); break; @@ -17157,8 +17485,8 @@ void XmlEncode::encodeTo(std::ostream &os) const } // The header is valid, check data // The next encBytes bytes must together be a valid utf-8 - // This means: bitpattern 10XX XXXX and the extracted value is - // sane (ish) + // This means: bitpattern 10XX XXXX and the extracted value is sane + // (ish) bool valid = true; uint32_t value = headerValue(c); for (std::size_t n = 1; n < encBytes; ++n) { @@ -17409,6 +17737,18 @@ std::string getFormattedDuration(double duration) return std::string(buffer); } +bool shouldShowDuration(IConfig const &config, double duration) +{ + if (config.showDurations() == ShowDurations::Always) { + return true; + } + if (config.showDurations() == ShowDurations::Never) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; +} + std::string serializeFilters(std::vector const &container) { ReusableStringStream oss; @@ -17698,11 +18038,6 @@ std::string CompactReporter::getDescription() return "Reports test results on a single line, suitable for IDEs"; } -ReporterPreferences CompactReporter::getPreferences() const -{ - return m_reporterPrefs; -} - void CompactReporter::noMatchingTestCases(std::string const &spec) { stream << "No test cases matched '" << spec << '\'' << std::endl; @@ -17732,8 +18067,9 @@ bool CompactReporter::assertionEnded(AssertionStats const &_assertionStats) void CompactReporter::sectionEnded(SectionStats const &_sectionStats) { - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } @@ -18133,7 +18469,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig const &config) {" mean", 14, ColumnInfo::Right}}; } else { - return {{"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, + return {{"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left}, {"samples mean std dev", 14, ColumnInfo::Right}, @@ -18201,8 +18537,9 @@ void ConsoleReporter::sectionEnded(SectionStats const &_sectionStats) stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; } if (m_headerPrinted) { @@ -18527,6 +18864,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter) #include #include #include +#include #include namespace Catch { @@ -18556,7 +18894,7 @@ std::string getCurrentTimestamp() #else std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return std::string(timeStamp); + return std::string(timeStamp, timeStampSize - 1); } std::string fileNameTag(const std::vector &tags) @@ -18567,6 +18905,18 @@ std::string fileNameTag(const std::vector &tags) return it->substr(1); return std::string(); } + +// Formats the duration in seconds to 3 decimal places. +// This is done because some genius defined Maven Surefire schema +// in a way that only accepts 3 decimal places, and tools like +// Jenkins use that schema for validation JUnit reporter output. +std::string formatDuration(double seconds) +{ + ReusableStringStream rss; + rss << std::fixed << std::setprecision(3) << seconds; + return rss.str(); +} + } // anonymous namespace JunitReporter::JunitReporter(ReporterConfig const &_config) @@ -18646,7 +18996,7 @@ void JunitReporter::writeGroup(TestGroupNode const &groupNode, double suiteTime) if (m_config->showDurations() == ShowDurations::Never) xml.writeAttribute("time", ""); else - xml.writeAttribute("time", suiteTime); + xml.writeAttribute("time", formatDuration(suiteTime)); xml.writeAttribute("timestamp", getCurrentTimestamp()); // Write properties if there are any @@ -18695,11 +19045,12 @@ void JunitReporter::writeTestCase(TestCaseNode const &testCaseNode) if (!m_config->name().empty()) className = m_config->name() + "." + className; - writeSection(className, "", rootSection); + writeSection(className, "", rootSection, stats.testInfo.okToFail()); } void JunitReporter::writeSection(std::string const &className, - std::string const &rootName, SectionNode const §ionNode) + std::string const &rootName, SectionNode const §ionNode, + bool testOkToFail) { std::string name = trim(sectionNode.stats.sectionInfo.name); if (!rootName.empty()) @@ -18716,8 +19067,18 @@ void JunitReporter::writeSection(std::string const &className, xml.writeAttribute("classname", className); xml.writeAttribute("name", name); } - xml.writeAttribute("time", - ::Catch::Detail::stringify(sectionNode.stats.durationInSeconds)); + xml.writeAttribute( + "time", formatDuration(sectionNode.stats.durationInSeconds)); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute("status", "run"); + + if (sectionNode.stats.assertions.failedButOk) { + xml.scopedElement("skipped").writeAttribute( + "message", "TEST_CASE tagged with !mayfail"); + } writeAssertions(sectionNode); @@ -18730,9 +19091,9 @@ void JunitReporter::writeSection(std::string const &className, } for (auto const &childNode : sectionNode.childSections) if (className.empty()) - writeSection(name, "", *childNode); + writeSection(name, "", *childNode, testOkToFail); else - writeSection(className, name, *childNode); + writeSection(className, name, *childNode, testOkToFail); } void JunitReporter::writeAssertions(SectionNode const §ionNode) @@ -19191,6 +19552,11 @@ void XmlReporter::testGroupEnded(TestGroupStats const &testGroupStats) .writeAttribute("failures", testGroupStats.totals.assertions.failed) .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", testGroupStats.totals.testCases.passed) + .writeAttribute("failures", testGroupStats.totals.testCases.failed) + .writeAttribute( + "expectedFailures", testGroupStats.totals.testCases.failedButOk); m_xml.endElement(); } @@ -19202,6 +19568,11 @@ void XmlReporter::testRunEnded(TestRunStats const &testRunStats) .writeAttribute("failures", testRunStats.totals.assertions.failed) .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", testRunStats.totals.testCases.passed) + .writeAttribute("failures", testRunStats.totals.testCases.failed) + .writeAttribute( + "expectedFailures", testRunStats.totals.testCases.failedButOk); m_xml.endElement(); } @@ -19509,13 +19880,12 @@ int main(int argc, char *const argv[]) #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) #define CATCH_BENCHMARK(...) \ - INTERNAL_CATCH_BENCHMARK( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), \ INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), \ INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) #define CATCH_BENCHMARK_ADVANCED(name) \ INTERNAL_CATCH_BENCHMARK_ADVANCED( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) #endif // CATCH_CONFIG_ENABLE_BENCHMARKING // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not @@ -19711,13 +20081,12 @@ int main(int argc, char *const argv[]) #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) #define BENCHMARK(...) \ - INTERNAL_CATCH_BENCHMARK( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), \ INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), \ INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) #define BENCHMARK_ADVANCED(name) \ INTERNAL_CATCH_BENCHMARK_ADVANCED( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) #endif // CATCH_CONFIG_ENABLE_BENCHMARKING using Catch::Detail::Approx; @@ -19767,10 +20136,10 @@ using Catch::Detail::Approx; #define CATCH_TEST_CASE(...) \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #define CATCH_TEST_CASE_METHOD(className, ...) \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #define CATCH_METHOD_AS_TEST_CASE(method, ...) #define CATCH_REGISTER_TEST_CASE(Function, ...) (void)(0) #define CATCH_SECTION(...) @@ -19781,7 +20150,7 @@ using Catch::Detail::Approx; #define CATCH_ANON_TEST_CASE() \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define CATCH_TEMPLATE_TEST_CASE(...) \ @@ -19830,10 +20199,10 @@ using Catch::Detail::Approx; // "BDD-style" convenience wrappers #define CATCH_SCENARIO(...) \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #define CATCH_SCENARIO_METHOD(className, ...) \ INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), className) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), className) #define CATCH_GIVEN(desc) #define CATCH_AND_GIVEN(desc) #define CATCH_WHEN(desc) @@ -19886,10 +20255,10 @@ using Catch::Detail::Approx; #define TEST_CASE(...) \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #define TEST_CASE_METHOD(className, ...) \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #define METHOD_AS_TEST_CASE(method, ...) #define REGISTER_TEST_CASE(Function, ...) (void)(0) #define SECTION(...) @@ -19899,7 +20268,7 @@ using Catch::Detail::Approx; #define SUCCEED(...) (void)(0) #define ANON_TEST_CASE() \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define TEMPLATE_TEST_CASE(...) \ @@ -19954,10 +20323,10 @@ using Catch::Detail::Approx; // "BDD-style" convenience wrappers #define SCENARIO(...) \ INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_)) #define SCENARIO_METHOD(className, ...) \ INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( \ - INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), className) + INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_T_E_S_T_), className) #define GIVEN(desc) #define AND_GIVEN(desc) diff --git a/tests/t00002/test_case.h b/tests/t00002/test_case.h index 7508d570..30c74df7 100644 --- a/tests/t00002/test_case.h +++ b/tests/t00002/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00002/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00003/test_case.h b/tests/t00003/test_case.h index 98db2116..bb5c1099 100644 --- a/tests/t00003/test_case.h +++ b/tests/t00003/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00003/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00004/test_case.h b/tests/t00004/test_case.h index 0d1c2e66..a8def959 100644 --- a/tests/t00004/test_case.h +++ b/tests/t00004/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00004/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00005/test_case.h b/tests/t00005/test_case.h index 7935a758..c16948e3 100644 --- a/tests/t00005/test_case.h +++ b/tests/t00005/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00005/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00006/test_case.h b/tests/t00006/test_case.h index b96d68e3..a289b6b2 100644 --- a/tests/t00006/test_case.h +++ b/tests/t00006/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00006/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00007/test_case.h b/tests/t00007/test_case.h index 427bcbf0..20531158 100644 --- a/tests/t00007/test_case.h +++ b/tests/t00007/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00007/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00008/test_case.h b/tests/t00008/test_case.h index 24bc1bc1..54576257 100644 --- a/tests/t00008/test_case.h +++ b/tests/t00008/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00008/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00009/test_case.h b/tests/t00009/test_case.h index cea1288f..5d6c1fff 100644 --- a/tests/t00009/test_case.h +++ b/tests/t00009/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00009/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00010/test_case.h b/tests/t00010/test_case.h index c99465a1..83ba5091 100644 --- a/tests/t00010/test_case.h +++ b/tests/t00010/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00010/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00011/test_case.h b/tests/t00011/test_case.h index c6dac32c..e7b79f96 100644 --- a/tests/t00011/test_case.h +++ b/tests/t00011/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00011/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00012/test_case.h b/tests/t00012/test_case.h index 426fafe6..22b71055 100644 --- a/tests/t00012/test_case.h +++ b/tests/t00012/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00012/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00013/test_case.h b/tests/t00013/test_case.h index 9d01e55a..5b1d20f3 100644 --- a/tests/t00013/test_case.h +++ b/tests/t00013/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00013/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00014/test_case.h b/tests/t00014/test_case.h index c843de34..56eecac5 100644 --- a/tests/t00014/test_case.h +++ b/tests/t00014/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00014/test_case.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,8 @@ TEST_CASE("t00014", "[test-case][class]") REQUIRE_THAT(puml, IsClassTemplate("A", "T,std::string")); REQUIRE_THAT(puml, IsClassTemplate("A", "bool,std::string")); REQUIRE_THAT(puml, IsClassTemplate("AString", "float")); + REQUIRE_THAT( + puml, !IsClassTemplate("std::std::function", "void(T...,int),int)")); REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); REQUIRE_THAT( diff --git a/tests/t00015/test_case.h b/tests/t00015/test_case.h index 0ea9d5f7..839f51f0 100644 --- a/tests/t00015/test_case.h +++ b/tests/t00015/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00015/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00016/test_case.h b/tests/t00016/test_case.h index 0f617472..4a51546c 100644 --- a/tests/t00016/test_case.h +++ b/tests/t00016/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00016/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00017/test_case.h b/tests/t00017/test_case.h index e31e27b7..767331b6 100644 --- a/tests/t00017/test_case.h +++ b/tests/t00017/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00017/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00018/test_case.h b/tests/t00018/test_case.h index d1ca11d7..4e896807 100644 --- a/tests/t00018/test_case.h +++ b/tests/t00018/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00018/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00019/test_case.h b/tests/t00019/test_case.h index 48fbaf5f..45d962bb 100644 --- a/tests/t00019/test_case.h +++ b/tests/t00019/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00019/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00020/test_case.h b/tests/t00020/test_case.h index 2aeb53ba..16e4e618 100644 --- a/tests/t00020/test_case.h +++ b/tests/t00020/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00020/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00021/test_case.h b/tests/t00021/test_case.h index f96f965f..7d10eb08 100644 --- a/tests/t00021/test_case.h +++ b/tests/t00021/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00021/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00022/test_case.h b/tests/t00022/test_case.h index 2cdb5b35..bd4bfcbb 100644 --- a/tests/t00022/test_case.h +++ b/tests/t00022/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00022/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00023/test_case.h b/tests/t00023/test_case.h index 8216c224..8503cab5 100644 --- a/tests/t00023/test_case.h +++ b/tests/t00023/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00023/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00024/test_case.h b/tests/t00024/test_case.h index 8b347b38..9082e2e4 100644 --- a/tests/t00024/test_case.h +++ b/tests/t00024/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00024/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00025/test_case.h b/tests/t00025/test_case.h index a5283d1b..6612f228 100644 --- a/tests/t00025/test_case.h +++ b/tests/t00025/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00025/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00026/test_case.h b/tests/t00026/test_case.h index ebd3ace6..f72120e9 100644 --- a/tests/t00026/test_case.h +++ b/tests/t00026/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00026/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00027/test_case.h b/tests/t00027/test_case.h index 803d97fe..378fbbeb 100644 --- a/tests/t00027/test_case.h +++ b/tests/t00027/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00027/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00028/test_case.h b/tests/t00028/test_case.h index 76426fbd..d0badd41 100644 --- a/tests/t00028/test_case.h +++ b/tests/t00028/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00028/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00029/test_case.h b/tests/t00029/test_case.h index d795fe66..54e53319 100644 --- a/tests/t00029/test_case.h +++ b/tests/t00029/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00029/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00030/test_case.h b/tests/t00030/test_case.h index 5de10b77..b6e5b3f4 100644 --- a/tests/t00030/test_case.h +++ b/tests/t00030/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00030/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00031/test_case.h b/tests/t00031/test_case.h index 8f52d254..e6191d66 100644 --- a/tests/t00031/test_case.h +++ b/tests/t00031/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00031/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00032/test_case.h b/tests/t00032/test_case.h index 1d245b01..87a54cae 100644 --- a/tests/t00032/test_case.h +++ b/tests/t00032/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00032/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00033/test_case.h b/tests/t00033/test_case.h index ceec5c6e..0c05f1ef 100644 --- a/tests/t00033/test_case.h +++ b/tests/t00033/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00033/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t00034/test_case.h b/tests/t00034/test_case.h index e2103835..56f264ea 100644 --- a/tests/t00034/test_case.h +++ b/tests/t00034/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00034/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t20001/test_case.h b/tests/t20001/test_case.h index 36027af3..0e2660b2 100644 --- a/tests/t20001/test_case.h +++ b/tests/t20001/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20001/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t20002/test_case.h b/tests/t20002/test_case.h index 01e80f02..90ea374d 100644 --- a/tests/t20002/test_case.h +++ b/tests/t20002/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20002/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/t30001/.clang-uml b/tests/t30001/.clang-uml new file mode 100644 index 00000000..1e5f3625 --- /dev/null +++ b/tests/t30001/.clang-uml @@ -0,0 +1,20 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t30001_package: + type: package + glob: + - ../../tests/t30001/t30001.cc + include: + namespaces: + - clanguml::t30001 + exclude: + namespaces: + - clanguml::t30001::detail + using_namespace: + - clanguml::t30001 + plantuml: + before: + - "' t30001 test package diagram" + after: + - "note right of @A(A::AA::AAA): A AAA note..." \ No newline at end of file diff --git a/tests/t30001/t30001.cc b/tests/t30001/t30001.cc new file mode 100644 index 00000000..822ef9af --- /dev/null +++ b/tests/t30001/t30001.cc @@ -0,0 +1,24 @@ +namespace clanguml { +namespace t30001 { +namespace A { +namespace AA { +namespace AAA { +} // namespace AAA +namespace BBB { +} // namespace BBB +} // namespace AA +namespace BB { +} // namespace BB +} // namespace A +namespace B { +namespace AA { +namespace AAA { +} // namespace AAA +namespace BBB { +} // namespace BBB +} // namespace AA +namespace BB { +} // namespace BB +} // namespace B +} // namespace t30001 +} // namespace clanguml diff --git a/tests/t30001/test_case.h b/tests/t30001/test_case.h new file mode 100644 index 00000000..deb80a36 --- /dev/null +++ b/tests/t30001/test_case.h @@ -0,0 +1,55 @@ +/** + * tests/t30001/test_case.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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("t30001", "[test-case][package]") +{ + auto [config, db] = load_config("t30001"); + + auto diagram = config.diagrams["t30001_package"]; + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t30001"})); + + REQUIRE(diagram->exclude.namespaces.size() == 1); + REQUIRE_THAT(diagram->exclude.namespaces, + VectorContains(std::string{"clanguml::t30001::detail"})); + + REQUIRE(diagram->should_include("clanguml::t30001::A")); + REQUIRE(!diagram->should_include("clanguml::t30001::detail::C")); + REQUIRE(!diagram->should_include("std::vector")); + + REQUIRE(diagram->name == "t30001_package"); + + auto model = generate_package_diagram(db, diagram); + + REQUIRE(model.name() == "t30001_package"); + + auto puml = generate_package_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsPackage("A")); + REQUIRE_THAT(puml, IsPackage("AAA")); + REQUIRE_THAT(puml, IsPackage("AAA")); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/t30002/.clang-uml b/tests/t30002/.clang-uml new file mode 100644 index 00000000..dbbd0717 --- /dev/null +++ b/tests/t30002/.clang-uml @@ -0,0 +1,18 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t30002_package: + type: package + glob: + - ../../tests/t30002/t30002.cc + include: + namespaces: + - clanguml::t30002 + exclude: + namespaces: + - clanguml::t30002::detail + using_namespace: + - clanguml::t30002 + plantuml: + before: + - "' t30002 test package diagram" \ No newline at end of file diff --git a/tests/t30002/t30002.cc b/tests/t30002/t30002.cc new file mode 100644 index 00000000..690ec7c9 --- /dev/null +++ b/tests/t30002/t30002.cc @@ -0,0 +1,98 @@ +#include +#include +#include + +namespace clanguml { +namespace t30002 { +namespace A::AA { +namespace A1 { +struct CA { +}; +} +namespace A2 { +struct CB { +}; +} +namespace A3 { +struct CC { +}; +} +namespace A4 { +struct CD { +}; +} +namespace A5 { +struct CE { +}; +} +namespace A6 { +struct CF { +}; +} +namespace A7 { +struct CG { +}; +} +namespace A8 { +struct CH { +}; +} +namespace A9 { +struct CI { +}; +} +namespace A10 { +struct CJ { +}; +} +namespace A11 { +struct CK { +}; +} +namespace A12 { +struct CL { +}; +} +namespace A13 { +struct CM { +}; +} +} +namespace B::BB::BBB { +struct CBA : public A::AA::A6::CF { + A::AA::A1::CA *ca_; + A::AA::A2::CB cb_; + std::shared_ptr cc_; + std::map> cd_; + + void ce(const std::vector /*ce_*/) { } + + std::shared_ptr cg() { return {}; } + + template + void ch(std::map> & /*ch_*/) + { + } + + template std::map> ci() + { + return {}; + } +}; + +void cj(std::unique_ptr /*cj_*/) { } + +std::unique_ptr ck() { return {}; } + +template +void cl(std::map> & /*ch_*/) +{ +} + +template std::map> cm() +{ + return {}; +} +} +} // namespace t30002 +} // namespace clanguml diff --git a/tests/t30002/test_case.h b/tests/t30002/test_case.h new file mode 100644 index 00000000..0bbd26ad --- /dev/null +++ b/tests/t30002/test_case.h @@ -0,0 +1,78 @@ +/** + * tests/t30002/test_case.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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("t30002", "[test-case][package]") +{ + auto [config, db] = load_config("t30002"); + + auto diagram = config.diagrams["t30002_package"]; + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t30002"})); + + REQUIRE(diagram->exclude.namespaces.size() == 1); + REQUIRE_THAT(diagram->exclude.namespaces, + VectorContains(std::string{"clanguml::t30002::detail"})); + + REQUIRE(diagram->should_include("clanguml::t30002::A")); + REQUIRE(!diagram->should_include("clanguml::t30002::detail::C")); + REQUIRE(!diagram->should_include("std::vector")); + + REQUIRE(diagram->name == "t30002_package"); + + auto model = generate_package_diagram(db, diagram); + + REQUIRE(model.name() == "t30002_package"); + + auto puml = generate_package_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsPackage("A1")); + REQUIRE_THAT(puml, IsPackage("A2")); + REQUIRE_THAT(puml, IsPackage("A3")); + REQUIRE_THAT(puml, IsPackage("A4")); + REQUIRE_THAT(puml, IsPackage("A5")); + REQUIRE_THAT(puml, IsPackage("A6")); + REQUIRE_THAT(puml, IsPackage("A7")); + REQUIRE_THAT(puml, IsPackage("A8")); + REQUIRE_THAT(puml, IsPackage("A9")); + REQUIRE_THAT(puml, IsPackage("A11")); + REQUIRE_THAT(puml, IsPackage("A12")); + REQUIRE_THAT(puml, IsPackage("A13")); + + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A1"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A2"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A3"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A4"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A5"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A6"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A7"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A8"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A9"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A10"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A11"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A12"))); + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A13"))); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/t30003/.clang-uml b/tests/t30003/.clang-uml new file mode 100644 index 00000000..74318921 --- /dev/null +++ b/tests/t30003/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t30003_package: + type: package + glob: + - ../../tests/t30003/t30003.cc + include: + namespaces: + - clanguml::t30003 + using_namespace: + - clanguml::t30003 + plantuml: + before: + - "' t30003 test package diagram" \ No newline at end of file diff --git a/tests/t30003/t30003.cc b/tests/t30003/t30003.cc new file mode 100644 index 00000000..b83621a8 --- /dev/null +++ b/tests/t30003/t30003.cc @@ -0,0 +1,32 @@ +namespace clanguml { +namespace t30003 { + +namespace ns1 { +namespace ns2_v1_0_0 { +class A { +}; +} + +namespace [[deprecated]] ns2_v0_9_0 { +class A { +}; +} + +namespace { +class Anon final { +}; +} +} + +namespace [[deprecated]] ns3 { + +namespace ns1::ns2 { +class Anon : public t30003::ns1::ns2_v1_0_0::A { +}; +} + +class B : public ns1::ns2::Anon { +}; +} +} +} \ No newline at end of file diff --git a/tests/t30003/test_case.h b/tests/t30003/test_case.h new file mode 100644 index 00000000..15005b27 --- /dev/null +++ b/tests/t30003/test_case.h @@ -0,0 +1,56 @@ +/** + * tests/t30003/test_case.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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("t30003", "[test-case][package]") +{ + auto [config, db] = load_config("t30003"); + + auto diagram = config.diagrams["t30003_package"]; + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t30003"})); + + REQUIRE(diagram->should_include("clanguml::t30003::A")); + REQUIRE(diagram->should_include("clanguml::t30003::C")); + REQUIRE(!diagram->should_include("std::vector")); + + REQUIRE(diagram->name == "t30003_package"); + + auto model = generate_package_diagram(db, diagram); + + REQUIRE(model.name() == "t30003_package"); + + auto puml = generate_package_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsPackage("ns1")); + REQUIRE_THAT(puml, IsPackage("ns2")); + REQUIRE_THAT(puml, IsPackage("ns3")); + REQUIRE_THAT(puml, IsPackage("ns2_v1_0_0")); + REQUIRE_THAT(puml, IsPackage("ns2_v0_9_0")); + + REQUIRE_THAT(puml, IsDeprecated(_A("ns2_v0_9_0"))); + REQUIRE_THAT(puml, IsDeprecated(_A("ns3"))); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/t30004/.clang-uml b/tests/t30004/.clang-uml new file mode 100644 index 00000000..ac7fcc9a --- /dev/null +++ b/tests/t30004/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t30004_package: + type: package + glob: + - ../../tests/t30004/t30004.cc + include: + namespaces: + - clanguml::t30004 + using_namespace: + - clanguml::t30004 + plantuml: + before: + - "' t30004 test package diagram" \ No newline at end of file diff --git a/tests/t30004/t30004.cc b/tests/t30004/t30004.cc new file mode 100644 index 00000000..611103f4 --- /dev/null +++ b/tests/t30004/t30004.cc @@ -0,0 +1,35 @@ +namespace clanguml { +namespace t30004 { + +/// @uml{style[#green]} +namespace A { + +/// @uml{note[ bottom ] Package AAA.} +namespace AAA { +} + +/// \uml{note[right] Package BBB.} +namespace BBB { +} + +/// +/// @uml{note:t30004_package[bottom] CCCC package note.} +/// This is package CCC. +namespace CCC { +} + +/// \uml{skip} +namespace DDD { +} + +/// @uml{style[#pink;line:red;line.bold;text:red]} +/// \uml{note[top] We skipped DDD.} +namespace EEE { +} + +/// \uml{note[top] Another CCC note.} +namespace CCC { +} +} +} +} \ No newline at end of file diff --git a/tests/t30004/test_case.h b/tests/t30004/test_case.h new file mode 100644 index 00000000..f3968d0a --- /dev/null +++ b/tests/t30004/test_case.h @@ -0,0 +1,53 @@ +/** + * tests/t30004/test_case.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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("t30004", "[test-case][package]") +{ + auto [config, db] = load_config("t30004"); + + auto diagram = config.diagrams["t30004_package"]; + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t30004"})); + + REQUIRE(diagram->should_include("clanguml::t30004::A")); + REQUIRE(diagram->should_include("clanguml::t30004::C")); + REQUIRE(!diagram->should_include("std::vector")); + + REQUIRE(diagram->name == "t30004_package"); + + auto model = generate_package_diagram(db, diagram); + + REQUIRE(model.name() == "t30004_package"); + + auto puml = generate_package_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsPackage("AAA")); + REQUIRE_THAT(puml, IsPackage("BBB")); + REQUIRE_THAT(puml, IsPackage("CCC")); + REQUIRE_THAT(puml, !IsPackage("DDD")); + REQUIRE_THAT(puml, IsPackage("EEE")); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/t30005/.clang-uml b/tests/t30005/.clang-uml new file mode 100644 index 00000000..1512c67f --- /dev/null +++ b/tests/t30005/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t30005_package: + type: package + glob: + - ../../tests/t30005/t30005.cc + include: + namespaces: + - clanguml::t30005 + using_namespace: + - clanguml::t30005 + plantuml: + before: + - "' t30005 test package diagram" \ No newline at end of file diff --git a/tests/t30005/t30005.cc b/tests/t30005/t30005.cc new file mode 100644 index 00000000..f87fad0a --- /dev/null +++ b/tests/t30005/t30005.cc @@ -0,0 +1,27 @@ +namespace clanguml { +namespace t30005 { + +namespace A::AA::AAA { +struct C1 { +}; +} + +namespace B::BB::BBB { +namespace A6 = A::AA::AAA; +namespace ASix = A6; +struct C2 { + ASix::C1 *cb; +}; +} + +namespace C::CC::CCC { +namespace A6 = A::AA::AAA; +namespace ASix = A6; +using ADSix = ASix::C1; +struct C2 { + ADSix *cc; +}; +} +} + +} diff --git a/tests/t30005/test_case.h b/tests/t30005/test_case.h new file mode 100644 index 00000000..d50234a2 --- /dev/null +++ b/tests/t30005/test_case.h @@ -0,0 +1,54 @@ +/** + * tests/t30005/test_case.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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("t30005", "[test-case][package]") +{ + auto [config, db] = load_config("t30005"); + + auto diagram = config.diagrams["t30005_package"]; + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t30005"})); + + REQUIRE(diagram->should_include("clanguml::t30005::A")); + REQUIRE(diagram->should_include("clanguml::t30005::C")); + REQUIRE(!diagram->should_include("std::vector")); + + REQUIRE(diagram->name == "t30005_package"); + + auto model = generate_package_diagram(db, diagram); + + REQUIRE(model.name() == "t30005_package"); + + auto puml = generate_package_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsPackage("AAA")); + REQUIRE_THAT(puml, IsPackage("BBB")); + REQUIRE_THAT(puml, IsPackage("CCC")); + + REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("AAA"))); + REQUIRE_THAT(puml, IsDependency(_A("CCC"), _A("AAA"))); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/t30006/.clang-uml b/tests/t30006/.clang-uml new file mode 100644 index 00000000..7899d3cc --- /dev/null +++ b/tests/t30006/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t30006_package: + type: package + glob: + - ../../tests/t30006/t30006.cc + include: + namespaces: + - clanguml::t30006 + using_namespace: + - clanguml::t30006 + plantuml: + before: + - "' t30006 test package diagram" \ No newline at end of file diff --git a/tests/t30006/t30006.cc b/tests/t30006/t30006.cc new file mode 100644 index 00000000..d9778875 --- /dev/null +++ b/tests/t30006/t30006.cc @@ -0,0 +1,29 @@ +namespace clanguml { +namespace t30006 { + +namespace B { +struct BB { +}; +} + +/// \uml{note[top] Top A note.} +namespace A { +struct A1 { + B::BB *b; +}; +} + +namespace C { +struct CC { +}; +} + +/// \uml{note[bottom] Bottom A note.} +namespace A { +struct A2 { + C::CC *c; +}; +} + +} +} \ No newline at end of file diff --git a/tests/t30006/test_case.h b/tests/t30006/test_case.h new file mode 100644 index 00000000..c837c9c6 --- /dev/null +++ b/tests/t30006/test_case.h @@ -0,0 +1,54 @@ +/** + * tests/t30006/test_case.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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("t30006", "[test-case][package]") +{ + auto [config, db] = load_config("t30006"); + + auto diagram = config.diagrams["t30006_package"]; + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t30006"})); + + REQUIRE(diagram->should_include("clanguml::t30006::A")); + REQUIRE(diagram->should_include("clanguml::t30006::C")); + REQUIRE(!diagram->should_include("std::vector")); + + REQUIRE(diagram->name == "t30006_package"); + + auto model = generate_package_diagram(db, diagram); + + REQUIRE(model.name() == "t30006_package"); + + auto puml = generate_package_puml(diagram, model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsPackage("A")); + REQUIRE_THAT(puml, IsPackage("B")); + REQUIRE_THAT(puml, IsPackage("C")); + + REQUIRE_THAT(puml, IsDependency(_A("A"), _A("B"))); + REQUIRE_THAT(puml, IsDependency(_A("A"), _A("C"))); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/t90000/test_case.h b/tests/t90000/test_case.h index d8ed9ac2..940b3e8c 100644 --- a/tests/t90000/test_case.h +++ b/tests/t90000/test_case.h @@ -1,7 +1,7 @@ /** * tests/t90000/test_case.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 2e3dc9b5..53010601 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -1,7 +1,7 @@ /** * tests/test_cases.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,18 @@ clanguml::class_diagram::model::diagram generate_class_diagram( return diagram_model; } +clanguml::package_diagram::model::diagram generate_package_diagram( + cppast::libclang_compilation_database &db, + std::shared_ptr diagram) +{ + auto diagram_model = + clanguml::package_diagram::generators::plantuml::generate(db, + diagram->name, + dynamic_cast(*diagram)); + + return diagram_model; +} + std::string generate_sequence_puml( std::shared_ptr config, clanguml::sequence_diagram::model::diagram &model) @@ -92,6 +104,20 @@ std::string generate_class_puml( return ss.str(); } +std::string generate_package_puml( + std::shared_ptr config, + clanguml::package_diagram::model::diagram &model) +{ + using namespace clanguml::package_diagram::generators::plantuml; + + std::stringstream ss; + + ss << generator( + dynamic_cast(*config), model); + + return ss.str(); +} + void save_puml(const std::string &path, const std::string &puml) { std::filesystem::path p{path}; @@ -147,6 +173,16 @@ using namespace clanguml::test::matchers; #include "t20001/test_case.h" #include "t20002/test_case.h" +// +// Package diagram tests +// +#include "t30001/test_case.h" +#include "t30002/test_case.h" +#include "t30003/test_case.h" +#include "t30004/test_case.h" +#include "t30005/test_case.h" +#include "t30006/test_case.h" + // // Other tests (e.g. configuration file) // diff --git a/tests/test_cases.h b/tests/test_cases.h index 12088b96..c18f3c5f 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -1,7 +1,7 @@ /** * tests/test_cases.h * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ #include "class_diagram/visitor/translation_unit_visitor.h" #include "config/config.h" #include "cx/compilation_database.h" +#include "package_diagram/generators/plantuml/package_diagram_generator.h" +#include "package_diagram/visitor/translation_unit_visitor.h" #include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h" #include "sequence_diagram/visitor/translation_unit_visitor.h" #include "util/util.h" @@ -32,12 +34,14 @@ #include "catch.h" +#include #include #include #include using Catch::Matchers::Contains; using Catch::Matchers::EndsWith; +using Catch::Matchers::Equals; using Catch::Matchers::StartsWith; using Catch::Matchers::VectorContains; using clanguml::cx::compilation_database; @@ -164,17 +168,31 @@ struct AliasMatcher { std::string operator()(const std::string &name) { - std::vector patterns; - patterns.push_back("class \"" + name + "\" as "); - patterns.push_back("abstract \"" + name + "\" as "); - patterns.push_back("enum \"" + name + "\" as "); + std::vector patterns; + + const std::string alias_regex("([A-Z]_[0-9]+)"); + + patterns.push_back( + std::regex{"class\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back( + std::regex{"abstract\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back( + std::regex{"enum\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back( + std::regex{"package\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back( + std::regex{"package\\s\\[" + name + "\\]\\sas\\s" + alias_regex}); + + std::smatch base_match; for (const auto &line : puml) { for (const auto &pattern : patterns) { - const auto idx = line.find(pattern); - if (idx != std::string::npos) { - std::string res = line.substr(idx + pattern.size()); - return trim(res); + if (std::regex_search(line, base_match, pattern)) { + if (base_match.size() == 2) { + std::ssub_match base_sub_match = base_match[1]; + std::string alias = base_sub_match.str(); + return trim(alias); + } } } } @@ -396,6 +414,20 @@ ContainsMatcher IsField(std::string const &name, return ContainsMatcher( CasedString(pattern + " : " + type, caseSensitivity)); } + +ContainsMatcher IsPackage(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher( + CasedString("package [" + str + "]", caseSensitivity)); +} + +ContainsMatcher IsDeprecated(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher( + CasedString(str + " <> ", caseSensitivity)); +} } } } diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 9989b495..7c3bdf30 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -106,6 +106,25 @@ test_cases: - name: t20002 title: Free function sequence diagram test case description: + Package diagrams: + - name: t30001 + title: Basic package diagram test case + description: + - name: t30002 + title: Package dependency test case + description: + - name: t30003 + title: Package deprecated attribute test case + description: + - name: t30004 + title: PlantUML package decorators test case + description: + - name: t30005 + title: Package namespace alias test case + description: + - name: t30006 + title: Package split namespace test case + description: Configuration diagrams: - name: t90000 title: Basic config test diff --git a/tests/test_decorator_parser.cc b/tests/test_decorator_parser.cc index 01cdd0db..233c47ee 100644 --- a/tests/test_decorator_parser.cc +++ b/tests/test_decorator_parser.cc @@ -1,7 +1,7 @@ /** * tests/test_decorator_parser.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/test_util.cc b/tests/test_util.cc index 80c102c0..c722e0cb 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -1,7 +1,7 @@ /** * tests/test_util.cc * - * Copyright (c) 2021 Bartek Kryza + * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/thirdparty/cppast b/thirdparty/cppast index 0d06dcdf..b2b871c9 160000 --- a/thirdparty/cppast +++ b/thirdparty/cppast @@ -1 +1 @@ -Subproject commit 0d06dcdf86bfdbf13fb20a4adbbbc6e536884411 +Subproject commit b2b871c97e360c2155650f52613df4670e859ebd diff --git a/util/generate_test_cases_docs.py b/util/generate_test_cases_docs.py index 8411d432..c3b61eb2 100755 --- a/util/generate_test_cases_docs.py +++ b/util/generate_test_cases_docs.py @@ -3,7 +3,7 @@ ## ## util/generate_test_cases_docs.py ## -## Copyright (c) 2021 Bartek Kryza +## Copyright (c) 2021-2022 Bartek Kryza ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License.