diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..a9964efa --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,60 @@ +Checks: >- + *, + -altera*, + -*braces-around-statements, + -*osx*, + -abseil*, + -android*, + -bugprone-branch-clone, + -bugprone-exception-escape, + -clang-analyzer-alpha.*, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-special-member-functions, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-non-private-member-variables-in-classes, + -cert-env33-c, + -cert-err58-cpp, + -fuchsia*, + -hicpp-no-array-decay, + -hicpp-special-member-functions, + -google-readability-todo, + -google-default-arguments, + -google-explicit-constructor, + -google-build-using-namespace, + -hicpp-signed-bitwise, + -hicpp-explicit-conversions, + -llvmlibc-*, + -llvm-header-guard, + -llvm-namespace-comment, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + -mpi*, + -objc*, + -openmp*, + -readability-inconsistent-declaration-parameter-name, + -readability-identifier-naming, + -readability-redundant-smartptr-get, + -readability-convert-member-functions-to-static, + -readability-function-cognitive-complexity, + -readability-const-return-type, + -darwin*, + -zircon* +WarningsAsErrors: '*' +HeaderFilterRegex: 'src' +CheckOptions: + - key: readability-identifier-naming.ClassMemberSuffix + value: '_' + - key: readability-identifier-naming.PublicMemberSuffix + value: '' + - key: performance-unnecessary-value-param.AllowedTypes + value: shared_ptr;weak_ptr + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: 1 + - key: hicpp-special-member-functions.AllowSoleDefaultDtor + value: 1 + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: 1 diff --git a/.clang-uml b/.clang-uml index 6e6d2e75..f515256c 100644 --- a/.clang-uml +++ b/.clang-uml @@ -16,6 +16,8 @@ diagrams: include!: uml/class_model_class_diagram.yml sequence_model_class: include!: uml/sequence_model_class_diagram.yml + main_sequence: + include!: uml/main_sequence_diagram.yml sequence_diagram_visitor_sequence: include!: uml/sequence_diagram_visitor_sequence_diagram.yml class_diagram_generator_sequence: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 855b47e9..edf03a0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,11 +11,14 @@ jobs: - name: Update package database run: sudo apt -y update - name: Install deps - run: sudo apt -y install git make gcc-10 g++-10 ccache cmake libyaml-cpp-dev llvm-12 clang-12 libclang-12-dev libclang-cpp12-dev lcov zlib1g-dev + run: sudo apt -y install git make gcc-10 g++-10 ccache cmake libyaml-cpp-dev llvm-12 clang-12 libclang-12-dev libclang-cpp12-dev clang-format-12 lcov zlib1g-dev - name: Select g++ version run: | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10 sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10 + - name: Check code formatting + run: | + make check-formatting - name: Build and unit test run: | NUMPROC=2 CMAKE_CXX_FLAGS="--coverage -fno-inline" CMAKE_EXE_LINKER_FLAGS="-lgcov --coverage" LLVM_VERSION=12 make test diff --git a/.gitignore b/.gitignore index d82c22e2..d4b8e02b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ compile_commands.json CTestTestfile.cmake Session.vim _deps +_tidy /build/ lib/ bin/ diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 00000000..0c83849c --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,3 @@ +# Authors + +Bartek Kryza \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a5f8c979..8c8aeaa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # CHANGELOG +### 0.3.0 * Added support for sequence diagrams with template code ### 0.2.2 diff --git a/CMakeLists.txt b/CMakeLists.txt index d06216ab..cd5d69a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_VERBOSE_MAKEFILE OFF) set(CMAKE_FIND_DEBUG_MODE OFF) +if(APPLE) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif(APPLE) # # clang-uml custom defines @@ -25,7 +28,7 @@ set(UML_HEADERS_DIR ${PROJECT_SOURCE_DIR}/src/uml) # option(LINK_LLVM_SHARED "Should LLVM be linked using shared libraries or statically" ON) set(LLVM_VERSION CACHE STRING "Path to custom llvm-config executable") -set(GIT_VERSION "0.1.0" CACHE STRING "clang-uml version") +set(GIT_VERSION "0.3.0" CACHE STRING "clang-uml version") # # Setup LLVM @@ -180,5 +183,8 @@ install(FILES README.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) # # Enable testing via CTest # -enable_testing() -add_subdirectory(tests) +option(BUILD_TESTS "" ON) +if(BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif(BUILD_TESTS) \ No newline at end of file diff --git a/Makefile b/Makefile index 95400d84..72c0ce4d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile # -# Copyright (c) 2021-2022 Bartek Kryza +# Copyright (c) 2021-2023 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,6 +27,9 @@ CMAKE_CXX_FLAGS ?= CMAKE_EXE_LINKER_FLAGS ?= GIT_VERSION ?= $(shell git describe --tags --always --abbrev=7) +PKG_VERSION ?= $(shell git describe --tags --always --abbrev=7 | tr - .) +GIT_COMMIT ?= $(shell git rev-parse HEAD) +GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) .PHONY: clean clean: @@ -95,8 +98,24 @@ clang-format: format: docker run --rm -v $(CURDIR):/root/sources bkryza/clang-format-check:1.3 +.PHONY: check-formatting +check-formatting: + ./util/check_formatting.sh + .PHONY: iwyu_fixes iwyu_fixes: debug python3 $(shell which iwyu_tool.py) -p debug > debug/iwyu.out python3 $(shell which fix_includes.py) -h --re_only="${PWD}/src/.*" < debug/iwyu.out python3 $(shell which fix_includes.py) -h --re_only="${PWD}/tests/.*" < debug/iwyu.out + +.PHONY: fedora_36 +fedora_36: + mkdir -p packaging/_BUILD/fedora/36 + git archive --format=tar.gz --prefix=clang-uml-$(PKG_VERSION)/ v$(GIT_VERSION) >packaging/_BUILD/fedora/36/clang-uml-$(PKG_VERSION).tar.gz + docker run --cpus="8" -v $(PWD):$(PWD) fedora:36 sh -c "dnf install -y make git && cd ${PWD} && make OS=fedora DIST=36 VERSION=${PKG_VERSION} COMMIT=${GIT_COMMIT} BRANCH=${GIT_BRANCH} -C packaging rpm" + +.PHONY: fedora_37 +fedora_37: + mkdir -p packaging/_BUILD/fedora/37 + git archive --format=tar.gz --prefix=clang-uml-$(PKG_VERSION)/ v$(GIT_VERSION) >packaging/_BUILD/fedora/37/clang-uml-$(PKG_VERSION).tar.gz + docker run --cpus="8" -v $(PWD):$(PWD) fedora:37 sh -c "dnf install -y make git && cd ${PWD} && make OS=fedora DIST=37 VERSION=${PKG_VERSION} COMMIT=${GIT_COMMIT} BRANCH=${GIT_BRANCH} -C packaging rpm" diff --git a/README.md b/README.md index c23c67cf..82fe12b4 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ -# clang-uml - C++ UML diagram generator based on Clang and PlantUML +# clang-uml - C++ UML diagram generator based on Clang [![Build status](https://github.com/bkryza/clang-uml/actions/workflows/build.yml/badge.svg)](https://github.com/bkryza/clang-uml/actions) [![Coverage](https://codecov.io/gh/bkryza/clang-uml/branch/master/graph/badge.svg)](https://codecov.io/gh/bkryza/clang-uml) -[![Version](https://img.shields.io/badge/version-0.2.2-blue)](https://github.com/bkryza/clang-uml/releases) +[![Version](https://img.shields.io/badge/version-0.3.0-blue)](https://github.com/bkryza/clang-uml/releases) -`clang-uml` is an automatic C++ to [PlantUML](https://plantuml.com) class, sequence -and package diagram generator, driven by YAML configuration files. The main idea behind the +`clang-uml` is an automatic C++ to UML class, sequence, package and include diagram generator, driven by +YAML configuration files. The main idea behind the project is to easily maintain up-to-date diagrams within a code-base or document legacy code. The configuration file or files for `clang-uml` define the type and contents of each generated diagram. +Currently the diagrams are generated in [PlantUML](https://plantuml.com) format. `clang-uml` currently supports C++ up to version 17. -> Current `master` version (and any release since `0.2.0`) has been refactored to use -> [Clang LibTooling](https://clang.llvm.org/docs/LibTooling.html) instead of libclang. -> Previous version is available in branch `0.1.x`, however it is not maintained. - ## Features + Main features supported so far include: * **Class diagram generation** @@ -34,6 +32,7 @@ Main features supported so far include: * Generation of try/catch blocks * Handling of template code including constexpr conditionals * Handling of lambda expressions + * Interactive links to online code to classes and call expressions * **Package diagram generation** * Generation of package diagram based on C++ namespaces * Interactive links to online code to packages @@ -43,11 +42,14 @@ Main features supported so far include: To see what `clang-uml` can do so far, checkout the diagrams generated for unit test cases [here](./docs/test_cases.md) and examples in [clang-uml-examples](https://github.com/bkryza/clang-uml-examples) repository. +More comprehensive documentation can be found [here](./docs/README.md). + ## Installation ### Distribution packages #### Ubuntu + ```bash sudo add-apt-repository ppa:bkryza/clang-uml sudo apt update @@ -55,6 +57,7 @@ sudo apt install clang-uml ``` #### Conda + ```bash conda config --add channels conda-forge conda config --set channel_priority strict @@ -62,6 +65,7 @@ conda install -c bkryza/label/clang-uml clang-uml ``` ### Building from source + First make sure that you have the following dependencies installed: ```bash @@ -98,21 +102,27 @@ LLVM_VERSION=14 make release ## Usage ### Generating compile commands database + `clang-uml` requires an up-to-date [compile_commands.json](https://clang.llvm.org/docs/JSONCompilationDatabase.html) file, containing the list of commands used for compiling the source code. Nowadays, this file can be generated rather easily using multiple methods: - * For [CMake](https://cmake.org/) projects, simply invoke the `cmake` command - as `cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ...` - * For Make projects checkout [compiledb](https://github.com/nickdiego/compiledb) or [Bear](https://github.com/rizsotto/Bear) - * For Boost-based projects try [commands_to_compilation_database](https://github.com/tee3/commands_to_compilation_database) - * For SCons, invoke `compilation_db` tool (requires SCons > 4.0.0) + +* For [CMake](https://cmake.org/) projects, simply invoke the `cmake` command + as `cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ...` +* For Make projects checkout [compiledb](https://github.com/nickdiego/compiledb) + or [Bear](https://github.com/rizsotto/Bear) +* For Boost-based projects + try [commands_to_compilation_database](https://github.com/tee3/commands_to_compilation_database) +* For SCons, invoke `compilation_db` tool (requires SCons > 4.0.0) ### Invocation + By default, `config-uml` will assume that the configuration file `.clang-uml` and compilation database `compile_commands.json` files are in the current directory, so if they are in the top level directory of a project, simply run: + ```bash clang-uml ``` @@ -121,6 +131,7 @@ The output path for diagrams, as well as alternative location of compilation database can be specified in `.clang-uml` configuration file. For other options checkout help: + ```bash clang-uml --help ``` @@ -154,6 +165,7 @@ 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 @@ -169,7 +181,7 @@ and checkout the SVG diagrams in `docs/diagrams` folder. #### Example -Source code: +The following C++ code: ```cpp template struct A { @@ -403,6 +415,7 @@ generates the following diagram (via PlantUML) based on include directives in th | Include (system) | ![dependency](docs/img/puml_dependency.png) | ### Diagram content filtering + For typical code bases, generating a single diagram from entire code or even a single namespace can be too big to be useful, e.g. as part of documentation. `clang-uml` allows specifying content to be included and excluded from each diagram using simple YAML configuration: @@ -440,43 +453,20 @@ exclude: - clanguml::common::ClassF ``` -### Comment decorators - -`clang-uml` provides a set of in-comment directives, called decorators, which allow custom control over -generation of UML diagrams from C++ and overriding default inference rules for relationships. - -The following decorators are currently supported: -- [note](docs/test_cases/t00028.md) - add a PlantUML note to a C++ entity -- [skip](docs/test_cases/t00029.md) - skip the underlying C++ entity -- [skiprelationship](docs/test_cases/t00029.md) - skip only relationship generation for a class property -- [composition](docs/test_cases/t00030.md) - document the property as composition -- [association](docs/test_cases/t00030.md) - document the property as association -- [aggregation](docs/test_cases/t00030.md) - document the property as aggregation -- [style](docs/test_cases/t00031.md) - add PlantUML style to a C++ entity - -### Doxygen integration -`clang-uml` decorstors can be omitted completely in [Doxygen](https://www.doxygen.nl/index.html), by adding the following -lines to the Doxygen config file: - -``` -ALIASES += clanguml="" -ALIASES += clanguml{1}="" -ALIASES += clanguml{2}="" -ALIASES += clanguml{3}="" -``` - ### Test cases The build-in test cases used for unit testing of the `clang-uml`, can be browsed [here](./docs/test_cases.md). ## Acknowledgements + This project relies on the following great tools: - * [Clang LibTooling](https://clang.llvm.org/docs/LibTooling.html) - a C++ library for creating tools based on Clang - * [PlantUML](https://plantuml.com/) - language and diagram for generating UML diagrams - * [Catch2](https://github.com/catchorg/Catch2) - C++ unit test framework - * [glob](https://github.com/p-ranav/glob) - Unix style path expansion for C++ - * [CLI11](https://github.com/CLIUtils/CLI11) - command line parser for C++ - * [inja](https://github.com/pantor/inja) - a template engine for modern C++ + +* [Clang LibTooling](https://clang.llvm.org/docs/LibTooling.html) - a C++ library for creating tools based on Clang +* [PlantUML](https://plantuml.com/) - language and diagram for generating UML diagrams +* [Catch2](https://github.com/catchorg/Catch2) - C++ unit test framework +* [glob](https://github.com/p-ranav/glob) - Unix style path expansion for C++ +* [CLI11](https://github.com/CLIUtils/CLI11) - command line parser for C++ +* [inja](https://github.com/pantor/inja) - a template engine for modern C++ ## Contributing diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..3c6a48f2 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,18 @@ +# Documentation + + +* [Quick start](./quick_start.md) +* Generating diagrams + * [Common options](./common_options.md) + * [Class diagrams](./class_diagrams.md) + * [Sequence diagrams](./sequence_diagrams.md) + * [Package diagrams](./package_diagrams.md) + * [Include diagrams](./include_diagrams.md) +* [Comment decorators](./comment_decorators.md) +* [Diagram filters](./diagram_filters.md) +* [Using Jinja templates](./jinja_templates.md) +* [Interactive SVG diagrams using links](./interactive_svg_diagrams.md) +* [Configuration file reference](./configuration_file.md) +* [Doxygen integration](./doxygen_integration.md) +* [Test cases documentation](./test_cases.md) +* [Troubleshooting](./troubleshooting.md) diff --git a/docs/class_diagrams.md b/docs/class_diagrams.md new file mode 100644 index 00000000..23b53f74 --- /dev/null +++ b/docs/class_diagrams.md @@ -0,0 +1,157 @@ +# Generating class diagrams + +The minimal config required to generate a class diagram is presented below: +```yaml +# Path to the directory where `compile_commands.json` can be found +compilation_database_dir: _build +# Output directory for the diagrams +output_directory: puml +# Diagrams definitions +diagrams: + # Diagram name + t00002_class: + # Type of diagram (has to be `class`) + type: class + # Include only translation units matching the following patterns + glob: + - src/*.cc + # Render all names in the diagram relative to specific namespace + using_namespace: + - ns1 + # Include only classes from specific namespace + include: + namespaces: + - ns1::ns2 +``` + +## Classes and their properties +The basic class diagram generated by `clang-uml` and rendered using PlantUML looks like this: + +![extension](test_cases/t00003_class.svg) + +Member types and method return types are rendered at the end after `:` sign. + +Static methods and members are underlined. + +In case method argument lists are too long and not required for diagram readability, they can be suppressed completely +or abbreviated by setting `generate_method_arguments` option to either `none`, `abbreviated` or `full` (default). + + +### Excluding private or protected members from the diagram +In order to only include public members in the class diagrams, we can add the following inclusion filters: +```yaml + include: + access: + - public +``` + +To render only classes without any properties an exclusion filter can be added: +```yaml + exclude: + access: + - public + - protected + - private +``` + +## Relationships + +The following table presents the PlantUML arrows representing each relationship in the class diagrams. + +| UML | PlantUML | +| ---- | --- | +| Inheritance | ![extension](img/puml_inheritance.png) | +| Association | ![association](img/puml_association.png) | +| Dependency | ![dependency](img/puml_dependency.png) | +| Aggregation | ![aggregation](img/puml_aggregation.png) | +| Composition | ![composition](img/puml_composition.png) | +| Template specialization/instantiation | ![specialization](img/puml_instantiation.png) | +| Nesting (inner class/enum) | ![nesting](img/puml_nested.png) | + + +By default, a member from which a relationship has been added to the diagram between 2 classes will also be rendered +inside the class. This behaviour can be however disabled by adding the following option to the +diagram definition: +```yaml +include_relations_also_as_members: false +``` + +### Relationships to classes in containers or smart pointers +`clang-uml` will automatically detect class members as well as method arguments, which reference or own +values of types relevant for a given diagram but wrapped in smart pointers or containers and still generate +relationship between these classes, for instance the following code: + +```cpp +class A { }; + +class B { }; + +class C { }; + +class R { +public: + std::unique_ptr a; + std::shared_ptr b; + std::weak_ptr c; +}; +``` + +generates the following diagram: + +![extension](test_cases/t00007_class.svg) + +## Inheritance diagrams + +A common type of class diagram is an inheritance diagram, where only subclasses of a specific base class are +included and only the inheritance relationships are rendered. This can be easily achieved in `clang-uml` through +inclusion filters: +```yaml + include: + subclasses: + - clanguml::t00039::A + relationships: + - inheritance +``` + +## Namespaces as packages +By default, `clang-uml` will render all element names including a namespace (relative to `using_namespace` property), +e.g. `ns1::ns2::MyClass`. +In order to generate packages in the diagram for each namespace instead, the following option must be set to `true`: + +```yaml +generate_packages: true +``` + +which results in the following diagram: + +![extension](test_cases/t00036_class.svg) + +## Class context diagram +Sometimes it's helpful to generate a class diagram depicting only direct relationships of a given class, e.g. +within the classes' documentation page, this can be easily achieved using `context` inclusion filter: + +```yaml + include: + context: + - ns1::MyClass +``` + +## Disabling dependency relationships +In many cases, dependency relationships between classes can clutter the diagram too much, for instance consider this +diagram: + +![extension](test_cases/t00019_class.svg) + +where the dependency relationships do not bring much information into the diagram. In such cases it might +be useful to disable dependency relationships for this diagram completely using the following exclusion filter: +```yaml + exclude: + relationships: + - dependency +``` + +Dependency relationships are inferred whenever a class uses another class, thus often dependency relationship +will be rendered in addition to other relationships such as association or inheritance. In the future there might +be an option to remove the redundant dependency relationships from the diagram automatically. + + diff --git a/docs/comment_decorators.md b/docs/comment_decorators.md new file mode 100644 index 00000000..1216bfa6 --- /dev/null +++ b/docs/comment_decorators.md @@ -0,0 +1,222 @@ +# Comment decorators + +`clang-uml` provides a set of in-comment directives, called decorators, which allow custom control over +generation of UML diagrams from C++ and overriding default inference rules for relationships. + +`clang-uml` decorators are specified in the following format: + +``` +\uml{[:][] } +``` + +or +``` +@uml{[:][] } +``` + +The optional `:` suffix will apply this decorator only to a specific diagram. + +Currently, the following decorators are supported. + +## `note` + +This decorator allows to specify directly in the code comments that should be included in the generated diagrams. + +The following code: +```cpp +/// \uml{note[top] A class note.} +class A { +}; + +/// \uml{note[] B class note.} +class B { +}; + +/// +/// @uml{note:t00028_class[bottom] C class note.} +/// This is class C. +class C { +}; + +/// \uml{note +/// D +/// class +/// note.} +class D { +}; + +/// \uml{note E template class note.} +template class E { + T param; +}; + +/// \uml{note:other_diagram[left] G class note.} +class G { +}; + +/// @uml{note[ bottom ] F enum note.} +enum class F { one, two, three }; + +/// \uml{note[right] R class note.} +class R { + explicit R(C &c) + : ccc(c) + { + } + + A aaa; + + B *bbb; + + C &ccc; + + std::vector> ddd; + + E eee; + + G **ggg; +}; +``` + +generates the following class diagram: + +![note](./test_cases/t00028_class.svg) + +# `skip` and `skiprelationship` +This decorator allows to skip the specific classes or methods from the diagrams, for instance the following code: +```cpp + +class A { +}; + +/// \uml{skip} +class B { +}; + +template class C { + T param; +}; + +/// @uml{skip:t00029_class} +template class D { + T param; +}; + +enum class E { one, two, three }; + +/// \uml{skip} +enum class F { red, green, blue }; + +class G1 { +}; + +class G2 { +}; + +class G3 { +}; + +class G4 { +}; + +struct R { + G1 g1; + + /// \uml{skip} + G2 g2; + + /// \uml{skiprelationship} + G3 &g3; + + std::shared_ptr g4; +}; +``` + +generates the following diagram: + +![skip](./test_cases/t00029_class.svg) + +## `composition`, `association` and `aggregation` + +These decorators allow to specify explicitly the type of relationship within a class diagram that should be +generated for a given class member. For instance the following code: + +```cpp + +class A { +}; + +class B { +}; + +class C { +}; + +class D { +}; + +class E { +}; + +struct R { + /// @uml{association[]} + A aaa; + + /// @uml{composition[0..1:1..*]} + std::vector bbb; + + /// @uml{aggregation[0..1:1..5]} + std::vector ccc; + + /// @uml{association[:1]} + D ddd; + + /// @uml{aggregation[:1]} + E *eee; +}; +``` + +generates the following diagram: + +![skip](./test_cases/t00030_class.svg) + + +## `style` +This decorator allows to specify in the code specific styles for diagram elements, for instance: + +```cpp + +/// @uml{style[#back:lightgreen|yellow;header:blue/red]} +class A { +}; + +/// @uml{style[#line.dotted:blue]} +enum B { one, two, three }; + +/// @uml{style[#pink;line:red;line.bold;text:red]} +template class C { + T ttt; +}; + +class D { +}; + +struct R { + /// @uml{style[#red,dashed,thickness=2]} + A *aaa; + + /// @uml{composition} + /// @uml{style[#green,dashed,thickness=4]} + std::vector bbb; + + /// @uml{style[#blue,dotted,thickness=8]} + C ccc; + + /// @uml{style[#blue,plain,thickness=16]} + D *ddd; +}; +``` + +generates the following diagram: + +![skip](./test_cases/t00031_class.svg) diff --git a/docs/common_options.md b/docs/common_options.md new file mode 100644 index 00000000..a13870ee --- /dev/null +++ b/docs/common_options.md @@ -0,0 +1,73 @@ +# Common diagram generation options + +## Overall configuration file structure +By default, `clang-uml` will look for file `.clang-uml` in the projects directory and read all diagrams definitions +from it. The file must be specified in YAML and it's overall structure is as follows: + +```yaml + +diagrams: + : + type: [class|sequence|package|include] + + : + type: [class|sequence|package|include] + + ... +``` + +The top level common options are inherited by specific diagrams, if the option is applicable to them and they themselves +do not override this option. + +For detailed reference of all configuration options see [here](./configuration_file.md). + +## Translation unit glob patterns +One of the key options of the diagram configuration is the list of translation units, which should be parsed to +get all necessary information for a diagram. + +The syntax is simple and based on glob patterns, which can be added to the configuration file as follows: + +```yaml + glob: + - src/dir1/*.cc + - src/dir3/*.cc +``` + +The glob patterns only need to match the translation units, which are also in the `compile_commands.json` file, i.e. +any files that match the glob patterns but are not in `compile_commands.json` will be ignored. In case the `glob` +pattern set does not much any translation units an error will be printed on the standard output. + +For small projects, the `glob` property can be omitted, which will result in `clang-uml` parsing all translation units +from `compile_commands.json` for the diagram. However for large projects, constraining the number of translation units +for each diagram to absolute minimum will significantly decrease the diagram generation times. + +## PlantUML custom directives +In case it's necessary to add some custom PlantUML declarations before or after the generated diagram content, +it can be achieved simply using the `plantuml` configuration properties, for instance: + +```yaml + plantuml: + before: + - left to right direction + after: + - note left of {{ alias("ns1::ns2::MyClass") }} This is my class. +``` + +These directive are useful for instance for adding notes to elements in the diagrams or customizing diagram layout +or style. + +Please note that when referring to diagram elements in the PlantUML directives, they must be added using Jinja +templates `alias` command as in the example above. + +More options can be found in the official PlantUML [documentation](https://plantuml.com/). + +## Adding debug information in the generated diagrams +Sometimes it is useful for debugging issues with the diagrams to have information on the exact source location, +from which given declaration or call expression was derived. By adding option: + +```yaml +debug_mode: true +``` + +the generated PlantUML diagram will contain comments before each line containing the source location of the +specific diagram element. \ No newline at end of file diff --git a/docs/configuration_file.md b/docs/configuration_file.md index f16714b0..76a777c2 100644 --- a/docs/configuration_file.md +++ b/docs/configuration_file.md @@ -5,6 +5,7 @@ * `output_directory` - path to the directory where PlantUML diagrams will be generated * `diagrams` - the map of diagrams to be generated, each diagram name is provided as the key of the diagram YAML node +* `debug_mode` - add inline debug information in the generated diagrams ### Diagram options * `type` - type of diagram, one of [`class`, `sequence`, `package`, `include`] @@ -22,7 +23,7 @@ * `dependants` - include all classes, which depend on the specified class * `dependencies` - include all classes, which are dependencies of the specified class * `context` - include only entities in direct relationship with specified classes -* `exclude` - definition of excqlusion patterns: +* `exclude` - definition of exclusion patterns: * `namespaces` - list of namespaces to exclude * `relationships` - list of relationships to exclude * `elements` - list of elements, i.e. specific classes, enums, templates to exclude @@ -37,112 +38,6 @@ * `before` - list of directives which will be added before the generated diagram * `after` - list of directives which will be added after the generated diagram -### Template engine -`clang-uml` integrates [inja](https://github.com/pantor/inja) template engine, with several -additional functions which can be used in textual directives within the configuration files, -notes and to generate links and tooltips to diagrams. - -The following, are the `clang-uml` additional template functions: -* `ltrim(string)` - left trims a string -* `rtrim(string)` - right trims a string -* `trim(string)` - trims a string -* `substr(string, offset, length)` - returns a substring of a string from offset of length -* `split(string)` - splits a string and returns a list of strings -* `replace(string, regex, replacement)` - returns a string with replace matches to regex with replacement string -* `abbrv(string, length)` - returns a string truncated to length including trailing ellipsis -* `element(string)` - returns the entire JSON context a given diagram element, including the following properties: - * `name` - name of the element - * `type` - type of diagram element (e.g. `class`, `enum`, `package`) - * `namespace` - fully qualified element namespace - * `full_name` - fully qualified element name - * `comment` [optional] - elements comment, if any - * `alias` - internal diagram element alias (e.g. PlantUML alias) -* `alias(string)` - returns a PlantUML alias of an C++ entity represented by string name -* `comment(string)` - returns a comment of an C++ entity represented by string name - -Templates allow complex postprocessing of the diagrams, for instance creation of customized PlantUML -notes in the diagrams from comments in the code. Below is an example of using the above commands to -generate notes in the PlantUML diagram from code comments (see also test case [t00050](./test_cases/t00050.md)): - -```yaml - plantuml: - after: - # Add a note left of the `A` class with the entire clas comment as content - - > - note left of {{ alias("A") }} - {{ comment("clanguml::t00050::A").formatted }} - end note - # Same as above - - > - note right of {{ element("clanguml::t00050::A").alias }} - {% set e=element("clanguml::t00050::A") %} {{ e.comment.formatted }} - end note - # Add a note left of class 'C' using trimmed text content from the class comment - - > - note left of {{ alias("C") }} #AABBCC - {{ trim(comment("clanguml::t00050::C").text) }} - end note - # For each element in the diagram (class, template, enum): - # - Add a note with \brief comment if exists - # - Add a note with \todo for each element which has it - # - Add a note with template parameter descriptions based on \tparam comment - - > - {# Render brief comments and todos, if any were written for an element #} - {% for e in diagram.elements %} - {% if existsIn(e, "comment") and existsIn(e.comment, "brief") %} - - note top of {{ e.alias }} {% if e.type == "class" %} #22AA22 {% else %} #2222AA {% endif %} - {% set c=e.comment %} {{ c.brief.0 }} - end note - - {% endif %} - {% if existsIn(e, "comment") and existsIn(e.comment, "todo") %} - {% set c=e.comment %} - {% for t in c.todo %} - note top of {{ e.alias }} #882222 - **TODO** - {{ t }} - end note - - {% endfor %} - - {% endif %} - {# Render template paramete if any #} - {% if existsIn(e, "comment") and existsIn(e.comment, "tparam") %} - {% set c=e.comment %} - - note top of {{ e.alias }} #AAAAFF - **Template parameters** - {% for tp in c.tparam %} - //{{ tp.name }}// {{ trim(tp.description) }} - {% endfor %} - end note - - {% endif %} - {% endfor %} -``` -Currently there are 2 available comment parsers: -* `plain` - default -* `clang` -They can be selected using `comment_parser` config option. - -#### `plain` comment parser -This parser provides only 2 options to the Jinja context: -* `comment.raw` - raw comment text, including comment markers such as `///` or `/**` -* `comment.formatted` - formatted entire comment - -#### `clang` comment parser -This parser uses Clang comment parsing API to extract commands from the command: -* `comment.raw` - raw comment text, including comment markers such as `///` or `/**` -* `comment.formatted` - formatted entire comment -* `comment..` - where command is the command used in the command e.g. `brief`, `todo`, etc. - and `N` is the index of the command in the array (each comment can have multiple instances of the - same command such as `\todo`) -* `comment.text` - entire text of the comment that is not attached to any command -* `comment.paragraph.` - array of plain text paragraphs, for instance if you don't use `\brief` - commands but often provide brief description as first sentence of the comment separated with a new line - from the rest of the comment - ## Example complete config ```yaml diff --git a/docs/diagram_filters.md b/docs/diagram_filters.md new file mode 100644 index 00000000..946217cf --- /dev/null +++ b/docs/diagram_filters.md @@ -0,0 +1,161 @@ +# Diagram filters + +Diagram filters are at the core of generating diagrams with `clang-uml`, as they allow to fine tune the scope +of each diagram, and thus provide you with a several small, but readable diagrams instead of a single huge diagram +that cannot be effectively browsed, printed or included in an online documentation of your project. + +Filters can be specified separate for each diagram, and they can be added as either `include` or `exclude` filters, +depending on which is more appropriate for a given diagram. + +For instance to include only C++ entities from a namespace `ns1::ns2` but not `ns1::ns2::detail` add the following +to your diagram configuration: + +```yaml + include: + namespaces: + - ns1::ns2 + exclude: + namespaces: + - ns1::ns2::detail +``` + +The following filters are available. + +## `namespaces` + +Allows to include or exclude entities from specific namespaces. + +## `elements` + +Allows to directly include or exclude specific entities from the diagrams, for instance to exclude a specific class +from an included namespace: + +```yaml + include: + namespaces: + - ns1::ns2 + exclude: + elements: + - ns1::ns2::MyClass +``` + +## `context` + +This filter allows to limit the diagram elements only to classes which are in direct relationship (of any kind) with +the specified class: + +```yaml + include: + context: + - ns1::ns2::MyClass +``` + + +## `relationships` + +This filter allows to include or exclude specific types of relationships from the diagram, for instance to only +include inheritance and template specialization/instantiation relationships add the following to the diagram: + +```yaml + include: + relationships: + - inheritance + - instantiation +``` + +The following relationships can be used in this filter: + * inheritance + * composition + * aggregation + * ownership + * association + * instantiation + * friendship + * dependency + +## `subclasses` + +This filter allows to include or exclude all subclasses of a given class in the diagram. + +## `specializations` + +This filter allows to include or exclude specializations and instantiations of a specific template from the diagram. + +## `dependants` and `dependencies` + +These filters allow to specify that only dependants or dependencies of a given class should be included in the diagram. +This can be useful for analyzing what classes in your project depend on some other class, which could have impact for +instance on refactoring. + +For instance the following code: +```cpp + +namespace dependants { +struct A { +}; + +struct B { + void b(A *a) { } +}; + +struct BB { + void bb(A *a) { } +}; + +struct C { + void c(B *b) { } +}; + +struct D { + void d(C *c) { } + void dd(BB *bb) { } +}; + +struct E { + void e(D *d) { } +}; + +struct F { +}; +} // namespace dependants + +namespace dependencies { + +struct G { +}; + +struct GG { +}; + +struct H { + void h(G *g) { } + void hh(GG *gg) { } +}; + +struct HH { + void hh(G *g) { } +}; + +struct I { + void i(H *h) { } +}; + +struct J { + void i(I *i) { } +}; +``` + +and the following filter: +```yaml + include: + dependants: + - clanguml::t00043::dependants::A + dependencies: + - clanguml::t00043::dependencies::J + relationships: + - dependency +``` + +generates the following diagram: + +![t00043_class](./test_cases/t00043_class.svg) diff --git a/docs/doxygen_integration.md b/docs/doxygen_integration.md new file mode 100644 index 00000000..119096c3 --- /dev/null +++ b/docs/doxygen_integration.md @@ -0,0 +1,14 @@ +# Doxygen integration + +`clang-uml` diagrams can be easily added to the Doxygen documentation using the image tag, however +[Doxygen](https://www.doxygen.nl/index.html) does not support the `clang-uml` specific commands. + +`clang-uml` decorators can be omitted completely in Doxygen, by adding the +following lines to the Doxygen config file: + +``` +ALIASES += uml="" +ALIASES += uml{1}="" +ALIASES += uml{2}="" +ALIASES += uml{3}="" +``` \ No newline at end of file diff --git a/docs/include_diagrams.md b/docs/include_diagrams.md new file mode 100644 index 00000000..7e92eda9 --- /dev/null +++ b/docs/include_diagrams.md @@ -0,0 +1,58 @@ +# Generating include diagrams + +Include diagrams allow to document the include dependencies among different parts of the project. This can be very useful +for instance to detect that a file was included from a module directory, on which specific part of the project +should not ever depend. + +The minimal config required to generate an include diagram is presented below: +```yaml +# Path to the directory where `compile_commands.json` can be found +compilation_database_dir: _build +# Output directory for the diagrams +output_directory: puml +# Diagrams definitions +diagrams: + # Diagram name + my_class_diagram: + # Type of diagram (has to be `include`) + type: include + # Include only translation units matching the following patterns + glob: + - src/*.cc + # Render the paths relative to this directory + relative_to: src + # Include also external system headers + generate_system_headers: true + # Include only classes and functions from files in `src` directory + include: + # Include only files belonging to these paths + paths: + - src +``` + +One distinctive option in `include` diagrams is `relative_to`, which tells `clang-uml` to render all filename +paths relative to this directory. + +The following table presents the PlantUML arrows representing relationships in the include diagrams. + +| UML | PlantUML | +| ---- | --- | +| Include (local) | ![association](img/puml_association.png) | +| Include (system) | ![dependency](img/puml_dependency.png) | + +## Tracking system headers directly included by projects files + +In case you would like to include the information about what system headers your projects file include simply add +the following option to the diagram: + +```yaml +generate_system_headers: true +``` + +This will include only system headers directly included from the projects source files (matched by `glob`) and not +their dependencies, for example: + +![t40001_include](./test_cases/t40001_include.svg) + +Please note that generating include diagram, which contains third party and system library headers will result +in a huge diagram that will be unlikely to be useful. \ No newline at end of file diff --git a/docs/interactive_svg_diagrams.md b/docs/interactive_svg_diagrams.md new file mode 100644 index 00000000..92a09d13 --- /dev/null +++ b/docs/interactive_svg_diagrams.md @@ -0,0 +1,15 @@ +# Interactive SVG diagrams + +`clang-uml` in combination with PlantUML's link generation in diagrams allows to generate interactive diagrams, +where clicking on any class, method or call expression can direct the user directly to the source code or some other +diagram or document available online. + +For instance to generate links to GitHub repository directly for most of diagram elements simple add this to your +`.clang-uml` file: +```yaml +generate_links: + link: 'https://github.com/myorg/myrepo/blob/{{ git.commit }}/{{ element.source.path }}#L{{ element.source.line }}' + tooltip: '{% if "comment" in element %}{{ abbrv(trim(replace(element.comment, "\n+", " ")), 256) }}{% else %}{{ element.name }}{% endif %}' +``` + +You can open example diagram [here](https://raw.githubusercontent.com/bkryza/clang-uml/master/docs/test_cases/t00014_class.svg) to see how it works in action. \ No newline at end of file diff --git a/docs/jinja_templates.md b/docs/jinja_templates.md new file mode 100644 index 00000000..168a0c9d --- /dev/null +++ b/docs/jinja_templates.md @@ -0,0 +1,111 @@ +# Template engine +`clang-uml` integrates [inja](https://github.com/pantor/inja) template engine, with several +additional functions which can be used in textual directives within the configuration files, +notes and to generate links and tooltips in diagrams. + +The following, are the `clang-uml` additional template functions: +* `ltrim(string)` - left trims a string +* `rtrim(string)` - right trims a string +* `trim(string)` - trims a string +* `substr(string, offset, length)` - returns a substring of a string from offset of length +* `split(string)` - splits a string and returns a list of strings +* `replace(string, regex, replacement)` - returns a string with replace matches to regex with replacement string +* `abbrv(string, length)` - returns a string truncated to length including trailing ellipsis +* `element(string)` - returns the entire JSON context a given diagram element, including the following properties: + * `name` - name of the element + * `type` - type of diagram element (e.g. `class`, `enum`, `package`) + * `namespace` - fully qualified element namespace + * `full_name` - fully qualified element name + * `comment` [optional] - elements comment, if any + * `alias` - internal diagram element alias (e.g. PlantUML alias) +* `alias(string)` - returns a PlantUML alias of an C++ entity represented by string name +* `comment(string)` - returns a comment of an C++ entity represented by string name + +Templates allow complex postprocessing of the diagrams, for instance creation of customized PlantUML +notes in the diagrams from comments in the code. Below is an example of using the above commands to +generate notes in the PlantUML diagram from code comments (see also test case [t00050](./test_cases/t00050.md)): + +```yaml + plantuml: + after: + # Add a note left of the `A` class with the entire clas comment as content + - > + note left of {{ alias("A") }} + {{ comment("clanguml::t00050::A").formatted }} + end note + # Same as above + - > + note right of {{ element("clanguml::t00050::A").alias }} + {% set e=element("clanguml::t00050::A") %} {{ e.comment.formatted }} + end note + # Add a note left of class 'C' using trimmed text content from the class comment + - > + note left of {{ alias("C") }} #AABBCC + {{ trim(comment("clanguml::t00050::C").text) }} + end note + # For each element in the diagram (class, template, enum): + # - Add a note with \brief comment if exists + # - Add a note with \todo for each element which has it + # - Add a note with template parameter descriptions based on \tparam comment + - > + {# Render brief comments and todos, if any were written for an element #} + {% for e in diagram.elements %} + {% if existsIn(e, "comment") and existsIn(e.comment, "brief") %} + + note top of {{ e.alias }} {% if e.type == "class" %} #22AA22 {% else %} #2222AA {% endif %} + {% set c=e.comment %} {{ c.brief.0 }} + end note + + {% endif %} + {% if existsIn(e, "comment") and existsIn(e.comment, "todo") %} + {% set c=e.comment %} + {% for t in c.todo %} + note top of {{ e.alias }} #882222 + **TODO** + {{ t }} + end note + + {% endfor %} + + {% endif %} + {# Render template paramete if any #} + {% if existsIn(e, "comment") and existsIn(e.comment, "tparam") %} + {% set c=e.comment %} + + note top of {{ e.alias }} #AAAAFF + **Template parameters** + {% for tp in c.tparam %} + //{{ tp.name }}// {{ trim(tp.description) }} + {% endfor %} + end note + + {% endif %} + {% endfor %} +``` + +### Accessing comment content +Text available in the code comment blocks can be accessed in the templates depending on the selected comment +parser. + +Currently there are 2 available comment parsers: +* `plain` - default +* `clang` - Clang's comment parser + +They can be selected using `comment_parser` config option. + +#### `plain` comment parser +This parser provides only 2 options to the Jinja context: +* `comment.raw` - raw comment text, including comment markers such as `///` or `/**` +* `comment.formatted` - formatted entire comment + +#### `clang` comment parser +This parser uses Clang comment parsing API to extract commands from the command: +* `comment.raw` - raw comment text, including comment markers such as `///` or `/**` +* `comment.formatted` - formatted entire comment +* `comment..` - where command is the command used in the command e.g. `brief`, `todo`, etc. + and `N` is the index of the command in the array (each comment can have multiple instances of the + same command such as `\todo`) +* `comment.text` - entire text of the comment that is not attached to any command +* `comment.paragraph.` - array of plain text paragraphs, for instance if you don't use `\brief` + commands but often provide brief description as first sentence of the comment separated with a new line + from the rest of the comment \ No newline at end of file diff --git a/docs/package_diagrams.md b/docs/package_diagrams.md new file mode 100644 index 00000000..03182010 --- /dev/null +++ b/docs/package_diagrams.md @@ -0,0 +1,151 @@ +# Generating package diagrams + +Package diagrams are simple diagrams which can be useful to determine the high level structure of a C++ project, +by rendering all projects namespaces as UML packages and their interdependencies. + +The minimal config required to generate a package diagram is presented below: +```yaml +# Path to the directory where `compile_commands.json` can be found +compilation_database_dir: _build +# Output directory for the diagrams +output_directory: puml +# Diagrams definitions +diagrams: + # Diagram name + my_class_diagram: + # Type of diagram (has to be `package`) + type: package + # Include only translation units matching the following patterns + glob: + - src/*.cc + # Include only classes and functions from files in `src` directory + include: + namespaces: + - ns1::ns2 +``` + +For instance the following C++ code: +``` +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 A14 { +struct CN { +}; +} +namespace A15 { +struct CO { +}; +} +namespace A16 { +struct CP { +}; +} +namespace A17 { +struct CR { +}; +} +} +namespace B::BB::BBB { +class CBA : public A::AA::A6::CF { +public: + A::AA::A1::CA *ca_; + A::AA::A2::CB cb_; + std::shared_ptr cc_; + std::map> *cd_; + std::array co_; + static A::AA::A16::CP *cp_; + + CBA() = default; + + CBA(A::AA::A14::CN *cn) { } + + friend A::AA::A17::CR; + + template CBA(std::tuple &items) { } + + void ce(const std::vector /*ce_*/) { } + + std::shared_ptr cg() { return {}; } + + template + void ch(std::map> &ch_) + { + } + + template + std::map> ci(T * /*t*/) + { + return {}; + } +}; + +void cj(std::unique_ptr /*cj_*/) { } + +std::unique_ptr ck() { return {}; } + +template +void cl(std::map> & /*ch_*/) +{ +} + +template std::map> cm() +{ + return {}; +} +} +``` + +generates the following diagram: + +![package_deps](./test_cases/t30002_package.svg) diff --git a/docs/quick_start.md b/docs/quick_start.md new file mode 100644 index 00000000..23054f05 --- /dev/null +++ b/docs/quick_start.md @@ -0,0 +1,56 @@ +# Quick start + +To add an initial class diagram to your project, follow these steps: + +1. Enter your projects top level directory and run: + ```bash + $ clang-uml --init + ``` +2. Edit the generated `.clang-uml` file and set the following: + ```yaml + # Path to `compile_commands.json` directory + compilation_database_dir: . + # Path to diagram output directory + output_directory: puml + diagrams: + # This is the name of the diagram + some_class_diagram: + type: class + # Parse only translation units in `src` subdirectory + glob: + - src/*.cc + # Render all names relative to `myproject` namespace + using_namespace: + - myproject + include: + # Include only elements in `myproject` namespace + namespaces: + - myproject + exclude: + # Exclude elements in `myproject::detail` namespace + namespaces: + - myproject::detail + ``` +3. Run `clang-uml` in the projects top directory: + ```bash + $ clang-uml + ``` +4. Generate SVG images from the PlantUML diagrams: + ```bash + $ plantuml -tsvg puml/*.puml + ``` +5. Add another diagram: + ```bash + $ clang-uml --add-sequence-diagram another_diagram + ``` +6. Now list the diagrams defined in the config: + ```bash + $ clang-uml -l + The following diagrams are defined in the config file: + - another_diagram [sequence] + - some_class_diagram [class] + ``` +7. Generate only the new diagram: + ```bash + clang-uml -n another_diagram + ``` diff --git a/docs/sequence_diagrams.md b/docs/sequence_diagrams.md new file mode 100644 index 00000000..46070fde --- /dev/null +++ b/docs/sequence_diagrams.md @@ -0,0 +1,236 @@ +# Generating sequence diagrams + +The minimal config required to generate a sequence diagram is presented below: +```yaml +# Path to the directory where `compile_commands.json` can be found +compilation_database_dir: _build +# Output directory for the diagrams +output_directory: puml +# Diagrams definitions +diagrams: + # Diagram name + my_class_diagram: + # Type of diagram (has to be `sequence`) + type: sequence + # Include only translation units matching the following patterns + glob: + - src/*.cc + # Include only classes and functions from files in `src` directory + include: + paths: + - src + # Exclude calls to/from `std` namespace + exclude: + namespaces: + - std + start_from: + - function: "main(int,const char**)" +``` + +## Sequence diagram overview + +Consider the following diagram: + +![extension](test_cases/t20029_sequence.svg) + +`clang-uml` generated sequence diagrams are not strictly speaking conforming to the UML specification. In order to +make them more useful for documenting modern C++ code, the following assumptions were made: + * Free functions are included in the sequence diagrams as standalone participants (in fact `clang-uml` can be used + to generate sequence diagrams from plain old C code). Functions can also be aggregated into file participants, + based on their place of declaration + * Call expressions in conditional expressions in block statements (e.g. `if` or `while`) are rendered inside the + PlantUML `alt` or `loop` blocks but wrapped in `[`, `]` brackets + * Lambda expressions are generated as standalone participants, whose name comprises the parent context where they + are defined and the exact source code location + +## Specifying diagram entry point +Sequence diagrams require an entry point for the diagram in order to determine, at which point in the code the sequence +diagram should start. Currently, the entry point can only be a method or a free function, both specified using `start_from` +configuration property, for instance: +```yaml + start_from: + - function: "main(int,char**)" +``` +or +```yaml +start_from: + - function: "clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *)" +``` + +The entrypoints must be fully qualified and they must match exactly the string representation of given function or +method in the `clang-uml` model, which can be frustrating after few attempts. +If not sure, the best way is to put anything in the `function` +property value at first, run the `clang-uml` on the diagram with verbose set to `-vvv` and look in the logs +for the relevant function signature. At the end of the diagram generation at this verbosity level, `clang-uml` will +generate a textual representation of all discovered activities relevant for this diagram, for instance if you're looking +_for exact signature of method `translation_unit_visitor::VisitCXXRecordDecl`, look for similar_ +output in the logs: + +```bash +[trace] [tid 3842954] [diagram.cc:194] Sequence id=1875210076312968845: +[trace] [tid 3842954] [diagram.cc:198] Activity id=1875210076312968845, from=clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *): +[trace] [tid 3842954] [diagram.cc:208] Message from=clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *), from_id=1875210076312968845, to=__UNRESOLVABLE_ID__, to_id=0, name=, type=if +[trace] [tid 3842954] [diagram.cc:217] Message from=clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *), from_id=1875210076312968845, to=clanguml::sequence_diagram::visitor::translation_unit_visitor::should_include(const clang::TagDecl *), to_id=664596622746486441, name=should_include, type=call +``` + +Then you just need to copy and paste the signature exactly and rerun `clang-uml`. + +## Grouping free functions by file +By default, `clang-uml` will generate a new participant for each call to a free function (not method), which can lead +to a very large number of participants in the diagram. If it's an issue, an option can be provided in the diagram +definition: +```yaml +combine_free_functions_into_file_participants: true +``` +which will aggregate free functions per source file where they were declared thus minimizing the +diagram size. An example of such diagram is presented below: + +![extension](test_cases/t20017_sequence.svg) + +## Lambda expressions in sequence diagrams +Lambda expressions in sequence diagrams are... tricky. There is currently tentative support, which follows the +following rules: + * If lambda expression is called within the scope of the diagram, the calls from the lambda will be placed + at the lambda invocation and not declaration + * If lambda expression is passed to some function or method, which is outside the scope of the diagram + (e.g. used in `std::transform` call) the call will be generated at the point where lambda is passed as parameter + * If the lambda is passed as template parameter in instantiation it will not be generated at the moment at all + +Another issue is the naming of lambda participants. Currently, each lambda is rendered in the diagram as a separate +class whose name is composed of the lambda location in the code (the only unique way of identifying lambdas I was able +to find). For example the following code: +```cpp +#include +#include +#include +#include +#include + +namespace clanguml { +namespace t20012 { +struct A { + void a() { aa(); } + + void aa() { aaa(); } + + void aaa() { } +}; + +struct B { + void b() { bb(); } + + void bb() { bbb(); } + + void bbb() { } + + void eb() { } +}; + +struct C { + void c() { cc(); } + + void cc() { ccc(); } + + void ccc() { } +}; + +struct D { + int add5(int arg) const { return arg + 5; } +}; + +class E { + std::optional> maybe_b; + std::shared_ptr a; + +public: + template void setup(F &&f) { f(maybe_b); } +}; + +template struct R { + R(F &&f) + : f_{std::move(f)} + { + } + + void r() { f_(); } + + F f_; +}; + +void tmain() +{ + A a; + B b; + C c; + + // The activity shouldn't be marked at the lambda definition, but + // wherever it is actually called... + auto alambda = [&a, &b]() { + a.a(); + b.b(); + }; + + // ...like here + alambda(); + + // There should be no call to B in the sequence diagram as the blambda + // is never called + [[maybe_unused]] auto blambda = [&b]() { b.b(); }; + + // Nested lambdas should also work + auto clambda = [alambda, &c]() { + c.c(); + alambda(); + }; + clambda(); + + R r{[&c]() { c.c(); }}; + + r.r(); + + D d; + + std::vector ints{0, 1, 2, 3, 4}; + std::transform(ints.begin(), ints.end(), ints.begin(), + [&d](auto i) { return d.add5(i); }); +} +} +} +``` + +generates the following diagram: + +![extension](test_cases/t20012_sequence.svg) + +## Customizing participants order +The default participant order in the sequence diagram can be suboptimal in the sense that consecutive calls +can go right, then left, then right again depending on the specific call chain in the code. It is however +possible to override this order in the diagram definition using `participants_order` property, +for instance like this test case: + +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t20029_sequence: + type: sequence + glob: + - ../../tests/t20029/t20029.cc + include: + namespaces: + - clanguml::t20029 + exclude: + access: + - private + using_namespace: + - clanguml::t20029 + start_from: + - function: clanguml::t20029::tmain() + participants_order: + - clanguml::t20029::tmain() + - clanguml::t20029::Encoder> + - clanguml::t20029::Retrier + - clanguml::t20029::ConnectionPool + - clanguml::t20029::encode_b64(std::string &&) +``` + diff --git a/docs/test_cases/t00002.md b/docs/test_cases/t00002.md index 4f76d19c..9f8d5e49 100644 --- a/docs/test_cases/t00002.md +++ b/docs/test_cases/t00002.md @@ -110,8 +110,8 @@ private: /// All the A pointers std::vector as; }; -} -} +} // namespace t00002 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00002_class.svg b/docs/test_cases/t00002_class.svg index 315f931f..8fba2607 100644 --- a/docs/test_cases/t00002_class.svg +++ b/docs/test_cases/t00002_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -21,8 +21,8 @@ foo_c() = 0 : void - - + + B @@ -31,8 +31,8 @@ foo_a() : void - - + + C @@ -41,18 +41,18 @@ foo_c() : void - - + + D - + - + as : std::vector<A *> @@ -60,18 +60,18 @@ foo_a() : void foo_c() : void - - + + E - + - + as : std::vector<A *> @@ -79,13 +79,13 @@ foo_a() : void foo_c() : void - + This is class A - + This is class B - + This is class D diff --git a/docs/test_cases/t00003.md b/docs/test_cases/t00003.md index e9d3d011..0ef4a4d2 100644 --- a/docs/test_cases/t00003.md +++ b/docs/test_cases/t00003.md @@ -73,8 +73,8 @@ private: }; int A::static_int = 1; -} -} +} // namespace t00003 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00003_class.svg b/docs/test_cases/t00003_class.svg index 8255329e..cfd4bc01 100644 --- a/docs/test_cases/t00003_class.svg +++ b/docs/test_cases/t00003_class.svg @@ -1,6 +1,6 @@ - + @@ -9,74 +9,74 @@ - - + + A - + - + public_member : int - + - + protected_member : int - + - + private_member : int - + - + a_ : int - + - + b_ : int - + - + c_ : int - + - + static_int : int - + - + static_const_int : const int - + - + auto_member : const unsigned long @@ -112,11 +112,11 @@ protected_method() : void private_method() : void - + - + compare : std::function<bool (const int)> diff --git a/docs/test_cases/t00004.md b/docs/test_cases/t00004.md index f713ca45..fc60fe3d 100644 --- a/docs/test_cases/t00004.md +++ b/docs/test_cases/t00004.md @@ -73,8 +73,8 @@ public: }; } -} -} +} // namespace t00004 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00004_class.svg b/docs/test_cases/t00004_class.svg index 69f22dc2..e4accd25 100644 --- a/docs/test_cases/t00004_class.svg +++ b/docs/test_cases/t00004_class.svg @@ -1,6 +1,6 @@ - + @@ -9,16 +9,16 @@ - - + + B - - + + B::AA @@ -28,8 +28,8 @@ AA_3 - - + + A @@ -40,16 +40,16 @@ foo2() const : void - - + + A::AA - - + + A::AA::Lights @@ -59,15 +59,15 @@ Red - - + + A::AA::AAA - + C::B @@ -75,8 +75,8 @@ int - - + + C @@ -84,39 +84,39 @@ T - + - + t : T - + - + b_int : B<int> - - + + C::AA - - + + C::AA::AAA - - + + C::AA::CCC @@ -125,8 +125,8 @@ CCC_2 - - + + C::B @@ -134,16 +134,16 @@ V - + - + b : V - - + + C::CC @@ -152,16 +152,16 @@ CC_2 - - + + detail::D - - + + detail::D::AA @@ -171,8 +171,8 @@ AA_3 - - + + detail::D::DD diff --git a/docs/test_cases/t00005.md b/docs/test_cases/t00005.md index 2284549b..8771f0e3 100644 --- a/docs/test_cases/t00005.md +++ b/docs/test_cases/t00005.md @@ -71,8 +71,8 @@ public: volatile J *j; mutable K *k; }; -} -} +} // namespace t00005 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00005_class.svg b/docs/test_cases/t00005_class.svg index 19019e03..e1a602a7 100644 --- a/docs/test_cases/t00005_class.svg +++ b/docs/test_cases/t00005_class.svg @@ -1,6 +1,6 @@ - + @@ -9,204 +9,204 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - - + + G - - + + H - - + + I - - + + J - - + + K - - + + R - + - + some_int : int - + - + some_int_pointer : int * - + - + some_int_pointer_pointer : int ** - + - + some_int_reference : int & - + - + a : A - + - + b : B * - + - + c : C & - + - + d : const D * - + - + e : const E & - + - + f : F && - + - + g : G ** - + - + h : H *** - + - + i : I *& - + - + j : volatile J * - + - + k : K * diff --git a/docs/test_cases/t00006.md b/docs/test_cases/t00006.md index 5ac6649e..63912be4 100644 --- a/docs/test_cases/t00006.md +++ b/docs/test_cases/t00006.md @@ -100,8 +100,8 @@ public: std::tuple ns; }; -} -} +} // namespace t00006 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00006_class.svg b/docs/test_cases/t00006_class.svg index 32733464..b55492ec 100644 --- a/docs/test_cases/t00006_class.svg +++ b/docs/test_cases/t00006_class.svg @@ -1,6 +1,6 @@ - + @@ -9,136 +9,136 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - - + + G - - + + H - - + + I - - + + J - - + + K - - + + L - - + + M - - + + N - - + + NN - - + + NNN - - + + custom_container @@ -146,15 +146,15 @@ T - + - + data : std::vector<T> - + custom_container @@ -162,102 +162,102 @@ E - - + + R - + - + a : std::vector<A> - + - + b : std::vector<B *> - + - + c : std::map<int,C> - + - + d : std::map<int,D *> - + - + e : custom_container<E> - + - + f : std::vector<std::vector<F>> - + - + g : std::map<int,std::vector<G *>> - + - + h : std::array<H,10> - + - + i : std::array<I *,5> - + - - j : J [10] + + j : J[10] - + - + k : K *[20] - + - + lm : std::vector<std::pair<L,M>> - + - + ns : std::tuple<N,NN,NNN> diff --git a/docs/test_cases/t00007.md b/docs/test_cases/t00007.md index e20572eb..0401caf3 100644 --- a/docs/test_cases/t00007.md +++ b/docs/test_cases/t00007.md @@ -37,8 +37,8 @@ public: std::shared_ptr b; std::weak_ptr c; }; -} -} +} // namespace t00007 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00007_class.svg b/docs/test_cases/t00007_class.svg index 03b5db76..839c3bde 100644 --- a/docs/test_cases/t00007_class.svg +++ b/docs/test_cases/t00007_class.svg @@ -1,6 +1,6 @@ - + @@ -9,56 +9,56 @@ - - + + A - - + + B - - + + C - - + + R - + - + a : std::unique_ptr<A> - + - + b : std::shared_ptr<B> - + - + c : std::weak_ptr<C> diff --git a/docs/test_cases/t00008.md b/docs/test_cases/t00008.md index fb856288..0da6070e 100644 --- a/docs/test_cases/t00008.md +++ b/docs/test_cases/t00008.md @@ -52,8 +52,8 @@ struct D { void add(int i) { ints.template_template.values.push_back(i); } }; -} -} +} // namespace t00008 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00008_class.svg b/docs/test_cases/t00008_class.svg index 39e9add7..035bec9e 100644 --- a/docs/test_cases/t00008_class.svg +++ b/docs/test_cases/t00008_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,51 +18,51 @@ T,P,CMP,int N - + - + value : T - + - + pointer : T * - + - + reference : T & - + - + values : std::vector<P> - + - + ints : std::array<int,N> - + - + comparator : CMP - - + + Vector @@ -70,16 +70,16 @@ T - + - + values : std::vector<T> - - + + B @@ -87,15 +87,15 @@ T,C<> - + - + template_template : C<T> - + B @@ -103,18 +103,18 @@ int,Vector - - + + D - + - + ints : B<int,Vector> diff --git a/docs/test_cases/t00009.md b/docs/test_cases/t00009.md index b413a49b..6b372686 100644 --- a/docs/test_cases/t00009.md +++ b/docs/test_cases/t00009.md @@ -35,8 +35,8 @@ public: A *astring; A> &avector; }; -} -} +} // namespace t00009 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00009_class.svg b/docs/test_cases/t00009_class.svg index bc138a31..e458498d 100644 --- a/docs/test_cases/t00009_class.svg +++ b/docs/test_cases/t00009_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,15 +18,15 @@ T - + - + value : T - + A @@ -34,7 +34,7 @@ int - + A @@ -42,7 +42,7 @@ std::string - + A @@ -50,32 +50,32 @@ std::vector<std::string> - - + + B - + - + aint : A<int> - + - + astring : A<std::string> * - + - + avector : A<std::vector<std::string>> & diff --git a/docs/test_cases/t00010.md b/docs/test_cases/t00010.md index 03f38f1c..1f8d9377 100644 --- a/docs/test_cases/t00010.md +++ b/docs/test_cases/t00010.md @@ -39,8 +39,8 @@ class C { public: B aintstring; }; -} -} +} // namespace t00010 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00010_class.svg b/docs/test_cases/t00010_class.svg index bdbb5fae..50872956 100644 --- a/docs/test_cases/t00010_class.svg +++ b/docs/test_cases/t00010_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,22 +18,22 @@ T,P - + - + first : T - + - + second : P - + A @@ -41,8 +41,8 @@ T,std::string - - + + B @@ -50,15 +50,15 @@ T - + - + astring : A<T,std::string> - + B @@ -66,18 +66,18 @@ int - - + + C - + - + aintstring : B<int> diff --git a/docs/test_cases/t00011.md b/docs/test_cases/t00011.md index 430f8dae..036dd5d2 100644 --- a/docs/test_cases/t00011.md +++ b/docs/test_cases/t00011.md @@ -49,8 +49,8 @@ public: void foo() { m_a->foo(); } A *m_a; }; -} -} +} // namespace t00011 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00011_class.svg b/docs/test_cases/t00011_class.svg index 2193ba1f..e53a7867 100644 --- a/docs/test_cases/t00011_class.svg +++ b/docs/test_cases/t00011_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + D @@ -18,16 +18,16 @@ T - + - + value : T - - + + A @@ -36,18 +36,18 @@ foo() : void - - + + B - + - + m_a : A * diff --git a/docs/test_cases/t00012.md b/docs/test_cases/t00012.md index cada73f8..36ba1629 100644 --- a/docs/test_cases/t00012.md +++ b/docs/test_cases/t00012.md @@ -55,8 +55,8 @@ class R { 3> c1; }; -} -} +} // namespace t00012 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00012_class.svg b/docs/test_cases/t00012_class.svg index ad23298b..aa169887 100644 --- a/docs/test_cases/t00012_class.svg +++ b/docs/test_cases/t00012_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,23 +18,23 @@ T,Ts... - + - + value : T - + - + values : std::variant<Ts...> - - + + B @@ -43,15 +43,15 @@ - + - + ints : std::array<int,sizeof...(Is)> - - + + C @@ -60,14 +60,14 @@ - + - + ints : std::array<T,sizeof...(Is)> - + A @@ -75,7 +75,7 @@ int,std::string,float - + A @@ -83,7 +83,7 @@ int,std::string,bool - + B @@ -91,7 +91,7 @@ 3,2,1 - + B @@ -99,7 +99,7 @@ 1,1,1,1 - + C @@ -107,50 +107,50 @@ std::map<int,std::vector<std::vector<std::vector<std::string>>>>,3,3,3 - - + + R - + - + a1 : A<int,std::string,float> - + - + a2 : A<int,std::string,bool> - + - + b1 : B<3,2,1> - + - + b2 : B<1,1,1,1> - + - + c1 : C<std::map<int,std::vector<std::vector<std::vector<std::string>>>>,3,3,3> - + Long template annotation diff --git a/docs/test_cases/t00013.md b/docs/test_cases/t00013.md index b6da68aa..9e7cf9fb 100644 --- a/docs/test_cases/t00013.md +++ b/docs/test_cases/t00013.md @@ -83,8 +83,8 @@ public: private: mutable E estring; }; -} -} +} // namespace t00013 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00013_class.svg b/docs/test_cases/t00013_class.svg index a96caa91..cc42084c 100644 --- a/docs/test_cases/t00013_class.svg +++ b/docs/test_cases/t00013_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + ABCD::F @@ -18,15 +18,15 @@ T - + - + f : T - + ABCD::F @@ -34,70 +34,70 @@ int - - + + A - + - + a : int - - + + B - + - + b : int - - + + C - + - + c : int - - + + D - + - + d : int print(R * r) : void - - + + E @@ -105,16 +105,16 @@ T - + - + e : T - - + + G @@ -122,22 +122,22 @@ T,Args... - + - + g : T - + - + args : std::tuple<Args...> - + E @@ -145,7 +145,7 @@ int - + G @@ -153,7 +153,7 @@ int,float,std::string - + E @@ -161,25 +161,25 @@ std::string - - + + R - + - + gintstring : G<int,float,std::string> - + - + estring : E<std::string> diff --git a/docs/test_cases/t00014.md b/docs/test_cases/t00014.md index a24cf000..7280649a 100644 --- a/docs/test_cases/t00014.md +++ b/docs/test_cases/t00014.md @@ -98,8 +98,8 @@ public: VoidCallback vcb; VectorPtr vps; }; -} -} +} // namespace t00014 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00014_class.svg b/docs/test_cases/t00014_class.svg index dc96eb98..e1a45e13 100644 --- a/docs/test_cases/t00014_class.svg +++ b/docs/test_cases/t00014_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,37 +18,37 @@ T,P - + - + t : T - + - + p : P - - + + B - + - + value : std::string - + A @@ -56,7 +56,7 @@ T,std::string - + A @@ -64,7 +64,7 @@ T,std::unique_ptr<std::string> - + A @@ -72,7 +72,7 @@ long,T - + A @@ -80,7 +80,7 @@ double,T - + A @@ -88,7 +88,7 @@ long,U - + A @@ -96,7 +96,7 @@ long,bool - + A @@ -104,7 +104,7 @@ double,bool - + A @@ -112,7 +112,7 @@ long,float - + A @@ -120,7 +120,7 @@ double,float - + A @@ -128,7 +128,7 @@ bool,std::string - + A @@ -136,7 +136,7 @@ float,std::unique_ptr<std::string> - + A @@ -144,7 +144,7 @@ int,std::string - + A @@ -152,7 +152,7 @@ std::string,std::string - + A @@ -160,7 +160,7 @@ char,std::string - + A @@ -168,116 +168,116 @@ wchar_t,std::string - - + + R - + - + bapair : PairPairBA<bool> - + - + abool : APtr<bool> - + - + aboolfloat : AAPtr<bool,float> - + - + afloat : ASharedPtr<float> - + - + boolstring : A<bool,std::string> - + - + floatstring : AStringPtr<float> - + - + intstring : AIntString - + - + stringstring : AStringString - + - + bstringstring : BStringString - + - + bs : BVector - + - + bs2 : BVector2 - + - + cb : SimpleCallback<ACharString> - + - + gcb : GenericCallback<R::AWCharString> - + - + vcb : VoidCallback - + - + vps : VectorPtr<B> @@ -307,9 +307,9 @@ - + - + @@ -375,9 +375,9 @@ bstringstring - + - + diff --git a/docs/test_cases/t00015.md b/docs/test_cases/t00015.md index 5c01e5ad..ff2cea51 100644 --- a/docs/test_cases/t00015.md +++ b/docs/test_cases/t00015.md @@ -36,7 +36,7 @@ namespace { class Anon final : public A { }; } -} +} // namespace ns1 namespace ns3 { @@ -48,8 +48,8 @@ class Anon : public t00015::ns1::A { class B : public ns1::ns2::Anon { }; } -} -} +} // namespace t00015 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00015_class.svg b/docs/test_cases/t00015_class.svg index cc224b49..37d05ac9 100644 --- a/docs/test_cases/t00015_class.svg +++ b/docs/test_cases/t00015_class.svg @@ -1,6 +1,6 @@ - + @@ -9,40 +9,40 @@ - - + + ns1::A - - + + ns1::ns2_v0_9_0::A - - + + ns1::Anon - - + + ns3::ns1::ns2::Anon - - + + ns3::B diff --git a/docs/test_cases/t00016.md b/docs/test_cases/t00016.md index 1f433fa7..74dfed28 100644 --- a/docs/test_cases/t00016.md +++ b/docs/test_cases/t00016.md @@ -44,8 +44,8 @@ template <> struct is_numeric { template <> struct is_numeric { enum { value = false }; }; -} -} +} // namespace t00016 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00016_class.svg b/docs/test_cases/t00016_class.svg index 232edc16..fb0384c1 100644 --- a/docs/test_cases/t00016_class.svg +++ b/docs/test_cases/t00016_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + is_numeric<> @@ -19,8 +19,8 @@ value : enum - - + + is_numeric @@ -29,8 +29,8 @@ value : enum - - + + is_numeric @@ -41,8 +41,8 @@ value : enum - - + + is_numeric @@ -53,8 +53,8 @@ value : enum - - + + is_numeric @@ -65,8 +65,8 @@ value : enum - - + + is_numeric @@ -77,8 +77,8 @@ value : enum - - + + is_numeric diff --git a/docs/test_cases/t00017.md b/docs/test_cases/t00017.md index 3b71f8f8..7cb8f176 100644 --- a/docs/test_cases/t00017.md +++ b/docs/test_cases/t00017.md @@ -83,8 +83,8 @@ private: volatile J *j; mutable K *k; }; -} -} +} // namespace t00017 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00017_class.svg b/docs/test_cases/t00017_class.svg index 135a3ef1..091332bf 100644 --- a/docs/test_cases/t00017_class.svg +++ b/docs/test_cases/t00017_class.svg @@ -1,6 +1,6 @@ - + @@ -9,127 +9,127 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - - + + G - - + + H - - + + I - - + + J - - + + K - - + + R - + - + some_int : int - + - + some_int_pointer : int * - + - + some_int_pointer_pointer : int ** - + - + some_int_reference : int & diff --git a/docs/test_cases/t00018.md b/docs/test_cases/t00018.md index 12e990f2..90576792 100644 --- a/docs/test_cases/t00018.md +++ b/docs/test_cases/t00018.md @@ -97,8 +97,8 @@ widget::widget(widget &&) = default; widget::~widget() = default; widget &widget::operator=(widget &&) = default; -} -} +} // namespace t00018 +} // namespace clanguml ``` File t00018_impl.cc @@ -126,9 +126,9 @@ void widget::draw(const clanguml::t00018::widget &w) if (w.shown()) std::cout << "drawing a non-const widget " << n << '\n'; } -} -} -} +} // namespace impl +} // namespace t00018 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00018_class.svg b/docs/test_cases/t00018_class.svg index eb37d88f..c4775c5f 100644 --- a/docs/test_cases/t00018_class.svg +++ b/docs/test_cases/t00018_class.svg @@ -1,6 +1,6 @@ - + @@ -9,18 +9,18 @@ - - + + impl::widget - + - + n : int @@ -30,18 +30,18 @@ draw(const widget & w) : void widget(int n) : void - - + + widget - + - + pImpl : std::unique_ptr<impl::widget> diff --git a/docs/test_cases/t00019_class.svg b/docs/test_cases/t00019_class.svg index 7e16eebd..c7a0d1cd 100644 --- a/docs/test_cases/t00019_class.svg +++ b/docs/test_cases/t00019_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Base @@ -25,8 +25,8 @@ m2() : std::string - - + + Layer1 @@ -39,8 +39,8 @@ m2() : std::string - - + + Layer2 @@ -51,8 +51,8 @@ all_calls_count() const : int - - + + Layer3 @@ -60,18 +60,18 @@ LowerLayer - + - + m_m1_calls : int - + - + m_m2_calls : int @@ -83,7 +83,7 @@ m1_calls() const : int m2_calls() const : int - + Layer3 @@ -91,7 +91,7 @@ Base - + Layer2 @@ -99,7 +99,7 @@ Layer3<Base> - + Layer1 @@ -107,18 +107,18 @@ Layer2<Layer3<Base>> - - + + A - + - + layers : std::unique_ptr<Layer1<Layer2<Layer3<Base>>>> diff --git a/docs/test_cases/t00020.md b/docs/test_cases/t00020.md index ac39d129..462db2a6 100644 --- a/docs/test_cases/t00020.md +++ b/docs/test_cases/t00020.md @@ -92,8 +92,8 @@ public: return std::make_unique(); } }; -} -} +} // namespace t00020 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00020_class.svg b/docs/test_cases/t00020_class.svg index acf87791..e45c6e67 100644 --- a/docs/test_cases/t00020_class.svg +++ b/docs/test_cases/t00020_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + ProductA @@ -21,8 +21,8 @@ sell(int price) const = 0 : bool - - + + ProductA1 @@ -31,8 +31,8 @@ sell(int price) const : bool - - + + ProductA2 @@ -41,8 +41,8 @@ sell(int price) const : bool - - + + ProductB @@ -53,8 +53,8 @@ buy(int price) const = 0 : bool - - + + ProductB1 @@ -63,8 +63,8 @@ buy(int price) const : bool - - + + ProductB2 @@ -73,8 +73,8 @@ buy(int price) const : bool - - + + AbstractFactory @@ -85,8 +85,8 @@ make_b() const = 0 : std::unique_ptr<ProductB> - - + + Factory1 @@ -97,8 +97,8 @@ make_b() const : std::unique_ptr<ProductB> - - + + Factory2 diff --git a/docs/test_cases/t00021.md b/docs/test_cases/t00021.md index b52976af..0d431d17 100644 --- a/docs/test_cases/t00021.md +++ b/docs/test_cases/t00021.md @@ -66,8 +66,8 @@ class B : public Item { public: void accept(const Visitor &visitor) const override { } }; -} -} +} // namespace t00021 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00021_class.svg b/docs/test_cases/t00021_class.svg index 4b041183..7769c451 100644 --- a/docs/test_cases/t00021_class.svg +++ b/docs/test_cases/t00021_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Visitor @@ -23,8 +23,8 @@ visit_B(const B & item) const = 0 : void - - + + Visitor1 @@ -35,8 +35,8 @@ visit_B(const B & item) const : void - - + + Visitor2 @@ -47,8 +47,8 @@ visit_B(const B & item) const : void - - + + Visitor3 @@ -59,8 +59,8 @@ visit_B(const B & item) const : void - - + + Item @@ -71,8 +71,8 @@ accept(const Visitor & visitor) const = 0 : void - - + + A @@ -81,8 +81,8 @@ accept(const Visitor & visitor) const : void - - + + B diff --git a/docs/test_cases/t00022.md b/docs/test_cases/t00022.md index 0dea7862..17101292 100644 --- a/docs/test_cases/t00022.md +++ b/docs/test_cases/t00022.md @@ -47,8 +47,8 @@ protected: void method1() override { } void method2() override { } }; -} -} +} // namespace t00022 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00022_class.svg b/docs/test_cases/t00022_class.svg index 2f5faf30..8e59572e 100644 --- a/docs/test_cases/t00022_class.svg +++ b/docs/test_cases/t00022_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -23,8 +23,8 @@ method2() = 0 : void - - + + A1 @@ -35,8 +35,8 @@ method2() : void - - + + A2 diff --git a/docs/test_cases/t00023.md b/docs/test_cases/t00023.md index 69f5d386..b7a5dadf 100644 --- a/docs/test_cases/t00023.md +++ b/docs/test_cases/t00023.md @@ -56,8 +56,8 @@ public: private: std::unique_ptr m_strategy; }; -} -} +} // namespace t00023 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00023_class.svg b/docs/test_cases/t00023_class.svg index 46fd4028..5919f197 100644 --- a/docs/test_cases/t00023_class.svg +++ b/docs/test_cases/t00023_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Strategy @@ -21,8 +21,8 @@ algorithm() = 0 : void - - + + StrategyA @@ -31,8 +31,8 @@ algorithm() : void - - + + StrategyB @@ -41,8 +41,8 @@ algorithm() : void - - + + StrategyC @@ -51,18 +51,18 @@ algorithm() : void - - + + Context - + - + m_strategy : std::unique_ptr<Strategy> diff --git a/docs/test_cases/t00024.md b/docs/test_cases/t00024.md index 869c08d9..18210cfa 100644 --- a/docs/test_cases/t00024.md +++ b/docs/test_cases/t00024.md @@ -55,8 +55,8 @@ public: private: std::shared_ptr m_target; }; -} -} +} // namespace t00024 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00024_class.svg b/docs/test_cases/t00024_class.svg index e8e1c0f1..8271f57c 100644 --- a/docs/test_cases/t00024_class.svg +++ b/docs/test_cases/t00024_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Target @@ -23,8 +23,8 @@ m2() = 0 : void - - + + Target1 @@ -35,8 +35,8 @@ m2() : void - - + + Target2 @@ -47,18 +47,18 @@ m2() : void - - + + Proxy - + - + m_target : std::shared_ptr<Target> diff --git a/docs/test_cases/t00025.md b/docs/test_cases/t00025.md index abf16eed..25c1ebdd 100644 --- a/docs/test_cases/t00025.md +++ b/docs/test_cases/t00025.md @@ -53,8 +53,8 @@ public: Proxy proxy1; Proxy proxy2; }; -} -} +} // namespace t00025 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00025_class.svg b/docs/test_cases/t00025_class.svg index f44b4cca..32597c63 100644 --- a/docs/test_cases/t00025_class.svg +++ b/docs/test_cases/t00025_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Target1 @@ -21,8 +21,8 @@ m2() : void - - + + Target2 @@ -33,8 +33,8 @@ m2() : void - - + + Proxy @@ -42,11 +42,11 @@ T - + - + m_target : std::shared_ptr<T> @@ -56,7 +56,7 @@ m1() : void m2() : void - + Proxy @@ -64,7 +64,7 @@ Target1 - + Proxy @@ -72,25 +72,25 @@ Target2 - - + + ProxyHolder - + - + proxy1 : Proxy<Target1> - + - + proxy2 : Proxy<Target2> diff --git a/docs/test_cases/t00026.md b/docs/test_cases/t00026.md index 3b31e67f..3e5b3729 100644 --- a/docs/test_cases/t00026.md +++ b/docs/test_cases/t00026.md @@ -74,8 +74,8 @@ struct StringMemento { Caretaker caretaker; Originator originator; }; -} -} +} // namespace t00026 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00026_class.svg b/docs/test_cases/t00026_class.svg index d9930e1d..8205cf98 100644 --- a/docs/test_cases/t00026_class.svg +++ b/docs/test_cases/t00026_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Memento @@ -18,11 +18,11 @@ T - + - + m_value : T @@ -30,8 +30,8 @@ Memento<T>(T && v) : void value() const : T - - + + Originator @@ -39,11 +39,11 @@ T - + - + m_value : T @@ -57,8 +57,8 @@ print() const : void set(T && v) : void - - + + Caretaker @@ -66,11 +66,11 @@ T - + - + m_mementos : std::unordered_map<std::string,Memento<T>> @@ -78,7 +78,7 @@ state(const std::string & n) : Memento<T> & set_state(const std::string & s, Memento<T> && m) : void - + Caretaker @@ -86,7 +86,7 @@ std::string - + Originator @@ -94,25 +94,25 @@ std::string - - + + StringMemento - + - + caretaker : Caretaker<std::string> - + - + originator : Originator<std::string> diff --git a/docs/test_cases/t00027.md b/docs/test_cases/t00027.md index 2c2bb38c..e94a8eee 100644 --- a/docs/test_cases/t00027.md +++ b/docs/test_cases/t00027.md @@ -71,8 +71,8 @@ struct Window { Text title; Text description; }; -} -} +} // namespace t00027 +} // namespace clanguml ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00027_class.svg b/docs/test_cases/t00027_class.svg index ec50e838..6d186345 100644 --- a/docs/test_cases/t00027_class.svg +++ b/docs/test_cases/t00027_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Shape @@ -21,14 +21,14 @@ ~Shape() = default : void - + Line - - + + Line @@ -39,14 +39,14 @@ display() : void - + Text - - + + Text @@ -57,8 +57,8 @@ display() : void - - + + ShapeDecorator @@ -67,8 +67,8 @@ display() = 0 : void - - + + Color @@ -79,8 +79,8 @@ display() : void - - + + Weight @@ -91,7 +91,7 @@ display() : void - + Line @@ -99,7 +99,7 @@ Color,Weight - + Line @@ -107,7 +107,7 @@ Color - + Text @@ -115,7 +115,7 @@ Color,Weight - + Text @@ -123,39 +123,39 @@ Color - - + + Window - + - + border : Line<Color,Weight> - + - + divider : Line<Color> - + - + title : Text<Color,Weight> - + - + description : Text<Color> diff --git a/docs/test_cases/t00028_class.svg b/docs/test_cases/t00028_class.svg index 3d19d310..49053bfa 100644 --- a/docs/test_cases/t00028_class.svg +++ b/docs/test_cases/t00028_class.svg @@ -1,6 +1,6 @@ - + @@ -9,54 +9,54 @@ - - + + A - + A class note. - - + + B - + B class note. - - + + C - + C class note. - - + + D - + D class note. - - + + E @@ -64,27 +64,27 @@ T - + - + param : T - + E template class note. - - + + G - - + + F @@ -94,10 +94,10 @@ three - + F enum note. - + E @@ -105,59 +105,59 @@ int - - + + R - + - + aaa : A - + - + bbb : B * - + - + ccc : C & - + - + ddd : std::vector<std::shared_ptr<D>> - + - + eee : E<int> - + - + ggg : G ** R(C & c) : void - + R class note. diff --git a/docs/test_cases/t00029_class.svg b/docs/test_cases/t00029_class.svg index e01c82ed..0f5d365b 100644 --- a/docs/test_cases/t00029_class.svg +++ b/docs/test_cases/t00029_class.svg @@ -1,6 +1,6 @@ - + @@ -9,16 +9,16 @@ - - + + A - - + + C @@ -26,16 +26,16 @@ T - + - + param : T - - + + E @@ -45,64 +45,64 @@ three - - + + G1 - - + + G2 - - + + G3 - - + + G4 - - + + R - + - + g1 : G1 - + - + g3 : G3 & - + - + g4 : std::shared_ptr<G4> diff --git a/docs/test_cases/t00030_class.svg b/docs/test_cases/t00030_class.svg index 97f166e7..eb489613 100644 --- a/docs/test_cases/t00030_class.svg +++ b/docs/test_cases/t00030_class.svg @@ -1,6 +1,6 @@ - + @@ -9,86 +9,86 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + R - + - + aaa : A - + - + bbb : std::vector<B> - + - + ccc : std::vector<C> - + - + ddd : D - + - + eee : E * diff --git a/docs/test_cases/t00031_class.svg b/docs/test_cases/t00031_class.svg index e40c741b..19b28932 100644 --- a/docs/test_cases/t00031_class.svg +++ b/docs/test_cases/t00031_class.svg @@ -1,33 +1,33 @@ - + - + - + - - - + + + A - - + + B @@ -37,8 +37,8 @@ three - - + + @@ -47,23 +47,23 @@ T - + - + ttt : T - - + + D - + C @@ -71,39 +71,39 @@ int - - + + R - + - + aaa : A * - + - + bbb : std::vector<B> - + - + ccc : C<int> - + - + ddd : D * diff --git a/docs/test_cases/t00032_class.svg b/docs/test_cases/t00032_class.svg index 42fa7a8a..639c815a 100644 --- a/docs/test_cases/t00032_class.svg +++ b/docs/test_cases/t00032_class.svg @@ -1,6 +1,6 @@ - + @@ -9,24 +9,24 @@ - - + + Base - - + + TBase - - + + A @@ -35,8 +35,8 @@ operator()() : void - - + + B @@ -45,8 +45,8 @@ operator()() : void - - + + C @@ -55,8 +55,8 @@ operator()() : void - - + + Overload @@ -64,15 +64,15 @@ T,L,Ts... - + - + counter : L - + Overload @@ -80,18 +80,18 @@ TBase,int,A,B,C - - + + R - + - + overload : Overload<TBase,int,A,B,C> diff --git a/docs/test_cases/t00033_class.svg b/docs/test_cases/t00033_class.svg index ebe61859..c887865f 100644 --- a/docs/test_cases/t00033_class.svg +++ b/docs/test_cases/t00033_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,16 +18,16 @@ T - + - + aaa : T - - + + B @@ -35,16 +35,16 @@ T - + - + bbb : T - - + + C @@ -52,30 +52,30 @@ T - + - + ccc : T - - + + D - + - + ddd : int - + C @@ -83,7 +83,7 @@ D - + B @@ -91,7 +91,7 @@ std::unique_ptr<C<D>> - + A @@ -99,18 +99,18 @@ B<std::unique_ptr<C<D>>> - - + + R - + - + abc : A<B<std::unique_ptr<C<D>>>> diff --git a/docs/test_cases/t00034_class.svg b/docs/test_cases/t00034_class.svg index 031c11ad..4b61a0ef 100644 --- a/docs/test_cases/t00034_class.svg +++ b/docs/test_cases/t00034_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,119 +9,115 @@ - - - - - Void - - - - operator==(const Void & ) const : bool - - operator!=(const Void & ) const : bool + + + + + Void + + + + operator==(const Void & ) const : bool + + operator!=(const Void & ) const : bool - - - - - lift_void - - T - - + + + + + lift_void + + T + + - - - - - lift_void - - + + + + + lift_void + + - - - - - lift_void - - void - - + + + + + lift_void + + void + + - - - - - drop_void - - T - - + + + + + drop_void + + T + + - - - - - drop_void - - + + + + + drop_void + + - - - - - drop_void - - Void - - + + + + + drop_void + + Void + + - - - - - A - - + + + + + A + + - - - - - R - + + + + + R + - - - + + + - - la : lift_void_t<A> * + + la : lift_void_t<A> * - - - + + + - - lv : lift_void_t<void> * + + lv : lift_void_t<void> * - - - - - - - - - la - - - - la - - - - lv + + + + + + + + + la + + + + la diff --git a/docs/test_cases/t00035_class.svg b/docs/test_cases/t00035_class.svg index 5adfc004..7a742a8d 100644 --- a/docs/test_cases/t00035_class.svg +++ b/docs/test_cases/t00035_class.svg @@ -1,6 +1,6 @@ - + @@ -9,40 +9,40 @@ - - + + Top - - + + Left - - + + Center - - + + Bottom - - + + Right diff --git a/docs/test_cases/t00036.md b/docs/test_cases/t00036.md index e8b17089..b1414819 100644 --- a/docs/test_cases/t00036.md +++ b/docs/test_cases/t00036.md @@ -38,8 +38,8 @@ struct B { }; } -} -} +} // namespace ns11 +} // namespace ns1 namespace ns2 { namespace ns22 { diff --git a/docs/test_cases/t00036_class.svg b/docs/test_cases/t00036_class.svg index c1dbe571..e9626292 100644 --- a/docs/test_cases/t00036_class.svg +++ b/docs/test_cases/t00036_class.svg @@ -1,6 +1,6 @@ - + @@ -9,23 +9,23 @@ - + ns1 - + ns11 - + ns111 - + ns2 - + ns22 - - + + E @@ -34,8 +34,8 @@ yellow - - + + A @@ -43,15 +43,15 @@ T - + - + a : T - + A @@ -59,23 +59,23 @@ int - - + + B - + - + a_int : A<int> - - + + C diff --git a/docs/test_cases/t00037_class.svg b/docs/test_cases/t00037_class.svg index a699262f..18568400 100644 --- a/docs/test_cases/t00037_class.svg +++ b/docs/test_cases/t00037_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,114 +9,114 @@ - - - - - ST - - + + + + + ST + + - - - + + + - - dimensions : struct (anonymous struct at /home/bartek/devel/clang-uml-libtooling/tests/t00037/t00037.cc:6:5) + + dimensions : ST::(anonymous_620) - - - + + + - - units : struct (anonymous struct at /home/bartek/devel/clang-uml-libtooling/tests/t00037/t00037.cc:14:5) + + units : ST::(anonymous_739) - - - - - ST::(dimensions) - + + + + + ST::(dimensions) + - - - + + + - - t : double + + t : double - - - + + + - - x : double + + x : double - - - + + + - - y : double + + y : double - - - + + + - - z : double + + z : double - - - - - - ST::(units) - + + + + + + ST::(units) + - - - + + + - - c : double + + c : double - - - + + + - - h : double + + h : double - - - - - - A - + + + + + + A + - - - + + + - - st : ST + + st : ST - - - A() : void - - - - dimensions - - - - units - - - - st + + + A() : void + + + + dimensions + + + + units + + + + st diff --git a/docs/test_cases/t00038_class.svg b/docs/test_cases/t00038_class.svg index 5d46c673..16017a26 100644 --- a/docs/test_cases/t00038_class.svg +++ b/docs/test_cases/t00038_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + thirdparty::ns1::color_t @@ -20,16 +20,16 @@ blue - - + + thirdparty::ns1::E - - + + property_t @@ -39,47 +39,47 @@ property_c - - + + A - - + + B - - + + C - - + + key_t - + - + key : std::string - - + + map @@ -88,16 +88,16 @@ - - + + map - - + + map @@ -106,8 +106,8 @@ - - + + map @@ -116,8 +116,8 @@ - - + + map @@ -126,8 +126,8 @@ - - + + map diff --git a/docs/test_cases/t00039.md b/docs/test_cases/t00039.md index dd643b5a..b1d8b6e5 100644 --- a/docs/test_cases/t00039.md +++ b/docs/test_cases/t00039.md @@ -22,6 +22,7 @@ diagrams: - inheritance exclude: namespaces: + - std - clanguml::t00039::detail - clanguml::t00039::ns3::detail ``` diff --git a/docs/test_cases/t00039_class.svg b/docs/test_cases/t00039_class.svg index 56274cef..1a667771 100644 --- a/docs/test_cases/t00039_class.svg +++ b/docs/test_cases/t00039_class.svg @@ -1,6 +1,6 @@ - + @@ -9,95 +9,95 @@ - - + + C - - + + D - - + + E - - + + CD - - + + DE - - + + CDE - - + + A - - + + AA - - + + AAA - + - + b : B * - - + + ns2::AAAA - - + + ns3::F @@ -105,16 +105,16 @@ T - + - + t : T * - - + + ns3::FF @@ -122,16 +122,16 @@ T,M - + - + m : M * - - + + ns3::FE @@ -139,16 +139,16 @@ T,M - + - + m : M * - - + + ns3::FFF @@ -156,11 +156,11 @@ T,M,N - + - + n : N * diff --git a/docs/test_cases/t00040_class.svg b/docs/test_cases/t00040_class.svg index 1dac18ca..c1b33b52 100644 --- a/docs/test_cases/t00040_class.svg +++ b/docs/test_cases/t00040_class.svg @@ -1,6 +1,6 @@ - + @@ -9,50 +9,50 @@ - - + + A - + - + ii_ : int get_a() : int - - + + AA - - + + AAA - + - + b : B * get_aaa() : int - - + + R diff --git a/docs/test_cases/t00041_class.svg b/docs/test_cases/t00041_class.svg index 40c86768..9d0363e8 100644 --- a/docs/test_cases/t00041_class.svg +++ b/docs/test_cases/t00041_class.svg @@ -1,6 +1,6 @@ - + @@ -9,100 +9,100 @@ - - + + R - - + + D - + - + rr : RR * - - + + E - - + + F - - + + RR - + - + e : E * - + - + f : F * - + - + g : detail::G * - - + + RRR - - + + ns1::N - - + + ns1::NN - - + + ns1::NM diff --git a/docs/test_cases/t00042_class.svg b/docs/test_cases/t00042_class.svg index 4f270011..546f7beb 100644 --- a/docs/test_cases/t00042_class.svg +++ b/docs/test_cases/t00042_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,16 +18,16 @@ T - + - + a : T - - + + A @@ -35,16 +35,16 @@ void - + - + a : void * - - + + B @@ -52,22 +52,22 @@ T,K - + - + b : T - + - + bb : K - + A @@ -75,7 +75,7 @@ double - + A @@ -83,7 +83,7 @@ std::string - + B diff --git a/docs/test_cases/t00043_class.svg b/docs/test_cases/t00043_class.svg index c84f0eb1..8df6d871 100644 --- a/docs/test_cases/t00043_class.svg +++ b/docs/test_cases/t00043_class.svg @@ -1,6 +1,6 @@ - + @@ -9,22 +9,22 @@ - + dependants - + dependencies - - + + A - - + + B @@ -33,8 +33,8 @@ b(dependants::A * a) : void - - + + BB @@ -43,8 +43,8 @@ bb(dependants::A * a) : void - - + + C @@ -53,8 +53,8 @@ c(dependants::B * b) : void - - + + D @@ -65,8 +65,8 @@ dd(dependants::BB * bb) : void - - + + E @@ -75,24 +75,24 @@ e(dependants::D * d) : void - - + + G - - + + GG - - + + H @@ -103,8 +103,8 @@ hh(dependencies::GG * gg) : void - - + + I @@ -113,8 +113,8 @@ i(dependencies::H * h) : void - - + + J diff --git a/docs/test_cases/t00044_class.svg b/docs/test_cases/t00044_class.svg index 3be1d723..97b6fe84 100644 --- a/docs/test_cases/t00044_class.svg +++ b/docs/test_cases/t00044_class.svg @@ -1,6 +1,6 @@ - + @@ -9,7 +9,7 @@ - + signal_handler @@ -17,8 +17,8 @@ ,A - - + + sink @@ -27,15 +27,15 @@ sink<signal_handler<type-parameter-0-0 (type-parameter-0-1...), type-parameter-0-2> >(sink<signal_handler<type-parameter-0-0 (type-parameter-0-1...),type-parameter-0-2>>::signal_t & sh) : void - + - + signal : sink<signal_handler<type-parameter-0-0 (type-parameter-0-1...),type-parameter-0-2>>::signal_t * - - + + sink @@ -46,23 +46,23 @@ sink<signal_handler<type-parameter-0-0 (type-parameter-0-1...), type-parameter-0-2> >(sink<signal_handler<type-parameter-0-0 (type-parameter-0-1...),type-parameter-0-2>>::signal_t & sh) : void - + - + signal : sink<signal_handler<type-parameter-0-0 (type-parameter-0-1...),type-parameter-0-2>>::signal_t * - - + + signal_handler - - + + signal_handler @@ -71,8 +71,8 @@ - - + + signal_handler @@ -81,8 +81,8 @@ - - + + sink diff --git a/docs/test_cases/t00045.md b/docs/test_cases/t00045.md index 0ef54a70..931c73f2 100644 --- a/docs/test_cases/t00045.md +++ b/docs/test_cases/t00045.md @@ -66,8 +66,8 @@ public: void foo(::AA &aa) { (void)aa; } }; -} -} +} // namespace ns2 +} // namespace ns1 ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00045_class.svg b/docs/test_cases/t00045_class.svg index a00c18e0..1d7f3d4c 100644 --- a/docs/test_cases/t00045_class.svg +++ b/docs/test_cases/t00045_class.svg @@ -1,6 +1,6 @@ - + @@ -9,32 +9,32 @@ - - + + A - - + + AA - - + + AAA - - + + AAAA @@ -42,103 +42,103 @@ T - + - + t : T - - + + ns1::A - - + + ns1::ns2::A - - + + ns1::ns2::B - - + + ns1::ns2::C - - + + ns1::ns2::D - - + + ns1::ns2::E - - + + ns1::ns2::AAA - - + + ns1::ns2::R - + - + a : ns1::ns2::A * - + - + ns1_a : ns1::A * - + - + ns1_ns2_a : ns1::ns2::A * - + - + root_a : ::A * diff --git a/docs/test_cases/t00046.md b/docs/test_cases/t00046.md index eb9f9db9..0a63fa02 100644 --- a/docs/test_cases/t00046.md +++ b/docs/test_cases/t00046.md @@ -57,8 +57,8 @@ public: void foo(::AA &aa) { (void)aa; } }; -} -} +} // namespace ns2 +} // namespace ns1 ``` ## Generated UML diagrams diff --git a/docs/test_cases/t00046_class.svg b/docs/test_cases/t00046_class.svg index aced2121..6dacbaf0 100644 --- a/docs/test_cases/t00046_class.svg +++ b/docs/test_cases/t00046_class.svg @@ -1,6 +1,6 @@ - + @@ -9,118 +9,118 @@ - + ns1 - + ns2 - + __gnu_cxx - - + + A - - + + A - - + + B - - + + C - - + + D - - + + E - - + + R - + - + a : ns1::ns2::A * - + - + ns1_a : ns1::A * - + - + ns1_ns2_a : ns1::ns2::A * - + - + root_a : ::A * - + - + i : std::vector<std::uint8_t> foo(AA & aa) : void - - + + A - - + + AA diff --git a/docs/test_cases/t00047.md b/docs/test_cases/t00047.md index b6a90b2a..ce7656a0 100644 --- a/docs/test_cases/t00047.md +++ b/docs/test_cases/t00047.md @@ -40,8 +40,8 @@ struct conditional_t { template using conditional = typename conditional_t::type; -} -} +} // namespace t00047 +} // namespace clanguml ``` ## Generated UML diagrams ![t00047_class](./t00047_class.svg "Test case for recursive variadic template") diff --git a/docs/test_cases/t00047_class.svg b/docs/test_cases/t00047_class.svg index e9f1e13b..d2ed27e4 100644 --- a/docs/test_cases/t00047_class.svg +++ b/docs/test_cases/t00047_class.svg @@ -1,6 +1,6 @@ - + @@ -9,16 +9,16 @@ - - + + conditional_t - - + + conditional_t @@ -27,8 +27,8 @@ - - + + conditional_t @@ -37,8 +37,8 @@ - - + + conditional_t @@ -47,8 +47,8 @@ - - + + conditional_t diff --git a/docs/test_cases/t00048_class.svg b/docs/test_cases/t00048_class.svg index c559d7e7..00f55152 100644 --- a/docs/test_cases/t00048_class.svg +++ b/docs/test_cases/t00048_class.svg @@ -1,6 +1,6 @@ - + @@ -9,25 +9,25 @@ - - + + Base - + - + base : int foo() = 0 : void - - + + BaseTemplate @@ -35,35 +35,35 @@ T - + - + base : T foo() = 0 : void - - + + B - + - + b : int foo() : void - - + + BTemplate @@ -71,35 +71,35 @@ T - + - + b : T foo() : void - - + + A - + - + a : int foo() : void - - + + ATemplate @@ -107,11 +107,11 @@ T - + - + a : T diff --git a/docs/test_cases/t00049.md b/docs/test_cases/t00049.md index db6bc110..ba4bfbe2 100644 --- a/docs/test_cases/t00049.md +++ b/docs/test_cases/t00049.md @@ -8,8 +8,10 @@ diagrams: type: class using_namespace: clanguml::t00049 type_aliases: - "std::vector": string_vector "std::basic_string": thestring + "std::string": thestring + "std::vector": string_vector + "std::vector": string_vector "std::map": intmap glob: - ../../tests/t00049/t00049.cc @@ -41,8 +43,8 @@ struct R { void set_int_map(A> &&int_map) { a_int_map = int_map; } }; -} -} +} // namespace t00049 +} // namespace clanguml ``` ## Generated UML diagrams ![t00049_class](./t00049_class.svg "Test case configurable type aliases") diff --git a/docs/test_cases/t00049_class.svg b/docs/test_cases/t00049_class.svg index 19ac5e9c..9cec667a 100644 --- a/docs/test_cases/t00049_class.svg +++ b/docs/test_cases/t00049_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,75 +9,75 @@ - - - - - A - - T - + + + + + A + + T + - - - + + + - - a : T + + a : T - - - get_a() : T & - - - - A - - intmap - - - - - - A - - thestring - - - - - - A - - std::vector<thestring> - - - - + + + get_a() : T & + + + + A + + intmap + + + + + + A + + thestring + + + + + + A + + string_vector + + + + R - + - + a_string : A<thestring> - + - + a_vector_string : A<string_vector> - + - + a_int_map : A<intmap> @@ -85,25 +85,25 @@ get_int_map() : A<intmap> set_int_map(A<intmap> && int_map) : void - - - - - - - - - - - - a_int_map - - - - a_string - - - - a_vector_string + + + + + + + + + + + + a_int_map + + + + a_string + + + + a_vector_string diff --git a/docs/test_cases/t00050.md b/docs/test_cases/t00050.md index 30265f6b..6d701f2a 100644 --- a/docs/test_cases/t00050.md +++ b/docs/test_cases/t00050.md @@ -16,9 +16,13 @@ diagrams: plantuml: after: - > - note left of {{ alias("A") }} - {{ comment("clanguml::t00050::A").formatted }} - end note + note left of {{ alias("NoSuchClass") }} + {{ comment("NoSuchClass").formatted }} + end note + - > + note left of {{ alias("A") }} + {{ comment("clanguml::t00050::A").formatted }} + end note - > note right of {{ element("clanguml::t00050::A").alias }} {% set e=element("clanguml::t00050::A") %} {{ e.comment.formatted }} @@ -180,8 +184,8 @@ class G { class NoComment { }; -} -} +} // namespace t00050 +} // namespace clanguml ``` ## Generated UML diagrams ![t00050_class](./t00050_class.svg "Test case for generating notes from comments using jinja templates") diff --git a/docs/test_cases/t00050_class.svg b/docs/test_cases/t00050_class.svg index 6f5aee5b..e4103f9c 100644 --- a/docs/test_cases/t00050_class.svg +++ b/docs/test_cases/t00050_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,178 +9,178 @@ - - - - - A - - + + + + + A + + - - - - - B - - + + + + + B + + - - - - - C - - + + + + + C + + - - - - - utils::D - - + + + + + utils::D + + - - - - - E - - E1 - E2 - E3 - + + + + + E + + E1 + E2 + E3 + - - - - - F - - T,V,int N - + + + + + F + + T,V,int N + - - - + + + - - t : T [N] + + t : T[N] - - - + + + - - v : V + + v : V - - - - - - G - - + + + + + + G + + - - - - - NoComment - - + + + + + NoComment + + - - + + Lorem ipsum dolor sit - - - Lorem ipsum dolor sit - - - Lorem ipsum dolor sit amet consectetur adipiscing elit, urna consequat felis - vehicula class ultricies mollis dictumst, aenean non a in donec nulla. - Phasellus ante pellentesque erat cum risus consequat imperdiet aliquam, - integer placerat et turpis mi eros nec lobortis taciti, vehicula nisl litora - tellus ligula porttitor metus. - - Vivamus integer non suscipit taciti mus etiam at primis tempor sagittis sit, - euismod libero facilisi aptent elementum felis blandit cursus gravida sociis - erat ante, eleifend lectus nullam dapibus netus feugiat curae curabitur est - ad. Massa curae fringilla porttitor quam sollicitudin iaculis aptent leo - ligula euismod dictumst, orci penatibus mauris eros etiam praesent erat - volutpat posuere hac. Metus fringilla nec ullamcorper odio aliquam lacinia - conubia mauris tempor, etiam ultricies proin quisque lectus sociis id - tristique, integer phasellus taciti pretium adipiscing tortor sagittis - ligula. - - Mollis pretium lorem primis senectus habitasse lectus scelerisque - donec, ultricies tortor suspendisse adipiscing fusce morbi volutpat - pellentesque, consectetur mi risus molestie curae malesuada cum. Dignissim - lacus convallis massa mauris enim ad mattis magnis senectus montes, mollis - taciti phasellus accumsan bibendum semper blandit suspendisse faucibus nibh - est, metus lobortis morbi cras magna vivamus per risus fermentum. Dapibus - imperdiet praesent magnis ridiculus congue gravida curabitur dictum - sagittis, enim et magna sit inceptos sodales parturient pharetra mollis, - aenean vel nostra tellus commodo pretium sapien sociosqu. - - - This is a short description of class G. - - - This is an intermediate description of class G. - - - This is a long description of class G. - - - Lorem ipsum - - - - TODO - 1. Write meaningful comment - - - - - TODO - 2. Write tests - - - - - TODO - 3. Implement - - - - Long comment example - - - - TODO - Implement... - - - - Simple array wrapper. - - - - Template parameters - - T - Type of array elements. - - V - Type of regular element. - - N - Size of T array. - - + + + Lorem ipsum dolor sit + + + Lorem ipsum dolor sit amet consectetur adipiscing elit, urna consequat felis + vehicula class ultricies mollis dictumst, aenean non a in donec nulla. + Phasellus ante pellentesque erat cum risus consequat imperdiet aliquam, + integer placerat et turpis mi eros nec lobortis taciti, vehicula nisl litora + tellus ligula porttitor metus. + + Vivamus integer non suscipit taciti mus etiam at primis tempor sagittis sit, + euismod libero facilisi aptent elementum felis blandit cursus gravida sociis + erat ante, eleifend lectus nullam dapibus netus feugiat curae curabitur est + ad. Massa curae fringilla porttitor quam sollicitudin iaculis aptent leo + ligula euismod dictumst, orci penatibus mauris eros etiam praesent erat + volutpat posuere hac. Metus fringilla nec ullamcorper odio aliquam lacinia + conubia mauris tempor, etiam ultricies proin quisque lectus sociis id + tristique, integer phasellus taciti pretium adipiscing tortor sagittis + ligula. + + Mollis pretium lorem primis senectus habitasse lectus scelerisque + donec, ultricies tortor suspendisse adipiscing fusce morbi volutpat + pellentesque, consectetur mi risus molestie curae malesuada cum. Dignissim + lacus convallis massa mauris enim ad mattis magnis senectus montes, mollis + taciti phasellus accumsan bibendum semper blandit suspendisse faucibus nibh + est, metus lobortis morbi cras magna vivamus per risus fermentum. Dapibus + imperdiet praesent magnis ridiculus congue gravida curabitur dictum + sagittis, enim et magna sit inceptos sodales parturient pharetra mollis, + aenean vel nostra tellus commodo pretium sapien sociosqu. + + + This is a short description of class G. + + + This is an intermediate description of class G. + + + This is a long description of class G. + + + Lorem ipsum + + + + TODO + 1. Write meaningful comment + + + + + TODO + 2. Write tests + + + + + TODO + 3. Implement + + + + Long comment example + + + + TODO + Implement... + + + + Simple array wrapper. + + + + Template parameters + + T + Type of array elements. + + V + Type of regular element. + + N + Size of T array. + + diff --git a/docs/test_cases/t20001.md b/docs/test_cases/t20001.md index 2636828b..836a0834 100644 --- a/docs/test_cases/t20001.md +++ b/docs/test_cases/t20001.md @@ -29,7 +29,6 @@ diagrams: File t20001.cc ```cpp #include -#include #include namespace clanguml { diff --git a/docs/test_cases/t20001_sequence.svg b/docs/test_cases/t20001_sequence.svg index b1c8087c..fa78e1e0 100644 --- a/docs/test_cases/t20001_sequence.svg +++ b/docs/test_cases/t20001_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,59 +9,59 @@ - - - - - - - + + + + + + + - - + + tmain() - + tmain() - - + + A - + A - - + + B - + B - - - - - - - - + + + + + + + + add(int,int) - + wrap_add3(int,int,int) - + add3(int,int,int) - + @@ -72,7 +72,7 @@ - + @@ -81,14 +81,14 @@ - + log_result(int) - + Main test function diff --git a/docs/test_cases/t20002_sequence.svg b/docs/test_cases/t20002_sequence.svg index 2f83f47f..dab2c16f 100644 --- a/docs/test_cases/t20002_sequence.svg +++ b/docs/test_cases/t20002_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,51 +9,51 @@ - - - - + + + + - - + + m1() - + m1() - - + + m2() - + m2() - - + + m3() - + m3() - - + + m4() - + m4() - - - - - + + + + + - + - + diff --git a/docs/test_cases/t20003_sequence.svg b/docs/test_cases/t20003_sequence.svg index 202ff5bb..cbb5b247 100644 --- a/docs/test_cases/t20003_sequence.svg +++ b/docs/test_cases/t20003_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,51 +9,51 @@ - - - - + + + + - - + + m1<T>(T) - + m1<T>(T) - - + + m2<T>(T) - + m2<T>(T) - - + + m3<T>(T) - + m3<T>(T) - - + + m4<T>(T) - + m4<T>(T) - - - - - + + + + + - + - + diff --git a/docs/test_cases/t20004_sequence.svg b/docs/test_cases/t20004_sequence.svg index 9f2ba2c2..8be86c0d 100644 --- a/docs/test_cases/t20004_sequence.svg +++ b/docs/test_cases/t20004_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,16 +9,16 @@ - - - - - - - - - - + + + + + + + + + + @@ -29,87 +29,87 @@ - - + + main() - + main() - - + + m1<float>(float) - + m1<float>(float) - - + + m1<unsigned long>(unsigned long) - + m1<unsigned long>(unsigned long) - - + + m4<unsigned long>(unsigned long) - + m4<unsigned long>(unsigned long) - - + + m1<std::string>(std::string) - + m1<std::string>(std::string) - - + + m2<std::string>(std::string) - + m2<std::string>(std::string) - - + + m1<int>(int) - + m1<int>(int) - - + + m2<int>(int) - + m2<int>(int) - - + + m3<int>(int) - + m3<int>(int) - - + + m4<int>(int) - + m4<int>(int) - - - - - - - - - - - + + + + + + + + + + + - + - + @@ -117,11 +117,11 @@ - + - + @@ -129,19 +129,19 @@ - + - + - + - + diff --git a/docs/test_cases/t20005_sequence.svg b/docs/test_cases/t20005_sequence.svg index 52ea8220..f2ab0585 100644 --- a/docs/test_cases/t20005_sequence.svg +++ b/docs/test_cases/t20005_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,42 +9,42 @@ - - - + + + - - + + C<T> - + C<T> - - + + B<T> - + B<T> - - + + A<T> - + A<T> - - - + + + c(T) - + b(T) - + a(T) diff --git a/docs/test_cases/t20006_sequence.svg b/docs/test_cases/t20006_sequence.svg index 61c2848f..da43de7f 100644 --- a/docs/test_cases/t20006_sequence.svg +++ b/docs/test_cases/t20006_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,22 +9,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -34,82 +34,82 @@ - - + + tmain() - + tmain() - - + + B<int> - + B<int> - - + + A<int> - + A<int> - - + + B<std::string> - + B<std::string> - - + + A<std::string> - + A<std::string> - - + + BB<int,int> - + BB<int,int> - - + + AA<int> - + AA<int> - - + + BB<int,std::string> - + BB<int,std::string> - - + + BB<int,float> - + BB<int,float> - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + b(int) - + a1(int) @@ -118,12 +118,12 @@ - + b(std::string) - + a2(std::string) @@ -132,59 +132,59 @@ - + bb1(int,int) - + aa1(int) - + bb2(int,int) - + aa2(int) - + bb1(int,std::string) - + aa2(int) - + bb2(int,std::string) - + aa1(int) - + bb1(int,float) - + bb2(int,float) - + aa2(int) diff --git a/docs/test_cases/t20007_sequence.svg b/docs/test_cases/t20007_sequence.svg index 0e12cc63..bc8ab780 100644 --- a/docs/test_cases/t20007_sequence.svg +++ b/docs/test_cases/t20007_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,57 +9,57 @@ - - - - + + + + - - + + tmain() - + tmain() - - + + Adder<int,int> - + Adder<int,int> - - + + Adder<int,float,double> - + Adder<int,float,double> - - + + Adder<std::string,std::string,std::string> - + Adder<std::string,std::string,std::string> - - - - - + + + + + add(int &&,int &&) - + add(int &&,float &&,double &&) - + add(std::string &&,std::string &&,std::string &&) diff --git a/docs/test_cases/t20008_sequence.svg b/docs/test_cases/t20008_sequence.svg index 30b53539..21f83c98 100644 --- a/docs/test_cases/t20008_sequence.svg +++ b/docs/test_cases/t20008_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,13 +9,13 @@ - - - - - - - + + + + + + + @@ -23,81 +23,81 @@ - - + + tmain() - + tmain() - - + + B<int> - + B<int> - - + + A<int> - + A<int> - - + + B<const char *> - + B<const char *> - - + + A<const char *> - + A<const char *> - - + + B<std::string> - + B<std::string> - - + + A<std::string> - + A<std::string> - - - - - - - - + + + + + + + + b(int) - + a1(int) - + b(const char *) - + a2(const char *) - + b(std::string) - + a3(std::string) diff --git a/docs/test_cases/t20009_sequence.svg b/docs/test_cases/t20009_sequence.svg index 7dbd5c5c..bd2ba6f5 100644 --- a/docs/test_cases/t20009_sequence.svg +++ b/docs/test_cases/t20009_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,13 +9,13 @@ - - - - - - - + + + + + + + @@ -23,81 +23,81 @@ - - + + tmain() - + tmain() - - + + B<std::string> - + B<std::string> - - + + A<std::string> - + A<std::string> - - + + B<int> - + B<int> - - + + A<int> - + A<int> - - + + B<float> - + B<float> - - + + A<float> - + A<float> - - - - - - - - + + + + + + + + b(std::string) - + a(std::string) - + b(int) - + a(int) - + b(float) - + a(float) diff --git a/docs/test_cases/t20010_sequence.svg b/docs/test_cases/t20010_sequence.svg index 7ec5e01e..f8cd1721 100644 --- a/docs/test_cases/t20010_sequence.svg +++ b/docs/test_cases/t20010_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,81 +9,81 @@ - - - - - - - - - + + + + + + + + + - - + + tmain() - + tmain() - - + + B<int> - + B<int> - - + + A - + A - - - - - - - - - - + + + + + + + + + + b1() - + a1() - + b2() - + a2() - + b3() - + a3() - + b4() - + a4() diff --git a/docs/test_cases/t20011_sequence.svg b/docs/test_cases/t20011_sequence.svg index f9f704d9..22a0083f 100644 --- a/docs/test_cases/t20011_sequence.svg +++ b/docs/test_cases/t20011_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,42 +9,42 @@ - - - - - - - - - - - - + + + + + + + + + + + + - - + + tmain() - + tmain() - - + + A - + A - - - - - - - - - - + + + + + + + + + + a(int) @@ -52,26 +52,26 @@ alt - + a(int) - + b(int) - + c(int) - + @@ -81,14 +81,14 @@ alt - + b(int) - + @@ -98,7 +98,7 @@ alt - + diff --git a/docs/test_cases/t20012_sequence.svg b/docs/test_cases/t20012_sequence.svg index 300204cf..777d6ee3 100644 --- a/docs/test_cases/t20012_sequence.svg +++ b/docs/test_cases/t20012_sequence.svg @@ -1,6 +1,6 @@ - + - + @@ -9,272 +9,272 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + tmain() - + tmain() - - - tmain()::(lambda t20012.cc:66:20) - - tmain()::(lambda t20012.cc:66:20) + + + tmain()::(lambda ../../tests/t20012/t20012.cc:66:20) + + tmain()::(lambda ../../tests/t20012/t20012.cc:66:20) - - - A - - A + + + A + + A - - - B - - B + + + B + + B - - - tmain()::(lambda t20012.cc:79:20) - - tmain()::(lambda t20012.cc:79:20) + + + tmain()::(lambda ../../tests/t20012/t20012.cc:79:20) + + tmain()::(lambda ../../tests/t20012/t20012.cc:79:20) - - - C - - C + + + C + + C - - - R<R::(lambda t20012.cc:85:9)> - - R<R::(lambda t20012.cc:85:9)> + + + R<R::(lambda ../../tests/t20012/t20012.cc:85:9)> + + R<R::(lambda ../../tests/t20012/t20012.cc:85:9)> - - - tmain()::(lambda t20012.cc:85:9) - - tmain()::(lambda t20012.cc:85:9) + + + tmain()::(lambda ../../tests/t20012/t20012.cc:85:9) + + tmain()::(lambda ../../tests/t20012/t20012.cc:85:9) - - - D - - D + + + D + + D - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + operator()() - - - - a() + + + + a() - - - - - - aa() + + + + + + aa() - - - - - - aaa() + + + + + + aaa() - - - - b() + + + + b() - - - - - - bb() + + + + + + bb() - - - - - - bbb() + + + + + + bbb() - - - - + + + + operator()() - - - - c() + + + + c() - - - - - - cc() + + + + + + cc() - - - - - - ccc() + + + + + + ccc() - - - - operator()() + + + + operator()() - - - - a() + + + + a() - - - - - - aa() + + + + + + aa() - - - - - - aaa() + + + + + + aaa() - - - - b() + + + + b() - - - - - - bb() + + + + + + bb() - - - - - - bbb() + + + + + + bbb() - - + + - - - - + + + + r() - - - - operator()() + + + + operator()() - - - - c() + + + + c() - - - - - - cc() + + + + + + cc() - - - - - - ccc() + + + + + + ccc() - - - - - + + + + + add5(int) - + diff --git a/docs/test_cases/t20013_sequence.svg b/docs/test_cases/t20013_sequence.svg index 866aee50..387a3220 100644 --- a/docs/test_cases/t20013_sequence.svg +++ b/docs/test_cases/t20013_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,47 +9,47 @@ - - - - - - - + + + + + + + - - + + tmain(int,char **) - + tmain(int,char **) - - + + B - + B - - + + A - + A - - - - - - - - + + + + + + + + b(int) - + a1(int) @@ -58,12 +58,12 @@ - + b(double) - + a2(double) @@ -72,12 +72,12 @@ - + b(const char *) - + a3(const char *) diff --git a/docs/test_cases/t20014_sequence.svg b/docs/test_cases/t20014_sequence.svg index 55a9c139..a284c0d3 100644 --- a/docs/test_cases/t20014_sequence.svg +++ b/docs/test_cases/t20014_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,56 +9,56 @@ - - - - - - - - + + + + + + + + - - + + tmain() - + tmain() - - + + B - + B - - + + A - + A - - + + C<B,int> - + C<B,int> - - - - - - - - - + + + + + + + + + b1(int,int) - + a1(int,int) @@ -67,12 +67,12 @@ - + b2(int,int) - + a2(int,int) @@ -81,17 +81,17 @@ - + c1(int,int) - + b1(int,int) - + a1(int,int) diff --git a/docs/test_cases/t20015_sequence.svg b/docs/test_cases/t20015_sequence.svg index 154e5189..eea2dc73 100644 --- a/docs/test_cases/t20015_sequence.svg +++ b/docs/test_cases/t20015_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,25 +9,25 @@ - - + + - - + + tmain() - + tmain() - - + + B - + B - - - + + + setup_a(std::shared_ptr<detail::A> &) diff --git a/docs/test_cases/t20016_sequence.svg b/docs/test_cases/t20016_sequence.svg index 24957575..53377fc1 100644 --- a/docs/test_cases/t20016_sequence.svg +++ b/docs/test_cases/t20016_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,53 +9,53 @@ - - - - - + + + + + - - + + tmain() - + tmain() - - + + B<long> - + B<long> - - + + A - + A - - - - - - + + + + + + b1(long) - + a1(int) - + b2(long) - + a2(const long &) diff --git a/docs/test_cases/t20017.md b/docs/test_cases/t20017.md index f4e0fb32..a06cd17b 100644 --- a/docs/test_cases/t20017.md +++ b/docs/test_cases/t20017.md @@ -13,6 +13,8 @@ diagrams: include: namespaces: - clanguml::t20017 + paths: + - . using_namespace: - clanguml::t20017 start_from: diff --git a/docs/test_cases/t20017_sequence.svg b/docs/test_cases/t20017_sequence.svg index f3d82e11..ef95b739 100644 --- a/docs/test_cases/t20017_sequence.svg +++ b/docs/test_cases/t20017_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,65 +9,65 @@ - - - - - - + + + + + + - + t20017.cc - + t20017.cc - + include/t20017_a.h - + include/t20017_a.h - + include/t20017_b.h - + include/t20017_b.h - - - - - - + + + + + + tmain() - + a3(int,int) - + b1(int,int) - + a2(int,int) - + a1(int,int) - + b2<int>(int,int) diff --git a/docs/test_cases/t20018_sequence.svg b/docs/test_cases/t20018_sequence.svg index 1c30c7e6..4c4c5717 100644 --- a/docs/test_cases/t20018_sequence.svg +++ b/docs/test_cases/t20018_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,14 +9,14 @@ - - - - - - - - + + + + + + + + @@ -25,93 +25,93 @@ - - + + tmain() - + tmain() - - + + Answer<Factorial<5>,120> - + Answer<Factorial<5>,120> - - + + Factorial<5> - + Factorial<5> - - + + Factorial<4> - + Factorial<4> - - + + Factorial<3> - + Factorial<3> - - + + Factorial<2> - + Factorial<2> - - + + Factorial<1> - + Factorial<1> - - + + Factorial<0> - + Factorial<0> - - - - - - - - - + + + + + + + + + print() - + print(int) - + print(int) - + print(int) - + print(int) - + print(int) - + print(int) diff --git a/docs/test_cases/t20019_sequence.svg b/docs/test_cases/t20019_sequence.svg index 478e4e23..54db09f7 100644 --- a/docs/test_cases/t20019_sequence.svg +++ b/docs/test_cases/t20019_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,95 +9,95 @@ - - - - - - - - - + + + + + + + + + - - + + tmain() - + tmain() - - + + Base<D1> - + Base<D1> - - + + D1 - + D1 - - + + Base<D2> - + Base<D2> - - + + D2 - + D2 - - - - - - - - - - + + + + + + + + + + name() - + impl() - + name() - + impl() - + name() - + impl() - + name() - + impl() diff --git a/docs/test_cases/t20020_sequence.svg b/docs/test_cases/t20020_sequence.svg index 33310850..a911d0d0 100644 --- a/docs/test_cases/t20020_sequence.svg +++ b/docs/test_cases/t20020_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,78 +9,78 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - - - + + + - - + + tmain() - + tmain() - - + + A - + A - - + + C - + C - - + + B - + B - - + + D<int> - + D<int> - - - - - - - - - - - - - + + + + + + + + + + + + + alt - + a1() @@ -91,7 +91,7 @@ alt - + [ @@ -100,7 +100,7 @@ - + [ @@ -109,7 +109,7 @@ - + b1() @@ -117,7 +117,7 @@ - + [ @@ -126,21 +126,21 @@ - + b2() - + a4() - + log() @@ -148,7 +148,7 @@ alt - + c1() @@ -156,7 +156,7 @@ alt - + @@ -169,7 +169,7 @@ - + @@ -179,7 +179,7 @@ alt - + d1(int,int) diff --git a/docs/test_cases/t20021_sequence.svg b/docs/test_cases/t20021_sequence.svg index ac80dcd8..ea923858 100644 --- a/docs/test_cases/t20021_sequence.svg +++ b/docs/test_cases/t20021_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,74 +9,74 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + tmain() - + tmain() - - + + C - + C - - + + A - + A - - + + B - + B - - - - - - - - - - - - + + + + + + + + + + + + loop - + [ c4() ] - + @@ -89,7 +89,7 @@ - + a3() @@ -102,7 +102,7 @@ loop - + [ @@ -111,7 +111,7 @@ - + [ @@ -120,7 +120,7 @@ - + [ @@ -129,14 +129,14 @@ - + a1() - + [ @@ -148,7 +148,7 @@ loop - + b2() @@ -158,7 +158,7 @@ loop - + [ @@ -167,7 +167,7 @@ - + b2() diff --git a/docs/test_cases/t20022_sequence.svg b/docs/test_cases/t20022_sequence.svg index 38c9eb89..89d173f1 100644 --- a/docs/test_cases/t20022_sequence.svg +++ b/docs/test_cases/t20022_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,39 +9,39 @@ - - - + + + - - + + tmain() - + tmain() - - + + A - + A - - + + B - + B - - - - + + + + a() - + b() diff --git a/docs/test_cases/t20023_sequence.svg b/docs/test_cases/t20023_sequence.svg index 9a6e3e04..2a5c5a7d 100644 --- a/docs/test_cases/t20023_sequence.svg +++ b/docs/test_cases/t20023_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,37 +9,37 @@ - - - - - - - + + + + + + + - - + + tmain() - + tmain() - - + + A - + A - - - - - - - + + + + + + + a() @@ -47,7 +47,7 @@ try - + @@ -60,7 +60,7 @@ [std::runtime_error &] - + @@ -73,7 +73,7 @@ [std::logic_error &] - + @@ -86,7 +86,7 @@ [...] - + diff --git a/docs/test_cases/t20024_sequence.svg b/docs/test_cases/t20024_sequence.svg index bbb00947..953173bd 100644 --- a/docs/test_cases/t20024_sequence.svg +++ b/docs/test_cases/t20024_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,23 +9,23 @@ - - - - - - - - - - - - + + + + + + + + + + + + - + @@ -33,36 +33,36 @@ - - + + tmain() - + tmain() - - + + A - + A - - + + B - + B - - - - - - - - - - - - + + + + + + + + + + + + select(enum_a) @@ -72,7 +72,7 @@ switch [zero] - + @@ -85,7 +85,7 @@ [one] - + @@ -98,7 +98,7 @@ [two] - + @@ -111,7 +111,7 @@ [default] - + @@ -124,7 +124,7 @@ - + select(colors) @@ -134,7 +134,7 @@ switch [enum colors::red] - + @@ -143,7 +143,7 @@ [enum colors::orange] - + @@ -152,7 +152,7 @@ [enum colors::green] - + @@ -161,7 +161,7 @@ [default] - + diff --git a/docs/test_cases/t20025_sequence.svg b/docs/test_cases/t20025_sequence.svg index 3af023f3..80e35ccb 100644 --- a/docs/test_cases/t20025_sequence.svg +++ b/docs/test_cases/t20025_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,41 +9,41 @@ - - - - + + + + - - + + tmain() - + tmain() - - + + A - + A - - + + add(int,int) - + add(int,int) - - - - - + + + + + a() - + @@ -52,7 +52,7 @@ - + diff --git a/docs/test_cases/t20026_sequence.svg b/docs/test_cases/t20026_sequence.svg index 946d1ec2..f093fbb8 100644 --- a/docs/test_cases/t20026_sequence.svg +++ b/docs/test_cases/t20026_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,25 +9,25 @@ - - + + - - + + tmain() - + tmain() - - + + A - + A - - - + + + a() diff --git a/docs/test_cases/t20027_sequence.svg b/docs/test_cases/t20027_sequence.svg index cd45ef25..4a102cd1 100644 --- a/docs/test_cases/t20027_sequence.svg +++ b/docs/test_cases/t20027_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,25 +9,25 @@ - - + + - - + + tmain() - + tmain() - - + + A - + A - - - + + + a() diff --git a/docs/test_cases/t20028_sequence.svg b/docs/test_cases/t20028_sequence.svg index 16d1cc0e..30f387ec 100644 --- a/docs/test_cases/t20028_sequence.svg +++ b/docs/test_cases/t20028_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,50 +9,50 @@ - - - - - - + + + + + + - - + + tmain() - + tmain() - - + + A - + A - - - - - + + + + + alt - + a() - + b() - + c() @@ -60,7 +60,7 @@ - + d() diff --git a/docs/test_cases/t20029_sequence.svg b/docs/test_cases/t20029_sequence.svg index 3a8f4bd0..c08ccac3 100644 --- a/docs/test_cases/t20029_sequence.svg +++ b/docs/test_cases/t20029_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,60 +9,60 @@ - - - - - - - - - - - + + + + + + + + + + + - - + + tmain() - + tmain() - - + + Encoder<Retrier<ConnectionPool>> - + Encoder<Retrier<ConnectionPool>> - - + + Retrier<ConnectionPool> - + Retrier<ConnectionPool> - - + + ConnectionPool - + ConnectionPool - - + + encode_b64(std::string &&) - + encode_b64(std::string &&) - - - - - - - - + + + + + + + + connect() @@ -73,21 +73,21 @@ alt - + [ send(std::string &&) ] - + encode(std::string &&) - + @@ -97,7 +97,7 @@ - + send(std::string &&) @@ -108,7 +108,7 @@ alt - + [ diff --git a/docs/test_cases/t30001_package.svg b/docs/test_cases/t30001_package.svg index f049ce5b..171c92a1 100644 --- a/docs/test_cases/t30001_package.svg +++ b/docs/test_cases/t30001_package.svg @@ -1,6 +1,6 @@ - + @@ -9,67 +9,67 @@ - - + + A - - + + AA - - + + B - - + + AA - - + + AAA - - + + BBB - - + + BB - - + + AAA - - + + BBB - - + + BB - + A AAA note... - + This is namespace AA in namespace A - + This is namespace AA in namespace B - - - + + + diff --git a/docs/test_cases/t30002_package.svg b/docs/test_cases/t30002_package.svg index 3a2d619b..1f3cc6b8 100644 --- a/docs/test_cases/t30002_package.svg +++ b/docs/test_cases/t30002_package.svg @@ -1,6 +1,6 @@ - + @@ -9,113 +9,113 @@ - - + + A - - + + AA - - + + B - - + + BB - - + + A1 - - + + A2 - - + + A3 - - + + A4 - - + + A5 - - + + A6 - - + + A7 - - + + A8 - - + + A9 - - + + A10 - - + + A11 - - + + A12 - - + + A13 - - + + A14 - - + + A15 - - + + A16 - - + + A17 - - + + BBB diff --git a/docs/test_cases/t30003_package.svg b/docs/test_cases/t30003_package.svg index 7c2fbabd..3875a464 100644 --- a/docs/test_cases/t30003_package.svg +++ b/docs/test_cases/t30003_package.svg @@ -1,6 +1,6 @@ - + @@ -9,35 +9,35 @@ - - + + ns1 - - + + ns3 «deprecated» - - + + ns1 - - + + ns2_v1_0_0 - - + + ns2_v0_9_0 «deprecated» - - + + ns2 diff --git a/docs/test_cases/t30004_package.svg b/docs/test_cases/t30004_package.svg index 49e6f379..ce848f69 100644 --- a/docs/test_cases/t30004_package.svg +++ b/docs/test_cases/t30004_package.svg @@ -1,6 +1,6 @@ - + @@ -9,46 +9,46 @@ - - + + A - + Package AAA. - + Package BBB. - + CCCC package note. - + We skipped DDD. - - + + AAA - - + + BBB - - + + CCC - - + + EEE - - - - + + + + diff --git a/docs/test_cases/t30005_package.svg b/docs/test_cases/t30005_package.svg index fe55e4d1..8c519ad9 100644 --- a/docs/test_cases/t30005_package.svg +++ b/docs/test_cases/t30005_package.svg @@ -1,6 +1,6 @@ - + @@ -9,48 +9,48 @@ - - + + A - - + + AA - - + + B - - + + BB - - + + C - - + + CC - - + + AAA - - + + BBB - - + + CCC diff --git a/docs/test_cases/t30006_package.svg b/docs/test_cases/t30006_package.svg index b3b3006f..aa59f356 100644 --- a/docs/test_cases/t30006_package.svg +++ b/docs/test_cases/t30006_package.svg @@ -1,6 +1,6 @@ - + @@ -9,25 +9,25 @@ - - + + B - - + + A - - + + C - + Top A note. - + diff --git a/docs/test_cases/t30007_package.svg b/docs/test_cases/t30007_package.svg index 4dab2829..5d6f47a6 100644 --- a/docs/test_cases/t30007_package.svg +++ b/docs/test_cases/t30007_package.svg @@ -1,6 +1,6 @@ - + @@ -9,30 +9,30 @@ - - + + A - - + + B - - + + AA - - + + C - + Compare layout with t30006. - + diff --git a/docs/test_cases/t30008_package.svg b/docs/test_cases/t30008_package.svg index 30a0f902..ed42d821 100644 --- a/docs/test_cases/t30008_package.svg +++ b/docs/test_cases/t30008_package.svg @@ -1,6 +1,6 @@ - + @@ -9,43 +9,43 @@ - - + + dependants - - + + dependencies - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F diff --git a/docs/test_cases/t40001.md b/docs/test_cases/t40001.md index 8fff7d03..c2c5764f 100644 --- a/docs/test_cases/t40001.md +++ b/docs/test_cases/t40001.md @@ -9,8 +9,7 @@ diagrams: # Provide the files to parse in order to look # for #include directives glob: - - ../../tests/t40001/**/*.cc - - ../../tests/t40001/**/*.h + - ../../tests/t40001/src/t40001.cc # Render the paths relative to this directory relative_to: ../../../tests/t40001 # Include also external system headers @@ -18,7 +17,7 @@ diagrams: include: # Include only headers belonging to these paths paths: - - ../../../tests/t40001 + - . plantuml: before: - "' t40001 test include diagram" diff --git a/docs/test_cases/t40001_include.svg b/docs/test_cases/t40001_include.svg index b912675e..e968350d 100644 --- a/docs/test_cases/t40001_include.svg +++ b/docs/test_cases/t40001_include.svg @@ -1,6 +1,6 @@ - + @@ -9,43 +9,43 @@ - + src - + include - + lib1 - - + + t40001.cc - - + + t40001_include1.h - - + + lib1.h - + string - + vector - + yaml-cpp/yaml.h - + This is a lib1 include dir - + This is a t40001_include1.h include file @@ -60,7 +60,7 @@ - - + + diff --git a/docs/test_cases/t40002.md b/docs/test_cases/t40002.md index ebea7abf..e51460be 100644 --- a/docs/test_cases/t40002.md +++ b/docs/test_cases/t40002.md @@ -9,18 +9,19 @@ diagrams: # Provide the files to parse in order to look # for #include directives glob: - - ../../tests/t40002/**/*.cc - - ../../tests/t40002/**/*.h + - ../../tests/t40002/src/t40002.cc + - ../../tests/t40002/src/lib1/lib1.cc + - ../../tests/t40002/src/lib2/lib2.cc # Render the paths relative to this directory relative_to: ../../../tests/t40002 include: - # Include only files belonging to these paths + # Include only files belonging to these paths relative to relative_to paths: - - ../../../tests/t40002 + - . exclude: paths: - # Exclude single header - - ../../../tests/t40002/include/lib2/lib2_detail.h + # Exclude single header relative to relative_to + - include/lib2/lib2_detail.h plantuml: before: - "' t40002 test include diagram" diff --git a/docs/test_cases/t40002_include.svg b/docs/test_cases/t40002_include.svg index 2aabc01a..004aff9c 100644 --- a/docs/test_cases/t40002_include.svg +++ b/docs/test_cases/t40002_include.svg @@ -1,6 +1,6 @@ - + @@ -9,46 +9,46 @@ - + src - + lib1 - + lib2 - + include - + lib1 - + lib2 - - + + t40002.cc - - + + lib1.cc - - + + lib2.cc - - + + lib1.h - - + + lib2.h diff --git a/docs/test_cases/t40003.md b/docs/test_cases/t40003.md index 1bc7bc15..d5fc9c01 100644 --- a/docs/test_cases/t40003.md +++ b/docs/test_cases/t40003.md @@ -9,17 +9,17 @@ diagrams: # Provide the files to parse in order to look # for #include directives glob: - - ../../tests/t40003/include/**/*.h - - ../../tests/t40003/src/**/*.cc + - ../../tests/t40003/src/dependants/t1.cc + - ../../tests/t40003/src/dependencies/t2.cc # Render the paths relative to this directory relative_to: ../../../tests/t40003 include: # Include only files which depend on t1.h dependants: - - ../../../tests/t40003/include/dependants/t1.h + - include/dependants/t1.h # and dependencies of t2.cc dependencies: - - ../../../tests/t40003/src/dependencies/t2.cc + - src/dependencies/t2.cc plantuml: before: - "' t40003 test include diagram" diff --git a/docs/test_cases/t40003_include.svg b/docs/test_cases/t40003_include.svg index 176651a8..3d20d32e 100644 --- a/docs/test_cases/t40003_include.svg +++ b/docs/test_cases/t40003_include.svg @@ -1,6 +1,6 @@ - + - + @@ -9,84 +9,84 @@ - - - include - - - dependants - - - dependencies - - - src - - - dependants - - - dependencies - - - - t3.h + + + src + + + dependants + + + dependencies + + + include + + + dependants + + + dependencies + + + + t1.cc - - - - t2.h + + + + t2.cc - - - - t1.h + + + + t3.h - - - - t3.h + + + + t2.h - - - - t2.h + + + + t1.h - - - - t1.h + + + + t3.h - - - - t5.h + + + + t2.h - - - - t1.cc + + + + t1.h - - - - t2.cc + + + + t5.h - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 00000000..cb624294 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,56 @@ +# Troubleshooting + +## General issues +### Diagram generated with PlantUML is cropped +When generating diagrams with PlantUML without specifying an output file format, the default is PNG. +Unfortunately PlantUML will not check if the diagram will fit in the default PNG size, and often the diagram +will be be incomplete in the picture. A better option is to specify SVG as output format and then convert +to PNG, e.g.: +```bash +$ plantuml -tsvg mydiagram.puml +$ convert +antialias mydiagram.svg mydiagram.png +``` + +## Class diagrams +### "fatal error: 'stddef.h' file not found" +This error means that Clang cannot find some standard headers in the include paths +specified in the `compile_commands.json`. This typically happens on macos and sometimes on Linux, when +the code was compiled with different Clang version than `clang-uml` itself. + +One solution to this issue is to add the following line to your `CMakeLists.txt` file: + +```cmake +set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +``` + +Another option is to make sure that the Clang is installed on the system (even if not used for building your +project), e.g.: +```bash +apt install clang +``` + +## Sequence diagrams +### Generated diagram is empty +In order to generate sequence diagram the `start_from` configuration option must have a valid starting point +for the diagram (e.g. `function`), which must match exactly the function signature in the `clang-uml` model. +Look for error in the console output such as: +```bash +Failed to find participant mynamespace::foo(int) for start_from condition +``` +which means that either you have a typo in the function signature in the configuration file, or that the function +was not defined in the translation units you specified in the `glob` patterns for this diagram. Run again the +`clang-uml` tool with `-vvv` option and look in the console output for any mentions of the function from +which the diagram should start and copy the exact signature into the configuration file. + +### Generated diagram contains several empty control blocks or calls which should not be there +Currently the filtering of call expressions and purging empty control blocks (e.g. loops or conditional statements), +within which no interesting calls were included in the diagram is not perfect. In case the regular `namespaces` filter +is not enough, it is useful to add also a `paths` filter, which will only include participants and call expressions +from files in a subdirectory of your project, e.g.: +```yaml + include: + namespaces: + - myproject + paths: + - src +``` \ No newline at end of file diff --git a/packaging/Makefile b/packaging/Makefile index a59d6c91..ae99c1e9 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -1,6 +1,6 @@ # Makefile # -# Copyright (c) 2021-2022 Bartek Kryza +# Copyright (c) 2021-2023 Bartek Kryza # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,6 +34,8 @@ VERSION ?= $(shell git describe --tags --always --abbrev=7) COMMIT ?= $(shell git rev-parse HEAD) BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) SOURCE_ARCHIVE ?= $(NAME)-$(VERSION).tar.$(TAR_EXT) +SOURCE_ARCHIVE_DEB ?= $(NAME)-$(VERSION)-$(REBUILD).tar.$(TAR_EXT) +SOURCE_ARCHIVE_RPM ?= $(NAME)-$(VERSION).tar.$(TAR_EXT) CONDA_TOKEN ?= # @@ -58,20 +60,52 @@ _BUILD/$(SOURCE_ARCHIVE): mkdir -p $(build_dir) git-archive-all --prefix=$(NAME)-$(VERSION)/ _BUILD/$(SOURCE_ARCHIVE) -deb: _BUILD/$(SOURCE_ARCHIVE) +_BUILD/$(SOURCE_ARCHIVE_DEB): + echo "############################" + echo "Creating source archive for DEB from latest commit $(COMMIT) - $(SOURCE_ARCHIVE)" + echo "############################" + mkdir -p $(build_dir) + git-archive-all --prefix=$(NAME)-$(VERSION)-$(REBUILD)/ _BUILD/$(SOURCE_ARCHIVE_DEB) + +_BUILD/$(SOURCE_ARCHIVE_RPM): + echo "############################" + echo "Creating source archive for RPM from latest commit $(COMMIT) - $(SOURCE_ARCHIVE)" + echo "############################" + mkdir -p $(build_dir) + git archive --format=tar.gz --prefix=$(NAME)-$(VERSION)/ v$(VERSION) >_BUILD/$(NAME)-$(VERSION).tar.gz + + +rpm: + echo "############################" + echo "Creating rpm package for $(OS) $(DIST)" + echo "Creating directory: ", $(build_dir)/$(NAME)-$(VERSION) + echo "Extracting source archive..." + echo "############################" + #rm -rf $(build_dir) + mkdir -p $(build_dir) + dnf install -y fedora-packager rpmdevtools gcc cmake git clang-devel clang-tools-extra ccache yaml-cpp llvm-devel wget yaml-cpp-devel + rpmdev-setuptree + cp $(build_dir)/$(SOURCE_ARCHIVE_RPM) /root/rpmbuild/SOURCES/ + cp fedora/clang-uml.spec /root/rpmbuild/SPECS/ + rpmbuild -ba --define 'git_version ${VERSION}' /root/rpmbuild/SPECS/clang-uml.spec + cp /root/rpmbuild/RPMS/x86_64/* $(build_dir) + cp /root/rpmbuild/SRPMS/* $(build_dir) + + +deb: _BUILD/$(SOURCE_ARCHIVE_DEB) echo "############################" echo "Creating deb source package for $(OS) $(DIST)" - echo "Creating directory: ", $(build_dir)/$(NAME)-$(VERSION) + echo "Creating directory: ", $(build_dir)/$(NAME)-$(VERSION)-$(REBUILD) echo "Extracting source archive..." echo "############################" rm -rf $(build_dir) mkdir -p $(build_dir) - cp _BUILD/$(SOURCE_ARCHIVE) $(build_dir) + cp _BUILD/$(SOURCE_ARCHIVE_DEB) $(build_dir) cd $(build_dir) - mkdir -p $(NAME)-$(VERSION) - tar xf $(SOURCE_ARCHIVE) -C $(NAME)-$(VERSION) --strip-components 1 - cp -R ../../../debian $(NAME)-$(VERSION)/debian - cd $(NAME)-$(VERSION) + mkdir -p $(NAME)-$(VERSION)-$(REBUILD) + tar xf $(SOURCE_ARCHIVE_DEB) -C $(NAME)-$(VERSION)-$(REBUILD) --strip-components 1 + cp -R ../../../debian $(NAME)-$(VERSION)-$(REBUILD)/debian + cd $(NAME)-$(VERSION)-$(REBUILD) $(call subst_template_dir,DATETIME,$(shell date -R),debian) $(call subst_template_dir,OS,${OS},debian) $(call subst_template_dir,NAME,${NAME},debian) @@ -82,24 +116,26 @@ deb: _BUILD/$(SOURCE_ARCHIVE) $(call subst_template_dir,MAINTAINER_EMAIL,${MAINTAINER_EMAIL},debian) $(call subst_template_dir,GIT_COMMIT,${COMMIT},debian) $(call subst_template_dir,GIT_BRANCH,${BRANCH},debian) - mk-origtargz ../$(NAME)-$(VERSION).tar.$(TAR_EXT) + #mk-origtargz ../$(NAME)-$(VERSION)-$(REBUILD).tar.$(TAR_EXT) cp debian/control.$(DIST) debian/control # BUILD SOURCE PACKAGE FOR LAUNCHPAD debuild -S -sa -us -d -k$(GPG_KEY) # BUILD LOCALLY BINARY PACKAGE # debuild -us -uc -conda: _BUILD/$(SOURCE_ARCHIVE) +conda: echo "############################" echo "Creating conda archive from source file $(SOURCE_ARCHIVE)" echo "############################" conda config --add channels conda-forge conda config --set channel_priority strict mkdir -p _BUILD/conda - cp _BUILD/$(SOURCE_ARCHIVE) _BUILD/conda/ + cd .. + git archive --format=tar.gz --prefix=clang-uml-$(VERSION)/ v$(VERSION) >packaging/_BUILD/conda/clang-uml-$(VERSION).tar.gz + cd packaging cp conda/meta.yaml.in conda/meta.yaml $(call subst_conda_meta_yaml,PKG_VERSION,${VERSION},conda) - $(call subst_conda_meta_yaml,PKG_SOURCE,..\/_BUILD\/clang-uml-$(VERSION).tar.$(TAR_EXT),conda) + $(call subst_conda_meta_yaml,PKG_SOURCE,..\/_BUILD\/conda\/clang-uml-$(VERSION).tar.$(TAR_EXT),conda) $(call subst_conda_meta_yaml,GIT_COMMIT,${COMMIT},conda) $(call subst_conda_meta_yaml,GIT_BRANCH,${BRANCH},conda) - conda build --user bkryza --token $(CONDA_TOKEN) conda + conda build --user bkryza --token $(CONDA_TOKEN) ./conda diff --git a/packaging/README.md b/packaging/README.md index 12da4597..5747277b 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -9,6 +9,7 @@ cd packaging make DIST=focal deb make DIST=jammy deb +make DIST=kinetic deb cd _BUILD/ubuntu/focal dput ppa:bkryza/clang-uml *.changes @@ -16,13 +17,25 @@ dput ppa:bkryza/clang-uml *.changes cd _BUILD/ubuntu/jammy dput ppa:bkryza/clang-uml *.changes +cd _BUILD/ubuntu/kinetic +dput ppa:bkryza/clang-uml *.changes + +``` + +## Fedora + +```bash +cd clang-uml +make fedora_36 +make fedora_37 +find packaging/_BUILD/fedora ``` ## Anaconda ```bash -docker run --rm -v $PWD:$PWD continuum/miniconda3 bash -conda install conda-build +docker run --rm -v $PWD:$PWD continuumio/miniconda3 bash +conda install conda-build make cd packaging -make conda +make CONDA_TOKEN= conda ``` \ No newline at end of file diff --git a/packaging/conda/build.sh b/packaging/conda/build.sh index 3819ba95..898635d2 100644 --- a/packaging/conda/build.sh +++ b/packaging/conda/build.sh @@ -9,7 +9,8 @@ export CLANGUML_GIT_TOPLEVEL_DIR=${SRC_DIR} cmake -DCMAKE_BUILD_TYPE=Release \ -DGIT_VERSION=${GIT_VERSION} \ -DCODE_COVERAGE=OFF \ - -DWITH_TESTS=ON \ + -DBUILD_TESTS=OFF \ + -DCMAKE_CXX_FLAGS="-Wno-nonnull -Wno-stringop-overflow" \ -DLLVM_CONFIG_PATH=${BUILD_PREFIX}/bin/llvm-config \ -DCONDA_BUILD_PREFIX=${BUILD_PREFIX} \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ @@ -18,6 +19,4 @@ cmake -DCMAKE_BUILD_TYPE=Release \ CTEST_OUTPUT_ON_FAILURE=1 make -j${CPU_COUNT} -CTEST_OUTPUT_ON_FAILURE=1 ctest -j${CPU_COUNT} - make install \ No newline at end of file diff --git a/packaging/conda/meta.yaml.in b/packaging/conda/meta.yaml.in index 4c890470..c436c1d8 100644 --- a/packaging/conda/meta.yaml.in +++ b/packaging/conda/meta.yaml.in @@ -9,7 +9,7 @@ source: url: "{{PKG_SOURCE}}" build: - binary_relocation: true + binary_relocation: false script_env: - PKG_VERSION - GIT_VERSION={{PKG_VERSION}} @@ -23,14 +23,16 @@ requirements: - {{ compiler('cxx') }} - conda-forge::pkg-config - conda-forge::yaml-cpp 0.7.0 - - conda-forge::clangdev 14.0.4 - - conda-forge::libclang 14.0.4 + - conda-forge::clangdev 15.0.6 + - conda-forge::libclang 15.0.6 + - conda-forge::libclang-cpp 15.0.6 - conda-forge::cmake - conda-forge::git - conda-forge::make # [unix] run: - conda-forge::yaml-cpp 0.7.0 - - conda-forge::libclang 14.0.4 + - conda-forge::libclang 15.0.6 + - conda-forge::libclang-cpp 15.0.6 test: commands: diff --git a/packaging/debian/changelog b/packaging/debian/changelog index 0112d0ac..e0669926 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -1,5 +1,5 @@ clang-uml ({{VERSION}}-0{{OS}}{{REBUILD}}ppa1~{{DISTRIBUTION}}) {{DISTRIBUTION}}; urgency=low - * Initial release + * Packages for release {{VERSION}} -- Bartek Kryza {{DATETIME}} diff --git a/packaging/debian/control.focal b/packaging/debian/control.focal index 0cf376d8..d33dae0f 100644 --- a/packaging/debian/control.focal +++ b/packaging/debian/control.focal @@ -8,10 +8,11 @@ Vcs-Browser: https://github.com/bkryza/clang-uml Vcs-Git: https://github.com/bkryza/clang-uml.git Homepage: https://github.com/bkryza/clang-uml + Package: clang-uml Architecture: any Section: utils -Depends: ${misc:Depends}, ${shlibs:Depends} +Depends: ${misc:Depends}, ${shlibs:Depends}, clang-12 Pre-Depends: ${misc:Pre-Depends} Description: Automatic C++ UML diagram generator based on Clang. . diff --git a/packaging/debian/control.jammy b/packaging/debian/control.jammy index 89ac8adf..71e085fe 100644 --- a/packaging/debian/control.jammy +++ b/packaging/debian/control.jammy @@ -8,10 +8,11 @@ Vcs-Browser: https://github.com/bkryza/clang-uml Vcs-Git: https://github.com/bkryza/clang-uml.git Homepage: https://github.com/bkryza/clang-uml + Package: clang-uml Architecture: any Section: utils -Depends: ${misc:Depends}, ${shlibs:Depends} +Depends: ${misc:Depends}, ${shlibs:Depends}, clang-14 Pre-Depends: ${misc:Pre-Depends} Description: Automatic C++ UML diagram generator based on Clang. . diff --git a/packaging/debian/control.kinetic b/packaging/debian/control.kinetic new file mode 100644 index 00000000..88f3b7f4 --- /dev/null +++ b/packaging/debian/control.kinetic @@ -0,0 +1,19 @@ +Source: clang-uml +Maintainer: Bartek Kryza +Section: devel +Priority: optional +Build-Depends: debhelper, make, gcc-12, g++-12, cmake (>= 3.16), libyaml-cpp-dev, llvm-15, llvm-15-dev, clang-15, clang-tools-15, libclang-15-dev, libclang-cpp15-dev, libmlir-15-dev, mlir-15-tools +Standards-Version: 4.3.0 +Vcs-Browser: https://github.com/bkryza/clang-uml +Vcs-Git: https://github.com/bkryza/clang-uml.git +Homepage: https://github.com/bkryza/clang-uml + + +Package: clang-uml +Architecture: any +Section: utils +Depends: ${misc:Depends}, ${shlibs:Depends}, clang-15 +Pre-Depends: ${misc:Pre-Depends} +Description: Automatic C++ UML diagram generator based on Clang. + . + This package provides the clang-uml binary. diff --git a/packaging/debian/copyright b/packaging/debian/copyright index bd54f076..6723034b 100644 --- a/packaging/debian/copyright +++ b/packaging/debian/copyright @@ -3,7 +3,7 @@ Upstream-Name: clang-uml Source: https://github.com/bkryza/clang-uml Files: * -Copyright: 2021-2022 Bartek Kryza +Copyright: 2021-2023 Bartek Kryza License: apache 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/packaging/debian/rules b/packaging/debian/rules index 99626ef6..d56f75bd 100755 --- a/packaging/debian/rules +++ b/packaging/debian/rules @@ -7,8 +7,14 @@ export CLANGUML_GIT_BRANCH={{GIT_BRANCH}} export CLANGUML_GIT_COMMIT={{GIT_COMMIT}} override_dh_auto_configure: - dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=release -DCMAKE_INSTALL_PREFIX=/usr -DGIT_VERSION={{VERSION}} + dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_CXX_FLAGS="-Wno-nonnull -Wno-stringop-overflow" \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DGIT_VERSION={{VERSION}} \ + -DBUILD_TESTS=OFF +override_dh_strip: + dh_strip --dbgsym-migration='clang-uml-dbg (<<0.3.0-1~)' %: dh $@ diff --git a/packaging/debian/source/format b/packaging/debian/source/format index 163aaf8d..89ae9db8 100644 --- a/packaging/debian/source/format +++ b/packaging/debian/source/format @@ -1 +1 @@ -3.0 (quilt) +3.0 (native) diff --git a/packaging/fedora/clang-uml.spec b/packaging/fedora/clang-uml.spec new file mode 100644 index 00000000..339cc6bb --- /dev/null +++ b/packaging/fedora/clang-uml.spec @@ -0,0 +1,68 @@ +%define _unpackaged_files_terminate_build 0 + +Name: clang-uml +Version: %{?git_version} +Release: 1%{?dist} +Summary: C++ UML diagram generator based on Clang +License: ASL 2.0 +URL: https://github.com/bkryza/clang-uml +Source0: clang-uml-%{version}.tar.gz + +BuildRequires: cmake +BuildRequires: git +BuildRequires: clang-devel +BuildRequires: clang-tools-extra +BuildRequires: ccache +BuildRequires: yaml-cpp-devel +BuildRequires: llvm-devel + +Requires: clang +Requires: yaml-cpp + +Requires(post): info +Requires(preun): info + +%description +clang-uml is an automatic C++ to UML class, sequence, package and include +diagram generator, driven by YAML configuration files. The main idea behind the +project is to easily maintain up-to-date diagrams within a code-base or +document legacy code. The configuration file or files for clang-uml define the +type and contents of each generated diagram. Currently the diagrams are +generated in PlantUML format. + + +%prep +%setup -q -n clang-uml-%{version} + +%build +cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_CXX_FLAGS="-Wno-nonnull -Wno-stringop-overflow" \ + -DCMAKE_NO_SYSTEM_FROM_IMPORTED=ON \ + -DCMAKE_INSTALL_PREFIX=%{_exec_prefix} \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DGIT_VERSION=%{version} \ + -DBUILD_TESTS=OFF + +make %{_smp_mflags} clang-uml + +%install +%make_install +rm -f %{buildroot}/%{_infodir}/dir + +%post +/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || : + +%preun +if [ $1 = 0 ] ; then +/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || : +fi + +%files +%{_bindir}/clang-uml + +%doc CHANGELOG.md README.md AUTHORS.md LICENSE.md +%license LICENSE.md + +%changelog +* Sun Jan 01 2023 Bartek Kryza +- Initial version of the package for Fedora \ No newline at end of file diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index 92b749fd..f139de4f 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,6 +73,8 @@ void generator::generate_alias(const class_ &c, std::ostream &ostr) const assert(!full_name.empty()); + print_debug(c, ostr); + ostr << class_type << " \"" << m_config.simplify_template_type(render_name(full_name)); @@ -84,6 +86,8 @@ void generator::generate_alias(const class_ &c, std::ostream &ostr) const void generator::generate_alias(const enum_ &e, std::ostream &ostr) const { + print_debug(e, ostr); + if (m_config.generate_packages()) ostr << "enum" << " \"" << e.name(); @@ -101,6 +105,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const { namespace plantuml_common = clanguml::common::generators::plantuml; + constexpr auto kAbbreviatedMethodArgumentsLength{15}; + const auto &uns = m_config.using_namespace(); std::string class_type{"class"}; @@ -125,6 +131,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const if (!m_model.should_include(m.access())) continue; + print_debug(m, ostr); + if (m.is_pure_virtual()) ostr << "{abstract} "; @@ -148,7 +156,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const auto args_string = fmt::format("{}", fmt::join(params, ", ")); if (m_config.generate_method_arguments() == config::method_arguments::abbreviated) { - args_string = clanguml::util::abbreviate(args_string, 10); + args_string = clanguml::util::abbreviate( + args_string, kAbbreviatedMethodArgumentsLength); } ostr << args_string; } @@ -191,10 +200,14 @@ void generator::generate(const class_ &c, std::ostream &ostr) const std::string destination; try { - destination = r.destination(); + auto target_element = m_model.get(r.destination()); + if (!target_element.has_value()) + throw error::uml_alias_missing{ + fmt::format("Missing element in the model for ID: {}", + r.destination())}; + + destination = target_element.value().full_name(false); - // TODO: Refactor destination to a namespace qualified entity - // name if (util::starts_with(destination, std::string{"::"})) destination = destination.substr(2, destination.size()); @@ -230,6 +243,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const rendered_relations.find(m.name()) != rendered_relations.end()) continue; + print_debug(m, ostr); + if (m.is_static()) ostr << "{static} "; @@ -270,7 +285,7 @@ void generator::generate_relationships( plantuml_common::to_plantuml(r.type(), r.style())); std::stringstream relstr; - clanguml::common::id_t destination; + clanguml::common::id_t destination{0}; try { destination = r.destination(); @@ -374,7 +389,7 @@ void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const if (!m_model.should_include(r.type())) continue; - clanguml::common::id_t destination; + clanguml::common::id_t destination{0}; std::stringstream relstr; try { destination = r.destination(); @@ -417,6 +432,7 @@ void generator::generate(const package &p, std::ostream &ostr) const // Don't generate packages from namespaces filtered out by // using_namespace if (!uns.starts_with({p.full_name(false)})) { + print_debug(p, ostr); ostr << "package [" << p.name() << "] "; ostr << "as " << p.alias(); @@ -431,19 +447,19 @@ void generator::generate(const package &p, std::ostream &ostr) const } for (const auto &subpackage : p) { - if (dynamic_cast(subpackage.get())) { + if (dynamic_cast(subpackage.get()) != nullptr) { // TODO: add option - generate_empty_packages const auto &sp = dynamic_cast(*subpackage); if (!sp.is_empty()) generate(sp, ostr); } - else if (dynamic_cast(subpackage.get())) { + else if (dynamic_cast(subpackage.get()) != nullptr) { if (m_model.should_include(*subpackage)) { generate_alias(dynamic_cast(*subpackage), ostr); generate(dynamic_cast(*subpackage), ostr); } } - else if (dynamic_cast(subpackage.get())) { + else if (dynamic_cast(subpackage.get()) != nullptr) { if (m_model.should_include(*subpackage)) { generate_alias(dynamic_cast(*subpackage), ostr); generate(dynamic_cast(*subpackage), ostr); @@ -466,19 +482,19 @@ void generator::generate_relationships( const package &p, std::ostream &ostr) const { for (const auto &subpackage : p) { - if (dynamic_cast(subpackage.get())) { + if (dynamic_cast(subpackage.get()) != nullptr) { // TODO: add option - generate_empty_packages const auto &sp = dynamic_cast(*subpackage); if (!sp.is_empty()) generate_relationships(sp, ostr); } - else if (dynamic_cast(subpackage.get())) { + else if (dynamic_cast(subpackage.get()) != nullptr) { if (m_model.should_include(*subpackage)) { generate_relationships( dynamic_cast(*subpackage), ostr); } } - else if (dynamic_cast(subpackage.get())) { + else if (dynamic_cast(subpackage.get()) != nullptr) { if (m_model.should_include(*subpackage)) { generate_relationships( dynamic_cast(*subpackage), ostr); @@ -496,18 +512,18 @@ void generator::generate(std::ostream &ostr) const generate_plantuml_directives(ostr, m_config.puml().before); for (const auto &p : m_model) { - if (dynamic_cast(p.get())) { + if (dynamic_cast(p.get()) != nullptr) { const auto &sp = dynamic_cast(*p); if (!sp.is_empty()) generate(sp, ostr); } - else if (dynamic_cast(p.get())) { + else if (dynamic_cast(p.get()) != nullptr) { if (m_model.should_include(*p)) { generate_alias(dynamic_cast(*p), ostr); generate(dynamic_cast(*p), ostr); } } - else if (dynamic_cast(p.get())) { + else if (dynamic_cast(p.get()) != nullptr) { if (m_model.should_include(*p)) { generate_alias(dynamic_cast(*p), ostr); generate(dynamic_cast(*p), ostr); @@ -516,15 +532,15 @@ void generator::generate(std::ostream &ostr) const } for (const auto &p : m_model) { - if (dynamic_cast(p.get())) { + if (dynamic_cast(p.get()) != nullptr) { generate_relationships(dynamic_cast(*p), ostr); } - else if (dynamic_cast(p.get())) { + else if (dynamic_cast(p.get()) != nullptr) { if (m_model.should_include(*p)) { generate_relationships(dynamic_cast(*p), ostr); } } - else if (dynamic_cast(p.get())) { + else if (dynamic_cast(p.get()) != nullptr) { if (m_model.should_include(*p)) { generate_relationships(dynamic_cast(*p), ostr); } @@ -537,4 +553,4 @@ void generator::generate(std::ostream &ostr) const ostr << "@enduml" << '\n'; } -} +} // namespace clanguml::class_diagram::generators::plantuml diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.h b/src/class_diagram/generators/plantuml/class_diagram_generator.h index 1c774019..b49ca272 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,7 +81,7 @@ private: std::string render_name(std::string name) const; }; -} -} -} -} +} // namespace plantuml +} // namespace generators +} // namespace class_diagram +} // namespace clanguml diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 88d365fc..69e35473 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/class.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,7 +68,7 @@ void class_::add_parent(class_parent &&parent) bases_.emplace_back(std::move(parent)); } -void class_::add_template(template_parameter tmplt) +void class_::add_template(template_parameter &&tmplt) { templates_.emplace_back(std::move(tmplt)); } @@ -194,4 +194,4 @@ int class_::calculate_template_specialization_match( return res; } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index c6e1cfd1..f4d10f35 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,7 +56,7 @@ public: void add_member(class_member &&member); void add_method(class_method &&method); void add_parent(class_parent &&parent); - void add_template(template_parameter tmplt); + void add_template(template_parameter &&tmplt); const std::vector &members() const; const std::vector &methods() const; @@ -105,7 +105,7 @@ private: std::string full_name_; }; -} +} // namespace clanguml::class_diagram::model namespace std { template <> @@ -119,4 +119,4 @@ struct hash> { return std::hash{}(key.get().id()); } }; -} +} // namespace std diff --git a/src/class_diagram/model/class_element.cc b/src/class_diagram/model/class_element.cc index 8c21456f..f449e49e 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,13 +18,15 @@ #include "class_element.h" +#include + namespace clanguml::class_diagram::model { -class_element::class_element(common::model::access_t access, - const std::string &name, const std::string &type) +class_element::class_element( + common::model::access_t access, std::string name, std::string type) : access_{access} - , name_{name} - , type_{type} + , name_{std::move(name)} + , type_{std::move(type)} { } @@ -42,4 +44,4 @@ inja::json class_element::context() const ctx["access"] = to_string(access()); return ctx; } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_element.h b/src/class_diagram/model/class_element.h index 441c26c9..fa6e53fc 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,8 +29,8 @@ namespace clanguml::class_diagram::model { class class_element : public common::model::decorated_element, public common::model::source_location { public: - class_element(common::model::access_t scope, const std::string &name, - const std::string &type); + class_element( + common::model::access_t scope, std::string name, std::string type); common::model::access_t access() const; std::string name() const; @@ -44,4 +44,4 @@ private: std::string type_; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_member.cc b/src/class_diagram/model/class_member.cc index 8bf041e6..4bf7a4c6 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,4 +37,4 @@ bool class_member::is_static() const { return is_static_; } void class_member::is_static(bool is_static) { is_static_ = is_static; } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_member.h b/src/class_diagram/model/class_member.h index 21cdf52c..4b760293 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,4 +41,4 @@ private: bool is_static_{false}; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_method.cc b/src/class_diagram/model/class_method.cc index cef773cd..fe84369b 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,4 +61,4 @@ void class_method::add_parameter(method_parameter &¶meter) { parameters_.emplace_back(std::move(parameter)); } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_method.h b/src/class_diagram/model/class_method.h index 277cd3c2..f047863e 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,4 +58,4 @@ private: bool is_defaulted_{false}; bool is_static_{false}; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_parent.cc b/src/class_diagram/model/class_parent.cc index 83669fef..7461ab2e 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,4 +35,4 @@ void class_parent::set_access(common::model::access_t access) common::model::access_t class_parent::access() const { return access_; } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_parent.h b/src/class_diagram/model/class_parent.h index 0ef20766..1e059097 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,9 +41,9 @@ public: common::model::access_t access() const; private: - clanguml::common::id_t id_; + clanguml::common::id_t id_{}; std::string name_; bool is_virtual_{false}; - common::model::access_t access_; + common::model::access_t access_{common::model::access_t::kPublic}; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index df121226..643379f0 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -205,7 +205,7 @@ bool diagram::add_enum(std::unique_ptr &&e) if (!has_enum(*e)) { if (add_element(ns, std::move(e))) { - enums_.emplace_back(std::move(e_ref)); + enums_.emplace_back(e_ref); return true; } } @@ -239,17 +239,14 @@ void diagram::get_parents( bool diagram::has_element( clanguml::common::model::diagram_element::id_t id) const { - for (const auto &c : classes_) { - if (c.get().id() == id) - return true; - } + const auto has_class = std::any_of(classes_.begin(), classes_.end(), + [id](const auto &c) { return c.get().id() == id; }); - for (const auto &c : enums_) { - if (c.get().id() == id) - return true; - } + if (has_class) + return true; - return false; + return std::any_of(enums_.begin(), enums_.end(), + [id](const auto &c) { return c.get().id() == id; }); } std::string diagram::to_alias( @@ -294,7 +291,7 @@ inja::json diagram::context() const return ctx; } -} +} // namespace clanguml::class_diagram::model namespace clanguml::common::model { template <> diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 0fda26ce..3d9703dd 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,7 +49,7 @@ public: const std::string &full_name) const override; common::optional_ref get( - const clanguml::common::model::diagram_element::id_t id) const override; + clanguml::common::model::diagram_element::id_t id) const override; const common::reference_vector &classes() const; @@ -82,10 +82,10 @@ public: void get_parents(clanguml::common::reference_set &parents) const; - friend void print_diagram_tree(const diagram &d, const int level); + friend void print_diagram_tree(const diagram &d, int level); bool has_element( - const clanguml::common::model::diagram_element::id_t id) const override; + clanguml::common::model::diagram_element::id_t id) const override; inja::json context() const override; @@ -96,7 +96,7 @@ private: std::map> type_aliases_; }; -} +} // namespace clanguml::class_diagram::model namespace clanguml::common::model { template <> diff --git a/src/class_diagram/model/enum.cc b/src/class_diagram/model/enum.cc index 41bbdb77..57af53db 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,4 +54,4 @@ std::vector &enum_::constants() { return constants_; } const std::vector &enum_::constants() const { return constants_; } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/enum.h b/src/class_diagram/model/enum.h index 4cbfc72a..e8285352 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,4 +49,4 @@ private: std::vector constants_; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/method_parameter.cc b/src/class_diagram/model/method_parameter.cc index 3942d43d..f072344c 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,4 +49,4 @@ std::string method_parameter::to_string( return fmt::format("{} {} = {}", type_ns, name(), default_value()); } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/method_parameter.h b/src/class_diagram/model/method_parameter.h index 04fdaea5..acd7ca73 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,4 +45,4 @@ private: std::string default_value_; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/template_parameter.cc b/src/class_diagram/model/template_parameter.cc index 0d3b31fe..efba7a7d 100644 --- a/src/class_diagram/model/template_parameter.cc +++ b/src/class_diagram/model/template_parameter.cc @@ -1,7 +1,7 @@ /** * src/class_diagram/model/template_parameter.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,11 +20,13 @@ #include "common/model/enums.h" #include +#include + namespace clanguml::class_diagram::model { template_parameter::template_parameter(const std::string &type, - const std::string &name, const std::string &default_value, bool is_variadic) - : default_value_{default_value} + const std::string &name, std::string default_value, bool is_variadic) + : default_value_{std::move(default_value)} , is_variadic_{is_variadic} { set_name(name); @@ -84,11 +86,9 @@ bool template_parameter::is_variadic() const noexcept { return is_variadic_; } bool template_parameter::is_specialization_of( const template_parameter &ct) const { - if ((ct.is_template_parameter() || ct.is_template_template_parameter()) && - !is_template_parameter()) - return true; - - return false; + return (ct.is_template_parameter() || + ct.is_template_template_parameter()) && + !is_template_parameter(); } void template_parameter::add_template_param(template_parameter &&ct) @@ -178,7 +178,8 @@ bool template_parameter::find_nested_relationships( std::vector> &nested_relationships, common::model::relationship_t hint, - std::function should_include) const + const std::function &should_include) + const { bool added_aggregation_relationship{false}; @@ -186,7 +187,7 @@ bool template_parameter::find_nested_relationships( // just add it and skip recursion (e.g. this is a user defined type) if (should_include(name())) { if (id()) { - nested_relationships.push_back({id().value(), hint}); + nested_relationships.emplace_back(id().value(), hint); added_aggregation_relationship = (hint == common::model::relationship_t::kAggregation); } @@ -198,8 +199,8 @@ bool template_parameter::find_nested_relationships( if (should_include(template_argument.name()) && template_argument.id()) { - nested_relationships.push_back( - {template_argument.id().value(), hint}); + nested_relationships.emplace_back( + template_argument.id().value(), hint); added_aggregation_relationship = (hint == common::model::relationship_t::kAggregation); @@ -215,4 +216,4 @@ bool template_parameter::find_nested_relationships( return added_aggregation_relationship; } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/template_parameter.h b/src/class_diagram/model/template_parameter.h index cbd79986..5f9392fd 100644 --- a/src/class_diagram/model/template_parameter.h +++ b/src/class_diagram/model/template_parameter.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/template_parameter.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ namespace clanguml::class_diagram::model { class template_parameter { public: template_parameter(const std::string &type = "", - const std::string &name = "", const std::string &default_value = "", + const std::string &name = "", std::string default_value = "", bool is_variadic = false); template_parameter(const template_parameter &right) = default; @@ -97,7 +97,8 @@ public: std::vector> &nested_relationships, common::model::relationship_t hint, - std::function should_include) const; + const std::function &should_include) + const; private: /// Represents the type of non-type template parameters @@ -129,4 +130,4 @@ private: std::optional id_; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/type_alias.cc b/src/class_diagram/model/type_alias.cc index 8a693d7e..7b3a3267 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,4 +31,4 @@ void type_alias::set_underlying_type(const std::string &type) std::string type_alias::underlying_type() const { return underlying_type_; } -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/type_alias.h b/src/class_diagram/model/type_alias.h index 68908ffd..b0461226 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,4 +34,4 @@ private: std::string underlying_type_; }; -} +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 59cefb23..4e9d8a6c 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,9 +34,7 @@ using clanguml::class_diagram::model::diagram; using clanguml::class_diagram::model::enum_; using clanguml::class_diagram::model::method_parameter; using clanguml::class_diagram::model::template_parameter; -using clanguml::class_diagram::model::type_alias; using clanguml::common::model::access_t; -using clanguml::common::model::decorated_element; using clanguml::common::model::namespace_; using clanguml::common::model::relationship; using clanguml::common::model::relationship_t; @@ -127,33 +125,31 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) const auto *parent = enm->getParent(); - if (parent && parent->isRecord()) { - // Here we have 2 options, either: - // - the parent is a regular C++ class/struct - // - the parent is a class template declaration/specialization - std::optional id_opt; - int64_t local_id = - static_cast(parent)->getID(); + std::optional id_opt; - id_opt = get_ast_local_id(local_id); + if (parent != nullptr) { + const auto *parent_record_decl = + clang::dyn_cast(parent); - // If not, check if the parent template declaration is in the model - if (!id_opt) { - if (static_cast(parent) - ->getDescribedTemplate()) { - local_id = static_cast(parent) - ->getDescribedTemplate() - ->getID(); + if (parent_record_decl != nullptr) { + int64_t local_id = parent_record_decl->getID(); - id_opt = get_ast_local_id(local_id); + // First check if the parent has been added to the diagram as + // regular class + id_opt = get_ast_local_id(local_id); + + // If not, check if the parent template declaration is in the model + if (!id_opt) { + if (parent_record_decl->getDescribedTemplate() != nullptr) { + local_id = + parent_record_decl->getDescribedTemplate()->getID(); + id_opt = get_ast_local_id(local_id); + } } } + } - if (!id_opt) { - LOG_WARN("Unknown parent for enum {}", qualified_name); - return true; - } - + if (id_opt) { auto parent_class = diagram_.get_class(*id_opt); assert(parent_class); @@ -209,7 +205,7 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( cls->getLocation().printToString(source_manager())); // TODO: Add support for classes defined in function/method bodies - if (cls->isLocalClass()) + if (cls->isLocalClass() != nullptr) return true; auto template_specialization_ptr = process_template_specialization(cls); @@ -255,7 +251,7 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl( cls->getQualifiedNameAsString(), cls->getLocation().printToString(source_manager())); - auto *template_type_specialization_ptr = + const auto *template_type_specialization_ptr = cls->getTemplatedDecl() ->getUnderlyingType() ->getAs(); @@ -315,10 +311,8 @@ bool translation_unit_visitor::VisitClassTemplateDecl( forward_declarations_.emplace(id, std::move(c_ptr)); return true; } - else { - process_class_declaration(*cls->getTemplatedDecl(), *c_ptr); - forward_declarations_.erase(id); - } + process_class_declaration(*cls->getTemplatedDecl(), *c_ptr); + forward_declarations_.erase(id); if (diagram_.should_include(*c_ptr)) { const auto name = c_ptr->full_name(); @@ -355,7 +349,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) ->getQualifiedNameAsString()); } - if (cls->isTemplated() && cls->getDescribedTemplate()) { + if (cls->isTemplated() && (cls->getDescribedTemplate() != nullptr)) { // If the described templated of this class is already in the model // skip it: if (get_ast_local_id(cls->getDescribedTemplate()->getID())) @@ -363,7 +357,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) } // TODO: Add support for classes defined in function/method bodies - if (cls->isLocalClass()) + if (cls->isLocalClass() != nullptr) return true; auto c_ptr = create_class_declaration(cls); @@ -387,8 +381,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) forward_declarations_.emplace(id, std::move(c_ptr)); return true; } - else - forward_declarations_.erase(id); + forward_declarations_.erase(id); if (diagram_.should_include(class_model)) { LOG_DBG("Adding class {} with id {}", class_model.full_name(false), @@ -423,30 +416,34 @@ std::unique_ptr translation_unit_visitor::create_class_declaration( const auto *parent = cls->getParent(); - if (parent && parent->isRecord()) { + std::optional id_opt; + + if (parent != nullptr) { + const auto *parent_record_decl = + clang::dyn_cast(parent); + + if (parent_record_decl != nullptr) { + int64_t local_id = parent_record_decl->getID(); + + // First check if the parent has been added to the diagram as + // regular class + id_opt = get_ast_local_id(local_id); + + // If not, check if the parent template declaration is in the model + if (!id_opt) { + if (parent_record_decl->getDescribedTemplate() != nullptr) { + local_id = + parent_record_decl->getDescribedTemplate()->getID(); + id_opt = get_ast_local_id(local_id); + } + } + } + } + + if (id_opt) { // Here we have 2 options, either: // - the parent is a regular C++ class/struct // - the parent is a class template declaration/specialization - std::optional id_opt; - int64_t local_id = - static_cast(parent)->getID(); - - // First check if the parent has been added to the diagram as regular - // class - id_opt = get_ast_local_id(local_id); - - // If not, check if the parent template declaration is in the model - if (!id_opt) { - local_id = static_cast(parent) - ->getDescribedTemplate() - ->getID(); - if (static_cast(parent) - ->getDescribedTemplate()) - id_opt = get_ast_local_id(local_id); - } - - assert(id_opt); - auto parent_class = diagram_.get_class(*id_opt); assert(parent_class); @@ -526,7 +523,8 @@ bool translation_unit_visitor::process_template_parameters( for (const auto *parameter : *template_declaration.getTemplateParameters()) { - if (clang::dyn_cast_or_null(parameter)) { + if (clang::dyn_cast_or_null(parameter) != + nullptr) { const auto *template_type_parameter = clang::dyn_cast_or_null(parameter); template_parameter ct; @@ -539,7 +537,7 @@ bool translation_unit_visitor::process_template_parameters( c.add_template(std::move(ct)); } else if (clang::dyn_cast_or_null( - parameter)) { + parameter) != nullptr) { const auto *template_nontype_parameter = clang::dyn_cast_or_null( parameter); @@ -553,7 +551,7 @@ bool translation_unit_visitor::process_template_parameters( c.add_template(std::move(ct)); } else if (clang::dyn_cast_or_null( - parameter)) { + parameter) != nullptr) { const auto *template_template_parameter = clang::dyn_cast_or_null( parameter); @@ -582,16 +580,20 @@ void translation_unit_visitor::process_template_record_containment( const auto *parent = record.getParent(); //->getOuterLexicalRecordContext(); - if (parent && - static_cast(parent) - ->getDescribedTemplate()) { - auto id_opt = - get_ast_local_id(static_cast(parent) - ->getDescribedTemplate() - ->getID()); + if (parent != nullptr) { + if (const auto *record_decl = + clang::dyn_cast(parent); + record_decl != nullptr) { + if (const auto *described_template = + record_decl->getDescribedTemplate(); + described_template != nullptr) { + auto id_opt = get_ast_local_id(described_template->getID()); - if (id_opt) { - element.add_relationship({relationship_t::kContainment, *id_opt}); + if (id_opt) { + element.add_relationship( + {relationship_t::kContainment, *id_opt}); + } + } } } } @@ -611,30 +613,34 @@ void translation_unit_visitor::process_record_containment( element.set_namespace(namespace_declaration.value()); } - const auto id = common::to_id( - *static_cast(record.getParent())); - - element.add_relationship({relationship_t::kContainment, id}); + if (const auto *record_decl = + clang::dyn_cast(record.getParent()); + record_decl != nullptr) { + element.add_relationship( + {relationship_t::kContainment, common::to_id(*record_decl)}); + } } void translation_unit_visitor::process_class_bases( const clang::CXXRecordDecl *cls, class_ &c) { - for (auto &base : cls->bases()) { + for (const auto &base : cls->bases()) { class_parent cp; auto name_and_ns = common::model::namespace_{ common::to_string(base.getType(), cls->getASTContext())}; cp.set_name(name_and_ns.to_string()); - if (base.getType()->getAs() != nullptr) - cp.set_id(common::to_id( - *base.getType()->getAs()->getDecl())); - else if (base.getType()->getAs() != - nullptr) { - auto template_specialization_ptr = build_template_instantiation( - *base.getType()->getAs(), - {}); + if (const auto *record_type = + base.getType()->getAs(); + record_type != nullptr) { + cp.set_id(common::to_id(*record_type->getDecl())); + } + else if (const auto *tsp = + base.getType()->getAs(); + tsp != nullptr) { + auto template_specialization_ptr = + build_template_instantiation(*tsp, {}); if (template_specialization_ptr) { cp.set_id(template_specialization_ptr->id()); } @@ -668,14 +674,19 @@ void translation_unit_visitor::process_template_specialization_children( } // Iterate over class template methods - for (auto const *decl_iterator : - clang::dyn_cast_or_null(cls)->decls()) { - auto const *method_template = - llvm::dyn_cast_or_null(decl_iterator); - if (method_template == nullptr) - continue; + if (const auto *cls_decl_context = + clang::dyn_cast_or_null(cls); + cls_decl_context != nullptr) { + for (auto const *decl_iterator : + clang::dyn_cast_or_null(cls)->decls()) { + auto const *method_template = + llvm::dyn_cast_or_null( + decl_iterator); + if (method_template == nullptr) + continue; - process_template_method(*method_template, c); + process_template_method(*method_template, c); + } } // Iterate over regular class fields @@ -690,7 +701,7 @@ void translation_unit_visitor::process_template_specialization_children( if (decl->getKind() == clang::Decl::Var) { const clang::VarDecl *variable_declaration{ dynamic_cast(decl)}; - if (variable_declaration && + if ((variable_declaration != nullptr) && variable_declaration->isStaticDataMember()) { process_static_field(*variable_declaration, c); } @@ -730,14 +741,18 @@ void translation_unit_visitor::process_class_children( } // Iterate over class template methods - for (auto const *decl_iterator : - clang::dyn_cast_or_null(cls)->decls()) { - auto const *method_template = - llvm::dyn_cast_or_null(decl_iterator); - if (method_template == nullptr) - continue; + if (const auto *cls_decl_context = + clang::dyn_cast_or_null(cls); + cls_decl_context != nullptr) { + for (auto const *decl_iterator : cls_decl_context->decls()) { + auto const *method_template = + llvm::dyn_cast_or_null( + decl_iterator); + if (method_template == nullptr) + continue; - process_template_method(*method_template, c); + process_template_method(*method_template, c); + } } // Iterate over regular class fields @@ -752,7 +767,7 @@ void translation_unit_visitor::process_class_children( if (decl->getKind() == clang::Decl::Var) { const clang::VarDecl *variable_declaration{ dynamic_cast(decl)}; - if (variable_declaration && + if ((variable_declaration != nullptr) && variable_declaration->isStaticDataMember()) { process_static_field(*variable_declaration, c); } @@ -790,7 +805,7 @@ void translation_unit_visitor::process_friend( nullptr) { // TODO: handle template friend } - else if (friend_type->getAs()) { + else if (friend_type->getAs() != nullptr) { const auto friend_type_name = friend_type->getAsRecordDecl()->getQualifiedNameAsString(); if (diagram().should_include(friend_type_name)) { @@ -898,20 +913,22 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, relationships, relationship_t::kAggregation); } else if (type->isEnumeralType()) { - relationships.emplace_back( - common::to_id(*type->getAs()->getDecl()), - relationship_hint); + if (const auto *enum_type = type->getAs(); + enum_type != nullptr) { + relationships.emplace_back( + common::to_id(*enum_type->getDecl()), relationship_hint); + } } else if (type->isRecordType()) { const auto *type_instantiation_decl = type->getAs(); - if (type_instantiation_decl != nullptr) { - if (type_instantiation_decl->isTypeAlias()) - type_instantiation_decl = - type_instantiation_decl->getAliasedType() - ->getAs(); - } + // if (type_instantiation_decl != nullptr) { + // if (type_instantiation_decl->isTypeAlias()) + // type_instantiation_decl = + // type_instantiation_decl->getAliasedType() + // ->getAs(); + // } if (type_instantiation_decl != nullptr) { for (const auto &template_argument : *type_instantiation_decl) { @@ -940,12 +957,12 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, clang::TemplateArgument::ArgKind::TemplateExpansion) { // pass } - else if (template_argument.getAsType() - ->getAs()) { + else if (const auto *function_type = + template_argument.getAsType() + ->getAs(); + function_type != nullptr) { for (const auto ¶m_type : - template_argument.getAsType() - ->getAs() - ->param_types()) { + function_type->param_types()) { result = find_relationships(param_type, relationships, relationship_t::kDependency); } @@ -1020,11 +1037,11 @@ void translation_unit_visitor::process_function_parameter( if (underlying_type->isPointerType()) underlying_type = underlying_type->getPointeeType(); - if (underlying_type->getAs() != - nullptr) { - process_function_parameter_find_relationships_in_template(c, - template_parameter_names, - *underlying_type->getAs()); + if (const auto *tsp = + underlying_type->getAs(); + tsp != nullptr) { + process_function_parameter_find_relationships_in_template( + c, template_parameter_names, *tsp); } } @@ -1033,7 +1050,7 @@ void translation_unit_visitor::process_function_parameter( void translation_unit_visitor:: process_function_parameter_find_relationships_in_template(class_ &c, - const std::set &template_parameter_names, + const std::set & /*template_parameter_names*/, const clang::TemplateSpecializationType &template_instantiation_type) { const auto template_field_decl_name = @@ -1177,7 +1194,7 @@ translation_unit_visitor::process_template_specialization( void translation_unit_visitor::process_template_specialization_argument( const clang::ClassTemplateSpecializationDecl *cls, class_ &template_instantiation, const clang::TemplateArgument &arg, - size_t argument_index, bool in_parameter_pack) + size_t argument_index, bool /*in_parameter_pack*/) { const auto argument_kind = arg.getKind(); @@ -1187,9 +1204,9 @@ void translation_unit_visitor::process_template_specialization_argument( // If this is a nested template type - add nested templates as // template arguments - if (arg.getAsType()->getAs()) { - const auto *nested_template_type = + if (const auto *nested_template_type = arg.getAsType()->getAs(); + nested_template_type != nullptr) { const auto nested_template_name = nested_template_type->getTemplateName() @@ -1199,8 +1216,7 @@ void translation_unit_visitor::process_template_specialization_argument( argument.set_name(nested_template_name); auto nested_template_instantiation = build_template_instantiation( - *arg.getAsType()->getAs(), - {&template_instantiation}); + *nested_template_type, {&template_instantiation}); argument.set_id(nested_template_instantiation->id()); @@ -1213,7 +1229,8 @@ void translation_unit_visitor::process_template_specialization_argument( simplify_system_template(argument, argument.to_string(config().using_namespace(), false)); } - else if (arg.getAsType()->getAs()) { + else if (arg.getAsType()->getAs() != + nullptr) { auto type_name = common::to_string(arg.getAsType(), cls->getASTContext()); @@ -1412,7 +1429,8 @@ std::unique_ptr translation_unit_visitor:: template_instantiation.set_name(template_decl->getNameAsString()); template_instantiation.set_namespace(ns); template_instantiation.set_id(template_decl->getID() + - (std::hash{}(full_template_specialization_name) >> 4)); + static_cast( + std::hash{}(full_template_specialization_name) >> 4U)); build_template_instantiation_process_template_arguments(parent, template_base_params, @@ -1480,14 +1498,16 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( /*is variadic */ bool>> template_base_params{}; - auto *template_type_ptr = &template_type_decl; - if (template_type_decl.isTypeAlias() && - template_type_decl.getAliasedType() - ->getAs()) - template_type_ptr = template_type_decl.getAliasedType() - ->getAs(); + const auto *template_type_ptr = &template_type_decl; - auto &template_type = *template_type_ptr; + if (template_type_decl.isTypeAlias()) { + if (const auto *tsp = template_type_decl.getAliasedType() + ->getAs(); + tsp != nullptr) + template_type_ptr = tsp; + } + + const auto &template_type = *template_type_ptr; // // Create class_ instance to hold the template instantiation @@ -1507,8 +1527,9 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( auto *class_template_decl{ clang::dyn_cast(template_decl)}; - if (class_template_decl && class_template_decl->getTemplatedDecl() && - class_template_decl->getTemplatedDecl()->getParent() && + if ((class_template_decl != nullptr) && + (class_template_decl->getTemplatedDecl() != nullptr) && + (class_template_decl->getTemplatedDecl()->getParent() != nullptr) && class_template_decl->getTemplatedDecl()->getParent()->isRecord()) { namespace_ ns{ @@ -1561,7 +1582,8 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( clang::dyn_cast_or_null( template_decl->getTemplatedDecl()); - if (templated_class_decl && templated_class_decl->hasDefinition()) + if ((templated_class_decl != nullptr) && + templated_class_decl->hasDefinition()) for (const auto &base : templated_class_decl->bases()) { const auto base_class_name = common::to_string( base.getType(), templated_class_decl->getASTContext(), false); @@ -1664,7 +1686,7 @@ void translation_unit_visitor:: const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl) { - auto arg_index = 0U; + auto arg_index = 0; for (const auto &arg : template_args) { const auto argument_kind = arg.getKind(); template_parameter argument; @@ -1693,11 +1715,11 @@ void translation_unit_visitor:: // the list of template params, from then this variable is true // and we can process following template parameters as belonging // to the variadic tuple - auto variadic_params = false; + [[maybe_unused]] auto variadic_params{false}; // In case any of the template arguments are base classes, add // them as parents of the current template instantiation class - if (template_base_params.size() > 0) { + if (!template_base_params.empty()) { variadic_params = build_template_instantiation_add_base_classes( template_instantiation, template_base_params, arg_index, variadic_params, argument); @@ -1740,24 +1762,25 @@ void translation_unit_visitor:: // If this is a nested template type - add nested templates as // template arguments - if (arg.getAsType()->getAs()) { + if (const auto *function_type = + arg.getAsType()->getAs(); + function_type != nullptr) { - for (const auto ¶m_type : - arg.getAsType()->getAs()->param_types()) { - - if (!param_type->getAs()) + for (const auto ¶m_type : function_type->param_types()) { + const auto *param_record_type = + param_type->getAs(); + if (param_record_type == nullptr) continue; - auto classTemplateSpecialization = + auto *classTemplateSpecialization = llvm::dyn_cast( param_type->getAsRecordDecl()); - if (classTemplateSpecialization) { + if (classTemplateSpecialization != nullptr) { // Read arg info as needed. auto nested_template_instantiation = build_template_instantiation_from_class_template_specialization( - *classTemplateSpecialization, - *param_type->getAs(), + *classTemplateSpecialization, *param_record_type, diagram().should_include( full_template_specialization_name) ? std::make_optional(&template_instantiation) @@ -1766,9 +1789,6 @@ void translation_unit_visitor:: const auto nested_template_name = classTemplateSpecialization->getQualifiedNameAsString(); - auto [tinst_ns, tinst_name] = - cx::util::split_ns(nested_template_name); - if (nested_template_instantiation) { if (parent.has_value()) parent.value()->add_relationship( @@ -1786,24 +1806,22 @@ void translation_unit_visitor:: } } } - else if (arg.getAsType()->getAs()) { - const auto *nested_template_type = - arg.getAsType()->getAs(); + else if (const auto *nested_template_type = + arg.getAsType()->getAs(); + nested_template_type != nullptr) { const auto nested_template_name = nested_template_type->getTemplateName() .getAsTemplateDecl() ->getQualifiedNameAsString(); - auto [tinst_ns, tinst_name] = cx::util::split_ns(nested_template_name); - argument.set_name(nested_template_name); - auto nested_template_instantiation = build_template_instantiation( - *arg.getAsType()->getAs(), - diagram().should_include(full_template_specialization_name) - ? std::make_optional(&template_instantiation) - : parent); + auto nested_template_instantiation = + build_template_instantiation(*nested_template_type, + diagram().should_include(full_template_specialization_name) + ? std::make_optional(&template_instantiation) + : parent); argument.set_id(nested_template_instantiation->id()); @@ -1838,7 +1856,7 @@ void translation_unit_visitor:: diagram().add_class(std::move(nested_template_instantiation)); } } - else if (arg.getAsType()->getAs()) { + else if (arg.getAsType()->getAs() != nullptr) { argument.is_template_parameter(true); argument.set_name( common::to_string(arg.getAsType(), template_decl->getASTContext())); @@ -1886,19 +1904,23 @@ void translation_unit_visitor:: argument.set_name( common::to_string(arg.getAsType(), template_decl->getASTContext())); - if (arg.getAsType()->getAs() && - arg.getAsType()->getAs()->getAsRecordDecl()) { - argument.set_id(common::to_id(arg)); + if (const auto *record_type = arg.getAsType()->getAs(); + record_type != nullptr) { + if (const auto *record_type_decl = record_type->getAsRecordDecl(); + record_type_decl != nullptr) { + argument.set_id(common::to_id(arg)); - if (diagram().should_include(full_template_specialization_name)) { - // Add dependency relationship to the parent - // template - template_instantiation.add_relationship( - {relationship_t::kDependency, common::to_id(arg)}); + if (diagram().should_include(full_template_specialization_name)) { + // Add dependency relationship to the parent + // template + template_instantiation.add_relationship( + {relationship_t::kDependency, common::to_id(arg)}); + } } } - else if (arg.getAsType()->getAs()) { - if (arg.getAsType()->getAs()->getAsTagDecl()) { + else if (const auto *enum_type = arg.getAsType()->getAs(); + enum_type != nullptr) { + if (enum_type->getAsTagDecl() != nullptr) { template_instantiation.add_relationship( {relationship_t::kDependency, common::to_id(arg)}); } @@ -2022,8 +2044,8 @@ void translation_unit_visitor::process_field( !field_type_is_template_template_parameter) { // Build the template instantiation for the field type - auto template_specialization_ptr = build_template_instantiation( - *field_type->getAs(), {&c}); + auto template_specialization_ptr = + build_template_instantiation(*template_field_type, {&c}); if (!field.skip_relationship() && template_specialization_ptr) { const auto &template_specialization = *template_specialization_ptr; @@ -2089,7 +2111,7 @@ void translation_unit_visitor::process_field( // Find relationship for the type if the type has not been added // as aggregation if (!template_instantiation_added_as_aggregation) { - if (field_type->getAsCXXRecordDecl() && + if ((field_type->getAsCXXRecordDecl() != nullptr) && field_type->getAsCXXRecordDecl()->getNameAsString().empty()) { // Relationships to fields whose type is an anonymous nested // struct have to be handled separately here @@ -2123,7 +2145,7 @@ void translation_unit_visitor::resolve_local_to_global_ids() { // TODO: Refactor to a map with relationships attached to references // to elements - for (auto &cls : diagram().classes()) { + for (const auto &cls : diagram().classes()) { for (auto &rel : cls.get().relationships()) { if (rel.type() == relationship_t::kInstantiation) { const auto maybe_local_id = rel.destination(); @@ -2145,15 +2167,14 @@ void translation_unit_visitor::finalize() } bool translation_unit_visitor::simplify_system_template( - template_parameter &ct, const std::string &full_name) + template_parameter &ct, const std::string &full_name) const { if (config().type_aliases().count(full_name) > 0) { ct.set_name(config().type_aliases().at(full_name)); ct.clear_params(); return true; } - else - return false; + return false; } void translation_unit_visitor::set_ast_local_id( @@ -2172,4 +2193,4 @@ translation_unit_visitor::get_ast_local_id(int64_t local_id) const return local_ast_id_map_.at(local_id); } -} +} // namespace clanguml::class_diagram::visitor diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 0e5fafb1..02778238 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,12 @@ using found_relationships_t = std::vector>; +/** + * @brief Class diagram translation unit visitor + * + * This class implements the @link clang::RecursiveASTVisitor interface + * for selected visitors relevant to generating class diagrams. + */ class translation_unit_visitor : public clang::RecursiveASTVisitor, public common::visitor::translation_unit_visitor { @@ -60,10 +66,28 @@ public: virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls); + /** + * @brief Get diagram model reference + * + * @return Reference to diagram model created by the visitor + */ clanguml::class_diagram::model::diagram &diagram() { return diagram_; } + /** + * @brief Get diagram config instance + * + * @return Reference to config instance + */ const clanguml::config::class_diagram &config() const { return config_; } + /** + * @brief Finalize diagram model + * + * This method is called after the entire AST has been visited by this + * visitor. It is used to perform necessary post processing on the diagram + * (e.g. resolve translation unit local element ID's into global ID's based + * on elements full names). + */ void finalize(); private: @@ -124,7 +148,7 @@ private: const clang::FriendDecl &f, clanguml::class_diagram::model::class_ &c); bool find_relationships(const clang::QualType &type, - found_relationships_t &, + found_relationships_t & /*relationships*/, clanguml::common::model::relationship_t relationship_hint); void add_relationships(clanguml::class_diagram::model::class_ &c, @@ -203,7 +227,7 @@ private: void resolve_local_to_global_ids(); bool simplify_system_template( - model::template_parameter &ct, const std::string &full_name); + model::template_parameter &ct, const std::string &full_name) const; /// Store the mapping from local clang entity id (obtained using /// getID()) method to clang-uml global id @@ -231,4 +255,4 @@ private: common::model::access_t>> anonymous_struct_relationships_; }; -} +} // namespace clanguml::class_diagram::visitor diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index c8f93d50..d47e05cc 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -1,7 +1,7 @@ /** * src/common/visitor/clang_utils.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,19 +64,21 @@ model::namespace_ get_tag_namespace(const clang::TagDecl &declaration) { model::namespace_ ns; - auto *parent{declaration.getParent()}; + const auto *parent{declaration.getParent()}; // First walk up to the nearest namespace, e.g. from nested class or enum - while (parent && !parent->isNamespace()) { + while ((parent != nullptr) && !parent->isNamespace()) { parent = parent->getParent(); } // Now build up the namespace std::deque namespace_tokens; - while (parent && parent->isNamespace()) { - const auto *ns_decl = static_cast(parent); - if (!ns_decl->isInline() && !ns_decl->isAnonymousNamespace()) - namespace_tokens.push_front(ns_decl->getNameAsString()); + while ((parent != nullptr) && parent->isNamespace()) { + if (const auto *ns_decl = clang::dyn_cast(parent); + ns_decl != nullptr) { + if (!ns_decl->isInline() && !ns_decl->isAnonymousNamespace()) + namespace_tokens.push_front(ns_decl->getNameAsString()); + } parent = parent->getParent(); } @@ -96,19 +98,21 @@ std::string get_tag_name(const clang::TagDecl &declaration) fmt::format("(anonymous_{})", std::to_string(declaration.getID())); } - if (declaration.getParent() && declaration.getParent()->isRecord()) { + if ((declaration.getParent() != nullptr) && + declaration.getParent()->isRecord()) { // If the record is nested within another record (e.g. class or struct) // we have to maintain a containment namespace in order to ensure // unique names within the diagram std::deque record_parent_names; record_parent_names.push_front(base_name); - auto *cls_parent{declaration.getParent()}; + const auto *cls_parent{declaration.getParent()}; while (cls_parent->isRecord()) { - auto parent_name = - static_cast(cls_parent) - ->getNameAsString(); - record_parent_names.push_front(parent_name); + if (const auto *record_decl = + clang::dyn_cast(cls_parent); + record_decl != nullptr) { + record_parent_names.push_front(record_decl->getNameAsString()); + } cls_parent = cls_parent->getParent(); } return fmt::format("{}", fmt::join(record_parent_names, "##")); @@ -170,7 +174,7 @@ std::string to_string(const clang::Expr *expr) clang::LangOptions lang_options; std::string result; llvm::raw_string_ostream ostream(result); - expr->printPretty(ostream, NULL, clang::PrintingPolicy(lang_options)); + expr->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options)); return result; } @@ -180,7 +184,7 @@ std::string to_string(const clang::Stmt *stmt) clang::LangOptions lang_options; std::string result; llvm::raw_string_ostream ostream(result); - stmt->printPretty(ostream, NULL, clang::PrintingPolicy(lang_options)); + stmt->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options)); return result; } @@ -190,7 +194,8 @@ std::string to_string(const clang::FunctionTemplateDecl *decl) std::vector template_parameters; // Handle template function for (const auto *parameter : *decl->getTemplateParameters()) { - if (clang::dyn_cast_or_null(parameter)) { + if (clang::dyn_cast_or_null(parameter) != + nullptr) { const auto *template_type_parameter = clang::dyn_cast_or_null(parameter); @@ -238,17 +243,13 @@ bool is_subexpr_of(const clang::Stmt *parent_stmt, const clang::Stmt *sub_stmt) if (parent_stmt == sub_stmt) return true; - for (const auto *e : parent_stmt->children()) { - if (is_subexpr_of(e, sub_stmt)) - return true; - } - - return false; + return std::any_of(parent_stmt->child_begin(), parent_stmt->child_end(), + [sub_stmt](const auto *e) { return is_subexpr_of(e, sub_stmt); }); } template <> id_t to_id(const std::string &full_name) { - return std::hash{}(full_name) >> 3; + return static_cast(std::hash{}(full_name) >> 3U); } template <> id_t to_id(const clang::NamespaceDecl &declaration) @@ -286,16 +287,17 @@ template <> id_t to_id(const std::filesystem::path &file) template <> id_t to_id(const clang::TemplateArgument &template_argument) { if (template_argument.getKind() == clang::TemplateArgument::Type) { - if (template_argument.getAsType()->getAs()) - return to_id(*template_argument.getAsType() - ->getAs() - ->getAsTagDecl()); - else if (template_argument.getAsType()->getAs()) - return to_id(*template_argument.getAsType() - ->getAs() - ->getAsRecordDecl()); + if (const auto *enum_type = + template_argument.getAsType()->getAs(); + enum_type != nullptr) + return to_id(*enum_type->getAsTagDecl()); + + if (const auto *record_type = + template_argument.getAsType()->getAs(); + record_type != nullptr) + return to_id(*record_type->getAsRecordDecl()); } throw std::runtime_error("Cannot generate id for template argument"); } -} +} // namespace clanguml::common diff --git a/src/common/clang_utils.h b/src/common/clang_utils.h index 5007bf9a..d8d75669 100644 --- a/src/common/clang_utils.h +++ b/src/common/clang_utils.h @@ -1,7 +1,7 @@ /** * src/common/clang_utils.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,9 +31,22 @@ class NamespaceDecl; } namespace clanguml::common { +/** + * @brief Convert @link clang::AccessSpecifier to @link model::access_t + * + * @param access_specifier Clang member access specifier + * @return Enum value of @link model::access_t + */ model::access_t access_specifier_to_access_t( clang::AccessSpecifier access_specifier); +/** + * @brief Generate full qualified name for @link clang::TagDecl instance + * + * @param declaration Input declaration + * @return String representation including any templates, parameters and + * attribtues + */ std::string get_tag_name(const clang::TagDecl &declaration); template std::string get_qualified_name(const T &declaration) @@ -77,8 +90,30 @@ std::string get_source_text_raw( std::string get_source_text( clang::SourceRange range, const clang::SourceManager &sm); +/** + * @brief Check if an expression is contained in another expression + * + * This method returns true if `sub_stmt` is equal to or is contained in the + * AST subtree of `parent_stmt` + * + * @param parent_stmt Parent statement + * @param sub_stmt Sub statement + * @return + */ bool is_subexpr_of(const clang::Stmt *parent_stmt, const clang::Stmt *sub_stmt); +/** + * @brief Forward template for convertions to ID from various entities + * + * These methods provide the main mechanism for generating globally unique + * identifiers for all elements in the diagrams. The identifiers must be unique + * between different translation units in order for element relationships to + * be properly rendered in diagrams. + * + * @tparam T Type of entity for which ID should be computed + * @param declaration Element (e.g. declaration) for which the ID is needed + * @return Unique ID + */ template id_t to_id(const T &declaration); template <> id_t to_id(const std::string &full_name); @@ -96,4 +131,4 @@ template <> id_t to_id(const clang::EnumType &type); template <> id_t to_id(const clang::TemplateSpecializationType &type); template <> id_t to_id(const std::filesystem::path &type); -} +} // namespace clanguml::common diff --git a/src/common/generators/plantuml/generator.cc b/src/common/generators/plantuml/generator.cc index b10fd4c4..e83e8de9 100644 --- a/src/common/generators/plantuml/generator.cc +++ b/src/common/generators/plantuml/generator.cc @@ -1,7 +1,7 @@ /** * src/common/generators/plantuml/generator.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 @@ namespace clanguml::common::generators::plantuml { -std::string to_plantuml(relationship_t r, std::string style) +std::string to_plantuml(relationship_t r, const std::string &style) { switch (r) { case relationship_t::kOwnership: @@ -70,4 +70,4 @@ std::string to_plantuml(message_t r) } } -} +} // namespace clanguml::common::generators::plantuml diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index 7abe0ee4..f5b9ea76 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -1,7 +1,7 @@ /** * src/common/generators/plantuml/generator.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,12 +37,24 @@ using clanguml::common::model::element; using clanguml::common::model::message_t; using clanguml::common::model::relationship_t; -std::string to_plantuml(relationship_t r, std::string style); +std::string to_plantuml(relationship_t r, const std::string &style); std::string to_plantuml(access_t scope); std::string to_plantuml(message_t r); +/** + * @brief Base class for diagram generators + * + * @tparam ConfigType Configuration type + * @tparam DiagramType Diagram model type + */ template class generator { public: + /** + * @brief Constructor + * + * @param config Reference to instance of @link clanguml::config::diagram + * @param model Reference to instance of @link clanguml::model::diagram + */ generator(ConfigType &config, DiagramType &model) : m_config{config} , m_model{model} @@ -53,22 +65,80 @@ public: virtual ~generator() = default; + /** + * @brief Generate diagram + * + * This method must be implemented in subclasses for specific diagram + * types. It is responsible for calling other methods in appropriate + * order to generate the diagram into the output stream. + * + * @param ostr Output stream + */ virtual void generate(std::ostream &ostr) const = 0; - template - friend std::ostream &operator<<(std::ostream &os, const generator &g); - + /** + * @brief Generate diagram layout hints + * + * This method adds to the diagram any layout hints that were provided + * in the configuration file. + * + * @param ostr Output stream + */ void generate_config_layout_hints(std::ostream &ostr) const; + /** + * @brief Generate PlantUML directives from config file. + * + * This method renders the PlantUML directives provided in the configuration + * file, including resolving any element aliases and Jinja templates. + * + * @param ostr Output stream + * @param directives List of directives from the configuration file + */ void generate_plantuml_directives( std::ostream &ostr, const std::vector &directives) const; + /** + * @brief Generate diagram notes + * + * This method adds any notes in the diagram, which were declared in the + * code using inline directives + * + * @param ostr Output stream + * @param element Element to which the note should be attached + */ void generate_notes( std::ostream &ostr, const model::element &element) const; + /** + * @brief Generate hyper link to element + * + * This method renders links to URL's based on templates provided + * in the configuration file (e.g. Git browser with specific line and + * column offset) + * + * @param ostr Output stream + * @param e Reference to diagram element + * @tparam E Diagram element type + */ template void generate_link(std::ostream &ostr, const E &e) const; + /** + * @brief Print debug information in diagram comments + * + * @param m Diagram element to describe + * @param ostr Output stream + */ + void print_debug( + const common::model::source_location &e, std::ostream &ostr) const; + /** + * @brief Update diagram Jinja context + * + * This method updates the diagram context with models properties + * which can be used to render Jinja templates in the diagram (e.g. + * in notes or links) + */ void update_context() const; protected: @@ -208,6 +278,11 @@ void generator::generate_plantuml_directives( ostr << directive << '\n'; } + catch (const clanguml::error::uml_alias_missing &e) { + LOG_ERROR("Failed to render PlantUML directive due to unresolvable " + "alias: {}", + e.what()); + } catch (const inja::json::parse_error &e) { LOG_ERROR("Failed to parse Jinja template: {}", d); } @@ -222,7 +297,7 @@ template void generator::generate_notes( std::ostream &ostr, const model::element &e) const { - for (auto decorator : e.decorators()) { + for (const auto &decorator : e.decorators()) { auto note = std::dynamic_pointer_cast(decorator); if (note && note->applies_to_diagram(m_config.name)) { ostr << "note " << note->position << " of " << e.alias() << '\n' @@ -278,6 +353,14 @@ void generator::generate_link(std::ostream &ostr, const E &e) const ostr << "]]"; } +template +void generator::print_debug( + const common::model::source_location &e, std::ostream &ostr) const +{ + if (m_config.debug_mode()) + ostr << "' " << e.file() << ":" << e.line() << '\n'; +} + template class diagram_ast_consumer : public clang::ASTConsumer { @@ -290,7 +373,7 @@ public: { } - virtual void HandleTranslationUnit(clang::ASTContext &ast_context) + void HandleTranslationUnit(clang::ASTContext &ast_context) override { visitor_.TraverseDecl(ast_context.getTranslationUnitDecl()); visitor_.finalize(); @@ -309,7 +392,7 @@ public: } std::unique_ptr CreateASTConsumer( - clang::CompilerInstance &CI, clang::StringRef file) override + clang::CompilerInstance &CI, clang::StringRef /*file*/) override { return std::make_unique< diagram_ast_consumer>( @@ -368,7 +451,7 @@ template generate( const clang::tooling::CompilationDatabase &db, const std::string &name, DiagramConfig &config, const std::vector &translation_units, - bool verbose = false) + bool /*verbose*/ = false) { LOG_INFO("Generating diagram {}.puml", name); @@ -482,6 +565,10 @@ template void generator::init_env() auto element_opt = m_model.get_with_namespace( args[0]->get(), m_config.using_namespace()); + if (!element_opt.has_value()) + throw clanguml::error::uml_alias_missing( + args[0]->get()); + return element_opt.value().alias(); }); @@ -492,19 +579,21 @@ template void generator::init_env() // m_env.add_callback("comment", 1, [this](inja::Arguments &args) { inja::json res{}; - auto element = m_model.get_with_namespace( + auto element_opt = m_model.get_with_namespace( args[0]->get(), m_config.using_namespace()); - if (element.has_value()) { - auto comment = element.value().comment(); + if (!element_opt.has_value()) + throw clanguml::error::uml_alias_missing( + args[0]->get()); - if (comment.has_value()) { - assert(comment.value().is_object()); - res = comment.value(); - } + auto comment = element_opt.value().comment(); + + if (comment.has_value()) { + assert(comment.value().is_object()); + res = comment.value(); } return res; }); } -} \ No newline at end of file +} // namespace clanguml::common::generators::plantuml \ No newline at end of file diff --git a/src/common/model/decorated_element.cc b/src/common/model/decorated_element.cc index 54422fba..8febf793 100644 --- a/src/common/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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,26 +22,25 @@ namespace clanguml::common::model { bool decorated_element::skip() const { - for (auto d : decorators_) - if (std::dynamic_pointer_cast(d)) - return true; - - return false; + return std::any_of( + decorators_.begin(), decorators_.end(), [](const auto &d) { + return std::dynamic_pointer_cast(d) != nullptr; + }); } bool decorated_element::skip_relationship() const { - for (auto d : decorators_) - if (std::dynamic_pointer_cast(d)) - return true; - - return false; + return std::any_of( + decorators_.begin(), decorators_.end(), [](const auto &d) { + return std::dynamic_pointer_cast( + d) != nullptr; + }); } std::pair decorated_element::get_relationship() const { - for (auto &d : decorators_) + for (const auto &d : decorators_) if (std::dynamic_pointer_cast(d)) return {relationship_t::kAssociation, std::dynamic_pointer_cast(d) @@ -60,7 +59,7 @@ decorated_element::get_relationship() const std::string decorated_element::style_spec() const { - for (auto d : decorators_) + for (const auto &d : decorators_) if (std::dynamic_pointer_cast(d)) return std::dynamic_pointer_cast(d)->spec; @@ -76,14 +75,14 @@ decorated_element::decorators() const void decorated_element::add_decorators( const std::vector> &decorators) { - for (auto d : decorators) { + for (const auto &d : decorators) { decorators_.push_back(d); } } void decorated_element::append(const decorated_element &de) { - for (auto d : de.decorators()) { + for (const auto &d : de.decorators()) { decorators_.push_back(d); } } @@ -91,4 +90,4 @@ void decorated_element::append(const decorated_element &de) std::optional decorated_element::comment() const { return comment_; } void decorated_element::set_comment(const comment_t &c) { comment_ = c; } -} +} // namespace clanguml::common::model diff --git a/src/common/model/decorated_element.h b/src/common/model/decorated_element.h index 828aef1c..bc723e71 100644 --- a/src/common/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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,4 +61,4 @@ private: std::optional comment_; }; -} +} // namespace clanguml::common::model diff --git a/src/common/model/diagram.cc b/src/common/model/diagram.cc index 788cc444..771fac61 100644 --- a/src/common/model/diagram.cc +++ b/src/common/model/diagram.cc @@ -1,7 +1,7 @@ /** * src/common/model/diagram.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,9 +27,9 @@ diagram::diagram() = default; diagram::~diagram() = default; -diagram::diagram(diagram &&) = default; +diagram::diagram(diagram &&) noexcept = default; -diagram &diagram::operator=(diagram &&) = default; +diagram &diagram::operator=(diagram &&) noexcept = default; common::optional_ref diagram::get_with_namespace(const std::string &name, const namespace_ &ns) const @@ -108,4 +108,4 @@ bool diagram::should_include(const common::model::source_file &f) const return filter_->should_include(f); } -} \ No newline at end of file +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/diagram.h b/src/common/model/diagram.h index 1c2cbe80..e669fd10 100644 --- a/src/common/model/diagram.h +++ b/src/common/model/diagram.h @@ -1,7 +1,7 @@ /** * src/common/model/diagram.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ public: const std::string &full_name) const = 0; virtual common::optional_ref get( - const diagram_element::id_t id) const = 0; + diagram_element::id_t id) const = 0; /// \brief Find element in diagram which can have full name or be /// relative to ns @@ -51,9 +51,9 @@ public: get_with_namespace(const std::string &name, const namespace_ &ns) const; diagram(const diagram &) = delete; - diagram(diagram &&); + diagram(diagram && /*unused*/) noexcept; diagram &operator=(const diagram &) = delete; - diagram &operator=(diagram &&); + diagram &operator=(diagram && /*unused*/) noexcept; void set_name(const std::string &name); std::string name() const; @@ -67,11 +67,11 @@ public: bool should_include(const element &e) const; bool should_include(const std::string &e) const; bool should_include(const source_file &path) const; - bool should_include(const relationship r) const; - bool should_include(const relationship_t r) const; - bool should_include(const access_t s) const; + bool should_include(relationship r) const; + bool should_include(relationship_t r) const; + bool should_include(access_t s) const; - virtual bool has_element(const diagram_element::id_t id) const + virtual bool has_element(const diagram_element::id_t /*id*/) const { return false; } @@ -88,4 +88,4 @@ private: }; template bool check_diagram_type(diagram_t t); -} +} // namespace clanguml::common::model diff --git a/src/common/model/diagram_element.cc b/src/common/model/diagram_element.cc index 2f160517..c4d01756 100644 --- a/src/common/model/diagram_element.cc +++ b/src/common/model/diagram_element.cc @@ -1,7 +1,7 @@ /** * src/common/model/diagram_element.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,14 +24,7 @@ namespace clanguml::common::model { -std::atomic_uint64_t diagram_element::m_nextId = 1; - -diagram_element::diagram_element() - : id_{0} - , nested_{false} - , complete_{false} -{ -} +diagram_element::diagram_element() = default; diagram_element::id_t diagram_element::id() const { return id_; } @@ -105,4 +98,4 @@ std::ostream &operator<<(std::ostream &out, const diagram_element &rhs) return out; } -} +} // namespace clanguml::common::model diff --git a/src/common/model/diagram_element.h b/src/common/model/diagram_element.h index e303edbc..af3a4a58 100644 --- a/src/common/model/diagram_element.h +++ b/src/common/model/diagram_element.h @@ -1,7 +1,7 @@ /** * src/common/model/diagram_element.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,12 +77,10 @@ public: void complete(bool completed); private: - id_t id_; + id_t id_{0}; std::string name_; std::vector relationships_; - bool nested_; - bool complete_; - - static std::atomic_uint64_t m_nextId; + bool nested_{false}; + bool complete_{false}; }; -} +} // namespace clanguml::common::model diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 592dd173..d11951f9 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -1,7 +1,7 @@ /** * src/common/model/diagram_filter.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,6 +18,8 @@ #include "diagram_filter.h" +#include + #include "class_diagram/model/class.h" #include "common/model/package.h" #include "include_diagram/model/diagram.h" @@ -155,7 +157,7 @@ tvl::value_t anyof_filter::match( namespace_filter::namespace_filter( filter_t type, std::vector namespaces) : filter_visitor{type} - , namespaces_{namespaces} + , namespaces_{std::move(namespaces)} { } @@ -192,30 +194,29 @@ tvl::value_t namespace_filter::match( return result; }); } - else { - return tvl::any_of( - namespaces_.begin(), namespaces_.end(), [&e](const auto &nsit) { - return e.get_namespace().starts_with(nsit); - }); - } + return tvl::any_of(namespaces_.begin(), namespaces_.end(), + [&e](const auto &nsit) { return e.get_namespace().starts_with(nsit); }); } element_filter::element_filter(filter_t type, std::vector elements) : filter_visitor{type} - , elements_{elements} + , elements_{std::move(elements)} { } tvl::value_t element_filter::match( const diagram & /*d*/, const element &e) const { - return tvl::any_of(elements_.begin(), elements_.end(), - [&e](const auto &el) { return e.full_name(false) == el; }); + return tvl::any_of( + elements_.begin(), elements_.end(), [&e](const auto &el) { + return (e.full_name(false) == el) || + (fmt::format("::{}", e.full_name(false)) == el); + }); } subclass_filter::subclass_filter(filter_t type, std::vector roots) : filter_visitor{type} - , roots_{roots} + , roots_{std::move(roots)} { } @@ -265,7 +266,7 @@ tvl::value_t subclass_filter::match(const diagram &d, const element &e) const relationship_filter::relationship_filter( filter_t type, std::vector relationships) : filter_visitor{type} - , relationships_{relationships} + , relationships_{std::move(relationships)} { } @@ -278,7 +279,7 @@ tvl::value_t relationship_filter::match( access_filter::access_filter(filter_t type, std::vector access) : filter_visitor{type} - , access_{access} + , access_{std::move(access)} { } @@ -291,7 +292,7 @@ tvl::value_t access_filter::match( context_filter::context_filter(filter_t type, std::vector context) : filter_visitor{type} - , context_{context} + , context_{std::move(context)} { } @@ -349,17 +350,29 @@ tvl::value_t context_filter::match(const diagram &d, const element &e) const } paths_filter::paths_filter(filter_t type, const std::filesystem::path &root, - std::vector p) + const std::vector &p) : filter_visitor{type} , root_{root} { - for (auto &&path : p) { + for (const auto &path : p) { std::filesystem::path absolute_path; - if (path.is_relative()) + if (path.string().empty() || path.string() == ".") + absolute_path = root; + else if (path.is_relative()) absolute_path = root / path; + else + absolute_path = path; - absolute_path = absolute_path.lexically_normal(); + try { + absolute_path = + std::filesystem::canonical(absolute_path.lexically_normal()); + } + catch (std::filesystem::filesystem_error &e) { + LOG_WARN("Cannot add non-existent path {} to paths filter", + path.string()); + continue; + } paths_.emplace_back(std::move(absolute_path)); } @@ -403,7 +416,7 @@ void diagram_filter::add_exclusive_filter(std::unique_ptr fv) } bool diagram_filter::should_include( - namespace_ ns, const std::string &name) const + const namespace_ &ns, const std::string &name) const { if (should_include(ns)) { element e{namespace_{}}; @@ -430,7 +443,7 @@ void diagram_filter::init_filters(const config::diagram &c) filter_t::kInclusive, c.include().access)); add_inclusive_filter(std::make_unique( - filter_t::kInclusive, c.base_directory(), c.include().paths)); + filter_t::kInclusive, c.relative_to(), c.include().paths)); // Include any of these matches even if one them does not match std::vector> element_filters; @@ -474,21 +487,11 @@ void diagram_filter::init_filters(const config::diagram &c) for (auto &&path : c.include().dependants) { std::filesystem::path dep_path{path}; - if (dep_path.is_relative()) { - dep_path = c.base_directory() / path; - dep_path = relative(dep_path, c.relative_to()); - } - dependants.emplace_back(dep_path.lexically_normal().string()); } for (auto &&path : c.include().dependencies) { std::filesystem::path dep_path{path}; - if (dep_path.is_relative()) { - dep_path = c.base_directory() / path; - dep_path = relative(dep_path, c.relative_to()); - } - dependencies.emplace_back(dep_path.lexically_normal().string()); } @@ -518,7 +521,7 @@ void diagram_filter::init_filters(const config::diagram &c) filter_t::kExclusive, c.exclude().namespaces)); add_exclusive_filter(std::make_unique( - filter_t::kExclusive, c.base_directory(), c.exclude().paths)); + filter_t::kExclusive, c.relative_to(), c.exclude().paths)); add_exclusive_filter(std::make_unique( filter_t::kExclusive, c.exclude().elements)); @@ -607,4 +610,4 @@ bool diagram_filter::should_include(const std::string &name) const return should_include(ns, n); } -} +} // namespace clanguml::common::model diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index 8330519a..7d5ceda5 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -1,7 +1,7 @@ /** * src/common/model/diagram_filter.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ #include "tvl.h" #include +#include namespace clanguml::common::model { @@ -87,7 +88,7 @@ struct anyof_filter : public filter_visitor { anyof_filter( filter_t type, std::vector> filters); - virtual ~anyof_filter() = default; + ~anyof_filter() override = default; tvl::value_t match( const diagram &d, const common::model::element &e) const override; @@ -102,7 +103,7 @@ private: struct namespace_filter : public filter_visitor { namespace_filter(filter_t type, std::vector namespaces); - virtual ~namespace_filter() = default; + ~namespace_filter() override = default; tvl::value_t match(const diagram &d, const namespace_ &ns) const override; @@ -115,7 +116,7 @@ private: struct element_filter : public filter_visitor { element_filter(filter_t type, std::vector elements); - virtual ~element_filter() = default; + ~element_filter() override = default; tvl::value_t match(const diagram &d, const element &e) const override; @@ -126,7 +127,7 @@ private: struct subclass_filter : public filter_visitor { subclass_filter(filter_t type, std::vector roots); - virtual ~subclass_filter() = default; + ~subclass_filter() override = default; tvl::value_t match(const diagram &d, const element &e) const override; @@ -140,13 +141,13 @@ struct edge_traversal_filter : public filter_visitor { edge_traversal_filter(filter_t type, relationship_t relationship, std::vector roots, bool forward = false) : filter_visitor{type} - , roots_{roots} + , roots_{std::move(roots)} , relationship_{relationship} , forward_{forward} { } - virtual ~edge_traversal_filter() = default; + ~edge_traversal_filter() override = default; tvl::value_t match(const diagram &d, const MatchOverrideT &e) const override { @@ -280,7 +281,7 @@ struct relationship_filter : public filter_visitor { relationship_filter( filter_t type, std::vector relationships); - virtual ~relationship_filter() = default; + ~relationship_filter() override = default; tvl::value_t match( const diagram &d, const relationship_t &r) const override; @@ -292,7 +293,7 @@ private: struct access_filter : public filter_visitor { access_filter(filter_t type, std::vector access); - virtual ~access_filter() = default; + ~access_filter() override = default; tvl::value_t match(const diagram &d, const access_t &a) const override; @@ -303,7 +304,7 @@ private: struct context_filter : public filter_visitor { context_filter(filter_t type, std::vector context); - virtual ~context_filter() = default; + ~context_filter() override = default; tvl::value_t match(const diagram &d, const element &r) const override; @@ -313,9 +314,9 @@ private: struct paths_filter : public filter_visitor { paths_filter(filter_t type, const std::filesystem::path &root, - std::vector p); + const std::vector &p); - virtual ~paths_filter() = default; + ~paths_filter() override = default; tvl::value_t match( const diagram &d, const common::model::source_file &r) const override; @@ -333,7 +334,7 @@ public: void add_exclusive_filter(std::unique_ptr fv); - bool should_include(namespace_ ns, const std::string &name) const; + bool should_include(const namespace_ &ns, const std::string &name) const; template bool should_include(const T &e) const { @@ -346,10 +347,7 @@ public: auto inc = tvl::all_of(inclusive_.begin(), inclusive_.end(), [this, &e](const auto &in) { return in->match(diagram_, e); }); - if (tvl::is_undefined(inc) || tvl::is_true(inc)) - return true; - - return false; + return static_cast(tvl::is_undefined(inc) || tvl::is_true(inc)); } private: @@ -363,4 +361,4 @@ private: template <> bool diagram_filter::should_include(const std::string &name) const; -} \ No newline at end of file +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/element.cc b/src/common/model/element.cc index a0581956..9ff0b19e 100644 --- a/src/common/model/element.cc +++ b/src/common/model/element.cc @@ -1,7 +1,7 @@ /** * src/common/model/element.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,11 +21,12 @@ #include "util/util.h" #include +#include namespace clanguml::common::model { -element::element(const namespace_ &using_namespace) - : using_namespace_{using_namespace} +element::element(namespace_ using_namespace) + : using_namespace_{std::move(using_namespace)} { } @@ -64,4 +65,4 @@ std::ostream &operator<<(std::ostream &out, const element &rhs) return out; } -} +} // namespace clanguml::common::model diff --git a/src/common/model/element.h b/src/common/model/element.h index b7a2b525..dad91ec1 100644 --- a/src/common/model/element.h +++ b/src/common/model/element.h @@ -1,7 +1,7 @@ /** * src/common/model/element.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,9 +34,9 @@ namespace clanguml::common::model { class element : public diagram_element { public: - element(const namespace_ &using_namespace); + element(namespace_ using_namespace); - virtual ~element() = default; + ~element() override = default; std::string name_and_ns() const { @@ -76,4 +76,4 @@ private: namespace_ ns_; namespace_ using_namespace_; }; -} +} // namespace clanguml::common::model diff --git a/src/common/model/enums.cc b/src/common/model/enums.cc index b984f26a..a0b66ffc 100644 --- a/src/common/model/enums.cc +++ b/src/common/model/enums.cc @@ -1,7 +1,7 @@ /** * src/common/model/enums.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -133,4 +133,4 @@ std::string to_string(const diagram_t t) } } -} +} // namespace clanguml::common::model diff --git a/src/common/model/enums.h b/src/common/model/enums.h index e73d92e6..e8c0779b 100644 --- a/src/common/model/enums.h +++ b/src/common/model/enums.h @@ -1,7 +1,7 @@ /** * src/common/model/enums.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,4 +79,4 @@ std::string to_string(message_t m); std::string to_string(diagram_t r); -} +} // namespace clanguml::common::model diff --git a/src/common/model/namespace.cc b/src/common/model/namespace.cc index 3039adb3..4dffb42e 100644 --- a/src/common/model/namespace.cc +++ b/src/common/model/namespace.cc @@ -1,7 +1,7 @@ /** * src/common/model/namespace.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/common/model/namespace.h b/src/common/model/namespace.h index 1a255b8c..0f2cf8ca 100644 --- a/src/common/model/namespace.h +++ b/src/common/model/namespace.h @@ -1,7 +1,7 @@ /** * src/common/model/namespace.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,12 +42,12 @@ template <> struct hash { std::size_t seed = key.size(); for (const auto &ns : key) { - seed ^= std::hash{}(ns) + 0x6a3712b5 + (seed << 6) + - (seed >> 2); + seed ^= + std::hash{}(ns) + clanguml::util::hash_seed(seed); } return seed; } }; -} +} // namespace std diff --git a/src/common/model/nested_trait.h b/src/common/model/nested_trait.h index 60b6f9b1..6433b8d0 100644 --- a/src/common/model/nested_trait.h +++ b/src/common/model/nested_trait.h @@ -1,7 +1,7 @@ /** * src/common/model/nested_trait.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,10 +30,10 @@ public: nested_trait() = default; nested_trait(const nested_trait &) = delete; - nested_trait(nested_trait &&) = default; + nested_trait(nested_trait &&) noexcept = default; nested_trait &operator=(const nested_trait &) = delete; - nested_trait &operator=(nested_trait &&) = default; + nested_trait &operator=(nested_trait &&) noexcept = default; virtual ~nested_trait() = default; @@ -70,11 +70,9 @@ public: if (parent && dynamic_cast *>(&parent.value())) return dynamic_cast &>(parent.value()) .template add_element(std::move(p)); - else { - spdlog::info("No parent element found at: {}", path.to_string()); - throw std::runtime_error( - "No parent element found for " + path.to_string()); - } + spdlog::info("No parent element found at: {}", path.to_string()); + throw std::runtime_error( + "No parent element found for " + path.to_string()); } template auto get_element(const Path &path) const @@ -171,4 +169,4 @@ private: std::vector> elements_; }; -} +} // namespace clanguml::common::model diff --git a/src/common/model/package.cc b/src/common/model/package.cc index 7116efea..4b9b6462 100644 --- a/src/common/model/package.cc +++ b/src/common/model/package.cc @@ -1,7 +1,7 @@ /** * src/common/model/package.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,4 +40,4 @@ bool package::is_deprecated() const { return is_deprecated_; } void package::set_deprecated(bool deprecated) { is_deprecated_ = deprecated; } -} \ No newline at end of file +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/package.h b/src/common/model/package.h index c688f94d..a4d13837 100644 --- a/src/common/model/package.h +++ b/src/common/model/package.h @@ -1,7 +1,7 @@ /** * src/common/model/package.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ public: private: bool is_deprecated_{false}; }; -} +} // namespace clanguml::common::model namespace std { template <> @@ -69,4 +69,4 @@ struct hash> { return std::hash{}(key.get().id()); } }; -} \ No newline at end of file +} // namespace std \ No newline at end of file diff --git a/src/common/model/path.h b/src/common/model/path.h index 18e8f414..9846f268 100644 --- a/src/common/model/path.h +++ b/src/common/model/path.h @@ -1,7 +1,7 @@ /** * src/common/model/path.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ public: path(path &&right) noexcept = default; - path &operator=(path &&right) = default; + path &operator=(path &&right) noexcept = default; path(std::initializer_list ns) { @@ -216,4 +216,4 @@ private: container_type path_; }; -} \ No newline at end of file +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/relationship.cc b/src/common/model/relationship.cc index f0d8ec86..624472cc 100644 --- a/src/common/model/relationship.cc +++ b/src/common/model/relationship.cc @@ -1,7 +1,7 @@ /** * src/common/model/relationship.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,17 +18,18 @@ #include "relationship.h" +#include + namespace clanguml::common::model { relationship::relationship(relationship_t type, int64_t destination, - access_t access, const std::string &label, - const std::string &multiplicity_source, - const std::string &multiplicity_destination) + access_t access, std::string label, std::string multiplicity_source, + std::string multiplicity_destination) : type_{type} , destination_{destination} - , multiplicity_source_{multiplicity_source} - , multiplicity_destination_{multiplicity_destination} - , label_{label} + , multiplicity_source_{std::move(multiplicity_source)} + , multiplicity_destination_{std::move(multiplicity_destination)} + , label_{std::move(label)} , access_{access} { } @@ -82,4 +83,4 @@ bool operator==(const relationship &l, const relationship &r) return l.type() == r.type() && l.destination() == r.destination() && l.label() == r.label(); } -} +} // namespace clanguml::common::model diff --git a/src/common/model/relationship.h b/src/common/model/relationship.h index d85fbc8a..3077d455 100644 --- a/src/common/model/relationship.h +++ b/src/common/model/relationship.h @@ -1,7 +1,7 @@ /** * src/common/model/relationship.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,9 +29,9 @@ class relationship : public common::model::decorated_element, public common::model::stylable_element { public: relationship(relationship_t type, int64_t destination, - access_t access = access_t::kPublic, const std::string &label = "", - const std::string &multiplicity_source = "", - const std::string &multiplicity_destination = ""); + access_t access = access_t::kPublic, std::string label = "", + std::string multiplicity_source = "", + std::string multiplicity_destination = ""); virtual ~relationship() = default; @@ -64,4 +64,4 @@ private: std::string label_; access_t access_; }; -} +} // namespace clanguml::common::model diff --git a/src/common/model/source_file.cc b/src/common/model/source_file.cc index 0a806652..49269d92 100644 --- a/src/common/model/source_file.cc +++ b/src/common/model/source_file.cc @@ -1,7 +1,7 @@ /** * src/common/model/source_file.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/common/model/source_file.h b/src/common/model/source_file.h index 5117be49..7ce58e3b 100644 --- a/src/common/model/source_file.h +++ b/src/common/model/source_file.h @@ -1,7 +1,7 @@ /** * src/common/model/source_file.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -92,7 +92,8 @@ public: { LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true)); - add_element(f->path(), std::move(f)); + const auto path = f->path(); + add_element(path, std::move(f)); } std::filesystem::path fs_path(const std::filesystem::path &base = {}) const @@ -118,7 +119,7 @@ private: source_file_t type_{source_file_t::kDirectory}; bool is_absolute_{false}; }; -} +} // namespace clanguml::common::model namespace std { @@ -130,15 +131,15 @@ template <> struct hash { std::size_t seed = key.size(); for (const auto &ns : key) { - seed ^= std::hash{}(ns) + 0x6a3712b5 + (seed << 6) + - (seed >> 2); + seed ^= + std::hash{}(ns) + clanguml::util::hash_seed(seed); } return seed; } }; -} +} // namespace std namespace std { template <> @@ -152,4 +153,4 @@ struct hash> { return std::hash{}(key.get().id()); } }; -} \ No newline at end of file +} // namespace std \ No newline at end of file diff --git a/src/common/model/source_location.cc b/src/common/model/source_location.cc index 8b44d0d5..faf71ab1 100644 --- a/src/common/model/source_location.cc +++ b/src/common/model/source_location.cc @@ -1,7 +1,7 @@ /** * src/common/model/source_location.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/common/model/source_location.h b/src/common/model/source_location.h index 38e880ef..ee96c4f3 100644 --- a/src/common/model/source_location.h +++ b/src/common/model/source_location.h @@ -1,7 +1,7 @@ /** * src/common/model/source_location.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,6 +18,7 @@ #pragma once #include +#include namespace clanguml::common::model { @@ -25,8 +26,8 @@ class source_location { public: source_location() = default; - source_location(const std::string &f, unsigned int l) - : file_{f} + source_location(std::string f, unsigned int l) + : file_{std::move(f)} , line_{l} { } @@ -46,6 +47,6 @@ public: private: std::string file_; unsigned int line_{0}; - unsigned int hash_; + unsigned int hash_{0}; }; -} \ No newline at end of file +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/stylable_element.cc b/src/common/model/stylable_element.cc index 05ca4625..0fb88d3d 100644 --- a/src/common/model/stylable_element.cc +++ b/src/common/model/stylable_element.cc @@ -1,7 +1,7 @@ /** * src/common/model/stylable_element.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/common/model/stylable_element.h b/src/common/model/stylable_element.h index ee025096..7d1a632a 100644 --- a/src/common/model/stylable_element.h +++ b/src/common/model/stylable_element.h @@ -1,7 +1,7 @@ /** * src/common/model/stylable_element.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,4 +30,4 @@ private: std::string style_; }; -} +} // namespace clanguml::common::model diff --git a/src/common/model/tvl.h b/src/common/model/tvl.h index 7889c789..eb4d0ebf 100644 --- a/src/common/model/tvl.h +++ b/src/common/model/tvl.h @@ -1,7 +1,7 @@ /** * src/class_diagram/model/tvl.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ inline value_t all_of(InputIterator first, InputIterator last, Predicate pred) for (InputIterator it = first; it != last; it++) { value_t m = pred(*it); if (m.has_value()) { - if (m.value() == true) { + if (m.value()) { res = true; } else { @@ -59,16 +59,14 @@ inline value_t any_of(InputIterator first, InputIterator last, Predicate pred) for (InputIterator it = first; it != last; it++) { value_t m = pred(*it); if (m.has_value()) { - if (m.value() == true) { + if (m.value()) { res = true; break; } - else { - res = false; - } + res = false; } } return res; } -}; \ No newline at end of file +} // namespace clanguml::common::model::tvl \ No newline at end of file diff --git a/src/common/types.h b/src/common/types.h index 0cb38440..7c9b18fd 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -1,7 +1,7 @@ /** * src/class_diagram/visitor/translation_unit_visitor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,10 +31,7 @@ template class optional_ref { public: using optional_type = T; - optional_ref() - : value_{nullptr} - { - } + optional_ref() = default; optional_ref(T *value) { value_ = value; } @@ -112,7 +109,7 @@ public: T *get() const { return value_; } private: - T *value_; + T *value_{nullptr}; }; template @@ -121,4 +118,4 @@ using reference_vector = std::vector>; template using reference_set = std::unordered_set>; -} // namespace clang::common \ No newline at end of file +} // namespace clanguml::common \ No newline at end of file diff --git a/src/common/visitor/comment/clang_visitor.cc b/src/common/visitor/comment/clang_visitor.cc index e3d24c1b..1e92e033 100644 --- a/src/common/visitor/comment/clang_visitor.cc +++ b/src/common/visitor/comment/clang_visitor.cc @@ -1,7 +1,7 @@ /** * src/common/visitor/comment/clang_visitor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,28 +87,34 @@ void clang_visitor::visit( clang::dyn_cast(block), traits, cmt); } else if (block_kind == Comment::BlockCommandCommentKind) { - auto *command = clang::dyn_cast(block); - auto command_info = traits.getCommandInfo(command->getCommandID()); + if (const auto *command = + clang::dyn_cast(block); + command != nullptr) { + const auto *command_info = + traits.getCommandInfo(command->getCommandID()); - if (command_info->IsBlockCommand && command_info->NumArgs == 0) { - // Visit block command with a single text argument, e.g.: - // \brief text - // \todo text - // ... - visit_block_command(command, traits, cmt); - } - else if (command_info->IsParamCommand) { - // Visit function param block: - // \param arg text - visit_param_command( - clang::dyn_cast(command), traits, cmt); - } - else if (command_info->IsTParamCommand) { - // Visit template param block: - // \tparam typename text - visit_tparam_command( - clang::dyn_cast(command), traits, - cmt); + if (command_info->IsBlockCommand && + command_info->NumArgs == 0U) { + // Visit block command with a single text argument, e.g.: + // \brief text + // \todo text + // ... + visit_block_command(command, traits, cmt); + } + else if (command_info->IsParamCommand) { + // Visit function param block: + // \param arg text + visit_param_command( + clang::dyn_cast(command), traits, + cmt); + } + else if (command_info->IsTParamCommand) { + // Visit template param block: + // \tparam typename text + visit_tparam_command( + clang::dyn_cast(command), traits, + cmt); + } } } } @@ -125,7 +131,7 @@ void clang_visitor::visit_block_command( std::string command_text; - for (auto paragraph_it = command->child_begin(); + for (const auto *paragraph_it = command->child_begin(); paragraph_it != command->child_end(); ++paragraph_it) { if ((*paragraph_it)->getCommentKind() == @@ -154,9 +160,13 @@ void clang_visitor::visit_param_command( std::string description; + if (command == nullptr) + return; + const auto name = command->getParamNameAsWritten().str(); - for (auto it = command->child_begin(); it != command->child_end(); ++it) { + for (const auto *it = command->child_begin(); it != command->child_end(); + ++it) { if ((*it)->getCommentKind() == Comment::ParagraphCommentKind) { visit_paragraph( @@ -185,9 +195,13 @@ void clang_visitor::visit_tparam_command( std::string description; + if (command == nullptr) + return; + const auto name = command->getParamNameAsWritten().str(); - for (auto it = command->child_begin(); it != command->child_end(); ++it) { + for (const auto *it = command->child_begin(); it != command->child_end(); + ++it) { if ((*it)->getCommentKind() == Comment::ParagraphCommentKind) { visit_paragraph( clang::dyn_cast(*it), traits, description); @@ -207,20 +221,24 @@ void clang_visitor::visit_tparam_command( void clang_visitor::visit_paragraph( const clang::comments::ParagraphComment *paragraph, - const clang::comments::CommandTraits &traits, std::string &text) + const clang::comments::CommandTraits & /*traits*/, std::string &text) { using clang::comments::Comment; using clang::comments::TextComment; - for (auto text_it = paragraph->child_begin(); + if (paragraph == nullptr) + return; + + for (const auto *text_it = paragraph->child_begin(); text_it != paragraph->child_end(); ++text_it) { - if ((*text_it)->getCommentKind() == Comment::TextCommentKind) { + if ((*text_it)->getCommentKind() == Comment::TextCommentKind && + clang::dyn_cast(*text_it) != nullptr) { // Merge paragraph lines into a single string - text += clang::dyn_cast((*text_it))->getText(); + text += clang::dyn_cast(*text_it)->getText(); text += "\n"; } } } -} \ No newline at end of file +} // namespace clanguml::common::visitor::comment \ No newline at end of file diff --git a/src/common/visitor/comment/clang_visitor.h b/src/common/visitor/comment/clang_visitor.h index 4718bf92..1c1ead7a 100644 --- a/src/common/visitor/comment/clang_visitor.h +++ b/src/common/visitor/comment/clang_visitor.h @@ -1,7 +1,7 @@ /** * src/common/visitor/comment/clang_visitor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,4 +51,4 @@ private: void visit_paragraph(const clang::comments::ParagraphComment *paragraph, const clang::comments::CommandTraits &traits, std::string &text); }; -} \ No newline at end of file +} // namespace clanguml::common::visitor::comment \ No newline at end of file diff --git a/src/common/visitor/comment/comment_visitor.cc b/src/common/visitor/comment/comment_visitor.cc index aabe8d75..deba2140 100644 --- a/src/common/visitor/comment/comment_visitor.cc +++ b/src/common/visitor/comment/comment_visitor.cc @@ -1,7 +1,7 @@ /** * src/common/visitor/comment/comment_visitor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,4 +30,4 @@ clang::SourceManager &comment_visitor::source_manager() return source_manager_; } -} \ No newline at end of file +} // namespace clanguml::common::visitor::comment \ No newline at end of file diff --git a/src/common/visitor/comment/comment_visitor.h b/src/common/visitor/comment/comment_visitor.h index 328d4a3a..d3233b84 100644 --- a/src/common/visitor/comment/comment_visitor.h +++ b/src/common/visitor/comment/comment_visitor.h @@ -1,7 +1,7 @@ /** * src/common/visitor/comment/comment_visitor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,4 +38,4 @@ public: private: clang::SourceManager &source_manager_; }; -} \ No newline at end of file +} // namespace clanguml::common::visitor::comment \ No newline at end of file diff --git a/src/common/visitor/comment/plain_visitor.cc b/src/common/visitor/comment/plain_visitor.cc index 4297f80b..10dc45e7 100644 --- a/src/common/visitor/comment/plain_visitor.cc +++ b/src/common/visitor/comment/plain_visitor.cc @@ -1,7 +1,7 @@ /** * src/common/visitor/comment/plain_visitor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,4 +47,4 @@ void plain_visitor::visit( e.set_comment(cmt); } -} \ No newline at end of file +} // namespace clanguml::common::visitor::comment \ No newline at end of file diff --git a/src/common/visitor/comment/plain_visitor.h b/src/common/visitor/comment/plain_visitor.h index 70842616..126ff3a4 100644 --- a/src/common/visitor/comment/plain_visitor.h +++ b/src/common/visitor/comment/plain_visitor.h @@ -1,7 +1,7 @@ /** * src/common/visitor/comment/plain_visitor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/common/visitor/translation_unit_visitor.cc b/src/common/visitor/translation_unit_visitor.cc index 3fc3ac2a..bd3a6071 100644 --- a/src/common/visitor/translation_unit_visitor.cc +++ b/src/common/visitor/translation_unit_visitor.cc @@ -1,7 +1,7 @@ /** * src/common/visitor/translation_unit_visitor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,6 +75,12 @@ void translation_unit_visitor::set_source_location( set_source_location(expr.getBeginLoc(), element); } +void translation_unit_visitor::set_source_location( + const clang::Stmt &stmt, clanguml::common::model::source_location &element) +{ + set_source_location(stmt.getBeginLoc(), element); +} + void translation_unit_visitor::set_source_location( const clang::SourceLocation &location, clanguml::common::model::source_location &element) @@ -86,4 +92,4 @@ void translation_unit_visitor::set_source_location( } } -} \ No newline at end of file +} // namespace clanguml::common::visitor \ No newline at end of file diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index 31a35a63..56f5a105 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -1,7 +1,7 @@ /** * src/common/visitor/translation_unit_visitor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,32 +35,76 @@ using found_relationships_t = std::vector>; +/** + * @brief Diagram translation unit visitor base class + * + * This class provides common interface for diagram translation unit + * visitors. + */ class translation_unit_visitor { public: + /** + * @brief Constructor + * + * @param sm Reference to @link clang::SourceManager instance + * @param config Reference to @link clanguml::config::diagram configuration + * instance + */ explicit translation_unit_visitor( clang::SourceManager &sm, const clanguml::config::diagram &config); + /** + * @brief Get clang::SourceManager + * @return Reference to @link clang::SourceManager used by this translation + * unit visitor + */ clang::SourceManager &source_manager() const; protected: + /** + * @brief Set source location in diagram element + * + * @param decl Reference to @link clang::Decl + * @param element Reference to element to be updated + */ void set_source_location(const clang::Decl &decl, clanguml::common::model::source_location &element); + /** + * @brief Set source location in diagram element + * + * @param expr Reference to @link clang::Expr + * @param element Reference to element to be updated + */ void set_source_location(const clang::Expr &expr, clanguml::common::model::source_location &element); + void set_source_location(const clang::Stmt &stmt, + clanguml::common::model::source_location &element); + + /** + * @brief Set source location in diagram element + * + * @param location Reference to @link clang::SourceLocation + * @param element Reference to element to be updated + */ void set_source_location(const clang::SourceLocation &location, clanguml::common::model::source_location &element); + /** + * @brief Set source location in diagram element + * + * @param decl Reference to @link clang::NamedDecl + * @param element Reference to element to be updated + */ void process_comment(const clang::NamedDecl &decl, clanguml::common::model::decorated_element &e); private: clang::SourceManager &source_manager_; - // Reference to diagram config const clanguml::config::diagram &config_; std::unique_ptr comment_visitor_; }; -} +} // namespace clanguml::common::visitor diff --git a/src/config/config.cc b/src/config/config.cc index 7891b2f5..d6061fb6 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -1,7 +1,7 @@ /** * src/config/config.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,8 +21,7 @@ #include -namespace clanguml { -namespace config { +namespace clanguml::config { config load(const std::string &config_file) { @@ -103,6 +102,7 @@ void inheritable_diagram_options::inherit( comment_parser.override(parent.comment_parser); combine_free_functions_into_file_participants.override( combine_free_functions_into_file_participants); + debug_mode.override(parent.debug_mode); } std::string inheritable_diagram_options::simplify_template_type( @@ -136,31 +136,31 @@ std::vector diagram::get_translation_units( void diagram::initialize_type_aliases() { - if (!type_aliases().count("std::basic_string")) { + if (type_aliases().count("std::basic_string") == 0U) { type_aliases().insert({"std::basic_string", "std::string"}); } - if (!type_aliases().count("std::basic_string,std::allocator>")) { + if (type_aliases().count("std::basic_string,std::allocator>") == 0U) { type_aliases().insert({"std::basic_string,std::allocator>", "std::string"}); } - if (!type_aliases().count("std::basic_string")) { + if (type_aliases().count("std::basic_string") == 0U) { type_aliases().insert({"std::basic_string", "std::wstring"}); } - if (!type_aliases().count("std::basic_string")) { + if (type_aliases().count("std::basic_string") == 0U) { type_aliases().insert( {"std::basic_string", "std::u16string"}); } - if (!type_aliases().count("std::basic_string")) { + if (type_aliases().count("std::basic_string") == 0U) { type_aliases().insert( {"std::basic_string", "std::u32string"}); } - if (!type_aliases().count("std::integral_constant")) { + if (type_aliases().count("std::integral_constant") == 0U) { type_aliases().insert( {"std::integral_constant", "std::true_type"}); } - if (!type_aliases().count("std::integral_constant")) { + if (type_aliases().count("std::integral_constant") == 0U) { type_aliases().insert( {"std::integral_constant", "std::false_type"}); } @@ -190,24 +190,24 @@ void class_diagram::initialize_relationship_hints() { using common::model::relationship_t; - if (!relationship_hints().count("std::vector")) { + if (relationship_hints().count("std::vector") == 0U) { relationship_hints().insert({"std::vector", {}}); } - if (!relationship_hints().count("std::unique_ptr")) { + if (relationship_hints().count("std::unique_ptr") == 0U) { relationship_hints().insert({"std::unique_ptr", {}}); } - if (!relationship_hints().count("std::shared_ptr")) { + if (relationship_hints().count("std::shared_ptr") == 0U) { relationship_hints().insert( {"std::shared_ptr", {relationship_t::kAssociation}}); } - if (!relationship_hints().count("std::weak_ptr")) { + if (relationship_hints().count("std::weak_ptr") == 0U) { relationship_hints().insert( {"std::weak_ptr", {relationship_t::kAssociation}}); } - if (!relationship_hints().count("std::tuple")) { + if (relationship_hints().count("std::tuple") == 0U) { relationship_hints().insert({"std::tuple", {}}); } - if (!relationship_hints().count("std::map")) { + if (relationship_hints().count("std::map") == 0U) { relationship_hint_t hint{relationship_t::kNone}; hint.argument_hints.insert({1, relationship_t::kAggregation}); relationship_hints().insert({"std::tuple", std::move(hint)}); @@ -218,8 +218,7 @@ template <> void append_value(plantuml &l, const plantuml &r) { l.append(r); } -} -} +} // namespace clanguml::config namespace YAML { using clanguml::common::model::access_t; @@ -310,13 +309,13 @@ std::shared_ptr parse_diagram_config(const Node &d) if (diagram_type == "class") { return std::make_shared(d.as()); } - else if (diagram_type == "sequence") { + if (diagram_type == "sequence") { return std::make_shared(d.as()); } - else if (diagram_type == "package") { + if (diagram_type == "package") { return std::make_shared(d.as()); } - else if (diagram_type == "include") { + if (diagram_type == "include") { return std::make_shared(d.as()); } @@ -553,6 +552,7 @@ template bool decode_diagram(const Node &node, T &rhs) get_option(node, rhs.generate_links); get_option(node, rhs.type_aliases); get_option(node, rhs.comment_parser); + get_option(node, rhs.debug_mode); return true; } @@ -595,6 +595,11 @@ template <> struct convert { get_option(node, rhs.combine_free_functions_into_file_participants); get_option(node, rhs.relative_to); get_option(node, rhs.participants_order); + get_option(node, rhs.generate_method_arguments); + + // Ensure relative_to has a value + if (!rhs.relative_to.has_value) + rhs.relative_to.set(std::filesystem::current_path()); rhs.initialize_type_aliases(); @@ -630,6 +635,9 @@ template <> struct convert { get_option(node, rhs.relative_to); get_option(node, rhs.generate_system_headers); + if (!rhs.relative_to) + rhs.relative_to.set(std::filesystem::current_path()); + // Convert the path in relative_to to an absolute path, with respect // to the directory where the `.clang-uml` configuration file is located if (rhs.relative_to) { @@ -728,6 +736,7 @@ template <> struct convert { get_option(node, rhs.generate_links); get_option(node, rhs.generate_system_headers); get_option(node, rhs.git); + get_option(node, rhs.debug_mode); rhs.base_directory.set(node["__parent_path"].as()); get_option(node, rhs.relative_to); @@ -767,4 +776,4 @@ template <> struct convert { return true; } }; -} +} // namespace YAML diff --git a/src/config/config.h b/src/config/config.h index 94fda926..2ebf3e49 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -1,7 +1,7 @@ /** * src/config/config.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,7 +79,7 @@ struct filter { enum class hint_t { up, down, left, right }; struct layout_hint { - hint_t hint; + hint_t hint{hint_t::up}; std::string entity; }; @@ -120,7 +120,7 @@ using relationship_hints_t = std::map; using type_aliases_t = std::map; -std::string to_string(const hint_t t); +std::string to_string(hint_t t); struct inheritable_diagram_options { option> glob{"glob"}; @@ -145,6 +145,7 @@ struct inheritable_diagram_options { option combine_free_functions_into_file_participants{ "combine_free_functions_into_file_participants", false}; option> participants_order{"participants_order"}; + option debug_mode{"debug_mode", false}; void inherit(const inheritable_diagram_options &parent); @@ -166,12 +167,12 @@ struct diagram : public inheritable_diagram_options { struct source_location { enum class location_t { usr, marker, fileline, function }; - location_t location_type; + location_t location_type{location_t::function}; std::string location; }; struct class_diagram : public diagram { - virtual ~class_diagram() = default; + ~class_diagram() override = default; common::model::diagram_t type() const override; @@ -182,7 +183,7 @@ struct class_diagram : public diagram { }; struct sequence_diagram : public diagram { - virtual ~sequence_diagram() = default; + ~sequence_diagram() override = default; common::model::diagram_t type() const override; @@ -190,7 +191,7 @@ struct sequence_diagram : public diagram { }; struct package_diagram : public diagram { - virtual ~package_diagram() = default; + ~package_diagram() override = default; common::model::diagram_t type() const override; @@ -198,7 +199,7 @@ struct package_diagram : public diagram { }; struct include_diagram : public diagram { - virtual ~include_diagram() = default; + ~include_diagram() override = default; common::model::diagram_t type() const override; @@ -216,5 +217,5 @@ struct config : public inheritable_diagram_options { }; config load(const std::string &config_file); -} -} +} // namespace config +} // namespace clanguml diff --git a/src/config/option.h b/src/config/option.h index d0232472..8cfece47 100644 --- a/src/config/option.h +++ b/src/config/option.h @@ -1,7 +1,7 @@ /** * src/config/option.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,6 +18,7 @@ #pragma once #include +#include namespace clanguml { namespace config { @@ -27,17 +28,17 @@ template void append_value(T &l, const T &r) { l = r; } enum class option_inherit_mode { kOverride, kAppend }; template struct option { - option(const std::string &name_, + option(std::string name_, option_inherit_mode im = option_inherit_mode::kOverride) - : name{name_} + : name{std::move(name_)} , value{} , inheritance_mode{im} { } - option(const std::string &name_, const T &initial_value, + option(std::string name_, T initial_value, option_inherit_mode im = option_inherit_mode::kOverride) - : name{name_} - , value{initial_value} + : name{std::move(name_)} + , value{std::move(initial_value)} , has_value{true} , inheritance_mode{im} { @@ -78,5 +79,5 @@ template struct option { bool has_value{false}; option_inherit_mode inheritance_mode; }; -} -} +} // namespace config +} // namespace clanguml diff --git a/src/cx/util.cc b/src/cx/util.cc index 56fcc7f0..73684113 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -1,7 +1,7 @@ /** * src/cx/util.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,14 +41,15 @@ std::pair split_ns( std::vector parse_unexposed_template_params(const std::string ¶ms, - std::function ns_resolve, int depth) + const std::function &ns_resolve, + int depth) { using class_diagram::model::template_parameter; std::vector res; auto it = params.begin(); - while (std::isspace(*it)) + while (std::isspace(*it) != 0) ++it; std::string type{}; diff --git a/src/cx/util.h b/src/cx/util.h index 3b0af753..f56aacbe 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -1,7 +1,7 @@ /** * src/cx/util.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ std::pair split_ns( std::vector parse_unexposed_template_params(const std::string ¶ms, - std::function ns_resolve, int depth = 0); + const std::function &ns_resolve, + int depth = 0); } // namespace clanguml::cx::util diff --git a/src/decorators/decorators.cc b/src/decorators/decorators.cc index 4bebf67f..8e062353 100644 --- a/src/decorators/decorators.cc +++ b/src/decorators/decorators.cc @@ -1,7 +1,7 @@ /** * src/decorators/decorators.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,37 +22,36 @@ #include #include -namespace clanguml { -namespace decorators { +namespace clanguml::decorators { std::shared_ptr decorator::from_string(std::string_view c) { if (c.find(note::label) == 0) { return note::from_string(c); } - else if (c.find(skip_relationship::label) == 0) { + if (c.find(skip_relationship::label) == 0) { return skip_relationship::from_string(c); } - else if (c.find(skip::label) == 0) { + if (c.find(skip::label) == 0) { return skip::from_string(c); } - else if (c.find(style::label) == 0) { + if (c.find(style::label) == 0) { return style::from_string(c); } - else if (c.find(aggregation::label) == 0) { + if (c.find(aggregation::label) == 0) { return aggregation::from_string(c); } - else if (c.find(composition::label) == 0) { + if (c.find(composition::label) == 0) { return composition::from_string(c); } - else if (c.find(association::label) == 0) { + if (c.find(association::label) == 0) { return association::from_string(c); } return {}; } -bool decorator::applies_to_diagram(std::string name) +bool decorator::applies_to_diagram(const std::string &name) { return diagrams.empty() || (std::find(diagrams.begin(), diagrams.end(), name) != diagrams.end()); @@ -63,7 +62,7 @@ decorator_toks decorator::tokenize(const std::string &label, std::string_view c) decorator_toks res; res.label = label; size_t pos{}; - auto it = c.begin(); + const auto *it = c.begin(); std::advance(it, label.size()); if (*it == ':') { @@ -72,11 +71,11 @@ decorator_toks decorator::tokenize(const std::string &label, std::string_view c) pos = std::distance(c.begin(), it); // If the diagram list is provided after ':', [] is mandatory // even if empty - auto d = c.substr(pos, c.find("[", pos) - pos); + auto d = c.substr(pos, c.find('[', pos) - pos); if (!d.empty()) { std::string d_str{d}; d_str.erase(std::remove_if(d_str.begin(), d_str.end(), - (int (*)(int))std::isspace), + static_cast(std::isspace)), d_str.end()); res.diagrams = util::split(d_str, ","); } @@ -88,16 +87,16 @@ decorator_toks decorator::tokenize(const std::string &label, std::string_view c) std::advance(it, 1); pos = std::distance(c.begin(), it); - res.param = c.substr(pos, c.find("]", pos) - pos); + res.param = c.substr(pos, c.find(']', pos) - pos); std::advance(it, res.param.size() + 1); } - else if (std::isspace(*it)) { + else if (std::isspace(*it) != 0) { std::advance(it, 1); } pos = std::distance(c.begin(), it); - res.text = c.substr(pos, c.find("}", pos) - pos); + res.text = c.substr(pos, c.find('}', pos) - pos); res.text = util::trim(res.text); res.param = util::trim(res.param); @@ -175,7 +174,7 @@ std::shared_ptr association::from_string(std::string_view c) } std::vector> parse( - std::string documentation_block, std::string clanguml_tag) + std::string documentation_block, const std::string &clanguml_tag) { std::vector> res; const std::string begin_tag{"@" + clanguml_tag}; @@ -191,7 +190,7 @@ std::vector> parse( auto pos = block_view.find("@" + clanguml_tag + "{"); while (pos < documentation_block.size()) { auto c_begin = pos + begin_tag_size; - auto c_end = documentation_block.find("}", c_begin); + auto c_end = documentation_block.find('}', c_begin); if (c_end == std::string::npos) return res; @@ -208,5 +207,4 @@ std::vector> parse( return res; }; -} // namespace decorators -} // namespace clanguml +} // namespace clanguml::decorators diff --git a/src/decorators/decorators.h b/src/decorators/decorators.h index 554759c0..f8296728 100644 --- a/src/decorators/decorators.h +++ b/src/decorators/decorators.h @@ -1,7 +1,7 @@ /** * src/decorators/decorators.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ struct decorator { static std::shared_ptr from_string(std::string_view c); - bool applies_to_diagram(std::string name); + bool applies_to_diagram(const std::string &name); protected: decorator_toks tokenize(const std::string &label, std::string_view c); @@ -97,7 +97,7 @@ struct association : public relationship { }; std::vector> parse( - std::string documentation_block, std::string clanguml_tag = "uml"); + std::string documentation_block, const std::string &clanguml_tag = "uml"); } // namespace decorators } // namespace clanguml diff --git a/src/include_diagram/generators/plantuml/include_diagram_generator.cc b/src/include_diagram/generators/plantuml/include_diagram_generator.cc index 507b48da..7c10148d 100644 --- a/src/include_diagram/generators/plantuml/include_diagram_generator.cc +++ b/src/include_diagram/generators/plantuml/include_diagram_generator.cc @@ -1,7 +1,7 @@ /** * src/include_diagram/generators/plantuml/include_diagram_generator.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -103,7 +103,7 @@ void generator::generate(std::ostream &ostr) const // Generate files and folders util::for_each_if( - m_model, [](const auto &f) { return true; }, + m_model, [](const auto & /*f*/) { return true; }, [this, &ostr](const auto &f) { generate(dynamic_cast(*f), ostr); }); @@ -120,4 +120,4 @@ void generator::generate(std::ostream &ostr) const ostr << "@enduml" << '\n'; } -} +} // namespace clanguml::include_diagram::generators::plantuml diff --git a/src/include_diagram/generators/plantuml/include_diagram_generator.h b/src/include_diagram/generators/plantuml/include_diagram_generator.h index c4ff08e5..5e2882b7 100644 --- a/src/include_diagram/generators/plantuml/include_diagram_generator.h +++ b/src/include_diagram/generators/plantuml/include_diagram_generator.h @@ -1,7 +1,7 @@ /** * src/include_diagram/generators/plantuml/include_diagram_generator.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,10 +57,10 @@ public: void generate(const source_file &e, std::ostream &ostr) const; - void generate(std::ostream &ostr) const; + void generate(std::ostream &ostr) const override; }; -} -} -} -} +} // namespace plantuml +} // namespace generators +} // namespace include_diagram +} // namespace clanguml diff --git a/src/include_diagram/model/diagram.cc b/src/include_diagram/model/diagram.cc index 84e465b9..f58f0bfb 100644 --- a/src/include_diagram/model/diagram.cc +++ b/src/include_diagram/model/diagram.cc @@ -1,7 +1,7 @@ /** * src/include_diagram/model/diagram.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -152,7 +152,7 @@ inja::json diagram::context() const return ctx; } -} +} // namespace clanguml::include_diagram::model namespace clanguml::common::model { template <> diff --git a/src/include_diagram/model/diagram.h b/src/include_diagram/model/diagram.h index 8be09603..df2f94b6 100644 --- a/src/include_diagram/model/diagram.h +++ b/src/include_diagram/model/diagram.h @@ -1,7 +1,7 @@ /** * src/include_diagram/model/diagram.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ public: const std::string &full_name) const override; common::optional_ref get( - const common::model::diagram_element::id_t id) const override; + common::model::diagram_element::id_t id) const override; void add_file(std::unique_ptr &&f); @@ -53,7 +53,7 @@ public: const std::string &name) const; common::optional_ref get_file( - const common::model::diagram_element::id_t id) const; + common::model::diagram_element::id_t id) const; std::string to_alias(const std::string &full_name) const; @@ -64,7 +64,7 @@ public: private: common::reference_vector files_; }; -} +} // namespace clanguml::include_diagram::model namespace clanguml::common::model { template <> diff --git a/src/include_diagram/visitor/translation_unit_visitor.cc b/src/include_diagram/visitor/translation_unit_visitor.cc index 122b270b..739ae473 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.cc +++ b/src/include_diagram/visitor/translation_unit_visitor.cc @@ -1,7 +1,7 @@ /** * src/include_diagram/visitor/translation_unit_visitor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,12 +43,23 @@ translation_unit_visitor::include_visitor::include_visitor( { } +#if LLVM_VERSION_MAJOR > 14 void translation_unit_visitor::include_visitor::InclusionDirective( - clang::SourceLocation hash_loc, const clang::Token &include_tok, - clang::StringRef file_name, bool is_angled, - clang::CharSourceRange filename_range, const clang::FileEntry *file, - clang::StringRef search_path, clang::StringRef relative_path, - const clang::Module *imported, clang::SrcMgr::CharacteristicKind file_type) + clang::SourceLocation hash_loc, const clang::Token & /*include_tok*/, + clang::StringRef /*file_name*/, bool is_angled, + clang::CharSourceRange /*filename_range*/, + clang::Optional file, clang::StringRef /*search_path*/, + clang::StringRef relative_path, const clang::Module * /*imported*/, + clang::SrcMgr::CharacteristicKind file_type) +#else +void translation_unit_visitor::include_visitor::InclusionDirective( + clang::SourceLocation hash_loc, const clang::Token & /*include_tok*/, + clang::StringRef /*file_name*/, bool is_angled, + clang::CharSourceRange /*filename_range*/, const clang::FileEntry *file, + clang::StringRef /*search_path*/, clang::StringRef relative_path, + const clang::Module * /*imported*/, + clang::SrcMgr::CharacteristicKind file_type) +#endif { using common::model::relationship; using common::model::source_file; @@ -65,7 +76,11 @@ void translation_unit_visitor::include_visitor::InclusionDirective( assert(diagram().get(current_file_id.value())); +#if LLVM_VERSION_MAJOR > 14 + auto include_path = std::filesystem::path(file->getDir().getName().str()); +#else auto include_path = std::filesystem::path(file->getDir()->getName().str()); +#endif include_path = include_path / file->getName().str(); include_path = include_path.lexically_normal(); @@ -229,4 +244,4 @@ translation_unit_visitor::include_visitor::process_source_file( return {}; } -} +} // namespace clanguml::include_diagram::visitor diff --git a/src/include_diagram/visitor/translation_unit_visitor.h b/src/include_diagram/visitor/translation_unit_visitor.h index d1c7467c..7b4c21ea 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.h +++ b/src/include_diagram/visitor/translation_unit_visitor.h @@ -1,7 +1,7 @@ /** * src/include_diagram/visitor/translation_unit_visitor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,20 +47,30 @@ public: clanguml::include_diagram::model::diagram &diagram, const clanguml::config::include_diagram &config); +#if LLVM_VERSION_MAJOR > 14 + void InclusionDirective(clang::SourceLocation hash_loc, + const clang::Token &include_tok, clang::StringRef file_name, + bool is_angled, clang::CharSourceRange filename_range, + clang::Optional file, + clang::StringRef search_path, clang::StringRef relative_path, + const clang::Module *imported, + clang::SrcMgr::CharacteristicKind file_type) override; +#else void InclusionDirective(clang::SourceLocation hash_loc, const clang::Token &include_tok, clang::StringRef file_name, bool is_angled, clang::CharSourceRange filename_range, const clang::FileEntry *file, clang::StringRef search_path, clang::StringRef relative_path, const clang::Module *imported, clang::SrcMgr::CharacteristicKind file_type) override; +#endif std::optional process_internal_header( const std::filesystem::path &include_path, bool is_system, - const common::id_t current_file_id); + common::id_t current_file_id); std::optional process_external_system_header( const std::filesystem::path &include_path, - const common::id_t current_file_id); + common::id_t current_file_id); std::optional process_source_file( const std::filesystem::path &file); @@ -102,4 +112,4 @@ private: // Reference to class diagram config const clanguml::config::include_diagram &config_; }; -} +} // namespace clanguml::include_diagram::visitor diff --git a/src/main.cc b/src/main.cc index efe97439..da969ce9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,7 +1,7 @@ /** * src/main.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,97 +24,125 @@ #include "util/util.h" #include "version.h" +#ifndef NDEBUG +#include +#endif + #include #include #include #include +#include #include #include #include #include -#include #include +#ifndef NDEBUG +namespace backward { +backward::SignalHandling sh; // NOLINT +} // namespace backward +#endif + using namespace clanguml; using config::config; -/// -/// Print the program version and basic information -/// +/** + * Print the program version and basic information + */ void print_version(); -//// -/// Print list of diagrams available in the configuration file -/// -/// \param cfg Configuration instance loaded from configuration file -/// +/** + * Print list of diagrams available in the configuration file + * + * @param cfg Configuration instance loaded from configuration file + */ void print_diagrams_list(const clanguml::config::config &cfg); -/// -/// Check if diagram output directory exists, if not create it -/// -/// \param dir Path to the output directory -/// \return True if directory exists or has been created -/// +/** + * Generate sample configuration file and exit. + * + * @return 0 on success or error code + */ +int create_config_file(); + +/** + * Add example diagram of given type to the config file. + * + * @param type Type of the sample diagram to add + * @param config_file_path Path to the config file + * @param name Name of the new diagram + * @return 0 on success or error code + */ +int add_config_diagram(clanguml::common::model::diagram_t type, + const std::string &config_file_path, const std::string &name); + +/** + * Check if diagram output directory exists, if not create it + * + * @param dir Path to the output directory + * @return True if directory exists or has been created + */ bool ensure_output_directory_exists(const std::string &dir); -/// -/// Generate specific diagram identified by name -/// -/// \param od Diagram output directory -/// \param name Name of the diagram as specified in the config file -/// \param diagram Diagram model instance -/// \param db Compilation database -/// \param translation_units List of translation units to be used for this -/// diagram -/// \param verbose Logging level -/// +/** + * Generate specific diagram identified by name + * + * @param od Diagram output directory + * @param name Name of the diagram as specified in the config file + * @param diagram Diagram model instance + * @param db Compilation database + * @param translation_units List of translation units to be used for this + * diagram + * @param verbose Logging level + */ void generate_diagram(const std::string &od, const std::string &name, std::shared_ptr diagram, const clang::tooling::CompilationDatabase &db, const std::vector &translation_units, bool verbose); -/// -/// Find translation units for diagrams. -/// -/// For each diagram to be generated, this function selects translation units -/// to be used for this diagram. The files are selected as an intersection -/// between all translation units found in the compilation database and the -/// `glob` patterns specified for each diagram in the configuration file. -/// -/// \param diagram_names List of diagram names to be generated -/// \param config Configuration instance -/// \param compilation_database_files All translation units in compilation -/// database -/// \param translation_units_map The output map containing translation -/// units for each diagram by name -/// +/** + * Find translation units for diagrams. + * + * For each diagram to be generated, this function selects translation units + * to be used for this diagram. The files are selected as an intersection + * between all translation units found in the compilation database and the + * `glob` patterns specified for each diagram in the configuration file. + * + * @param diagram_names List of diagram names to be generated + * @param config Configuration instance + * @param compilation_database_files All translation units in compilation + * database + * @param translation_units_map The output map containing translation + * units for each diagram by name + */ void find_translation_units_for_diagrams( const std::vector &diagram_names, clanguml::config::config &config, const std::vector &compilation_database_files, std::map> &translation_units_map); -/// -/// Generate diagrams. -/// -/// This function generates all diagrams specified in the configuration file -/// and in the command line. -/// -/// \param diagram_names List of diagram names to be generated -/// \param config Configuration instance -/// \param od Output directory where diagrams should be written -/// \param db Compilation database instance -/// \param verbose Verbosity level -/// \param thread_count Number of diagrams to be generated in parallel -/// \param translation_units_map List of translation units to be used for each -/// diagram -/// +/** + * Generate diagrams. + * + * This function generates all diagrams specified in the configuration file + * and in the command line. + * + * @param diagram_names List of diagram names to be generated + * @param config Configuration instance + * @param od Output directory where diagrams should be written + * @param db Compilation database instance + * @param verbose Verbosity level + * @param thread_count Number of diagrams to be generated in parallel + * @param translation_units_map List of translation units to be used for each + * diagram + */ void generate_diagrams(const std::vector &diagram_names, clanguml::config::config &config, const std::string &od, - const std::unique_ptr &db, - const int verbose, const unsigned int thread_count, + const std::unique_ptr &db, int verbose, + unsigned int thread_count, const std::map> &translation_units_map); @@ -130,6 +158,12 @@ int main(int argc, const char *argv[]) bool show_version{false}; int verbose{0}; bool list_diagrams{false}; + bool quiet{false}; + bool initialize{false}; + std::optional add_class_diagram; + std::optional add_sequence_diagram; + std::optional add_package_diagram; + std::optional add_include_diagram; app.add_option( "-c,--config", config_path, "Location of configuration file"); @@ -142,9 +176,20 @@ int main(int argc, const char *argv[]) app.add_option("-t,--thread-count", thread_count, "Thread pool size (0 = hardware concurrency)"); app.add_flag("-V,--version", show_version, "Print version and exit"); - app.add_flag("-v,--verbose", verbose, "Verbose logging"); + app.add_flag("-v,--verbose", verbose, + "Verbose logging (use multiple times to increase - e.g. -vvv)"); + app.add_flag("-q,--quiet", quiet, "Minimal logging"); app.add_flag("-l,--list-diagrams", list_diagrams, "Print list of diagrams defined in the config file"); + app.add_flag("--init", initialize, "Initialize example config file"); + app.add_option( + "--add-class-diagram", add_class_diagram, "Add class diagram config"); + app.add_option("--add-sequence-diagram", add_sequence_diagram, + "Add sequence diagram config"); + app.add_option("--add-package-diagram", add_package_diagram, + "Add package diagram config"); + app.add_option("--add-include-diagram", add_include_diagram, + "Add include diagram config"); CLI11_PARSE(app, argc, argv); @@ -153,6 +198,35 @@ int main(int argc, const char *argv[]) return 0; } + if (initialize) { + return create_config_file(); + } + + verbose++; + + if (quiet) + verbose = 0; + + if (add_class_diagram) { + return add_config_diagram(clanguml::common::model::diagram_t::kClass, + config_path, *add_class_diagram); + } + + if (add_sequence_diagram) { + return add_config_diagram(clanguml::common::model::diagram_t::kSequence, + config_path, *add_sequence_diagram); + } + + if (add_package_diagram) { + return add_config_diagram(clanguml::common::model::diagram_t::kPackage, + config_path, *add_package_diagram); + } + + if (add_include_diagram) { + return add_config_diagram(clanguml::common::model::diagram_t::kInclude, + config_path, *add_include_diagram); + } + clanguml::util::setup_logging(verbose); clanguml::config::config config; @@ -209,87 +283,6 @@ int main(int argc, const char *argv[]) return 0; } -void generate_diagrams(const std::vector &diagram_names, - clanguml::config::config &config, const std::string &od, - const std::unique_ptr &db, - const int verbose, const unsigned int thread_count, - const std::map> - &translation_units_map) -{ - util::thread_pool_executor generator_executor{thread_count}; - std::vector> futs; - - 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() && !util::contains(diagram_names, name)) - continue; - - const auto &valid_translation_units = translation_units_map.at(name); - - if (valid_translation_units.empty()) { - LOG_ERROR( - "Diagram {} generation failed: no translation units found", - name); - continue; - } - - futs.emplace_back(generator_executor.add( - [&od, &name = name, &diagram = diagram, db = std::ref(*db), - translation_units = valid_translation_units, verbose]() { - try { - generate_diagram( - od, name, diagram, db, translation_units, verbose); - } - catch (std::runtime_error &e) { - LOG_ERROR(e.what()); - } - })); - } - - for (auto &fut : futs) { - fut.get(); - } -} - -void find_translation_units_for_diagrams( - const std::vector &diagram_names, - clanguml::config::config &config, - const std::vector &compilation_database_files, - std::map> &translation_units_map) -{ - const auto current_directory = std::filesystem::current_path(); - - 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() && !util::contains(diagram_names, name)) - continue; - - // If glob is not defined use all translation units from the - // compilation database - if (!diagram->glob.has_value) { - translation_units_map[name] = compilation_database_files; - } - // Otherwise, get all translation units matching the glob from diagram - // configuration - else { - const std::vector translation_units = - diagram->get_translation_units(current_directory); - - std::vector valid_translation_units{}; - std::copy_if(compilation_database_files.begin(), - compilation_database_files.end(), - std::back_inserter(valid_translation_units), - [&translation_units](const auto &tu) { - return util::contains(translation_units, tu); - }); - - translation_units_map[name] = std::move(valid_translation_units); - } - } -} - void generate_diagram(const std::string &od, const std::string &name, std::shared_ptr diagram, const clang::tooling::CompilationDatabase &db, @@ -372,6 +365,87 @@ void generate_diagram(const std::string &od, const std::string &name, ofs.close(); } +void generate_diagrams(const std::vector &diagram_names, + clanguml::config::config &config, const std::string &od, + const std::unique_ptr &db, + const int verbose, const unsigned int thread_count, + const std::map> + &translation_units_map) +{ + util::thread_pool_executor generator_executor{thread_count}; + std::vector> futs; + + 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() && !util::contains(diagram_names, name)) + continue; + + const auto &valid_translation_units = translation_units_map.at(name); + + if (valid_translation_units.empty()) { + LOG_ERROR( + "Diagram {} generation failed: no translation units found", + name); + continue; + } + + futs.emplace_back(generator_executor.add( + [&od, &name = name, &diagram = diagram, db = std::ref(*db), + translation_units = valid_translation_units, verbose]() { + try { + generate_diagram( + od, name, diagram, db, translation_units, verbose != 0); + } + catch (std::runtime_error &e) { + LOG_ERROR(e.what()); + } + })); + } + + for (auto &fut : futs) { + fut.get(); + } +} + +void find_translation_units_for_diagrams( + const std::vector &diagram_names, + clanguml::config::config &config, + const std::vector &compilation_database_files, + std::map> &translation_units_map) +{ + const auto current_directory = std::filesystem::current_path(); + + 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() && !util::contains(diagram_names, name)) + continue; + + // If glob is not defined use all translation units from the + // compilation database + if (!diagram->glob.has_value) { + translation_units_map[name] = compilation_database_files; + } + // Otherwise, get all translation units matching the glob from diagram + // configuration + else { + const std::vector translation_units = + diagram->get_translation_units(current_directory); + + std::vector valid_translation_units{}; + std::copy_if(compilation_database_files.begin(), + compilation_database_files.end(), + std::back_inserter(valid_translation_units), + [&translation_units](const auto &tu) { + return util::contains(translation_units, tu); + }); + + translation_units_map[name] = std::move(valid_translation_units); + } + } +} + bool ensure_output_directory_exists(const std::string &dir) { namespace fs = std::filesystem; @@ -393,13 +467,16 @@ bool ensure_output_directory_exists(const std::string &dir) void print_version() { + constexpr auto kLLVMBackendPackageStringLength{5}; std::cout << "clang-uml " << clanguml::version::CLANG_UML_VERSION << '\n'; - std::cout << "Copyright (C) 2021-2022 Bartek Kryza " + std::cout << "Copyright (C) 2021-2023 Bartek Kryza " << '\n'; - std::cout << "Built with LLVM version: " - << std::string{BACKEND_PACKAGE_STRING}.substr(5) << std::endl; - std::cout << "Using LLVM version: " << clang::getClangFullVersion() + std::cout << "Built against LLVM/Clang libraries version: " + << std::string{BACKEND_PACKAGE_STRING}.substr( + kLLVMBackendPackageStringLength) << std::endl; + std::cout << "Using LLVM/Clang libraries version: " + << clang::getClangFullVersion() << std::endl; } void print_diagrams_list(const clanguml::config::config &cfg) @@ -412,3 +489,131 @@ void print_diagrams_list(const clanguml::config::config &cfg) cout << '\n'; } } + +int create_config_file() +{ + namespace fs = std::filesystem; + using std::cout; + + fs::path config_file{"./.clang-uml"}; + + if (fs::exists(config_file)) { + cout << "ERROR: .clang-uml file already exists\n"; + return 1; + } + + YAML::Emitter out; + out.SetIndent(2); + out << YAML::BeginMap; + out << YAML::Comment("Change to directory where compile_commands.json is"); + out << YAML::Key << "compilation_database_dir" << YAML::Value << "."; + out << YAML::Newline + << YAML::Comment("Change to directory where diagram should be written"); + out << YAML::Key << "output_directory" << YAML::Value << "docs/diagrams"; + out << YAML::Key << "diagrams" << YAML::Value; + out << YAML::BeginMap; + out << YAML::Key << "example_class_diagram" << YAML::Value; + out << YAML::BeginMap; + out << YAML::Key << "type" << YAML::Value << "class"; + out << YAML::Key << "glob" << YAML::Value; + out << YAML::BeginSeq << "src/*.cpp" << YAML::EndSeq; + out << YAML::Key << "using_namespace" << YAML::Value; + out << YAML::BeginSeq << "myproject" << YAML::EndSeq; + out << YAML::Key << "include"; + out << YAML::BeginMap; + out << YAML::Key << "namespaces"; + out << YAML::BeginSeq << "myproject" << YAML::EndSeq; + out << YAML::EndMap; + out << YAML::Key << "exclude"; + out << YAML::BeginMap; + out << YAML::Key << "namespaces"; + out << YAML::BeginSeq << "myproject::detail" << YAML::EndSeq; + out << YAML::EndMap; + out << YAML::EndMap; + out << YAML::EndMap; + out << YAML::EndMap; + out << YAML::Newline; + + std::ofstream ofs(config_file); + ofs << out.c_str(); + ofs.close(); + + return 0; +} + +int add_config_diagram(clanguml::common::model::diagram_t type, + const std::string &config_file_path, const std::string &name) +{ + fs::path config_file{config_file_path}; + + if (!fs::exists(config_file)) { + std::cerr << "ERROR: " << config_file_path << " file doesn't exists\n"; + return 1; + } + + YAML::Node doc = YAML::LoadFile(config_file); + + for (YAML::const_iterator it = doc["diagrams"].begin(); + it != doc["diagrams"].end(); ++it) { + if (it->first.as() == name) { + std::cerr << "ERROR: " << config_file_path + << " file already contains '" << name << "' diagram"; + return 1; + } + } + + if (type == clanguml::common::model::diagram_t::kClass) { + doc["diagrams"][name]["type"] = "class"; + doc["diagrams"][name]["glob"] = std::vector{{"src/*.cpp"}}; + doc["diagrams"][name]["using_namespace"] = + std::vector{{"myproject"}}; + doc["diagrams"][name]["include"]["namespaces"] = + std::vector{{"myproject"}}; + doc["diagrams"][name]["exclude"]["namespaces"] = + std::vector{{"myproject::detail"}}; + } + else if (type == clanguml::common::model::diagram_t::kSequence) { + doc["diagrams"][name]["type"] = "sequence"; + doc["diagrams"][name]["glob"] = std::vector{{"src/*.cpp"}}; + doc["diagrams"][name]["combine_free_functions_into_file_participants"] = + true; + doc["diagrams"][name]["using_namespace"] = + std::vector{{"myproject"}}; + doc["diagrams"][name]["include"]["paths"] = + std::vector{{"src"}}; + doc["diagrams"][name]["exclude"]["namespaces"] = + std::vector{{"myproject::detail"}}; + doc["diagrams"][name]["start_from"] = + std::vector>{ + {{"function", "main(int,const char **)"}}}; + } + else if (type == clanguml::common::model::diagram_t::kPackage) { + doc["diagrams"][name]["type"] = "package"; + doc["diagrams"][name]["glob"] = std::vector{{"src/*.cpp"}}; + doc["diagrams"][name]["using_namespace"] = + std::vector{{"myproject"}}; + doc["diagrams"][name]["include"]["namespaces"] = + std::vector{{"myproject"}}; + doc["diagrams"][name]["exclude"]["namespaces"] = + std::vector{{"myproject::detail"}}; + } + else if (type == clanguml::common::model::diagram_t::kInclude) { + doc["diagrams"][name]["type"] = "include"; + doc["diagrams"][name]["glob"] = std::vector{{"src/*.cpp"}}; + doc["diagrams"][name]["relative_to"] = "."; + doc["diagrams"][name]["include"]["paths"] = + std::vector{{"src"}}; + } + + YAML::Emitter out; + out.SetIndent(2); + + out << doc; + out << YAML::Newline; + + std::ofstream ofs(config_file); + ofs << out.c_str(); + ofs.close(); + + return 0; +} \ No newline at end of file diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.cc b/src/package_diagram/generators/plantuml/package_diagram_generator.cc index b8dd8fd1..126f527d 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.cc +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.cc @@ -1,7 +1,7 @@ /** * src/package_diagram/generators/plantuml/package_diagram_generator.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -118,4 +118,4 @@ void generator::generate(std::ostream &ostr) const ostr << "@enduml" << '\n'; } -} +} // namespace clanguml::package_diagram::generators::plantuml diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.h b/src/package_diagram/generators/plantuml/package_diagram_generator.h index 13324f0f..a8165aaa 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.h +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.h @@ -1,7 +1,7 @@ /** * src/package_diagram/generators/plantuml/package_diagram_generator.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,10 +57,10 @@ public: void generate(const package &e, std::ostream &ostr) const; - void generate(std::ostream &ostr) const; + void generate(std::ostream &ostr) const override; }; -} -} -} -} +} // namespace plantuml +} // namespace generators +} // namespace package_diagram +} // namespace clanguml diff --git a/src/package_diagram/model/diagram.cc b/src/package_diagram/model/diagram.cc index aee461d2..ff197f1f 100644 --- a/src/package_diagram/model/diagram.cc +++ b/src/package_diagram/model/diagram.cc @@ -1,7 +1,7 @@ /** * src/package_diagram/model/diagram.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,7 @@ inja::json diagram::context() const return ctx; } -} +} // namespace clanguml::package_diagram::model namespace clanguml::common::model { template <> diff --git a/src/package_diagram/model/diagram.h b/src/package_diagram/model/diagram.h index 72c762c7..07e0d1a4 100644 --- a/src/package_diagram/model/diagram.h +++ b/src/package_diagram/model/diagram.h @@ -1,7 +1,7 @@ /** * src/package_diagram/model/diagram.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public: const std::string &full_name) const override; common::optional_ref get( - const clanguml::common::model::diagram_element::id_t id) const override; + clanguml::common::model::diagram_element::id_t id) const override; void add_package(std::unique_ptr &&p); @@ -54,17 +54,17 @@ public: const std::string &name) const; common::optional_ref get_package( - const clanguml::common::model::diagram_element::id_t id) const; + clanguml::common::model::diagram_element::id_t id) const; std::string to_alias( - const clanguml::common::model::diagram_element::id_t) const; + clanguml::common::model::diagram_element::id_t /*id*/) const; inja::json context() const override; private: common::reference_vector packages_; }; -} +} // namespace clanguml::package_diagram::model namespace clanguml::common::model { template <> diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index efb5bf2e..15e85d33 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -1,7 +1,7 @@ /** * src/package_diagram/visitor/translation_unit_visitor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,7 +28,6 @@ namespace clanguml::package_diagram::visitor { -using clanguml::class_diagram::model::type_alias; using clanguml::common::model::access_t; using clanguml::common::model::namespace_; using clanguml::common::model::package; @@ -198,15 +197,19 @@ void translation_unit_visitor::process_class_children( } } - // Iterate over class template methods - for (auto const *decl_iterator : - clang::dyn_cast_or_null(&cls)->decls()) { - auto const *method_template = - llvm::dyn_cast_or_null(decl_iterator); - if (method_template == nullptr) - continue; + if (const auto *decl_context = + clang::dyn_cast_or_null(&cls); + decl_context != nullptr) { + // Iterate over class template methods + for (auto const *decl_iterator : decl_context->decls()) { + auto const *method_template = + llvm::dyn_cast_or_null( + decl_iterator); + if (method_template == nullptr) + continue; - process_template_method(*method_template, relationships); + process_template_method(*method_template, relationships); + } } // Iterate over regular class fields @@ -221,7 +224,7 @@ void translation_unit_visitor::process_class_children( if (decl->getKind() == clang::Decl::Var) { const clang::VarDecl *variable_declaration{ dynamic_cast(decl)}; - if (variable_declaration && + if ((variable_declaration != nullptr) && variable_declaration->isStaticDataMember()) { process_static_field(*variable_declaration, relationships); } @@ -238,7 +241,7 @@ void translation_unit_visitor::process_class_children( void translation_unit_visitor::process_class_bases( const clang::CXXRecordDecl &cls, found_relationships_t &relationships) { - for (auto &base : cls.bases()) { + for (const auto &base : cls.bases()) { find_relationships(base.getType(), relationships); } } @@ -333,18 +336,14 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, relationships, relationship_t::kAggregation); } else if (type->isEnumeralType()) { - relationships.emplace_back( - common::to_id(*type->getAs()), relationship_hint); - } - else if (auto *template_specialization_type = - type->getAs()) { - if (template_specialization_type != nullptr) { - if (template_specialization_type->isTypeAlias()) - template_specialization_type = - template_specialization_type->getAliasedType() - ->getAs(); + if (const auto *enum_type = type->getAs(); + enum_type != nullptr) { + relationships.emplace_back( + common::to_id(*enum_type), relationship_hint); } - + } + else if (const auto *template_specialization_type = + type->getAs()) { if (template_specialization_type != nullptr) { for (const auto &template_argument : *template_specialization_type) { @@ -373,12 +372,12 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, clang::TemplateArgument::ArgKind::TemplateExpansion) { // pass } - else if (template_argument.getAsType() - ->getAs()) { + else if (const auto *function_type = + template_argument.getAsType() + ->getAs(); + function_type != nullptr) { for (const auto ¶m_type : - template_argument.getAsType() - ->getAs() - ->param_types()) { + function_type->param_types()) { result = find_relationships(param_type, relationships, relationship_t::kDependency); } @@ -412,4 +411,4 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, return result; } -} +} // namespace clanguml::package_diagram::visitor diff --git a/src/package_diagram/visitor/translation_unit_visitor.h b/src/package_diagram/visitor/translation_unit_visitor.h index 097a58a4..49729a35 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.h +++ b/src/package_diagram/visitor/translation_unit_visitor.h @@ -1,7 +1,7 @@ /** * src/package_diagram/visitor/translation_unit_visitor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,4 +96,4 @@ private: // Reference to class diagram config const clanguml::config::package_diagram &config_; }; -} +} // namespace clanguml::package_diagram::visitor diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index 8e285299..253d19ba 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,26 +58,36 @@ void generator::generate_call(const message &m, std::ostream &ostr) const std::string message; + model::function::message_render_mode render_mode = + model::function::message_render_mode::full; + + if (m_config.generate_method_arguments() == + config::method_arguments::abbreviated) + render_mode = model::function::message_render_mode::abbreviated; + else if (m_config.generate_method_arguments() == + config::method_arguments::none) + render_mode = model::function::message_render_mode::no_arguments; + if (to.value().type_name() == "method") { message = dynamic_cast(to.value()) - .message_name(model::function::message_render_mode::full); + .message_name(render_mode); } else if (m_config.combine_free_functions_into_file_participants()) { if (to.value().type_name() == "function") { - message = - dynamic_cast(to.value()) - .message_name(model::function::message_render_mode::full); + message = dynamic_cast(to.value()) + .message_name(render_mode); } else if (to.value().type_name() == "function_template") { - message = - dynamic_cast(to.value()) - .message_name(model::function::message_render_mode::full); + message = dynamic_cast(to.value()) + .message_name(render_mode); } } const std::string from_alias = generate_alias(from.value()); const std::string to_alias = generate_alias(to.value()); + print_debug(m, ostr); + ostr << from_alias << " " << common::generators::plantuml::to_plantuml(message_t::kCall) << " "; @@ -161,57 +171,70 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, visited.pop_back(); } else if (m.type() == message_t::kIf) { + print_debug(m, ostr); ostr << "alt\n"; } else if (m.type() == message_t::kElseIf) { + print_debug(m, ostr); ostr << "else\n"; } else if (m.type() == message_t::kElse) { + print_debug(m, ostr); ostr << "else\n"; } else if (m.type() == message_t::kIfEnd) { ostr << "end\n"; } else if (m.type() == message_t::kWhile) { + print_debug(m, ostr); ostr << "loop\n"; } else if (m.type() == message_t::kWhileEnd) { ostr << "end\n"; } else if (m.type() == message_t::kFor) { + print_debug(m, ostr); ostr << "loop\n"; } else if (m.type() == message_t::kForEnd) { ostr << "end\n"; } else if (m.type() == message_t::kDo) { + print_debug(m, ostr); ostr << "loop\n"; } else if (m.type() == message_t::kDoEnd) { ostr << "end\n"; } else if (m.type() == message_t::kTry) { + print_debug(m, ostr); ostr << "group try\n"; } else if (m.type() == message_t::kCatch) { + print_debug(m, ostr); ostr << "else " << m.message_name() << '\n'; } else if (m.type() == message_t::kTryEnd) { + print_debug(m, ostr); ostr << "end\n"; } else if (m.type() == message_t::kSwitch) { + print_debug(m, ostr); ostr << "group switch\n"; } else if (m.type() == message_t::kCase) { + print_debug(m, ostr); ostr << "else " << m.message_name() << '\n'; } else if (m.type() == message_t::kSwitchEnd) { ostr << "end\n"; } else if (m.type() == message_t::kConditional) { + print_debug(m, ostr); ostr << "alt\n"; } else if (m.type() == message_t::kElse) { + print_debug(m, ostr); ostr << "else\n"; } else if (m.type() == message_t::kConditionalEnd) { @@ -271,6 +294,8 @@ void generator::generate_participant( const auto &class_participant = m_model.get_participant(class_id).value(); + print_debug(class_participant, ostr); + ostr << "participant \"" << render_name(m_config.using_namespace().relative( class_participant.full_name(false))) @@ -302,7 +327,7 @@ void generator::generate_participant( if (is_participant_generated(file_id)) return; - [[maybe_unused]] const auto &relative_to = + const auto &relative_to = std::filesystem::canonical(m_config.relative_to()); auto participant_name = std::filesystem::relative( @@ -317,6 +342,8 @@ void generator::generate_participant( generated_participants_.emplace(file_id); } else { + print_debug(participant, ostr); + ostr << "participant \"" << m_config.using_namespace().relative( participant.full_name(false)) @@ -331,8 +358,6 @@ void generator::generate_participant( generated_participants_.emplace(participant_id); } - - return; } bool generator::is_participant_generated(common::id_t id) const @@ -359,7 +384,7 @@ void generator::generate(std::ostream &ostr) const for (const auto &sf : m_config.start_from()) { if (sf.location_type == source_location::location_t::function) { - common::model::diagram_element::id_t start_from; + common::model::diagram_element::id_t start_from{0}; for (const auto &[k, v] : m_model.sequences()) { const auto &caller = *m_model.participants().at(v.from()); std::string vfrom = caller.full_name(false); @@ -370,6 +395,13 @@ void generator::generate(std::ostream &ostr) const } } + if (start_from == 0) { + LOG_WARN("Failed to find participant with {} for start_from " + "condition", + sf.location); + continue; + } + // Use this to break out of recurrent loops std::vector visited_participants; @@ -388,13 +420,22 @@ void generator::generate(std::ostream &ostr) const std::string from_alias = generate_alias(from.value()); + model::function::message_render_mode render_mode = + model::function::message_render_mode::full; + + if (m_config.generate_method_arguments() == + config::method_arguments::abbreviated) + render_mode = model::function::message_render_mode::abbreviated; + else if (m_config.generate_method_arguments() == + config::method_arguments::none) + render_mode = + model::function::message_render_mode::no_arguments; + if (from.value().type_name() == "method" || m_config.combine_free_functions_into_file_participants()) { ostr << "[->" << " " << from_alias << " : " - << from.value().message_name( - model::function::message_render_mode::full) - << std::endl; + << from.value().message_name(render_mode) << std::endl; } ostr << "activate " << from_alias << std::endl; @@ -437,4 +478,4 @@ std::string generator::generate_alias( return participant.alias(); } -} +} // namespace clanguml::sequence_diagram::generators::plantuml diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h index 07e99c3c..d86c8ea7 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,7 +62,7 @@ public: std::ostream &ostr, std::vector &visited) const; - void generate(std::ostream &ostr) const; + void generate(std::ostream &ostr) const override; private: bool is_participant_generated(common::id_t id) const; @@ -73,7 +73,7 @@ private: std::string generate_alias(const model::participant &participant) const; }; -} -} -} -} +} // namespace plantuml +} // namespace generators +} // namespace sequence_diagram +} // namespace clanguml diff --git a/src/sequence_diagram/model/activity.cc b/src/sequence_diagram/model/activity.cc index e2c921ca..b8c2438d 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,4 +35,4 @@ void activity::set_from(common::model::diagram_element::id_t f) { from_ = f; } common::model::diagram_element::id_t activity::from() const { return from_; } -} +} // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/model/activity.h b/src/sequence_diagram/model/activity.h index 2622ad4c..64477642 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,4 +44,4 @@ private: std::vector messages_; }; -} +} // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/model/diagram.cc b/src/sequence_diagram/model/diagram.cc index de7b7980..5947d4d6 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,228 +96,42 @@ activity &diagram::get_activity(common::model::diagram_element::id_t id) return sequences_.at(id); } -void diagram::add_for_stmt( - const common::model::diagram_element::id_t current_caller_id) +void diagram::add_message(model::message &&message) { - add_loop_stmt(current_caller_id, common::model::message_t::kFor); -} - -void diagram::end_for_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - end_loop_stmt(current_caller_id, common::model::message_t::kForEnd); -} - -void diagram::add_while_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - add_loop_stmt(current_caller_id, common::model::message_t::kWhile); -} - -void diagram::end_while_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - end_loop_stmt(current_caller_id, common::model::message_t::kWhileEnd); -} - -void diagram::add_do_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - add_loop_stmt(current_caller_id, common::model::message_t::kDo); -} - -void diagram::end_do_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - end_loop_stmt(current_caller_id, common::model::message_t::kDoEnd); -} - -void diagram::add_loop_stmt( - const common::model::diagram_element::id_t current_caller_id, - common::model::message_t type) -{ - using clanguml::common::model::message_t; - - if (current_caller_id == 0) - return; - - if (sequences_.find(current_caller_id) == sequences_.end()) { - activity a{current_caller_id}; - sequences_.insert({current_caller_id, std::move(a)}); + const auto caller_id = message.from(); + if (sequences_.find(caller_id) == sequences_.end()) { + activity a{caller_id}; + sequences_.insert({caller_id, std::move(a)}); } - get_activity(current_caller_id).add_message({type, current_caller_id}); + get_activity(caller_id).add_message(std::move(message)); } -void diagram::end_loop_stmt( - const common::model::diagram_element::id_t current_caller_id, - common::model::message_t type) +void diagram::add_block_message(model::message &&message) { - using clanguml::common::model::message_t; + add_message(std::move(message)); +} - if (current_caller_id == 0) - return; +void diagram::end_block_message( + model::message &&message, common::model::message_t start_type) +{ + const auto caller_id = message.from(); - message m{type, current_caller_id}; + if (sequences_.find(caller_id) != sequences_.end()) { + auto ¤t_messages = get_activity(caller_id).messages(); - message_t loop_type = message_t::kWhile; - - if (type == message_t::kForEnd) - loop_type = message_t::kFor; - else if (type == message_t::kDoEnd) - loop_type = message_t::kDo; - - if (sequences_.find(current_caller_id) != sequences_.end()) { - auto ¤t_messages = get_activity(current_caller_id).messages(); - - if (current_messages.back().type() == loop_type) { - current_messages.pop_back(); - } - else { - current_messages.emplace_back(std::move(m)); - } + fold_or_end_block_statement( + std::move(message), start_type, current_messages); } } -void diagram::add_if_stmt( - const common::model::diagram_element::id_t current_caller_id, - common::model::message_t type) +void diagram::add_case_stmt_message(model::message &&m) { using clanguml::common::model::message_t; + const auto caller_id = m.from(); - if (sequences_.find(current_caller_id) == sequences_.end()) { - activity a{current_caller_id}; - sequences_.insert({current_caller_id, std::move(a)}); - } - - get_activity(current_caller_id).add_message({type, current_caller_id}); -} - -void diagram::end_if_stmt( - const common::model::diagram_element::id_t current_caller_id, - common::model::message_t type) -{ - using clanguml::common::model::message_t; - - message m{message_t::kIfEnd, current_caller_id}; - - if (sequences_.find(current_caller_id) != sequences_.end()) { - - auto ¤t_messages = get_activity(current_caller_id).messages(); - // Remove the if/else messages if there were no calls - // added to the diagram between them - auto last_if_it = - std::find_if(current_messages.rbegin(), current_messages.rend(), - [](const message &m) { return m.type() == message_t::kIf; }); - - bool last_if_block_is_empty = - std::none_of(current_messages.rbegin(), last_if_it, - [](const message &m) { return m.type() == message_t::kCall; }); - - if (!last_if_block_is_empty) { - current_messages.emplace_back(std::move(m)); - } - else { - current_messages.erase( - (last_if_it + 1).base(), current_messages.end()); - } - } -} - -void diagram::add_try_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - if (sequences_.find(current_caller_id) == sequences_.end()) { - activity a{current_caller_id}; - sequences_.insert({current_caller_id, std::move(a)}); - } - - get_activity(current_caller_id) - .add_message({message_t::kTry, current_caller_id}); -} - -void diagram::end_try_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - message m{message_t::kTryEnd, current_caller_id}; - - if (sequences_.find(current_caller_id) != sequences_.end()) { - auto ¤t_messages = get_activity(current_caller_id).messages(); - - if (current_messages.back().type() == message_t::kTry) { - current_messages.pop_back(); - } - else { - current_messages.emplace_back(std::move(m)); - } - } -} - -void diagram::add_catch_stmt( - const int64_t current_caller_id, std::string caught_type) -{ - using clanguml::common::model::message_t; - - if (sequences_.find(current_caller_id) == sequences_.end()) { - activity a{current_caller_id}; - sequences_.insert({current_caller_id, std::move(a)}); - } - - message m{message_t::kCatch, current_caller_id}; - m.set_message_name(std::move(caught_type)); - - get_activity(current_caller_id).add_message(std::move(m)); -} - -void diagram::add_switch_stmt( - common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - if (sequences_.find(current_caller_id) == sequences_.end()) { - activity a{current_caller_id}; - sequences_.insert({current_caller_id, std::move(a)}); - } - - message m{message_t::kSwitch, current_caller_id}; - - get_activity(current_caller_id).add_message(std::move(m)); -} - -void diagram::end_switch_stmt( - common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - message m{message_t::kTryEnd, current_caller_id}; - - if (sequences_.find(current_caller_id) != sequences_.end()) { - auto ¤t_messages = get_activity(current_caller_id).messages(); - - if (current_messages.back().type() == message_t::kSwitch) { - current_messages.pop_back(); - } - else { - current_messages.emplace_back(std::move(m)); - } - } -} - -void diagram::add_case_stmt( - common::model::diagram_element::id_t current_caller_id, - const std::string &case_label) -{ - using clanguml::common::model::message_t; - - message m{message_t::kCase, current_caller_id}; - m.set_message_name(case_label); - - if (sequences_.find(current_caller_id) != sequences_.end()) { - auto ¤t_messages = get_activity(current_caller_id).messages(); + if (sequences_.find(caller_id) != sequences_.end()) { + auto ¤t_messages = get_activity(caller_id).messages(); if (current_messages.back().type() == message_t::kCase) { // Do nothing - fallthroughs not supported yet... @@ -328,72 +142,6 @@ void diagram::add_case_stmt( } } -void diagram::add_default_stmt( - common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - message m{message_t::kCase, current_caller_id}; - m.set_message_name("default"); - - if (sequences_.find(current_caller_id) != sequences_.end()) { - auto ¤t_messages = get_activity(current_caller_id).messages(); - - if (current_messages.back().type() == message_t::kCase) { - current_messages.pop_back(); - } - else { - current_messages.emplace_back(std::move(m)); - } - } -} - -void diagram::add_conditional_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - if (sequences_.find(current_caller_id) == sequences_.end()) { - activity a{current_caller_id}; - sequences_.insert({current_caller_id, std::move(a)}); - } - - get_activity(current_caller_id) - .add_message({message_t::kConditional, current_caller_id}); -} - -void diagram::add_conditional_elsestmt( - const common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - get_activity(current_caller_id) - .add_message({message_t::kElse, current_caller_id}); -} - -void diagram::end_conditional_stmt( - const common::model::diagram_element::id_t current_caller_id) -{ - using clanguml::common::model::message_t; - - message m{message_t::kConditionalEnd, current_caller_id}; - - if (sequences_.find(current_caller_id) != sequences_.end()) { - auto ¤t_messages = get_activity(current_caller_id).messages(); - - if (current_messages.at(current_messages.size() - 1).type() == - message_t::kElse && - current_messages.at(current_messages.size() - 2).type() == - message_t::kConditional) { - current_messages.pop_back(); - current_messages.pop_back(); - } - else { - current_messages.emplace_back(std::move(m)); - } - } -} - bool diagram::started() const { return started_; } void diagram::started(bool s) { started_ = s; } @@ -475,7 +223,32 @@ void diagram::print() const } } } + +void diagram::fold_or_end_block_statement(message &&m, + const common::model::message_t statement_begin, + std::vector ¤t_messages) const +{ + bool is_empty_statement{true}; + + auto rit = current_messages.rbegin(); + for (; rit != current_messages.rend(); rit++) { + if (rit->type() == statement_begin) { + break; + } + if (rit->type() == common::model::message_t::kCall) { + is_empty_statement = false; + break; + } + } + + if (is_empty_statement) { + current_messages.erase((rit + 1).base(), current_messages.end()); + } + else { + current_messages.emplace_back(std::move(m)); + } } +} // namespace clanguml::sequence_diagram::model namespace clanguml::common::model { template <> diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 439bea41..631e5017 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ public: const std::string &full_name) const override; common::optional_ref get( - const common::model::diagram_element::id_t id) const override; + common::model::diagram_element::id_t id) const override; std::string to_alias(const std::string &full_name) const; @@ -63,7 +63,7 @@ public: } template - const common::optional_ref get_participant( + common::optional_ref get_participant( common::model::diagram_element::id_t id) const { if (participants_.find(id) == participants_.end()) { @@ -80,44 +80,14 @@ public: activity &get_activity(common::model::diagram_element::id_t id); - void add_if_stmt(common::model::diagram_element::id_t current_caller_id, - common::model::message_t type); - void end_if_stmt(common::model::diagram_element::id_t current_caller_id, - common::model::message_t type); + void add_message(model::message &&message); - void add_try_stmt(common::model::diagram_element::id_t current_caller_id); - void end_try_stmt(common::model::diagram_element::id_t current_caller_id); + void add_block_message(model::message &&message); - void add_loop_stmt(common::model::diagram_element::id_t current_caller_id, - common::model::message_t type); - void end_loop_stmt(common::model::diagram_element::id_t current_caller_id, - common::model::message_t type); + void end_block_message( + model::message &&message, common::model::message_t start_type); - void add_while_stmt(common::model::diagram_element::id_t current_caller_id); - void end_while_stmt(common::model::diagram_element::id_t current_caller_id); - - void add_do_stmt(common::model::diagram_element::id_t current_caller_id); - void end_do_stmt(common::model::diagram_element::id_t current_caller_id); - - void add_for_stmt(common::model::diagram_element::id_t current_caller_id); - void end_for_stmt(common::model::diagram_element::id_t current_caller_id); - - void add_switch_stmt( - common::model::diagram_element::id_t current_caller_id); - void end_switch_stmt( - common::model::diagram_element::id_t current_caller_id); - void add_case_stmt(common::model::diagram_element::id_t current_caller_id); - void add_case_stmt(common::model::diagram_element::id_t current_caller_id, - const std::string &case_label); - void add_default_stmt( - common::model::diagram_element::id_t current_caller_id); - - void add_conditional_stmt( - common::model::diagram_element::id_t current_caller_id); - void add_conditional_elsestmt( - common::model::diagram_element::id_t current_caller_id); - void end_conditional_stmt( - common::model::diagram_element::id_t current_caller_id); + void add_case_stmt_message(model::message &&m); bool started() const; void started(bool s); @@ -139,11 +109,11 @@ public: const std::set & active_participants() const; - void add_catch_stmt( - const common::model::diagram_element::id_t current_caller_id, - std::string caught_type); - private: + void fold_or_end_block_statement(message &&m, + common::model::message_t statement_begin, + std::vector ¤t_messages) const; + bool started_{false}; std::map sequences_; @@ -154,7 +124,7 @@ private: std::set active_participants_; }; -} +} // namespace clanguml::sequence_diagram::model namespace clanguml::common::model { template <> diff --git a/src/sequence_diagram/model/message.cc b/src/sequence_diagram/model/message.cc index 19a7fb21..180b3c16 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,4 +57,4 @@ void message::set_message_scope(common::model::message_scope_t scope) common::model::message_scope_t message::message_scope() const { return scope_; } -} +} // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/model/message.h b/src/sequence_diagram/model/message.h index c25c0f2d..81cb67a6 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,4 +67,4 @@ private: std::string return_type_{}; }; -} +} // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/model/participant.cc b/src/sequence_diagram/model/participant.cc index fdf61a28..b898d818 100644 --- a/src/sequence_diagram/model/participant.cc +++ b/src/sequence_diagram/model/participant.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/participant.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,16 +68,14 @@ template_trait::templates() const } int template_trait::calculate_template_specialization_match( - const template_trait &other, const std::string &full_name) const + const template_trait &other, const std::string & /*full_name*/) const { int res{}; - // std::string left = name_and_ns(); - // // TODO: handle variadic templates - // if ((name_and_ns() != full_name) || - // (templates().size() != other.templates().size())) { - // return res; - // } + // TODO: handle variadic templates + if (templates().size() != other.templates().size()) { + return res; + } // Iterate over all template arguments for (auto i = 0U; i < other.templates().size(); i++) { @@ -193,9 +191,18 @@ std::string function::full_name_no_ns() const std::string function::message_name(message_render_mode mode) const { + constexpr auto kAbbreviatedMethodArgumentsLength{15}; + if (mode == message_render_mode::no_arguments) { return fmt::format("{}(){}", name(), is_const() ? " const" : ""); } + if (mode == message_render_mode::abbreviated) { + return fmt::format("{}({}){}", name(), + clanguml::util::abbreviate( + fmt::format("{}", fmt::join(parameters_, ",")), + kAbbreviatedMethodArgumentsLength), + is_const() ? " const" : ""); + } return fmt::format("{}({}){}", name(), fmt::join(parameters_, ","), is_const() ? " const" : ""); @@ -225,7 +232,7 @@ method::method(const common::model::namespace_ &using_namespace) { } -const std::string method::method_name() const { return method_name_; } +std::string method::method_name() const { return method_name_; } std::string method::alias() const { @@ -253,12 +260,21 @@ std::string method::full_name(bool /*relative*/) const std::string method::message_name(message_render_mode mode) const { + constexpr auto kAbbreviatedMethodArgumentsLength{15}; + const std::string style = is_static() ? "__" : ""; if (mode == message_render_mode::no_arguments) { return fmt::format("{}{}(){}{}", style, method_name(), is_const() ? " const" : "", style); } + if (mode == message_render_mode::abbreviated) { + return fmt::format("{}({}){}", name(), + clanguml::util::abbreviate( + fmt::format("{}", fmt::join(parameters(), ",")), + kAbbreviatedMethodArgumentsLength), + is_const() ? " const" : ""); + } return fmt::format("{}{}({}){}{}", style, method_name(), fmt::join(parameters(), ","), is_const() ? " const" : "", style); @@ -322,6 +338,8 @@ std::string function_template::full_name_no_ns() const std::string function_template::message_name(message_render_mode mode) const { + constexpr auto kAbbreviatedMethodArgumentsLength{15}; + std::ostringstream s; render_template_params(s, using_namespace(), true); std::string template_params = s.str(); @@ -330,9 +348,16 @@ std::string function_template::message_name(message_render_mode mode) const return fmt::format( "{}{}(){}", name(), template_params, is_const() ? " const" : ""); } + if (mode == message_render_mode::abbreviated) { + return fmt::format("{}({}){}", name(), + clanguml::util::abbreviate( + fmt::format("{}", fmt::join(parameters(), ",")), + kAbbreviatedMethodArgumentsLength), + is_const() ? " const" : ""); + } return fmt::format("{}{}({}){}", name(), template_params, fmt::join(parameters(), ","), is_const() ? " const" : ""); } -} \ No newline at end of file +} // namespace clanguml::sequence_diagram::model \ No newline at end of file diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index dce84dec..061635e2 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/model/participant.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -135,7 +135,7 @@ struct lambda : public class_ { }; struct function : public participant { - enum class message_render_mode { full, no_arguments }; + enum class message_render_mode { full, abbreviated, no_arguments }; function(const common::model::namespace_ &using_namespace); @@ -185,7 +185,7 @@ struct method : public function { std::string type_name() const override { return "method"; } - const std::string method_name() const; + std::string method_name() const; std::string alias() const override; @@ -206,7 +206,7 @@ struct method : public function { std::string to_string() const override; private: - diagram_element::id_t class_id_; + diagram_element::id_t class_id_{}; std::string method_name_; std::string class_full_name_; }; @@ -227,4 +227,4 @@ struct function_template : public function, public template_trait { std::string message_name(message_render_mode mode) const override; }; -} +} // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/visitor/call_expression_context.cc b/src/sequence_diagram/visitor/call_expression_context.cc index dc8afcc2..0c0999b8 100644 --- a/src/sequence_diagram/visitor/call_expression_context.cc +++ b/src/sequence_diagram/visitor/call_expression_context.cc @@ -1,7 +1,7 @@ /** * src/sequence_diagram/visitor/call_expression_context.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,16 +20,7 @@ namespace clanguml::sequence_diagram::visitor { -call_expression_context::call_expression_context() - : current_class_decl_{nullptr} - , current_class_template_decl_{nullptr} - , current_class_template_specialization_decl_{nullptr} - , current_method_decl_{nullptr} - , current_function_decl_{nullptr} - , current_function_template_decl_{nullptr} - , current_caller_id_{0} -{ -} +call_expression_context::call_expression_context() = default; void call_expression_context::reset() { @@ -66,21 +57,25 @@ bool call_expression_context::valid() const (current_function_template_decl_ != nullptr); } -clang::ASTContext *call_expression_context::get_ast_context() +clang::ASTContext *call_expression_context::get_ast_context() const { - if (current_class_template_specialization_decl_) + if (current_class_template_specialization_decl_ != nullptr) return ¤t_class_template_specialization_decl_->getASTContext(); - if (current_class_template_decl_) + if (current_class_template_decl_ != nullptr) return ¤t_class_template_decl_->getASTContext(); - if (current_class_decl_) + if (current_class_decl_ != nullptr) return ¤t_class_decl_->getASTContext(); - if (current_function_template_decl_) + if (current_function_template_decl_ != nullptr) return ¤t_function_template_decl_->getASTContext(); - return ¤t_function_decl_->getASTContext(); + if (current_function_decl_ != nullptr) { + return ¤t_function_decl_->getASTContext(); + } + + return nullptr; } void call_expression_context::update(clang::CXXRecordDecl *cls) @@ -113,7 +108,7 @@ void call_expression_context::update(clang::FunctionDecl *function) // Check if this function is a part of template function declaration, // If no - reset the current_function_template_decl_ - if (current_function_template_decl_ && + if ((current_function_template_decl_ != nullptr) && current_function_template_decl_->getQualifiedNameAsString() != function->getQualifiedNameAsString()) { current_function_template_decl_ = nullptr; @@ -327,13 +322,13 @@ void call_expression_context::leave_conditionaloperator() bool call_expression_context::is_expr_in_current_control_statement_condition( const clang::Stmt *stmt) const { - if (current_ifstmt()) { + if (current_ifstmt() != nullptr) { if (common::is_subexpr_of(current_ifstmt()->getCond(), stmt)) { return true; } } - if (current_elseifstmt()) { + if (current_elseifstmt() != nullptr) { if (common::is_subexpr_of(current_elseifstmt()->getCond(), stmt)) { return true; } @@ -376,7 +371,7 @@ bool call_expression_context::is_expr_in_current_control_statement_condition( } } - if (current_conditionaloperator()) { + if (current_conditionaloperator() != nullptr) { if (common::is_subexpr_of( current_conditionaloperator()->getCond(), stmt)) { return true; @@ -387,4 +382,4 @@ bool call_expression_context::is_expr_in_current_control_statement_condition( return false; } -} \ No newline at end of file +} // namespace clanguml::sequence_diagram::visitor \ No newline at end of file diff --git a/src/sequence_diagram/visitor/call_expression_context.h b/src/sequence_diagram/visitor/call_expression_context.h index cde69033..b4f1a6b8 100644 --- a/src/sequence_diagram/visitor/call_expression_context.h +++ b/src/sequence_diagram/visitor/call_expression_context.h @@ -1,7 +1,7 @@ /** * src/sequence_diagram/visitor/call_expression_context.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ struct call_expression_context { bool valid() const; - clang::ASTContext *get_ast_context(); + clang::ASTContext *get_ast_context() const; void update(clang::CXXRecordDecl *cls); @@ -99,16 +99,16 @@ struct call_expression_context { bool is_expr_in_current_control_statement_condition( const clang::Stmt *stmt) const; - clang::CXXRecordDecl *current_class_decl_; - clang::ClassTemplateDecl *current_class_template_decl_; + clang::CXXRecordDecl *current_class_decl_{nullptr}; + clang::ClassTemplateDecl *current_class_template_decl_{nullptr}; clang::ClassTemplateSpecializationDecl - *current_class_template_specialization_decl_; - clang::CXXMethodDecl *current_method_decl_; - clang::FunctionDecl *current_function_decl_; - clang::FunctionTemplateDecl *current_function_template_decl_; + *current_class_template_specialization_decl_{nullptr}; + clang::CXXMethodDecl *current_method_decl_{nullptr}; + clang::FunctionDecl *current_function_decl_{nullptr}; + clang::FunctionTemplateDecl *current_function_template_decl_{nullptr}; private: - std::int64_t current_caller_id_; + std::int64_t current_caller_id_{0}; std::stack current_lambda_caller_id_; std::stack call_expr_stack_; @@ -122,4 +122,4 @@ private: std::stack conditional_operator_stack_; }; -} \ No newline at end of file +} // namespace clanguml::sequence_diagram::visitor \ No newline at end of file diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index b0691110..de5a4bbf 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,6 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, : common::visitor::translation_unit_visitor{sm, config} , diagram_{diagram} , config_{config} - , call_expression_context_{} { } @@ -65,173 +64,159 @@ translation_unit_visitor::config() const return config_; } -bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) +bool translation_unit_visitor::VisitCXXRecordDecl( + clang::CXXRecordDecl *declaration) { - // Skip system headers - if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) + if (!should_include(declaration)) return true; - if (!diagram().should_include(cls->getQualifiedNameAsString())) - return true; - - if (cls->isTemplated() && cls->getDescribedTemplate()) { - // If the described templated of this class is already in the model - // skip it: - auto local_id = cls->getDescribedTemplate()->getID(); - if (get_unique_id(local_id)) + // Skip this class if it's parent template is already in the model + if (declaration->isTemplated() && + declaration->getDescribedTemplate() != nullptr) { + if (get_unique_id(declaration->getDescribedTemplate()->getID())) return true; } // TODO: Add support for classes defined in function/method bodies - if (cls->isLocalClass()) + if (declaration->isLocalClass() != nullptr) return true; LOG_TRACE("Visiting class declaration at {}", - cls->getBeginLoc().printToString(source_manager())); + declaration->getBeginLoc().printToString(source_manager())); // Build the class declaration and store it in the diagram, even // if we don't need it for any of the participants of this diagram - auto c_ptr = this->create_class_declaration(cls); + auto class_model_ptr = create_class_model(declaration); - if (!c_ptr) + if (!class_model_ptr) return true; context().reset(); - const auto cls_id = c_ptr->id(); + const auto class_id = class_model_ptr->id(); - set_unique_id(cls->getID(), cls_id); + set_unique_id(declaration->getID(), class_id); auto &class_model = diagram() - .get_participant(cls_id) + .get_participant(class_id) .has_value() ? *diagram() - .get_participant(cls_id) + .get_participant(class_id) .get() - : *c_ptr; + : *class_model_ptr; - auto id = class_model.id(); - if (!cls->isCompleteDefinition()) { - forward_declarations_.emplace(id, std::move(c_ptr)); + if (!declaration->isCompleteDefinition()) { + forward_declarations_.emplace(class_id, std::move(class_model_ptr)); return true; } - else { - forward_declarations_.erase(id); - } + + forward_declarations_.erase(class_id); if (diagram().should_include(class_model)) { LOG_DBG("Adding class {} with id {}", class_model.full_name(false), class_model.id()); - assert(class_model.id() == cls_id); + assert(class_model.id() == class_id); - context().set_caller_id(cls_id); - context().update(cls); + context().set_caller_id(class_id); + context().update(declaration); - diagram().add_participant(std::move(c_ptr)); + diagram().add_participant(std::move(class_model_ptr)); } else { - LOG_DBG("Skipping class {} with id {}", class_model.full_name(), - class_model.id()); + LOG_DBG( + "Skipping class {} with id {}", class_model.full_name(), class_id); } return true; } bool translation_unit_visitor::VisitClassTemplateDecl( - clang::ClassTemplateDecl *cls) + clang::ClassTemplateDecl *declaration) { - if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) - return true; - - if (!diagram().should_include(cls->getQualifiedNameAsString())) + if (!should_include(declaration)) return true; LOG_TRACE("Visiting class template declaration {} at {} [{}]", - cls->getQualifiedNameAsString(), - cls->getLocation().printToString(source_manager()), (void *)cls); + declaration->getQualifiedNameAsString(), + declaration->getLocation().printToString(source_manager()), + (void *)declaration); - auto c_ptr = create_class_declaration(cls->getTemplatedDecl()); + auto class_model_ptr = create_class_model(declaration->getTemplatedDecl()); - if (!c_ptr) + if (!class_model_ptr) return true; // Override the id with the template id, for now we don't care about the // underlying templated class id - process_template_parameters(*cls, *c_ptr); + process_template_parameters(*declaration, *class_model_ptr); - const auto cls_full_name = c_ptr->full_name(false); - const auto id = common::to_id(cls_full_name); + const auto class_full_name = class_model_ptr->full_name(false); + const auto id = common::to_id(class_full_name); - c_ptr->set_id(id); + class_model_ptr->set_id(id); - set_unique_id(cls->getID(), id); + set_unique_id(declaration->getID(), id); - if (!cls->getTemplatedDecl()->isCompleteDefinition()) { - forward_declarations_.emplace(id, std::move(c_ptr)); + if (!declaration->getTemplatedDecl()->isCompleteDefinition()) { + forward_declarations_.emplace(id, std::move(class_model_ptr)); return true; } - else { - forward_declarations_.erase(id); - } + forward_declarations_.erase(id); - if (diagram().should_include(*c_ptr)) { - LOG_DBG("Adding class template {} with id {}", cls_full_name, id); + if (diagram().should_include(*class_model_ptr)) { + LOG_DBG("Adding class template {} with id {}", class_full_name, id); context().set_caller_id(id); - context().update(cls); + context().update(declaration); - diagram().add_participant(std::move(c_ptr)); + diagram().add_participant(std::move(class_model_ptr)); } return true; } bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( - clang::ClassTemplateSpecializationDecl *cls) + clang::ClassTemplateSpecializationDecl *declaration) { - if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) - return true; - - if (!diagram().should_include(cls->getQualifiedNameAsString())) + if (!should_include(declaration)) return true; LOG_TRACE("Visiting template specialization declaration {} at {}", - cls->getQualifiedNameAsString(), - cls->getLocation().printToString(source_manager())); + declaration->getQualifiedNameAsString(), + declaration->getLocation().printToString(source_manager())); // TODO: Add support for classes defined in function/method bodies - if (cls->isLocalClass()) + if (declaration->isLocalClass() != nullptr) return true; - auto template_specialization_ptr = process_template_specialization(cls); + auto template_specialization_ptr = + process_template_specialization(declaration); if (!template_specialization_ptr) return true; - const auto cls_full_name = template_specialization_ptr->full_name(false); - const auto id = common::to_id(cls_full_name); + const auto class_full_name = template_specialization_ptr->full_name(false); + const auto id = common::to_id(class_full_name); template_specialization_ptr->set_id(id); - set_unique_id(cls->getID(), id); + set_unique_id(declaration->getID(), id); - if (!cls->isCompleteDefinition()) { + if (!declaration->isCompleteDefinition()) { forward_declarations_.emplace( id, std::move(template_specialization_ptr)); return true; } - else { - forward_declarations_.erase(id); - } + forward_declarations_.erase(id); if (diagram().should_include(*template_specialization_ptr)) { LOG_DBG("Adding class template specialization {} with id {}", - cls_full_name, id); + class_full_name, id); context().set_caller_id(id); - context().update(cls); + context().update(declaration); diagram().add_participant(std::move(template_specialization_ptr)); } @@ -239,231 +224,171 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( return true; } -bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) +bool translation_unit_visitor::VisitCXXMethodDecl( + clang::CXXMethodDecl *declaration) { - if (!diagram().should_include(m->getParent()->getQualifiedNameAsString())) + if (!should_include(declaration)) return true; - if (!m->isThisDeclarationADefinition()) { - if (m->getDefinition()) - return VisitCXXMethodDecl( - static_cast(m->getDefinition())); + if (!declaration->isThisDeclarationADefinition()) { + if (auto *declaration_definition = declaration->getDefinition(); + declaration_definition != nullptr) { + if (auto *method_definition = clang::dyn_cast( + declaration_definition); + method_definition != nullptr) { + return VisitCXXMethodDecl(method_definition); + } + } } LOG_TRACE("Visiting method {} in class {} [{}]", - m->getQualifiedNameAsString(), - m->getParent()->getQualifiedNameAsString(), (void *)m->getParent()); + declaration->getQualifiedNameAsString(), + declaration->getParent()->getQualifiedNameAsString(), + (void *)declaration->getParent()); - context().update(m); + context().update(declaration); - auto m_ptr = std::make_unique( - config().using_namespace()); + auto method_model_ptr = create_method_model(declaration); - common::model::namespace_ ns{m->getQualifiedNameAsString()}; - auto method_name = ns.name(); - m_ptr->set_method_name(method_name); - ns.pop_back(); - m_ptr->set_name(ns.name()); - ns.pop_back(); - - clang::Decl *parent_decl = m->getParent(); - - if (context().current_class_template_decl_) - parent_decl = context().current_class_template_decl_; - - LOG_DBG("Getting method's class with local id {}", parent_decl->getID()); - - if (!get_participant(parent_decl)) { - LOG_INFO("Cannot find parent class_ for method {} in class {}", - m->getQualifiedNameAsString(), - m->getParent()->getQualifiedNameAsString()); + if (!method_model_ptr) return true; - } - const auto &method_class = - get_participant(parent_decl).value(); + process_comment(*declaration, *method_model_ptr); - m_ptr->is_void(m->getReturnType()->isVoidType()); + set_source_location(*declaration, *method_model_ptr); - m_ptr->set_class_id(method_class.id()); - m_ptr->set_class_full_name(method_class.full_name(false)); - m_ptr->set_name( - get_participant(m_ptr->class_id()).value().full_name_no_ns() + - "::" + m->getNameAsString()); - m_ptr->is_static(m->isStatic()); + const auto method_full_name = method_model_ptr->full_name(false); - for (const auto *param : m->parameters()) { - m_ptr->add_parameter(config().using_namespace().relative( - simplify_system_template(common::to_string( - param->getType(), m->getASTContext(), false)))); - } - - process_comment(*m, *m_ptr); - - set_source_location(*m, *m_ptr); - - const auto method_full_name = m_ptr->full_name(false); - - m_ptr->set_id(common::to_id(method_full_name)); + method_model_ptr->set_id(common::to_id(method_full_name)); // Callee methods in call expressions are referred to by first declaration // id - if (m->isThisDeclarationADefinition()) { - set_unique_id(m->getFirstDecl()->getID(), m_ptr->id()); + if (declaration->isThisDeclarationADefinition()) { + set_unique_id( + declaration->getFirstDecl()->getID(), method_model_ptr->id()); } - set_unique_id(m->getID(), m_ptr->id()); // This is probably not necessary? + set_unique_id(declaration->getID(), method_model_ptr->id()); - LOG_TRACE("Set id {} --> {} for method name {} [{}]", m->getID(), - m_ptr->id(), method_full_name, m->isThisDeclarationADefinition()); + LOG_TRACE("Set id {} --> {} for method name {} [{}]", declaration->getID(), + method_model_ptr->id(), method_full_name, + declaration->isThisDeclarationADefinition()); - context().update(m); + context().update(declaration); - context().set_caller_id(m_ptr->id()); + context().set_caller_id(method_model_ptr->id()); - diagram().add_participant(std::move(m_ptr)); + diagram().add_participant(std::move(method_model_ptr)); return true; } -bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f) +bool translation_unit_visitor::VisitFunctionDecl( + clang::FunctionDecl *declaration) { - if (f->isCXXClassMember()) + if (declaration->isCXXClassMember()) return true; - const auto function_name = f->getQualifiedNameAsString(); - - if (!diagram().should_include(function_name)) + if (!should_include(declaration)) return true; - if (!f->isThisDeclarationADefinition()) { - if (f->getDefinition()) + if (!declaration->isThisDeclarationADefinition()) { + if (auto *declaration_definition = declaration->getDefinition(); + declaration_definition != nullptr) return VisitFunctionDecl( - static_cast(f->getDefinition())); + static_cast(declaration_definition)); } - LOG_TRACE("Visiting function declaration {} at {}", function_name, - f->getLocation().printToString(source_manager())); + LOG_TRACE("Visiting function declaration {} at {}", + declaration->getQualifiedNameAsString(), + declaration->getLocation().printToString(source_manager())); - if (f->isTemplated()) { - if (f->getDescribedTemplate()) { + if (declaration->isTemplated()) { + if (declaration->getDescribedTemplate() != nullptr) { // If the described templated of this function is already in the // model skip it: - if (get_unique_id(f->getDescribedTemplate()->getID())) + if (get_unique_id(declaration->getDescribedTemplate()->getID())) return true; } } - if (f->isFunctionTemplateSpecialization()) { - auto f_ptr = build_function_template_instantiation(*f); + std::unique_ptr function_model_ptr{}; - f_ptr->set_id(common::to_id(f_ptr->full_name(false))); - - f_ptr->is_void(f->getReturnType()->isVoidType()); - - context().update(f); - - context().set_caller_id(f_ptr->id()); - - if (f->isThisDeclarationADefinition()) { - set_unique_id(f->getFirstDecl()->getID(), f_ptr->id()); - } - set_unique_id(f->getID(), f_ptr->id()); - - process_comment(*f, *f_ptr); - - set_source_location(*f, *f_ptr); - - // TODO: Handle overloaded functions with different arguments - diagram().add_participant(std::move(f_ptr)); + if (declaration->isFunctionTemplateSpecialization()) { + function_model_ptr = + build_function_template_instantiation(*declaration); } else { - auto f_ptr = std::make_unique( - config().using_namespace()); - - common::model::namespace_ ns{function_name}; - f_ptr->set_name(ns.name()); - ns.pop_back(); - f_ptr->set_namespace(ns); - - for (const auto *param : f->parameters()) { - f_ptr->add_parameter(simplify_system_template(common::to_string( - param->getType(), f->getASTContext(), false))); - } - - f_ptr->set_id(common::to_id(f_ptr->full_name(false))); - - f_ptr->is_void(f->getReturnType()->isVoidType()); - - context().update(f); - - context().set_caller_id(f_ptr->id()); - - if (f->isThisDeclarationADefinition()) { - set_unique_id(f->getFirstDecl()->getID(), f_ptr->id()); - } - set_unique_id(f->getID(), f_ptr->id()); - - process_comment(*f, *f_ptr); - - set_source_location(*f, *f_ptr); - - // TODO: Handle overloaded functions with different arguments - diagram().add_participant(std::move(f_ptr)); + function_model_ptr = build_function_model(*declaration); } + if (!function_model_ptr) + return true; + + function_model_ptr->set_id( + common::to_id(function_model_ptr->full_name(false))); + + function_model_ptr->is_void(declaration->getReturnType()->isVoidType()); + + context().update(declaration); + + context().set_caller_id(function_model_ptr->id()); + + if (declaration->isThisDeclarationADefinition()) { + set_unique_id( + declaration->getFirstDecl()->getID(), function_model_ptr->id()); + } + + set_unique_id(declaration->getID(), function_model_ptr->id()); + + process_comment(*declaration, *function_model_ptr); + + set_source_location(*declaration, *function_model_ptr); + + diagram().add_participant(std::move(function_model_ptr)); + return true; } bool translation_unit_visitor::VisitFunctionTemplateDecl( - clang::FunctionTemplateDecl *function_template) + clang::FunctionTemplateDecl *declaration) { - const auto function_name = function_template->getQualifiedNameAsString(); - - if (!diagram().should_include(function_name)) + if (!should_include(declaration)) return true; + const auto function_name = declaration->getQualifiedNameAsString(); + LOG_TRACE("Visiting function template declaration {} at {}", function_name, - function_template->getLocation().printToString(source_manager())); + declaration->getLocation().printToString(source_manager())); - auto f_ptr = std::make_unique( - config().using_namespace()); + auto function_template_model = build_function_template(*declaration); - common::model::namespace_ ns{function_name}; - f_ptr->set_name(ns.name()); - ns.pop_back(); - f_ptr->set_namespace(ns); + process_comment(*declaration, *function_template_model); - process_template_parameters(*function_template, *f_ptr); + set_source_location(*declaration, *function_template_model); - for (const auto *param : - function_template->getTemplatedDecl()->parameters()) { - f_ptr->add_parameter(simplify_system_template(common::to_string( - param->getType(), function_template->getASTContext(), false))); - } + function_template_model->is_void( + declaration->getAsFunction()->getReturnType()->isVoidType()); - f_ptr->set_id(common::to_id(f_ptr->full_name(false))); + function_template_model->set_id( + common::to_id(function_template_model->full_name(false))); - f_ptr->is_void( - function_template->getAsFunction()->getReturnType()->isVoidType()); + context().update(declaration); - process_comment(*function_template, *f_ptr); + context().set_caller_id(function_template_model->id()); - set_source_location(*function_template, *f_ptr); + set_unique_id(declaration->getID(), function_template_model->id()); - context().update(function_template); - - context().set_caller_id(f_ptr->id()); - - set_unique_id(function_template->getID(), f_ptr->id()); - - diagram().add_participant(std::move(f_ptr)); + diagram().add_participant(std::move(function_template_model)); return true; } bool translation_unit_visitor::VisitLambdaExpr(clang::LambdaExpr *expr) { + if (!should_include(expr)) + return true; + const auto lambda_full_name = expr->getLambdaClass()->getCanonicalDecl()->getNameAsString(); @@ -477,37 +402,38 @@ bool translation_unit_visitor::VisitLambdaExpr(clang::LambdaExpr *expr) // Create lambda class participant auto *cls = expr->getLambdaClass(); - auto c_ptr = create_class_declaration(cls); + auto lambda_class_model_ptr = create_class_model(cls); - if (!c_ptr) + if (!lambda_class_model_ptr) return true; - const auto cls_id = c_ptr->id(); + const auto cls_id = lambda_class_model_ptr->id(); set_unique_id(cls->getID(), cls_id); // Create lambda class operator() participant - auto m_ptr = std::make_unique( - config().using_namespace()); + auto lambda_method_model_ptr = + std::make_unique( + config().using_namespace()); - common::model::namespace_ ns{c_ptr->get_namespace()}; - auto method_name = "operator()"; - m_ptr->set_method_name(method_name); - ns.pop_back(); + const auto *method_name = "operator()"; + lambda_method_model_ptr->set_method_name(method_name); - m_ptr->set_class_id(cls_id); - m_ptr->set_class_full_name(c_ptr->full_name(false)); + lambda_method_model_ptr->set_class_id(cls_id); + lambda_method_model_ptr->set_class_full_name( + lambda_class_model_ptr->full_name(false)); - diagram().add_participant(std::move(c_ptr)); + diagram().add_participant(std::move(lambda_class_model_ptr)); - m_ptr->set_id(common::to_id( + lambda_method_model_ptr->set_id(common::to_id( get_participant(cls_id).value().full_name(false) + "::" + method_name)); - context().enter_lambda_expression(m_ptr->id()); + context().enter_lambda_expression(lambda_method_model_ptr->id()); - set_unique_id(expr->getCallOperator()->getID(), m_ptr->id()); + set_unique_id( + expr->getCallOperator()->getID(), lambda_method_model_ptr->id()); - diagram().add_participant(std::move(m_ptr)); + diagram().add_participant(std::move(lambda_method_model_ptr)); [[maybe_unused]] const auto is_generic_lambda = expr->isGenericLambda(); @@ -521,6 +447,7 @@ bool translation_unit_visitor::TraverseLambdaExpr(clang::LambdaExpr *expr) RecursiveASTVisitor::TraverseLambdaExpr(expr); + // lambda context is entered inside the visitor context().leave_lambda_expression(); return true; @@ -568,6 +495,9 @@ bool translation_unit_visitor::TraverseCompoundStmt(clang::CompoundStmt *stmt) using clanguml::sequence_diagram::model::activity; using clanguml::sequence_diagram::model::message; + if (stmt == nullptr) + return true; + const auto *current_ifstmt = context().current_ifstmt(); const auto *current_elseifstmt = context().current_elseifstmt(); @@ -578,10 +508,10 @@ bool translation_unit_visitor::TraverseCompoundStmt(clang::CompoundStmt *stmt) if (current_elseifstmt->getElse() == stmt) { const auto current_caller_id = context().caller_id(); - if (current_caller_id) { - diagram() - .get_activity(current_caller_id) - .add_message({message_t::kElse, current_caller_id}); + if (current_caller_id != 0) { + model::message m{message_t::kElse, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_message(std::move(m)); } } } @@ -589,10 +519,10 @@ bool translation_unit_visitor::TraverseCompoundStmt(clang::CompoundStmt *stmt) if (current_ifstmt->getElse() == stmt) { const auto current_caller_id = context().caller_id(); - if (current_caller_id) { - diagram() - .get_activity(current_caller_id) - .add_message({message_t::kElse, current_caller_id}); + if (current_caller_id != 0) { + model::message m{message_t::kElse, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_message(std::move(m)); } } } @@ -625,21 +555,28 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt) } } - if (current_caller_id && !stmt->isConstexpr()) { - context().enter_ifstmt(stmt); + if ((current_caller_id != 0) && !stmt->isConstexpr()) { if (elseif_block) { context().enter_elseifstmt(stmt); - diagram().add_if_stmt(current_caller_id, message_t::kElseIf); + + message m{message_t::kElseIf, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } else { - diagram().add_if_stmt(current_caller_id, message_t::kIf); + context().enter_ifstmt(stmt); + + message m{message_t::kIf, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } } RecursiveASTVisitor::TraverseIfStmt(stmt); - if (current_caller_id && !stmt->isConstexpr() && !elseif_block) { - diagram().end_if_stmt(current_caller_id, message_t::kIfEnd); + if ((current_caller_id != 0) && !stmt->isConstexpr() && !elseif_block) { + diagram().end_block_message( + {message_t::kIfEnd, current_caller_id}, message_t::kIf); } return true; @@ -653,14 +590,17 @@ bool translation_unit_visitor::TraverseWhileStmt(clang::WhileStmt *stmt) const auto current_caller_id = context().caller_id(); - if (current_caller_id) { + if (current_caller_id != 0) { context().enter_loopstmt(stmt); - diagram().add_while_stmt(current_caller_id); + message m{message_t::kWhile, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseWhileStmt(stmt); - if (current_caller_id) { - diagram().end_while_stmt(current_caller_id); + if (current_caller_id != 0) { + diagram().end_block_message( + {message_t::kWhileEnd, current_caller_id}, message_t::kWhile); context().leave_loopstmt(); } @@ -675,16 +615,19 @@ bool translation_unit_visitor::TraverseDoStmt(clang::DoStmt *stmt) const auto current_caller_id = context().caller_id(); - if (current_caller_id) { + if (current_caller_id != 0) { context().enter_loopstmt(stmt); - diagram().add_do_stmt(current_caller_id); + message m{message_t::kDo, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseDoStmt(stmt); - if (current_caller_id) { + if (current_caller_id != 0) { + diagram().end_block_message( + {message_t::kDoEnd, current_caller_id}, message_t::kDo); context().leave_loopstmt(); - diagram().end_do_stmt(current_caller_id); } return true; @@ -698,16 +641,19 @@ bool translation_unit_visitor::TraverseForStmt(clang::ForStmt *stmt) const auto current_caller_id = context().caller_id(); - if (current_caller_id) { + if (current_caller_id != 0) { context().enter_loopstmt(stmt); - diagram().add_for_stmt(current_caller_id); + message m{message_t::kFor, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseForStmt(stmt); - if (current_caller_id) { + if (current_caller_id != 0) { + diagram().end_block_message( + {message_t::kForEnd, current_caller_id}, message_t::kFor); context().leave_loopstmt(); - diagram().end_for_stmt(current_caller_id); } return true; @@ -721,16 +667,19 @@ bool translation_unit_visitor::TraverseCXXTryStmt(clang::CXXTryStmt *stmt) const auto current_caller_id = context().caller_id(); - if (current_caller_id) { + if (current_caller_id != 0) { context().enter_trystmt(stmt); - diagram().add_try_stmt(current_caller_id); + message m{message_t::kTry, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseCXXTryStmt(stmt); - if (current_caller_id) { + if (current_caller_id != 0) { + diagram().end_block_message( + {message_t::kTryEnd, current_caller_id}, message_t::kTry); context().leave_trystmt(); - diagram().end_try_stmt(current_caller_id); } return true; @@ -744,7 +693,7 @@ bool translation_unit_visitor::TraverseCXXCatchStmt(clang::CXXCatchStmt *stmt) const auto current_caller_id = context().caller_id(); - if (current_caller_id && context().current_trystmt()) { + if ((current_caller_id != 0) && (context().current_trystmt() != nullptr)) { std::string caught_type; if (stmt->getCaughtType().isNull()) caught_type = "..."; @@ -752,7 +701,9 @@ bool translation_unit_visitor::TraverseCXXCatchStmt(clang::CXXCatchStmt *stmt) caught_type = common::to_string( stmt->getCaughtType(), *context().get_ast_context()); - diagram().add_catch_stmt(current_caller_id, std::move(caught_type)); + model::message m{message_t::kCatch, current_caller_id}; + m.set_message_name(std::move(caught_type)); + diagram().add_message(std::move(m)); } RecursiveASTVisitor::TraverseCXXCatchStmt(stmt); @@ -769,17 +720,20 @@ bool translation_unit_visitor::TraverseCXXForRangeStmt( const auto current_caller_id = context().caller_id(); - if (current_caller_id) { + if (current_caller_id != 0) { context().enter_loopstmt(stmt); - diagram().add_for_stmt(current_caller_id); + message m{message_t::kFor, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseCXXForRangeStmt( stmt); - if (current_caller_id) { + if (current_caller_id != 0) { + diagram().end_block_message( + {message_t::kForEnd, current_caller_id}, message_t::kFor); context().leave_loopstmt(); - diagram().end_for_stmt(current_caller_id); } return true; @@ -787,18 +741,23 @@ bool translation_unit_visitor::TraverseCXXForRangeStmt( bool translation_unit_visitor::TraverseSwitchStmt(clang::SwitchStmt *stmt) { + using clanguml::common::model::message_t; + const auto current_caller_id = context().caller_id(); - if (current_caller_id) { + if (current_caller_id != 0) { context().enter_switchstmt(stmt); - diagram().add_switch_stmt(current_caller_id); + model::message m{message_t::kSwitch, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseSwitchStmt(stmt); - if (current_caller_id) { + if (current_caller_id != 0) { context().leave_switchstmt(); - diagram().end_switch_stmt(current_caller_id); + diagram().end_block_message( + {message_t::kSwitchEnd, current_caller_id}, message_t::kSwitch); } return true; @@ -806,11 +765,14 @@ bool translation_unit_visitor::TraverseSwitchStmt(clang::SwitchStmt *stmt) bool translation_unit_visitor::TraverseCaseStmt(clang::CaseStmt *stmt) { + using clanguml::common::model::message_t; + const auto current_caller_id = context().caller_id(); - if (current_caller_id) { - diagram().add_case_stmt( - current_caller_id, common::to_string(stmt->getLHS())); + if (current_caller_id != 0) { + model::message m{message_t::kCase, current_caller_id}; + m.set_message_name(common::to_string(stmt->getLHS())); + diagram().add_case_stmt_message(std::move(m)); } RecursiveASTVisitor::TraverseCaseStmt(stmt); @@ -820,10 +782,14 @@ bool translation_unit_visitor::TraverseCaseStmt(clang::CaseStmt *stmt) bool translation_unit_visitor::TraverseDefaultStmt(clang::DefaultStmt *stmt) { + using clanguml::common::model::message_t; + const auto current_caller_id = context().caller_id(); - if (current_caller_id) { - diagram().add_default_stmt(current_caller_id); + if (current_caller_id != 0) { + model::message m{message_t::kCase, current_caller_id}; + m.set_message_name("default"); + diagram().add_case_stmt_message(std::move(m)); } RecursiveASTVisitor::TraverseDefaultStmt(stmt); @@ -834,11 +800,15 @@ bool translation_unit_visitor::TraverseDefaultStmt(clang::DefaultStmt *stmt) bool translation_unit_visitor::TraverseConditionalOperator( clang::ConditionalOperator *stmt) { + using clanguml::common::model::message_t; + const auto current_caller_id = context().caller_id(); - if (current_caller_id) { + if (current_caller_id != 0) { context().enter_conditionaloperator(stmt); - diagram().add_conditional_stmt(current_caller_id); + model::message m{message_t::kConditional, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseStmt( @@ -847,16 +817,20 @@ bool translation_unit_visitor::TraverseConditionalOperator( RecursiveASTVisitor::TraverseStmt( stmt->getTrueExpr()); - if (current_caller_id) { - diagram().add_conditional_elsestmt(current_caller_id); + if (current_caller_id != 0) { + model::message m{message_t::kElse, current_caller_id}; + set_source_location(*stmt, m); + diagram().add_message(std::move(m)); } RecursiveASTVisitor::TraverseStmt( stmt->getFalseExpr()); - if (current_caller_id) { + if (current_caller_id != 0) { context().leave_conditionaloperator(); - diagram().end_conditional_stmt(current_caller_id); + diagram().end_block_message( + {message_t::kConditionalEnd, current_caller_id}, + message_t::kConditional); } return true; @@ -870,20 +844,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) using clanguml::sequence_diagram::model::activity; using clanguml::sequence_diagram::model::message; - if (context().caller_id() == 0) - return true; - - // Skip casts, moves and such - if (expr->isCallToStdMove()) - return true; - - if (expr->isImplicitCXXThis()) - return true; - - if (clang::dyn_cast_or_null(expr)) - return true; - - if (!context().valid()) + if (!should_include(expr)) return true; LOG_TRACE("Visiting call expression at {} [caller_id = {}]", @@ -942,12 +903,12 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) callee_decl = expr->getDirectCallee(); } - if (!callee_decl) { + if (callee_decl == nullptr) { // // Call to a method of a class template // if (clang::dyn_cast_or_null( - expr->getCallee())) { + expr->getCallee()) != nullptr) { if (!process_class_template_method_call_expression(m, expr)) { return true; } @@ -957,7 +918,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) // functions // else if (clang::dyn_cast_or_null( - expr->getCallee())) { + expr->getCallee()) != nullptr) { if (!process_unresolved_lookup_call_expression(m, expr)) return true; } @@ -1040,14 +1001,13 @@ bool translation_unit_visitor::process_class_method_call_expression( std::string method_name = method_decl->getQualifiedNameAsString(); - auto *callee_decl = method_decl ? method_decl->getParent() : nullptr; + const auto *callee_decl = + method_decl != nullptr ? method_decl->getParent() : nullptr; - if (!(callee_decl && - diagram().should_include(callee_decl->getQualifiedNameAsString()))) + if (callee_decl == nullptr) return false; - if (!diagram().should_include( - common::access_specifier_to_access_t(method_decl->getAccess()))) + if (!should_include(callee_decl) || !should_include(method_decl)) return false; m.set_to(method_decl->getID()); @@ -1067,52 +1027,33 @@ bool translation_unit_visitor::process_class_method_call_expression( bool translation_unit_visitor::process_class_template_method_call_expression( model::message &m, const clang::CallExpr *expr) { - auto *dependent_member_callee = + const auto *dependent_member_callee = clang::dyn_cast_or_null( expr->getCallee()); + if (dependent_member_callee == nullptr) + return false; + if (is_callee_valid_template_specialization(dependent_member_callee)) { - const auto *template_declaration = - dependent_member_callee->getBaseType() - ->getAs() - ->getTemplateName() - .getAsTemplateDecl(); + if (const auto *tst = dependent_member_callee->getBaseType() + ->getAs(); + tst != nullptr) { + const auto *template_declaration = + tst->getTemplateName().getAsTemplateDecl(); - std::string callee_method_full_name; + std::string callee_method_full_name; - // First check if the primary template is already in the - // participants map - if (get_participant(template_declaration).has_value()) { - callee_method_full_name = - get_participant(template_declaration).value().full_name(false) + - "::" + dependent_member_callee->getMember().getAsString(); - - for (const auto &[id, p] : diagram().participants()) { - const auto p_full_name = p->full_name(false); - - if (p_full_name.find(callee_method_full_name + "(") == 0) { - // TODO: This selects the first matching template method - // without considering arguments!!! - m.set_to(id); - break; - } - } - } - // Otherwise check if it is a smart pointer - else if (is_smart_pointer(template_declaration)) { - const auto *argument_template = - template_declaration->getTemplateParameters() - ->asArray() - .front(); - - if (get_participant(argument_template).has_value()) { - callee_method_full_name = get_participant(argument_template) + // First check if the primary template is already in the + // participants map + if (get_participant(template_declaration).has_value()) { + callee_method_full_name = get_participant(template_declaration) .value() .full_name(false) + "::" + dependent_member_callee->getMember().getAsString(); for (const auto &[id, p] : diagram().participants()) { const auto p_full_name = p->full_name(false); + if (p_full_name.find(callee_method_full_name + "(") == 0) { // TODO: This selects the first matching template method // without considering arguments!!! @@ -1121,15 +1062,43 @@ bool translation_unit_visitor::process_class_template_method_call_expression( } } } - else - return false; + // Otherwise check if it is a smart pointer + else if (is_smart_pointer(template_declaration)) { + const auto *argument_template = + template_declaration->getTemplateParameters() + ->asArray() + .front(); + + if (get_participant(argument_template).has_value()) { + callee_method_full_name = get_participant(argument_template) + .value() + .full_name(false) + + "::" + + dependent_member_callee->getMember().getAsString(); + + for (const auto &[id, p] : diagram().participants()) { + const auto p_full_name = p->full_name(false); + if (p_full_name.find(callee_method_full_name + "(") == + 0) { + // TODO: This selects the first matching template + // method + // without considering arguments!!! + m.set_to(id); + break; + } + } + } + else + return false; + } + + m.set_message_name( + dependent_member_callee->getMember().getAsString()); + + if (get_unique_id(template_declaration->getID())) + diagram().add_active_participant( + get_unique_id(template_declaration->getID()).value()); } - - m.set_message_name(dependent_member_callee->getMember().getAsString()); - - if (get_unique_id(template_declaration->getID())) - diagram().add_active_participant( - get_unique_id(template_declaration->getID()).value()); } else { LOG_DBG("Skipping call due to unresolvable " @@ -1150,14 +1119,19 @@ bool translation_unit_visitor::process_function_call_expression( const auto *callee_function = callee_decl->getAsFunction(); - if (!callee_function) + if (callee_function == nullptr) + return false; + + if (!should_include(callee_function)) + return false; + + // Skip free functions declared in files outside of included paths + if (config().combine_free_functions_into_file_participants() && + !diagram().should_include(common::model::source_file{m.file()})) return false; auto callee_name = callee_function->getQualifiedNameAsString() + "()"; - if (!diagram().should_include(callee_name)) - return false; - std::unique_ptr f_ptr; if (!get_unique_id(callee_function->getID()).has_value()) { @@ -1168,8 +1142,7 @@ bool translation_unit_visitor::process_function_call_expression( m.set_to(get_unique_id(callee_function->getID()).value()); } - auto message_name = callee_name; - m.set_message_name(message_name.substr(0, message_name.size() - 2)); + m.set_message_name(callee_name.substr(0, callee_name.size() - 2)); if (f_ptr) diagram().add_participant(std::move(f_ptr)); @@ -1178,17 +1151,18 @@ bool translation_unit_visitor::process_function_call_expression( } bool translation_unit_visitor::process_unresolved_lookup_call_expression( - model::message &m, const clang::CallExpr *expr) + model::message &m, const clang::CallExpr *expr) const { // This is probably a template - auto *unresolved_expr = + const auto *unresolved_expr = clang::dyn_cast_or_null(expr->getCallee()); - if (unresolved_expr) { + if (unresolved_expr != nullptr) { for (const auto *decl : unresolved_expr->decls()) { - if (clang::dyn_cast_or_null(decl)) { + if (clang::dyn_cast_or_null(decl) != + nullptr) { // Yes, it's a template - auto *ftd = + const auto *ftd = clang::dyn_cast_or_null(decl); if (!get_unique_id(ftd->getID()).has_value()) @@ -1208,21 +1182,19 @@ bool translation_unit_visitor::process_unresolved_lookup_call_expression( bool translation_unit_visitor::is_callee_valid_template_specialization( const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const { - const bool base_type_is_not_null = - !dependent_member_expr->getBaseType().isNull(); + if (dependent_member_expr == nullptr) + return false; - const bool base_type_is_specialization_type = - dependent_member_expr->getBaseType() - ->getAs() != nullptr; + if (dependent_member_expr->getBaseType().isNull()) + return false; - const bool base_type_is_not_pointer_type = - base_type_is_specialization_type && - !dependent_member_expr->getBaseType() - ->getAs() - ->isPointerType(); + const auto *tst = dependent_member_expr->getBaseType() + ->getAs(); - return (base_type_is_not_null && base_type_is_specialization_type && - base_type_is_not_pointer_type); + if (tst == nullptr) + return false; + + return !(tst->isPointerType()); } bool translation_unit_visitor::is_smart_pointer( @@ -1236,7 +1208,7 @@ bool translation_unit_visitor::is_smart_pointer( } std::unique_ptr -translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls) +translation_unit_visitor::create_class_model(clang::CXXRecordDecl *cls) { assert(cls != nullptr); @@ -1260,29 +1232,32 @@ translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls) const auto *parent = cls->getParent(); - if (parent && parent->isRecord()) { + if ((parent != nullptr) && parent->isRecord()) { // Here we have 2 options, either: // - the parent is a regular C++ class/struct // - the parent is a class template declaration/specialization std::optional id_opt; - int64_t local_id = - static_cast(parent)->getID(); + const auto *parent_record_decl = + clang::dyn_cast(parent); + + assert(parent_record_decl != nullptr); + + int64_t local_id = parent_record_decl->getID(); // First check if the parent has been added to the diagram as // regular class id_opt = get_unique_id(local_id); // If not, check if the parent template declaration is in the model - if (!id_opt) { - local_id = static_cast(parent) - ->getDescribedTemplate() - ->getID(); - if (static_cast(parent) - ->getDescribedTemplate()) + if (!id_opt && + (parent_record_decl->getDescribedTemplate() != nullptr)) { + parent_record_decl->getDescribedTemplate()->getID(); + if (parent_record_decl->getDescribedTemplate() != nullptr) id_opt = get_unique_id(local_id); } - assert(id_opt); + if (!id_opt) + return {}; auto parent_class = diagram_.get_participant( @@ -1319,14 +1294,14 @@ translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls) } else if (cls->isLambda()) { c.is_lambda(true); - if (cls->getParent()) { + if (cls->getParent() != nullptr) { const auto type_name = make_lambda_name(cls); c.set_name(type_name); c.set_namespace(ns); c.set_id(common::to_id(c.full_name(false))); - // Check if lambda is declared as an argument passed to a + // TODO: Check if lambda is declared as an argument passed to a // function/method call } else { @@ -1368,7 +1343,8 @@ bool translation_unit_visitor::process_template_parameters( for (const auto *parameter : *template_declaration.getTemplateParameters()) { - if (clang::dyn_cast_or_null(parameter)) { + if (clang::dyn_cast_or_null(parameter) != + nullptr) { const auto *template_type_parameter = clang::dyn_cast_or_null(parameter); template_parameter ct; @@ -1378,10 +1354,10 @@ bool translation_unit_visitor::process_template_parameters( ct.set_default_value(""); ct.is_variadic(template_type_parameter->isParameterPack()); - c.add_template(std::move(ct)); + c.add_template(ct); } else if (clang::dyn_cast_or_null( - parameter)) { + parameter) != nullptr) { const auto *template_nontype_parameter = clang::dyn_cast_or_null( parameter); @@ -1392,10 +1368,10 @@ bool translation_unit_visitor::process_template_parameters( ct.set_default_value(""); ct.is_variadic(template_nontype_parameter->isParameterPack()); - c.add_template(std::move(ct)); + c.add_template(ct); } else if (clang::dyn_cast_or_null( - parameter)) { + parameter) != nullptr) { const auto *template_template_parameter = clang::dyn_cast_or_null( parameter); @@ -1406,7 +1382,7 @@ bool translation_unit_visitor::process_template_parameters( ct.set_default_value(""); ct.is_variadic(template_template_parameter->isParameterPack()); - c.add_template(std::move(ct)); + c.add_template(ct); } else { // pass @@ -1433,6 +1409,30 @@ translation_unit_visitor::get_unique_id(int64_t local_id) const return local_ast_id_map_.at(local_id); } +std::unique_ptr +translation_unit_visitor::build_function_template( + const clang::FunctionTemplateDecl &declaration) +{ + auto function_template_model_ptr = + std::make_unique( + config().using_namespace()); + + common::model::namespace_ ns{declaration.getQualifiedNameAsString()}; + function_template_model_ptr->set_name(ns.name()); + ns.pop_back(); + function_template_model_ptr->set_namespace(ns); + + process_template_parameters(declaration, *function_template_model_ptr); + + for (const auto *param : declaration.getTemplatedDecl()->parameters()) { + function_template_model_ptr->add_parameter( + simplify_system_template(common::to_string( + param->getType(), declaration.getASTContext(), false))); + } + + return function_template_model_ptr; +} + std::unique_ptr translation_unit_visitor::build_function_template_instantiation( const clang::FunctionDecl &decl) @@ -1474,16 +1474,37 @@ translation_unit_visitor::build_function_template_instantiation( return template_instantiation_ptr; } +std::unique_ptr translation_unit_visitor::build_function_model( + const clang::FunctionDecl &declaration) +{ + auto function_model_ptr = + std::make_unique( + config().using_namespace()); + + common::model::namespace_ ns{declaration.getQualifiedNameAsString()}; + function_model_ptr->set_name(ns.name()); + ns.pop_back(); + function_model_ptr->set_namespace(ns); + + for (const auto *param : declaration.parameters()) { + function_model_ptr->add_parameter( + simplify_system_template(common::to_string( + param->getType(), declaration.getASTContext(), false))); + } + + return function_model_ptr; +} + void translation_unit_visitor:: build_template_instantiation_process_template_arguments( model::template_trait *parent, - std::deque> &template_base_params, + std::deque> + & /*template_base_params*/, const clang::ArrayRef &template_args, model::template_trait &template_instantiation, const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl) { - auto arg_index = 0U; for (const auto &arg : template_args) { const auto argument_kind = arg.getKind(); class_diagram::model::template_parameter argument; @@ -1511,9 +1532,7 @@ void translation_unit_visitor:: simplify_system_template( argument, argument.to_string(config().using_namespace(), false)); - template_instantiation.add_template(std::move(argument)); - - arg_index++; + template_instantiation.add_template(argument); } } @@ -1553,8 +1572,8 @@ void translation_unit_visitor:: void translation_unit_visitor:: build_template_instantiation_process_tag_argument( - model::template_trait &template_instantiation, - const std::string &full_template_specialization_name, + model::template_trait & /*template_instantiation*/, + const std::string & /*full_template_specialization_name*/, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, class_diagram::model::template_parameter &argument) const @@ -1569,7 +1588,7 @@ void translation_unit_visitor:: void translation_unit_visitor:: build_template_instantiation_process_type_argument( - model::template_trait *parent, + model::template_trait * /*parent*/, const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, @@ -1582,66 +1601,27 @@ void translation_unit_visitor:: // If this is a nested template type - add nested templates as // template arguments - if (arg.getAsType()->getAs()) { - - // for (const auto ¶m_type : - // arg.getAsType()->getAs()->param_types()) - // { - // - // if (!param_type->getAs()) - // continue; - // - // auto classTemplateSpecialization = - // llvm::dyn_cast( - // param_type->getAsRecordDecl()); - // - // if (classTemplateSpecialization) { - // // Read arg info as needed. - // auto nested_template_instantiation = - // build_template_instantiation_from_class_template_specialization( - // *classTemplateSpecialization, - // *param_type->getAs(), - // diagram().should_include( - // full_template_specialization_name) - // ? - // std::make_optional(&template_instantiation) - // : parent); - // } - // } + if (arg.getAsType()->getAs() != nullptr) { + // TODO } - else if (arg.getAsType()->getAs()) { - const auto *nested_template_type = - arg.getAsType()->getAs(); + else if (const auto *nested_template_type = + arg.getAsType()->getAs(); + nested_template_type != nullptr) { const auto nested_template_name = nested_template_type->getTemplateName() .getAsTemplateDecl() ->getQualifiedNameAsString(); - auto [tinst_ns, tinst_name] = cx::util::split_ns(nested_template_name); - argument.set_name(nested_template_name); - // auto nested_template_instantiation = - // build_template_instantiation( - // *arg.getAsType()->getAs(), - // diagram().should_include(full_template_specialization_name) - // ? std::make_optional(&template_instantiation) - // : parent); - // - // argument.set_id(nested_template_instantiation->id()); - // - // for (const auto &t : - // nested_template_instantiation->templates()) - // argument.add_template_param(t); - // Check if this template should be simplified (e.g. system // template aliases such as 'std:basic_string' should // be simply 'std::string') simplify_system_template( argument, argument.to_string(config().using_namespace(), false)); } - else if (arg.getAsType()->getAs()) { + else if (arg.getAsType()->getAs() != nullptr) { argument.is_template_parameter(true); argument.set_name( common::to_string(arg.getAsType(), template_decl->getASTContext())); @@ -1697,7 +1677,7 @@ translation_unit_visitor::process_template_specialization( void translation_unit_visitor::process_template_specialization_argument( const clang::ClassTemplateSpecializationDecl *cls, model::class_ &template_instantiation, const clang::TemplateArgument &arg, - size_t argument_index, bool in_parameter_pack) + size_t argument_index, bool /*in_parameter_pack*/) { const auto argument_kind = arg.getKind(); @@ -1707,9 +1687,9 @@ void translation_unit_visitor::process_template_specialization_argument( // If this is a nested template type - add nested templates as // template arguments - if (arg.getAsType()->getAs()) { - const auto *nested_template_type = + if (const auto *nested_template_type = arg.getAsType()->getAs(); + nested_template_type != nullptr) { const auto nested_template_name = nested_template_type->getTemplateName() @@ -1719,8 +1699,7 @@ void translation_unit_visitor::process_template_specialization_argument( argument.set_name(nested_template_name); auto nested_template_instantiation = build_template_instantiation( - *arg.getAsType()->getAs(), - &template_instantiation); + *nested_template_type, &template_instantiation); argument.set_id(nested_template_instantiation->id()); @@ -1733,7 +1712,8 @@ void translation_unit_visitor::process_template_specialization_argument( simplify_system_template(argument, argument.to_string(config().using_namespace(), false)); } - else if (arg.getAsType()->getAs()) { + else if (arg.getAsType()->getAs() != + nullptr) { auto type_name = common::to_string(arg.getAsType(), cls->getASTContext()); @@ -1764,7 +1744,7 @@ void translation_unit_visitor::process_template_specialization_argument( argument.set_name(type_name); } - else if (arg.getAsType()->getAsCXXRecordDecl() && + else if ((arg.getAsType()->getAsCXXRecordDecl() != nullptr) && arg.getAsType()->getAsCXXRecordDecl()->isLambda()) { if (get_unique_id(arg.getAsType()->getAsCXXRecordDecl()->getID()) .has_value()) { @@ -1833,20 +1813,20 @@ void translation_unit_visitor::process_template_specialization_argument( simplify_system_template( argument, argument.to_string(config().using_namespace(), false)); - template_instantiation.add_template(std::move(argument)); + template_instantiation.add_template(argument); } else if (argument_kind == clang::TemplateArgument::Integral) { class_diagram::model::template_parameter argument; argument.is_template_parameter(false); argument.set_type(std::to_string(arg.getAsIntegral().getExtValue())); - template_instantiation.add_template(std::move(argument)); + template_instantiation.add_template(argument); } else if (argument_kind == clang::TemplateArgument::Expression) { class_diagram::model::template_parameter argument; argument.is_template_parameter(false); argument.set_type(common::get_source_text( arg.getAsExpr()->getSourceRange(), source_manager())); - template_instantiation.add_template(std::move(argument)); + template_instantiation.add_template(argument); } else if (argument_kind == clang::TemplateArgument::TemplateExpansion) { class_diagram::model::template_parameter argument; @@ -1884,14 +1864,14 @@ translation_unit_visitor::build_template_instantiation( /*is variadic */ bool>> template_base_params{}; - auto *template_type_ptr = &template_type_decl; + const auto *template_type_ptr = &template_type_decl; if (template_type_decl.isTypeAlias() && - template_type_decl.getAliasedType() - ->getAs()) + (template_type_decl.getAliasedType() + ->getAs() != nullptr)) template_type_ptr = template_type_decl.getAliasedType() ->getAs(); - auto &template_type = *template_type_ptr; + const auto &template_type = *template_type_ptr; // // Create class_ instance to hold the template instantiation @@ -1911,8 +1891,9 @@ translation_unit_visitor::build_template_instantiation( auto *class_template_decl{ clang::dyn_cast(template_decl)}; - if (class_template_decl && class_template_decl->getTemplatedDecl() && - class_template_decl->getTemplatedDecl()->getParent() && + if ((class_template_decl != nullptr) && + (class_template_decl->getTemplatedDecl() != nullptr) && + (class_template_decl->getTemplatedDecl()->getParent() != nullptr) && class_template_decl->getTemplatedDecl()->getParent()->isRecord()) { common::model::namespace_ ns{ @@ -1965,7 +1946,8 @@ translation_unit_visitor::build_template_instantiation( clang::dyn_cast_or_null( template_decl->getTemplatedDecl()); - if (templated_class_decl && templated_class_decl->hasDefinition()) + if ((templated_class_decl != nullptr) && + templated_class_decl->hasDefinition()) for (const auto &base : templated_class_decl->bases()) { const auto base_class_name = common::to_string( base.getType(), templated_class_decl->getASTContext(), false); @@ -2016,6 +1998,9 @@ translation_unit_visitor::build_template_instantiation( for (const auto &[id, c] : diagram().participants()) { const auto *participant_as_class = dynamic_cast(c.get()); + if (participant_as_class == nullptr) + continue; + if ((participant_as_class != nullptr) && (*participant_as_class == template_instantiation)) continue; @@ -2034,8 +2019,6 @@ translation_unit_visitor::build_template_instantiation( auto templated_decl_id = template_type.getTemplateName().getAsTemplateDecl()->getID(); - // auto templated_decl_local_id = - // get_unique_id(templated_decl_id).value_or(0); if (best_match_id > 0) { destination = best_match_full_name; @@ -2055,7 +2038,8 @@ translation_unit_visitor::build_template_instantiation( void translation_unit_visitor:: process_unexposed_template_specialization_parameters( const std::string &type_name, - class_diagram::model::template_parameter &tp, model::class_ &c) const + class_diagram::model::template_parameter &tp, + model::class_ & /*c*/) const { auto template_params = cx::util::parse_unexposed_template_params( type_name, [](const std::string &t) { return t; }); @@ -2066,15 +2050,15 @@ void translation_unit_visitor:: } bool translation_unit_visitor::simplify_system_template( - class_diagram::model::template_parameter &ct, const std::string &full_name) + class_diagram::model::template_parameter &ct, + const std::string &full_name) const { if (config().type_aliases().count(full_name) > 0) { ct.set_name(config().type_aliases().at(full_name)); ct.clear_params(); return true; } - else - return false; + return false; } std::string translation_unit_visitor::simplify_system_template( @@ -2095,8 +2079,9 @@ std::string translation_unit_visitor::make_lambda_name( const auto location = cls->getLocation(); const auto file_line = source_manager().getSpellingLineNumber(location); const auto file_column = source_manager().getSpellingColumnNumber(location); - const std::string file_name = - util::split(source_manager().getFilename(location).str(), "/").back(); + const std::string file_name = std::filesystem::relative( + source_manager().getFilename(location).str(), config().relative_to()) + .string(); if (context().caller_id() != 0 && get_participant(context().caller_id()).has_value()) { @@ -2160,4 +2145,135 @@ void translation_unit_visitor::finalize() } } } + +std::unique_ptr +translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration) +{ + auto method_model_ptr = std::make_unique( + config().using_namespace()); + + common::model::namespace_ ns{declaration->getQualifiedNameAsString()}; + auto method_name = ns.name(); + method_model_ptr->set_method_name(method_name); + ns.pop_back(); + method_model_ptr->set_name(ns.name()); + ns.pop_back(); + + clang::Decl *parent_decl = declaration->getParent(); + + if (context().current_class_template_decl_ != nullptr) + parent_decl = context().current_class_template_decl_; + + LOG_DBG("Getting method's class with local id {}", parent_decl->getID()); + + if (!get_participant(parent_decl)) { + LOG_DBG("Cannot find parent class_ for method {} in class {}", + declaration->getQualifiedNameAsString(), + declaration->getParent()->getQualifiedNameAsString()); + return {}; + } + + const auto &method_class = + get_participant(parent_decl).value(); + + method_model_ptr->is_void(declaration->getReturnType()->isVoidType()); + + method_model_ptr->set_class_id(method_class.id()); + method_model_ptr->set_class_full_name(method_class.full_name(false)); + method_model_ptr->set_name(get_participant(method_model_ptr->class_id()) + .value() + .full_name_no_ns() + + "::" + declaration->getNameAsString()); + method_model_ptr->is_static(declaration->isStatic()); + + for (const auto *param : declaration->parameters()) { + method_model_ptr->add_parameter(config().using_namespace().relative( + simplify_system_template(common::to_string( + param->getType(), declaration->getASTContext(), false)))); + } + + return method_model_ptr; } + +bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const +{ + if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin())) + return false; + + const auto decl_file = decl->getLocation().printToString(source_manager()); + + return diagram().should_include(decl->getQualifiedNameAsString()) && + diagram().should_include(common::model::source_file{decl_file}); +} + +bool translation_unit_visitor::should_include( + const clang::LambdaExpr *expr) const +{ + const auto expr_file = expr->getBeginLoc().printToString(source_manager()); + return diagram().should_include(common::model::source_file{expr_file}); +} + +bool translation_unit_visitor::should_include(const clang::CallExpr *expr) const +{ + if (context().caller_id() == 0) + return false; + + // Skip casts, moves and such + if (expr->isCallToStdMove()) + return false; + + if (expr->isImplicitCXXThis()) + return false; + + if (clang::dyn_cast_or_null(expr) != nullptr) + return false; + + if (!context().valid()) + return false; + + const auto expr_file = expr->getBeginLoc().printToString(source_manager()); + return diagram().should_include(common::model::source_file{expr_file}); +} + +bool translation_unit_visitor::should_include( + const clang::CXXMethodDecl *decl) const +{ + if (!should_include(decl->getParent())) + return false; + + if (!diagram().should_include( + common::access_specifier_to_access_t(decl->getAccess()))) + return false; + + LOG_DBG("Including method {}", decl->getQualifiedNameAsString()); + + return true; +} + +bool translation_unit_visitor::should_include( + const clang::FunctionDecl *decl) const +{ + const auto decl_file = decl->getLocation().printToString(source_manager()); + + return diagram().should_include(decl->getQualifiedNameAsString()) && + diagram().should_include(common::model::source_file{decl_file}); +} + +bool translation_unit_visitor::should_include( + const clang::FunctionTemplateDecl *decl) const +{ + return should_include(decl->getAsFunction()); +} + +bool translation_unit_visitor::should_include( + const clang::ClassTemplateDecl *decl) const +{ + if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin())) + return false; + + const auto decl_file = decl->getLocation().printToString(source_manager()); + + return diagram().should_include(decl->getQualifiedNameAsString()) && + diagram().should_include(common::model::source_file{decl_file}); +} +} // namespace clanguml::sequence_diagram::visitor diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 8fb09c45..a094559c 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,16 +57,16 @@ public: bool TraverseLambdaExpr(clang::LambdaExpr *expr); - bool VisitCXXMethodDecl(clang::CXXMethodDecl *method); + bool VisitCXXMethodDecl(clang::CXXMethodDecl *declaration); - bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls); + bool VisitCXXRecordDecl(clang::CXXRecordDecl *declaration); - bool VisitClassTemplateDecl(clang::ClassTemplateDecl *cls); + bool VisitClassTemplateDecl(clang::ClassTemplateDecl *declaration); bool VisitClassTemplateSpecializationDecl( - clang::ClassTemplateSpecializationDecl *cls); + clang::ClassTemplateSpecializationDecl *declaration); - bool VisitFunctionDecl(clang::FunctionDecl *function_declaration); + bool VisitFunctionDecl(clang::FunctionDecl *declaration); bool VisitFunctionTemplateDecl( clang::FunctionTemplateDecl *function_declaration); @@ -120,7 +120,7 @@ public: } template - const common::optional_ref get_participant(const clang::Decl *decl) const + common::optional_ref get_participant(const clang::Decl *decl) const { assert(decl != nullptr); @@ -143,8 +143,8 @@ public: } template - const common::optional_ref get_participant( - const common::model::diagram_element::id_t id) const + common::optional_ref get_participant( + common::model::diagram_element::id_t id) const { if (diagram().participants().find(id) == diagram().participants().end()) return {}; @@ -163,8 +163,19 @@ public: int64_t local_id) const; private: + bool should_include(const clang::TagDecl *decl) const; + bool should_include(const clang::LambdaExpr *expr) const; + bool should_include(const clang::CallExpr *expr) const; + bool should_include(const clang::CXXMethodDecl *decl) const; + bool should_include(const clang::FunctionDecl *decl) const; + bool should_include(const clang::FunctionTemplateDecl *decl) const; + bool should_include(const clang::ClassTemplateDecl *decl) const; + std::unique_ptr - create_class_declaration(clang::CXXRecordDecl *cls); + create_class_model(clang::CXXRecordDecl *cls); + + std::unique_ptr + create_method_model(clang::CXXMethodDecl *cls); bool process_template_parameters( const clang::TemplateDecl &template_declaration, @@ -173,6 +184,12 @@ private: std::unique_ptr build_function_template_instantiation(const clang::FunctionDecl &pDecl); + std::unique_ptr build_function_model( + const clang::FunctionDecl &declaration); + + std::unique_ptr build_function_template( + const clang::FunctionTemplateDecl &declaration); + void build_template_instantiation_process_template_arguments( model::template_trait *parent, std::deque> &template_base_params, @@ -226,7 +243,7 @@ private: model::class_ *parent); bool simplify_system_template(class_diagram::model::template_parameter &ct, - const std::string &full_name); + const std::string &full_name) const; std::string simplify_system_template(const std::string &full_name) const; @@ -250,7 +267,7 @@ private: model::message &m, const clang::CallExpr *expr); bool process_unresolved_lookup_call_expression( - model::message &m, const clang::CallExpr *expr); + model::message &m, const clang::CallExpr *expr) const; void push_message(clang::CallExpr *expr, model::message &&m); @@ -283,4 +300,4 @@ private: common::model::access_t>> anonymous_struct_relationships_; }; -} +} // namespace clanguml::sequence_diagram::visitor diff --git a/src/util/error.h b/src/util/error.h index 46bf94e3..ab7bb761 100644 --- a/src/util/error.h +++ b/src/util/error.h @@ -1,7 +1,7 @@ /** * src/util/error.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,4 +34,4 @@ struct substring_delimiter_not_found : public virtual std::runtime_error { { } }; -} +} // namespace clanguml::error diff --git a/src/util/thread_pool_executor.cc b/src/util/thread_pool_executor.cc index b72c60c9..7eaaf79d 100644 --- a/src/util/thread_pool_executor.cc +++ b/src/util/thread_pool_executor.cc @@ -1,7 +1,7 @@ /** * src/util/thread_pool_executor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ thread_pool_executor::thread_pool_executor(unsigned int pool_size) pool_size = std::thread::hardware_concurrency(); for (auto i = 0U; i < pool_size; i++) { - threads_.push_back(std::thread{&thread_pool_executor::worker, this}); + threads_.emplace_back(&thread_pool_executor::worker, this); } } @@ -90,4 +90,4 @@ std::packaged_task thread_pool_executor::get() return res; } -} +} // namespace clanguml::util diff --git a/src/util/thread_pool_executor.h b/src/util/thread_pool_executor.h index 52e19fdf..bb748f9d 100644 --- a/src/util/thread_pool_executor.h +++ b/src/util/thread_pool_executor.h @@ -1,7 +1,7 @@ /** * src/util/thread_pool_executor.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,7 +27,12 @@ class thread_pool_executor { public: thread_pool_executor(); - thread_pool_executor(unsigned int pool_size); + thread_pool_executor(const thread_pool_executor &) = delete; + thread_pool_executor(thread_pool_executor &&) = delete; + thread_pool_executor &operator=(const thread_pool_executor &) = delete; + thread_pool_executor &operator=(thread_pool_executor &&) = delete; + + explicit thread_pool_executor(unsigned int pool_size); ~thread_pool_executor(); @@ -48,4 +53,4 @@ private: std::vector threads_; }; -} \ No newline at end of file +} // namespace clanguml::util \ No newline at end of file diff --git a/src/util/util.cc b/src/util/util.cc index bda4497b..3849f544 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -1,7 +1,7 @@ /** * src/util/util.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,10 +21,9 @@ #include -namespace clanguml { -namespace util { +namespace clanguml::util { -const std::string WHITESPACE = " \n\r\t\f\v"; +static const auto WHITESPACE = " \n\r\t\f\v"; void setup_logging(int verbose) { @@ -49,7 +48,9 @@ void setup_logging(int verbose) std::string get_process_output(const std::string &command) { - std::array buffer; + constexpr size_t kBufferSize{1024}; + + std::array buffer{}; std::string result; std::unique_ptr pipe( popen(command.c_str(), "r"), pclose); @@ -67,7 +68,7 @@ std::string get_process_output(const std::string &command) std::string get_env(const std::string &name) { - const char *value = std::getenv(name.c_str()); + const char *value = std::getenv(name.c_str()); // NOLINT if (value == nullptr) return {}; @@ -93,7 +94,7 @@ bool is_git_repository() std::string get_git_branch() { - const auto env = get_env("CLANGUML_GIT_BRANCH"); + auto env = get_env("CLANGUML_GIT_BRANCH"); if (!env.empty()) return env; @@ -103,7 +104,7 @@ std::string get_git_branch() std::string get_git_revision() { - const auto env = get_env("CLANGUML_GIT_REVISION"); + auto env = get_env("CLANGUML_GIT_REVISION"); if (!env.empty()) return env; @@ -113,7 +114,7 @@ std::string get_git_revision() std::string get_git_commit() { - const auto env = get_env("CLANGUML_GIT_COMMIT"); + auto env = get_env("CLANGUML_GIT_COMMIT"); if (!env.empty()) return env; @@ -123,7 +124,7 @@ std::string get_git_commit() std::string get_git_toplevel_dir() { - const auto env = get_env("CLANGUML_GIT_TOPLEVEL_DIR"); + auto env = get_env("CLANGUML_GIT_TOPLEVEL_DIR"); if (!env.empty()) return env; @@ -153,7 +154,7 @@ std::vector split( if (!contains(str, delimiter)) result.push_back(str); else - while (str.size()) { + while (static_cast(!str.empty()) != 0U) { auto index = str.find(delimiter); if (index != std::string::npos) { auto tok = str.substr(0, index); @@ -210,7 +211,7 @@ std::string abbreviate(const std::string &s, const unsigned int max_length) bool find_element_alias( const std::string &input, std::tuple &result) { - std::regex alias_regex("(@A\\([^\\).]+\\))"); + std::regex alias_regex(R"((@A\([^\).]+\)))"); auto alias_it = std::sregex_iterator(input.begin(), input.end(), alias_regex); @@ -229,8 +230,8 @@ bool find_element_alias( return true; } -bool replace_all( - std::string &input, std::string pattern, std::string replace_with) +bool replace_all(std::string &input, const std::string &pattern, + const std::string &replace_with) { bool replaced{false}; @@ -280,5 +281,13 @@ template <> bool ends_with(const std::string &value, const std::string &suffix) return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin()); } +std::size_t hash_seed(std::size_t seed) +{ + constexpr auto kSeedStart{0x6a3712b5}; + constexpr auto kSeedShiftFirst{6}; + constexpr auto kSeedShiftSecond{2}; + + return kSeedStart + (seed << kSeedShiftFirst) + (seed >> kSeedShiftSecond); } -} + +} // namespace clanguml::util diff --git a/src/util/util.h b/src/util/util.h index a5b5b696..2f2e47eb 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -1,7 +1,7 @@ /** * src/util/util.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,41 +21,40 @@ #include #include +#include #include -#include #include #include #include -namespace clanguml { -namespace util { +namespace clanguml::util { std::string ltrim(const std::string &s); std::string rtrim(const std::string &s); std::string trim(const std::string &s); -#define __FILENAME__ \ +#define FILENAME_ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define LOG_ERROR(fmt__, ...) \ - spdlog::get("console")->error(std::string("[{}:{}] ") + fmt__, \ - __FILENAME__, __LINE__, ##__VA_ARGS__) + spdlog::get("console")->error( \ + std::string("[{}:{}] ") + fmt__, FILENAME_, __LINE__, ##__VA_ARGS__) #define LOG_WARN(fmt__, ...) \ - spdlog::get("console")->warn(std::string("[{}:{}] ") + fmt__, \ - __FILENAME__, __LINE__, ##__VA_ARGS__) + spdlog::get("console")->warn( \ + std::string("[{}:{}] ") + fmt__, FILENAME_, __LINE__, ##__VA_ARGS__) #define LOG_INFO(fmt__, ...) \ - spdlog::get("console")->info(std::string("[{}:{}] ") + fmt__, \ - __FILENAME__, __LINE__, ##__VA_ARGS__) + spdlog::get("console")->info( \ + std::string("[{}:{}] ") + fmt__, FILENAME_, __LINE__, ##__VA_ARGS__) #define LOG_DBG(fmt__, ...) \ - spdlog::get("console")->debug(std::string("[{}:{}] ") + fmt__, \ - __FILENAME__, __LINE__, ##__VA_ARGS__) + spdlog::get("console")->debug( \ + std::string("[{}:{}] ") + fmt__, FILENAME_, __LINE__, ##__VA_ARGS__) #define LOG_TRACE(fmt__, ...) \ - spdlog::get("console")->trace(std::string("[{}:{}] ") + fmt__, \ - __FILENAME__, __LINE__, ##__VA_ARGS__) + spdlog::get("console")->trace( \ + std::string("[{}:{}] ") + fmt__, FILENAME_, __LINE__, ##__VA_ARGS__) /** * @brief Setup spdlog logger. @@ -114,7 +113,7 @@ std::string unqualify(const std::string &s); * @param max_length Maximum length * @return Abbreviated string */ -std::string abbreviate(const std::string &s, const unsigned int max_length); +std::string abbreviate(const std::string &s, unsigned int max_length); /** * @brief Find element alias in Puml note @@ -136,8 +135,8 @@ bool find_element_alias( * * @return True if at least on replacement was made */ -bool replace_all( - std::string &input, std::string pattern, std::string replace_with); +bool replace_all(std::string &input, const std::string &pattern, + const std::string &replace_with); /** * @brief Appends a vector to a vector. @@ -247,5 +246,6 @@ void for_each_if(const T &collection, C &&cond, F &&func) }); } -} // namespace util -} // namespace clanguml \ No newline at end of file +std::size_t hash_seed(std::size_t seed); + +} // namespace clanguml::util \ No newline at end of file diff --git a/src/version.h.in b/src/version.h.in index fb0a72a7..75c2b0fd 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,7 +1,7 @@ /** * src/version.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/CMakeLists.txt b/tests/CMakeLists.txt index 21167c13..8d6a31ac 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,12 +4,18 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 17) -set(TEST_DISABLE_WARNINGS "-Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable -Wno-attributes") +set(TEST_DISABLE_WARNINGS_DEBUG "-Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable -Wno-attributes -Wno-nonnull") +set(TEST_DISABLE_WARNINGS_RELEASE "${TEST_DISABLE_WARNINGS} -Wno-aggressive-loop-optimizations") + + +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS} ${TEST_DISABLE_WARNINGS_RELEASE}") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS} ${TEST_DISABLE_WARNINGS_DEBUG}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS} ${TEST_DISABLE_WARNINGS}") if(APPLE) # Without this, clang-uml test cases fail with error saying that clang cannot find stdarg.h - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${LLVM_LIBRARY_DIR}/clang/${LLVM_PACKAGE_VERSION}/include") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${LLVM_LIBRARY_DIR}/clang/${LLVM_PACKAGE_VERSION}/include") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -I${LLVM_LIBRARY_DIR}/clang/${LLVM_PACKAGE_VERSION}/include") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -I${LLVM_LIBRARY_DIR}/clang/${LLVM_PACKAGE_VERSION}/include") endif(APPLE) file(GLOB_RECURSE TEST_CASE_SOURCES t*/*.cc) diff --git a/tests/t00002/t00002.cc b/tests/t00002/t00002.cc index aa862420..a8fe99b8 100644 --- a/tests/t00002/t00002.cc +++ b/tests/t00002/t00002.cc @@ -82,5 +82,5 @@ private: /// All the A pointers std::vector as; }; -} -} +} // namespace t00002 +} // namespace clanguml diff --git a/tests/t00002/test_case.h b/tests/t00002/test_case.h index 8883a6f8..91849435 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00003.cc b/tests/t00003/t00003.cc index 49433586..d5656ce3 100644 --- a/tests/t00003/t00003.cc +++ b/tests/t00003/t00003.cc @@ -53,5 +53,5 @@ private: }; int A::static_int = 1; -} -} +} // namespace t00003 +} // namespace clanguml diff --git a/tests/t00003/test_case.h b/tests/t00003/test_case.h index e112743c..4eb977d5 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00004.cc b/tests/t00004/t00004.cc index 19a6a265..74881853 100644 --- a/tests/t00004/t00004.cc +++ b/tests/t00004/t00004.cc @@ -51,5 +51,5 @@ public: }; } -} -} +} // namespace t00004 +} // namespace clanguml diff --git a/tests/t00004/test_case.h b/tests/t00004/test_case.h index 7eb64f94..5ee0019c 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00005.cc b/tests/t00005/t00005.cc index ea482596..995b729f 100644 --- a/tests/t00005/t00005.cc +++ b/tests/t00005/t00005.cc @@ -51,5 +51,5 @@ public: volatile J *j; mutable K *k; }; -} -} +} // namespace t00005 +} // namespace clanguml diff --git a/tests/t00005/test_case.h b/tests/t00005/test_case.h index bd809996..567e18a9 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00006.cc b/tests/t00006/t00006.cc index 8018dc67..d6f80352 100644 --- a/tests/t00006/t00006.cc +++ b/tests/t00006/t00006.cc @@ -80,5 +80,5 @@ public: std::tuple ns; }; -} -} +} // namespace t00006 +} // namespace clanguml diff --git a/tests/t00006/test_case.h b/tests/t00006/test_case.h index 9d2a5846..46577533 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00007.cc b/tests/t00007/t00007.cc index 35ea0ce9..af9c1813 100644 --- a/tests/t00007/t00007.cc +++ b/tests/t00007/t00007.cc @@ -17,5 +17,5 @@ public: std::shared_ptr b; std::weak_ptr c; }; -} -} +} // namespace t00007 +} // namespace clanguml diff --git a/tests/t00007/test_case.h b/tests/t00007/test_case.h index 9c18d33e..6eccc517 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00008.cc b/tests/t00008/t00008.cc index 763162f8..f711fda7 100644 --- a/tests/t00008/t00008.cc +++ b/tests/t00008/t00008.cc @@ -32,5 +32,5 @@ struct D { void add(int i) { ints.template_template.values.push_back(i); } }; -} -} +} // namespace t00008 +} // namespace clanguml diff --git a/tests/t00008/test_case.h b/tests/t00008/test_case.h index 360850f3..ede6a434 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00009.cc b/tests/t00009/t00009.cc index 74f15079..48412b48 100644 --- a/tests/t00009/t00009.cc +++ b/tests/t00009/t00009.cc @@ -15,5 +15,5 @@ public: A *astring; A> &avector; }; -} -} +} // namespace t00009 +} // namespace clanguml diff --git a/tests/t00009/test_case.h b/tests/t00009/test_case.h index d21f3214..5ad171c3 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00010.cc b/tests/t00010/t00010.cc index 3aded309..0c8b3ab3 100644 --- a/tests/t00010/t00010.cc +++ b/tests/t00010/t00010.cc @@ -19,5 +19,5 @@ class C { public: B aintstring; }; -} -} +} // namespace t00010 +} // namespace clanguml diff --git a/tests/t00010/test_case.h b/tests/t00010/test_case.h index 2409a5e9..245f622c 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00011.cc b/tests/t00011/t00011.cc index 20649aab..e8b03e3c 100644 --- a/tests/t00011/t00011.cc +++ b/tests/t00011/t00011.cc @@ -29,5 +29,5 @@ public: void foo() { m_a->foo(); } A *m_a; }; -} -} +} // namespace t00011 +} // namespace clanguml diff --git a/tests/t00011/test_case.h b/tests/t00011/test_case.h index b3212db1..f28cde67 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00012.cc b/tests/t00012/t00012.cc index 82f292a0..c3bc3416 100644 --- a/tests/t00012/t00012.cc +++ b/tests/t00012/t00012.cc @@ -32,5 +32,5 @@ class R { 3> c1; }; -} -} +} // namespace t00012 +} // namespace clanguml diff --git a/tests/t00012/test_case.h b/tests/t00012/test_case.h index bd76911f..aa979ee7 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00013.cc b/tests/t00013/t00013.cc index d132b142..58022d07 100644 --- a/tests/t00013/t00013.cc +++ b/tests/t00013/t00013.cc @@ -62,5 +62,5 @@ public: private: mutable E estring; }; -} -} +} // namespace t00013 +} // namespace clanguml diff --git a/tests/t00013/test_case.h b/tests/t00013/test_case.h index 3b0375f0..84da76fa 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00014.cc b/tests/t00014/t00014.cc index c065b3f7..8b01e1c1 100644 --- a/tests/t00014/t00014.cc +++ b/tests/t00014/t00014.cc @@ -76,5 +76,5 @@ public: VoidCallback vcb; VectorPtr vps; }; -} -} +} // namespace t00014 +} // namespace clanguml diff --git a/tests/t00014/test_case.h b/tests/t00014/test_case.h index 81c9f3dc..54e8ef2e 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00015/t00015.cc b/tests/t00015/t00015.cc index 736eb9ff..7b5bcd71 100644 --- a/tests/t00015/t00015.cc +++ b/tests/t00015/t00015.cc @@ -16,7 +16,7 @@ namespace { class Anon final : public A { }; } -} +} // namespace ns1 namespace ns3 { @@ -28,5 +28,5 @@ class Anon : public t00015::ns1::A { class B : public ns1::ns2::Anon { }; } -} -} +} // namespace t00015 +} // namespace clanguml diff --git a/tests/t00015/test_case.h b/tests/t00015/test_case.h index 9ba5d4a9..fd934a22 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00016.cc b/tests/t00016/t00016.cc index 8e6c74b9..c4bcad6c 100644 --- a/tests/t00016/t00016.cc +++ b/tests/t00016/t00016.cc @@ -24,5 +24,5 @@ template <> struct is_numeric { template <> struct is_numeric { enum { value = false }; }; -} -} +} // namespace t00016 +} // namespace clanguml diff --git a/tests/t00016/test_case.h b/tests/t00016/test_case.h index 1bc4e8d5..b3304a33 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00017.cc b/tests/t00017/t00017.cc index 92464186..e5d30b17 100644 --- a/tests/t00017/t00017.cc +++ b/tests/t00017/t00017.cc @@ -62,5 +62,5 @@ private: volatile J *j; mutable K *k; }; -} -} +} // namespace t00017 +} // namespace clanguml diff --git a/tests/t00017/test_case.h b/tests/t00017/test_case.h index e659fa6c..33f1c000 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00018.cc b/tests/t00018/t00018.cc index 59c31634..3c1fac90 100644 --- a/tests/t00018/t00018.cc +++ b/tests/t00018/t00018.cc @@ -18,5 +18,5 @@ widget::widget(widget &&) = default; widget::~widget() = default; widget &widget::operator=(widget &&) = default; -} -} +} // namespace t00018 +} // namespace clanguml diff --git a/tests/t00018/t00018_impl.cc b/tests/t00018/t00018_impl.cc index 8bcaef8a..f8c25dfb 100644 --- a/tests/t00018/t00018_impl.cc +++ b/tests/t00018/t00018_impl.cc @@ -21,6 +21,6 @@ void widget::draw(const clanguml::t00018::widget &w) if (w.shown()) std::cout << "drawing a non-const widget " << n << '\n'; } -} -} -} +} // namespace impl +} // namespace t00018 +} // namespace clanguml diff --git a/tests/t00018/test_case.h b/tests/t00018/test_case.h index d1c096bd..db47adc6 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 420bf676..db0e16a0 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00020.cc b/tests/t00020/t00020.cc index b8cd07e9..ee3ba2b8 100644 --- a/tests/t00020/t00020.cc +++ b/tests/t00020/t00020.cc @@ -66,5 +66,5 @@ public: return std::make_unique(); } }; -} -} +} // namespace t00020 +} // namespace clanguml diff --git a/tests/t00020/test_case.h b/tests/t00020/test_case.h index 256a8227..131c920a 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00021.cc b/tests/t00021/t00021.cc index 0a338a12..2c627c97 100644 --- a/tests/t00021/t00021.cc +++ b/tests/t00021/t00021.cc @@ -46,5 +46,5 @@ class B : public Item { public: void accept(const Visitor &visitor) const override { } }; -} -} +} // namespace t00021 +} // namespace clanguml diff --git a/tests/t00021/test_case.h b/tests/t00021/test_case.h index 0764bab4..bfd4e6eb 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00022.cc b/tests/t00022/t00022.cc index d226c4db..19548384 100644 --- a/tests/t00022/t00022.cc +++ b/tests/t00022/t00022.cc @@ -27,5 +27,5 @@ protected: void method1() override { } void method2() override { } }; -} -} +} // namespace t00022 +} // namespace clanguml diff --git a/tests/t00022/test_case.h b/tests/t00022/test_case.h index b6c380d9..e16e979a 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00023.cc b/tests/t00023/t00023.cc index 3d3cdd94..34b48af2 100644 --- a/tests/t00023/t00023.cc +++ b/tests/t00023/t00023.cc @@ -36,5 +36,5 @@ public: private: std::unique_ptr m_strategy; }; -} -} +} // namespace t00023 +} // namespace clanguml diff --git a/tests/t00023/test_case.h b/tests/t00023/test_case.h index 69ddf1df..5dd70267 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00024.cc b/tests/t00024/t00024.cc index d082aee8..e2c6c14e 100644 --- a/tests/t00024/t00024.cc +++ b/tests/t00024/t00024.cc @@ -35,5 +35,5 @@ public: private: std::shared_ptr m_target; }; -} -} +} // namespace t00024 +} // namespace clanguml diff --git a/tests/t00024/test_case.h b/tests/t00024/test_case.h index dbb63c17..bf270004 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00025.cc b/tests/t00025/t00025.cc index 091b8185..6c609492 100644 --- a/tests/t00025/t00025.cc +++ b/tests/t00025/t00025.cc @@ -33,5 +33,5 @@ public: Proxy proxy1; Proxy proxy2; }; -} -} +} // namespace t00025 +} // namespace clanguml diff --git a/tests/t00025/test_case.h b/tests/t00025/test_case.h index a10c0fef..ac0dab92 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00026.cc b/tests/t00026/t00026.cc index 747e8f4b..46956fac 100644 --- a/tests/t00026/t00026.cc +++ b/tests/t00026/t00026.cc @@ -54,5 +54,5 @@ struct StringMemento { Caretaker caretaker; Originator originator; }; -} -} +} // namespace t00026 +} // namespace clanguml diff --git a/tests/t00026/test_case.h b/tests/t00026/test_case.h index ef93d93a..8c92e05a 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00027.cc b/tests/t00027/t00027.cc index 7f0ea89c..e6cfc674 100644 --- a/tests/t00027/t00027.cc +++ b/tests/t00027/t00027.cc @@ -51,5 +51,5 @@ struct Window { Text title; Text description; }; -} -} +} // namespace t00027 +} // namespace clanguml diff --git a/tests/t00027/test_case.h b/tests/t00027/test_case.h index 2a053038..bdbb0221 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 6b0b279c..9d09e6c5 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 3fb4482e..b893e488 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 df353c21..80949e45 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 d53ae8e6..5c18ec98 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 af2b1295..b5f0994e 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 c195981d..92ab70eb 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 395668e3..26016195 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00035/test_case.h b/tests/t00035/test_case.h index faf01d48..3b263cc6 100644 --- a/tests/t00035/test_case.h +++ b/tests/t00035/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00035/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00036/t00036.cc b/tests/t00036/t00036.cc index 952c4ec3..2ba646b7 100644 --- a/tests/t00036/t00036.cc +++ b/tests/t00036/t00036.cc @@ -18,8 +18,8 @@ struct B { }; } -} -} +} // namespace ns11 +} // namespace ns1 namespace ns2 { namespace ns22 { diff --git a/tests/t00036/test_case.h b/tests/t00036/test_case.h index ccd1ec14..0e011476 100644 --- a/tests/t00036/test_case.h +++ b/tests/t00036/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00036/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00037/test_case.h b/tests/t00037/test_case.h index 5347e4de..320a1bbe 100644 --- a/tests/t00037/test_case.h +++ b/tests/t00037/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00037/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00038/test_case.h b/tests/t00038/test_case.h index 59211c89..6593ea2e 100644 --- a/tests/t00038/test_case.h +++ b/tests/t00038/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00038/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00039/.clang-uml b/tests/t00039/.clang-uml index e16ac2f2..4ba5bdca 100644 --- a/tests/t00039/.clang-uml +++ b/tests/t00039/.clang-uml @@ -19,5 +19,6 @@ diagrams: - inheritance exclude: namespaces: + - std - clanguml::t00039::detail - clanguml::t00039::ns3::detail \ No newline at end of file diff --git a/tests/t00039/test_case.h b/tests/t00039/test_case.h index 4aff1a61..3431c2bf 100644 --- a/tests/t00039/test_case.h +++ b/tests/t00039/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00039/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00040/test_case.h b/tests/t00040/test_case.h index bcc75cba..1b91814b 100644 --- a/tests/t00040/test_case.h +++ b/tests/t00040/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00040/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00041/test_case.h b/tests/t00041/test_case.h index 6174ff58..65f56ffa 100644 --- a/tests/t00041/test_case.h +++ b/tests/t00041/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00041/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00042/test_case.h b/tests/t00042/test_case.h index 1422b761..ace2c9b7 100644 --- a/tests/t00042/test_case.h +++ b/tests/t00042/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00042/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00043/test_case.h b/tests/t00043/test_case.h index c90d6bc6..131cef08 100644 --- a/tests/t00043/test_case.h +++ b/tests/t00043/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00043/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00044/test_case.h b/tests/t00044/test_case.h index 6444e8c8..8f98b0ba 100644 --- a/tests/t00044/test_case.h +++ b/tests/t00044/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00044/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00045/t00045.cc b/tests/t00045/t00045.cc index 6ef0cbda..09e3167d 100644 --- a/tests/t00045/t00045.cc +++ b/tests/t00045/t00045.cc @@ -49,5 +49,5 @@ public: void foo(::AA &aa) { (void)aa; } }; -} -} +} // namespace ns2 +} // namespace ns1 diff --git a/tests/t00045/test_case.h b/tests/t00045/test_case.h index c43d53cd..d78e1b49 100644 --- a/tests/t00045/test_case.h +++ b/tests/t00045/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00045/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00046/t00046.cc b/tests/t00046/t00046.cc index 3af06b22..6a26a248 100644 --- a/tests/t00046/t00046.cc +++ b/tests/t00046/t00046.cc @@ -39,5 +39,5 @@ public: void foo(::AA &aa) { (void)aa; } }; -} -} +} // namespace ns2 +} // namespace ns1 diff --git a/tests/t00046/test_case.h b/tests/t00046/test_case.h index 6806fec9..b6f7912a 100644 --- a/tests/t00046/test_case.h +++ b/tests/t00046/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00046/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00047/t00047.cc b/tests/t00047/t00047.cc index 56ae8ca6..9cff730e 100644 --- a/tests/t00047/t00047.cc +++ b/tests/t00047/t00047.cc @@ -22,5 +22,5 @@ struct conditional_t { template using conditional = typename conditional_t::type; -} -} \ No newline at end of file +} // namespace t00047 +} // namespace clanguml \ No newline at end of file diff --git a/tests/t00047/test_case.h b/tests/t00047/test_case.h index 042b1b9a..66e75d88 100644 --- a/tests/t00047/test_case.h +++ b/tests/t00047/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00047/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00048/test_case.h b/tests/t00048/test_case.h index e522af7a..f652bad9 100644 --- a/tests/t00048/test_case.h +++ b/tests/t00048/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00048/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t00049/.clang-uml b/tests/t00049/.clang-uml index 10f19ce1..b8653678 100644 --- a/tests/t00049/.clang-uml +++ b/tests/t00049/.clang-uml @@ -5,8 +5,10 @@ diagrams: type: class using_namespace: clanguml::t00049 type_aliases: - "std::vector": string_vector "std::basic_string": thestring + "std::string": thestring + "std::vector": string_vector + "std::vector": string_vector "std::map": intmap glob: - ../../tests/t00049/t00049.cc diff --git a/tests/t00049/t00049.cc b/tests/t00049/t00049.cc index 2bbd92b2..ee08de9c 100644 --- a/tests/t00049/t00049.cc +++ b/tests/t00049/t00049.cc @@ -19,5 +19,5 @@ struct R { void set_int_map(A> &&int_map) { a_int_map = int_map; } }; -} -} \ No newline at end of file +} // namespace t00049 +} // namespace clanguml \ No newline at end of file diff --git a/tests/t00049/test_case.h b/tests/t00049/test_case.h index 55dbae28..c4cfc515 100644 --- a/tests/t00049/test_case.h +++ b/tests/t00049/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00049/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,8 +52,7 @@ TEST_CASE("t00049", "[test-case][class]") REQUIRE_THAT(puml, (IsField("a_int_map", "A"))); // Check if all relationships exist - REQUIRE_THAT( - puml, IsInstantiation(_A("A"), _A("A>"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); diff --git a/tests/t00050/.clang-uml b/tests/t00050/.clang-uml index eb7a1326..bf7ef914 100644 --- a/tests/t00050/.clang-uml +++ b/tests/t00050/.clang-uml @@ -13,9 +13,13 @@ diagrams: plantuml: after: - > - note left of {{ alias("A") }} - {{ comment("clanguml::t00050::A").formatted }} - end note + note left of {{ alias("NoSuchClass") }} + {{ comment("NoSuchClass").formatted }} + end note + - > + note left of {{ alias("A") }} + {{ comment("clanguml::t00050::A").formatted }} + end note - > note right of {{ element("clanguml::t00050::A").alias }} {% set e=element("clanguml::t00050::A") %} {{ e.comment.formatted }} diff --git a/tests/t00050/t00050.cc b/tests/t00050/t00050.cc index fe2953b5..00cf7580 100644 --- a/tests/t00050/t00050.cc +++ b/tests/t00050/t00050.cc @@ -99,5 +99,5 @@ class G { class NoComment { }; -} -} \ No newline at end of file +} // namespace t00050 +} // namespace clanguml \ No newline at end of file diff --git a/tests/t00050/test_case.h b/tests/t00050/test_case.h index 675a1ac1..b7fe4109 100644 --- a/tests/t00050/test_case.h +++ b/tests/t00050/test_case.h @@ -1,7 +1,7 @@ /** * tests/t00050/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20001.cc b/tests/t20001/t20001.cc index 488c19fe..2c7cdaca 100644 --- a/tests/t20001/t20001.cc +++ b/tests/t20001/t20001.cc @@ -1,5 +1,4 @@ #include -#include #include namespace clanguml { diff --git a/tests/t20001/test_case.h b/tests/t20001/test_case.h index 3b3cf30f..b35edd17 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 d053c8ac..0967f912 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20003/test_case.h b/tests/t20003/test_case.h index 7a17c539..0d3ebb16 100644 --- a/tests/t20003/test_case.h +++ b/tests/t20003/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20003/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20004/test_case.h b/tests/t20004/test_case.h index fc827d68..f67bc340 100644 --- a/tests/t20004/test_case.h +++ b/tests/t20004/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20004/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20005/test_case.h b/tests/t20005/test_case.h index add8b058..d22b0e2c 100644 --- a/tests/t20005/test_case.h +++ b/tests/t20005/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20005/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20006/test_case.h b/tests/t20006/test_case.h index dcedefb0..e7a25810 100644 --- a/tests/t20006/test_case.h +++ b/tests/t20006/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20006/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20007/test_case.h b/tests/t20007/test_case.h index 5f639b51..de94d5b6 100644 --- a/tests/t20007/test_case.h +++ b/tests/t20007/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20007/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20008/test_case.h b/tests/t20008/test_case.h index e5f7703b..9bda4062 100644 --- a/tests/t20008/test_case.h +++ b/tests/t20008/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20008/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20009/test_case.h b/tests/t20009/test_case.h index bddc823e..d67cb134 100644 --- a/tests/t20009/test_case.h +++ b/tests/t20009/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20009/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20010/test_case.h b/tests/t20010/test_case.h index 9e2c6594..7b928fbd 100644 --- a/tests/t20010/test_case.h +++ b/tests/t20010/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20010/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20011/test_case.h b/tests/t20011/test_case.h index f0640636..571f207b 100644 --- a/tests/t20011/test_case.h +++ b/tests/t20011/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20011/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20012/test_case.h b/tests/t20012/test_case.h index e639dd23..71f1a1f1 100644 --- a/tests/t20012/test_case.h +++ b/tests/t20012/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20012/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,35 +36,43 @@ TEST_CASE("t20012", "[test-case][sequence]") // Check if all calls exist REQUIRE_THAT(puml, - HasCall(_A("tmain()"), _A("tmain()::(lambda t20012.cc:66:20)"), + HasCall(_A("tmain()"), + _A("tmain()::(lambda ../../tests/t20012/t20012.cc:66:20)"), "operator()()")); - REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:66:20)"), _A("A"), "a()")); + REQUIRE_THAT(puml, + HasCall(_A("tmain()::(lambda ../../tests/t20012/t20012.cc:66:20)"), + _A("A"), "a()")); REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "aa()")); REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "aaa()")); - REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:66:20)"), _A("B"), "b()")); + REQUIRE_THAT(puml, + HasCall(_A("tmain()::(lambda ../../tests/t20012/t20012.cc:66:20)"), + _A("B"), "b()")); REQUIRE_THAT(puml, HasCall(_A("B"), _A("B"), "bb()")); REQUIRE_THAT(puml, HasCall(_A("B"), _A("B"), "bbb()")); - REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:79:20)"), _A("C"), "c()")); + REQUIRE_THAT(puml, + HasCall(_A("tmain()::(lambda ../../tests/t20012/t20012.cc:79:20)"), + _A("C"), "c()")); REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "cc()")); REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "ccc()")); REQUIRE_THAT(puml, - HasCall(_A("tmain()::(lambda t20012.cc:79:20)"), - _A("tmain()::(lambda t20012.cc:66:20)"), "operator()()")); + HasCall(_A("tmain()::(lambda ../../tests/t20012/t20012.cc:79:20)"), + _A("tmain()::(lambda ../../tests/t20012/t20012.cc:66:20)"), + "operator()()")); REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "ccc()")); REQUIRE_THAT(puml, - HasCall(_A("tmain()"), _A("R"), "r()")); + HasCall(_A("tmain()"), + _A("R"), "r()")); REQUIRE_THAT(puml, - HasCall(_A("R"), - _A("tmain()::(lambda t20012.cc:85:9)"), "operator()()")); - REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:85:9)"), _A("C"), "c()")); + HasCall(_A("R"), + _A("tmain()::(lambda ../../tests/t20012/t20012.cc:85:9)"), + "operator()()")); + REQUIRE_THAT(puml, + HasCall(_A("tmain()::(lambda ../../tests/t20012/t20012.cc:85:9)"), + _A("C"), "c()")); REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("D"), "add5(int)")); diff --git a/tests/t20013/test_case.h b/tests/t20013/test_case.h index 0b100356..ed2da262 100644 --- a/tests/t20013/test_case.h +++ b/tests/t20013/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20013/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20014/test_case.h b/tests/t20014/test_case.h index f92ed6bb..ab49e0f6 100644 --- a/tests/t20014/test_case.h +++ b/tests/t20014/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20014/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20015/test_case.h b/tests/t20015/test_case.h index 54186b73..4587898a 100644 --- a/tests/t20015/test_case.h +++ b/tests/t20015/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20015/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20016/test_case.h b/tests/t20016/test_case.h index 537e94c7..11e020a3 100644 --- a/tests/t20016/test_case.h +++ b/tests/t20016/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20016/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20017/.clang-uml b/tests/t20017/.clang-uml index 0aa577ba..860e217d 100644 --- a/tests/t20017/.clang-uml +++ b/tests/t20017/.clang-uml @@ -10,6 +10,8 @@ diagrams: include: namespaces: - clanguml::t20017 + paths: + - . using_namespace: - clanguml::t20017 start_from: diff --git a/tests/t20017/test_case.h b/tests/t20017/test_case.h index ee5ede2f..dd1278d9 100644 --- a/tests/t20017/test_case.h +++ b/tests/t20017/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20017/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20018/test_case.h b/tests/t20018/test_case.h index d48116cb..985d4722 100644 --- a/tests/t20018/test_case.h +++ b/tests/t20018/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20018/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20019/test_case.h b/tests/t20019/test_case.h index 3e7bb3f7..ebc0e759 100644 --- a/tests/t20019/test_case.h +++ b/tests/t20019/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20019/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20020/test_case.h b/tests/t20020/test_case.h index 3fd1aaec..2855a2e1 100644 --- a/tests/t20020/test_case.h +++ b/tests/t20020/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20020/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20021/test_case.h b/tests/t20021/test_case.h index f6e57132..24b01727 100644 --- a/tests/t20021/test_case.h +++ b/tests/t20021/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20021/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20022/test_case.h b/tests/t20022/test_case.h index 9ffa41ed..2f68ac04 100644 --- a/tests/t20022/test_case.h +++ b/tests/t20022/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20022/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20023/test_case.h b/tests/t20023/test_case.h index b381887b..82f50fd4 100644 --- a/tests/t20023/test_case.h +++ b/tests/t20023/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20023/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20024/test_case.h b/tests/t20024/test_case.h index b91bbf48..acea65fc 100644 --- a/tests/t20024/test_case.h +++ b/tests/t20024/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20024/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20025/test_case.h b/tests/t20025/test_case.h index 2d6c55f7..1c097c82 100644 --- a/tests/t20025/test_case.h +++ b/tests/t20025/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20025/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20026/test_case.h b/tests/t20026/test_case.h index e3298fcf..324a348a 100644 --- a/tests/t20026/test_case.h +++ b/tests/t20026/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20026/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20027/test_case.h b/tests/t20027/test_case.h index 70c54825..48b359dd 100644 --- a/tests/t20027/test_case.h +++ b/tests/t20027/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20027/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20028/test_case.h b/tests/t20028/test_case.h index ecee16ef..44c7677d 100644 --- a/tests/t20028/test_case.h +++ b/tests/t20028/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20028/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t20029/test_case.h b/tests/t20029/test_case.h index 346ce8ce..bf0b820f 100644 --- a/tests/t20029/test_case.h +++ b/tests/t20029/test_case.h @@ -1,7 +1,7 @@ /** * tests/t20029/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/test_case.h b/tests/t30001/test_case.h index be84d0fd..4d4ac8f0 100644 --- a/tests/t30001/test_case.h +++ b/tests/t30001/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30001/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t30002/test_case.h b/tests/t30002/test_case.h index 0eb66a3e..9a58f79d 100644 --- a/tests/t30002/test_case.h +++ b/tests/t30002/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30002/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t30003/test_case.h b/tests/t30003/test_case.h index 7a7d9321..5ea54ed4 100644 --- a/tests/t30003/test_case.h +++ b/tests/t30003/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30003/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t30004/test_case.h b/tests/t30004/test_case.h index ac6700b5..4dcd529e 100644 --- a/tests/t30004/test_case.h +++ b/tests/t30004/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30004/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t30005/test_case.h b/tests/t30005/test_case.h index 57fb3d6c..2d3fc166 100644 --- a/tests/t30005/test_case.h +++ b/tests/t30005/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30005/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t30006/test_case.h b/tests/t30006/test_case.h index 944da322..7b711888 100644 --- a/tests/t30006/test_case.h +++ b/tests/t30006/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30006/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t30007/test_case.h b/tests/t30007/test_case.h index 3212d067..9d1dbbbd 100644 --- a/tests/t30007/test_case.h +++ b/tests/t30007/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30007/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t30008/test_case.h b/tests/t30008/test_case.h index 9d62f64c..70bae7b8 100644 --- a/tests/t30008/test_case.h +++ b/tests/t30008/test_case.h @@ -1,7 +1,7 @@ /** * tests/t30008/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/t40001/.clang-uml b/tests/t40001/.clang-uml index 918e7f64..c4c549b5 100644 --- a/tests/t40001/.clang-uml +++ b/tests/t40001/.clang-uml @@ -6,8 +6,7 @@ diagrams: # Provide the files to parse in order to look # for #include directives glob: - - ../../tests/t40001/**/*.cc - - ../../tests/t40001/**/*.h + - ../../tests/t40001/src/t40001.cc # Render the paths relative to this directory relative_to: ../../../tests/t40001 # Include also external system headers @@ -15,7 +14,7 @@ diagrams: include: # Include only headers belonging to these paths paths: - - ../../../tests/t40001 + - . plantuml: before: - "' t40001 test include diagram" diff --git a/tests/t40001/test_case.h b/tests/t40001/test_case.h index 31240a19..d3a15495 100644 --- a/tests/t40001/test_case.h +++ b/tests/t40001/test_case.h @@ -1,7 +1,7 @@ /** * tests/t40001/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,7 +16,7 @@ * limitations under the License. */ -TEST_CASE("t40001", "[test-case][package]") +TEST_CASE("t40001", "[test-case][include]") { auto [config, db] = load_config("t40001"); diff --git a/tests/t40002/.clang-uml b/tests/t40002/.clang-uml index 8daf8264..84c790a2 100644 --- a/tests/t40002/.clang-uml +++ b/tests/t40002/.clang-uml @@ -6,18 +6,19 @@ diagrams: # Provide the files to parse in order to look # for #include directives glob: - - ../../tests/t40002/**/*.cc - - ../../tests/t40002/**/*.h + - ../../tests/t40002/src/t40002.cc + - ../../tests/t40002/src/lib1/lib1.cc + - ../../tests/t40002/src/lib2/lib2.cc # Render the paths relative to this directory relative_to: ../../../tests/t40002 include: - # Include only files belonging to these paths + # Include only files belonging to these paths relative to relative_to paths: - - ../../../tests/t40002 + - . exclude: paths: - # Exclude single header - - ../../../tests/t40002/include/lib2/lib2_detail.h + # Exclude single header relative to relative_to + - include/lib2/lib2_detail.h plantuml: before: - "' t40002 test include diagram" \ No newline at end of file diff --git a/tests/t40002/src/lib2/lib2.cc b/tests/t40002/src/lib2/lib2.cc index 89450d88..49133ed2 100644 --- a/tests/t40002/src/lib2/lib2.cc +++ b/tests/t40002/src/lib2/lib2.cc @@ -11,4 +11,4 @@ int foo() { return foo1(); } int foo22() { return 22; } -} \ No newline at end of file +} // namespace clanguml::t40002::lib2 \ No newline at end of file diff --git a/tests/t40002/test_case.h b/tests/t40002/test_case.h index 15aa503a..fa5357fc 100644 --- a/tests/t40002/test_case.h +++ b/tests/t40002/test_case.h @@ -1,7 +1,7 @@ /** * tests/t40002/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,7 +16,7 @@ * limitations under the License. */ -TEST_CASE("t40002", "[test-case][package]") +TEST_CASE("t40002", "[test-case][include]") { auto [config, db] = load_config("t40002"); diff --git a/tests/t40003/.clang-uml b/tests/t40003/.clang-uml index 64f03790..be81eb3a 100644 --- a/tests/t40003/.clang-uml +++ b/tests/t40003/.clang-uml @@ -6,17 +6,17 @@ diagrams: # Provide the files to parse in order to look # for #include directives glob: - - ../../tests/t40003/include/**/*.h - - ../../tests/t40003/src/**/*.cc + - ../../tests/t40003/src/dependants/t1.cc + - ../../tests/t40003/src/dependencies/t2.cc # Render the paths relative to this directory relative_to: ../../../tests/t40003 include: # Include only files which depend on t1.h dependants: - - ../../../tests/t40003/include/dependants/t1.h + - include/dependants/t1.h # and dependencies of t2.cc dependencies: - - ../../../tests/t40003/src/dependencies/t2.cc + - src/dependencies/t2.cc plantuml: before: - "' t40003 test include diagram" \ No newline at end of file diff --git a/tests/t40003/test_case.h b/tests/t40003/test_case.h index 9ad79ce2..8c240777 100644 --- a/tests/t40003/test_case.h +++ b/tests/t40003/test_case.h @@ -1,7 +1,7 @@ /** * tests/t40003/test_case.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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,7 +16,7 @@ * limitations under the License. */ -TEST_CASE("t40003", "[test-case][package]") +TEST_CASE("t40003", "[test-case][include]") { auto [config, db] = load_config("t40003"); diff --git a/tests/t90000/test_case.h b/tests/t90000/test_case.h index 3cfb23a8..3f40682f 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 ce863832..2fb79ecb 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -1,7 +1,7 @@ /** * tests/test_cases.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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.h b/tests/test_cases.h index 5930191c..2a929ff6 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -1,7 +1,7 @@ /** * tests/test_cases.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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_config.cc b/tests/test_config.cc index dc2ed3ba..3b07867a 100644 --- a/tests/test_config.cc +++ b/tests/test_config.cc @@ -1,7 +1,7 @@ /** * tests/test_config.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -154,7 +154,7 @@ TEST_CASE("Test config layout", "[unit-test]") CHECK(cfg.diagrams.size() == 2); - auto &def = static_cast( + [[maybe_unused]] auto &def = static_cast( *cfg.diagrams["class_main"]); auto check_layout = [](const auto &diagram, diff --git a/tests/test_config_data/filters.yml b/tests/test_config_data/filters.yml index f6bca1e2..478892c3 100644 --- a/tests/test_config_data/filters.yml +++ b/tests/test_config_data/filters.yml @@ -3,18 +3,18 @@ output_directory: output diagrams: include_test: - type: class + type: include + relative_to: ../../../src glob: - src/**/*.cc - src/**/*.h include: paths: - - dir1 - - dir2/dir1 - - file1.h + - class_diagram/ + - common + - util + - main.cc exclude: paths: - - dir1/file9.h - - dir2/dir1/file9.h - - dir1/dir3 - - file9.h \ No newline at end of file + - sequence_diagram + - util/error.h \ No newline at end of file diff --git a/tests/test_decorator_parser.cc b/tests/test_decorator_parser.cc index 233c47ee..60c86d2e 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-2022 Bartek Kryza + * Copyright (c) 2021-2023 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_filters.cc b/tests/test_filters.cc index 99942631..7caea613 100644 --- a/tests/test_filters.cc +++ b/tests/test_filters.cc @@ -1,7 +1,7 @@ /** * tests/test_filters.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,11 +32,6 @@ TEST_CASE("Test diagram paths filter", "[unit-test]") using clanguml::common::model::diagram_filter; using clanguml::common::model::source_file; - auto make_path = [](std::string_view p) { - return source_file{ - std::filesystem::current_path() / "test_config_data" / p}; - }; - auto cfg = clanguml::config::load("./test_config_data/filters.yml"); CHECK(cfg.diagrams.size() == 1); @@ -45,10 +40,15 @@ TEST_CASE("Test diagram paths filter", "[unit-test]") diagram_filter filter(diagram, config); - CHECK(filter.should_include(make_path("dir1/file1.h"))); - CHECK(filter.should_include(make_path("dir1/dir2/file1.h"))); - CHECK(filter.should_include(make_path("dir1/dir2/dir3/dir4/file1.h"))); - CHECK_FALSE(filter.should_include(make_path("dir1/file9.h"))); - CHECK_FALSE(filter.should_include(make_path("dir1/dir3/file1.h"))); - CHECK_FALSE(filter.should_include(make_path("dir2/dir1/file9.h"))); + auto make_path = [&](std::string_view p) { + return source_file{config.relative_to() / p}; + }; + + CHECK(filter.should_include( + make_path("class_diagram/visitor/translation_unit_visitor.h"))); + CHECK(filter.should_include(make_path("main.cc"))); + CHECK(filter.should_include(make_path("util/util.cc"))); + CHECK_FALSE(filter.should_include(make_path("util/error.h"))); + CHECK_FALSE(filter.should_include( + make_path("sequence_diagram/visitor/translation_unit_visitor.h"))); } diff --git a/tests/test_model.cc b/tests/test_model.cc index 8a8350ac..05ebd01e 100644 --- a/tests/test_model.cc +++ b/tests/test_model.cc @@ -1,7 +1,7 @@ /** * tests/test_model.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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_thread_pool_executor.cc b/tests/test_thread_pool_executor.cc index e4a209e3..45655992 100644 --- a/tests/test_thread_pool_executor.cc +++ b/tests/test_thread_pool_executor.cc @@ -1,7 +1,7 @@ /** * tests/test_thread_pool_executor.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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 453e9252..06410159 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -1,7 +1,7 @@ /** * tests/test_util.cc * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 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/backward-cpp/LICENSE.txt b/thirdparty/backward-cpp/LICENSE.txt new file mode 100644 index 00000000..269e8abb --- /dev/null +++ b/thirdparty/backward-cpp/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright 2013 Google Inc. All Rights Reserved. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/backward-cpp/backward.hpp b/thirdparty/backward-cpp/backward.hpp new file mode 100644 index 00000000..3bab716c --- /dev/null +++ b/thirdparty/backward-cpp/backward.hpp @@ -0,0 +1,4495 @@ +/* + * backward.hpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 +#define H_6B9572DA_A64B_49E6_B234_051480991C89 + +#ifndef __cplusplus +#error "It's not going to compile without a C++ compiler..." +#endif + +#if defined(BACKWARD_CXX11) +#elif defined(BACKWARD_CXX98) +#else +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +#define BACKWARD_CXX11 +#define BACKWARD_ATLEAST_CXX11 +#define BACKWARD_ATLEAST_CXX98 +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define BACKWARD_ATLEAST_CXX17 +#endif +#else +#define BACKWARD_CXX98 +#define BACKWARD_ATLEAST_CXX98 +#endif +#endif + +// You can define one of the following (or leave it to the auto-detection): +// +// #define BACKWARD_SYSTEM_LINUX +// - specialization for linux +// +// #define BACKWARD_SYSTEM_DARWIN +// - specialization for Mac OS X 10.5 and later. +// +// #define BACKWARD_SYSTEM_WINDOWS +// - specialization for Windows (Clang 9 and MSVC2017) +// +// #define BACKWARD_SYSTEM_UNKNOWN +// - placebo implementation, does nothing. +// +#if defined(BACKWARD_SYSTEM_LINUX) +#elif defined(BACKWARD_SYSTEM_DARWIN) +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +#elif defined(BACKWARD_SYSTEM_WINDOWS) +#else +#if defined(__linux) || defined(__linux__) +#define BACKWARD_SYSTEM_LINUX +#elif defined(__APPLE__) +#define BACKWARD_SYSTEM_DARWIN +#elif defined(_WIN32) +#define BACKWARD_SYSTEM_WINDOWS +#else +#define BACKWARD_SYSTEM_UNKNOWN +#endif +#endif + +#define NOINLINE __attribute__((noinline)) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BACKWARD_SYSTEM_LINUX) + +// On linux, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtime in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace seems to be a little bit more portable than libunwind, but on +// linux, it uses unwind anyway, but abstract away a tiny information that is +// sadly really important in order to get perfectly accurate stack traces. +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#endif + +// On linux, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_DW 1 +// - libdw gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dw": +// - apt-get install libdw-dev +// - g++/clang++ -ldw ... +// +// #define BACKWARD_HAS_BFD 1 +// - With libbfd, you get a fair amount of details: +// - object filename +// - function name +// - source filename +// - line numbers +// - source code snippet (assuming the file is accessible) +// - You need to link with the lib "bfd": +// - apt-get install binutils-dev +// - g++/clang++ -lbfd ... +// +// #define BACKWARD_HAS_DWARF 1 +// - libdwarf gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dwarf": +// - apt-get install libdwarf-dev +// - g++/clang++ -ldwarf ... +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_DW == 1 +#elif BACKWARD_HAS_BFD == 1 +#elif BACKWARD_HAS_DWARF == 1 +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_DW +#define BACKWARD_HAS_DW 0 +#undef BACKWARD_HAS_BFD +#define BACKWARD_HAS_BFD 0 +#undef BACKWARD_HAS_DWARF +#define BACKWARD_HAS_DWARF 0 +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#ifdef __ANDROID__ +// Old Android API levels define _Unwind_Ptr in both link.h and +// unwind.h Rename the one in link.h as we are not going to be using +// it +#define _Unwind_Ptr _Unwind_Ptr_Custom +#include +#undef _Unwind_Ptr +#else +#include +#endif +#if defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) +// Linux kernel header required for the struct pt_regs definition +// to access the NIP (Next Instruction Pointer) register value +#include +#endif +#include +#include +#include +#include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#else +#include +#endif + +#if BACKWARD_HAS_BFD == 1 +// NOTE: defining PACKAGE{,_VERSION} is required before including +// bfd.h on some platforms, see also: +// https://sourceware.org/bugzilla/show_bug.cgi?id=14243 +#ifndef PACKAGE +#define PACKAGE +#endif +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION +#endif +#include +#endif + +#if BACKWARD_HAS_DW == 1 +#include +#include +#include +#endif + +#if BACKWARD_HAS_DWARF == 1 +#include +#include +#include +#include +#include +#endif + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +// then we shall rely on backtrace +#include +#endif + +#endif // defined(BACKWARD_SYSTEM_LINUX) + +#if defined(BACKWARD_SYSTEM_DARWIN) +// On Darwin, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtime in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind comes from clang, which implements an API compatible version. +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace is available by default, though it does not produce as much +// information as another library might. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#endif + +// On Darwin, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#include +#include +#include +#include + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +#include +#endif +#endif // defined(BACKWARD_SYSTEM_DARWIN) + +#if defined(BACKWARD_SYSTEM_WINDOWS) + +#include +#include +#include + +#include + +#ifdef _WIN64 +typedef SSIZE_T ssize_t; +#else +typedef int ssize_t; +#endif + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include + +#include +#include + +#ifndef __clang__ +#undef NOINLINE +#define NOINLINE __declspec(noinline) +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "dbghelp.lib") +#endif + +// Comment / packing is from stackoverflow: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 +// Some versions of imagehlp.dll lack the proper packing directives themselves +// so we need to do it. +#pragma pack(push, before_imagehlp, 8) +#include +#pragma pack(pop, before_imagehlp) + +// TODO maybe these should be undefined somewhere else? +#undef BACKWARD_HAS_UNWIND +#undef BACKWARD_HAS_BACKTRACE +#if BACKWARD_HAS_PDB_SYMBOL == 1 +#else +#undef BACKWARD_HAS_PDB_SYMBOL +#define BACKWARD_HAS_PDB_SYMBOL 1 +#endif + +#endif + +#if BACKWARD_HAS_UNWIND == 1 + +#include +// while gcc's unwind.h defines something like that: +// extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); +// extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); +// +// clang's unwind.h defines something like this: +// uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); +// +// Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we +// cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr +// anyway. +// +// Luckily we can play on the fact that the guard macros have a different name: +#ifdef __CLANG_UNWIND_H +// In fact, this function still comes from libgcc (on my different linux boxes, +// clang links against libgcc). +#include +extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *); +#endif + +#endif // BACKWARD_HAS_UNWIND == 1 + +#if BACKWARD_HAS_LIBUNWIND == 1 +#define UNW_LOCAL_ONLY +#include +#endif // BACKWARD_HAS_LIBUNWIND == 1 + +#ifdef BACKWARD_ATLEAST_CXX11 +#include +#include // for std::swap +namespace backward { +namespace details { +template struct hashtable { + typedef std::unordered_map type; +}; +using std::move; +} // namespace details +} // namespace backward +#else // NOT BACKWARD_ATLEAST_CXX11 +#define nullptr NULL +#define override +#include +namespace backward { +namespace details { +template struct hashtable { + typedef std::map type; +}; +template const T &move(const T &v) { return v; } +template T &move(T &v) { return v; } +} // namespace details +} // namespace backward +#endif // BACKWARD_ATLEAST_CXX11 + +namespace backward { +namespace details { +#if defined(BACKWARD_SYSTEM_WINDOWS) +const char kBackwardPathDelimiter[] = ";"; +#else +const char kBackwardPathDelimiter[] = ":"; +#endif +} // namespace details +} // namespace backward + +namespace backward { + +namespace system_tag { +struct linux_tag; // seems that I cannot call that "linux" because the name +// is already defined... so I am adding _tag everywhere. +struct darwin_tag; +struct windows_tag; +struct unknown_tag; + +#if defined(BACKWARD_SYSTEM_LINUX) +typedef linux_tag current_tag; +#elif defined(BACKWARD_SYSTEM_DARWIN) +typedef darwin_tag current_tag; +#elif defined(BACKWARD_SYSTEM_WINDOWS) +typedef windows_tag current_tag; +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +typedef unknown_tag current_tag; +#else +#error "May I please get my system defines?" +#endif +} // namespace system_tag + +namespace trace_resolver_tag { +#if defined(BACKWARD_SYSTEM_LINUX) +struct libdw; +struct libbfd; +struct libdwarf; +struct backtrace_symbol; + +#if BACKWARD_HAS_DW == 1 +typedef libdw current; +#elif BACKWARD_HAS_BFD == 1 +typedef libbfd current; +#elif BACKWARD_HAS_DWARF == 1 +typedef libdwarf current; +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_DARWIN) +struct backtrace_symbol; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_WINDOWS) +struct pdb_symbol; +#if BACKWARD_HAS_PDB_SYMBOL == 1 +typedef pdb_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#endif +} // namespace trace_resolver_tag + +namespace details { + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef const T type; }; + +template struct deleter { + template void operator()(U &ptr) const { (*F)(ptr); } +}; + +template struct default_delete { + void operator()(T &ptr) const { delete ptr; } +}; + +template > +class handle { + struct dummy; + T _val; + bool _empty; + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(const handle &) = delete; + handle &operator=(const handle &) = delete; +#endif + +public: + ~handle() { + if (!_empty) { + Deleter()(_val); + } + } + + explicit handle() : _val(), _empty(true) {} + explicit handle(T val) : _val(val), _empty(false) { + if (!_val) + _empty = true; + } + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(handle &&from) : _empty(true) { swap(from); } + handle &operator=(handle &&from) { + swap(from); + return *this; + } +#else + explicit handle(const handle &from) : _empty(true) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + handle &operator=(const handle &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + + void reset(T new_val) { + handle tmp(new_val); + swap(tmp); + } + + void update(T new_val) { + _val = new_val; + _empty = !static_cast(new_val); + } + + operator const dummy *() const { + if (_empty) { + return nullptr; + } + return reinterpret_cast(_val); + } + T get() { return _val; } + T release() { + _empty = true; + return _val; + } + void swap(handle &b) { + using std::swap; + swap(b._val, _val); // can throw, we are safe here. + swap(b._empty, _empty); // should not throw: if you cannot swap two + // bools without throwing... It's a lost cause anyway! + } + + T &operator->() { return _val; } + const T &operator->() const { return _val; } + + typedef typename rm_ptr::type &ref_t; + typedef const typename rm_ptr::type &const_ref_t; + ref_t operator*() { return *_val; } + const_ref_t operator*() const { return *_val; } + ref_t operator[](size_t idx) { return _val[idx]; } + + // Watch out, we've got a badass over here + T *operator&() { + _empty = false; + return &_val; + } +}; + +// Default demangler implementation (do nothing). +template struct demangler_impl { + static std::string demangle(const char *funcname) { return funcname; } +}; + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +template <> struct demangler_impl { + demangler_impl() : _demangle_buffer_length(0) {} + + std::string demangle(const char *funcname) { + using namespace details; + char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(), + &_demangle_buffer_length, nullptr); + if (result) { + _demangle_buffer.update(result); + return result; + } + return funcname; + } + +private: + details::handle _demangle_buffer; + size_t _demangle_buffer_length; +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +struct demangler : public demangler_impl {}; + +// Split a string on the platform's PATH delimiter. Example: if delimiter +// is ":" then: +// "" --> [] +// ":" --> ["",""] +// "::" --> ["","",""] +// "/a/b/c" --> ["/a/b/c"] +// "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"] +// etc. +inline std::vector split_source_prefixes(const std::string &s) { + std::vector out; + size_t last = 0; + size_t next = 0; + size_t delimiter_size = sizeof(kBackwardPathDelimiter) - 1; + while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) { + out.push_back(s.substr(last, next - last)); + last = next + delimiter_size; + } + if (last <= s.length()) { + out.push_back(s.substr(last)); + } + return out; +} + +} // namespace details + +/*************** A TRACE ***************/ + +struct Trace { + void *addr; + size_t idx; + + Trace() : addr(nullptr), idx(0) {} + + explicit Trace(void *_addr, size_t _idx) : addr(_addr), idx(_idx) {} +}; + +struct ResolvedTrace : public Trace { + + struct SourceLoc { + std::string function; + std::string filename; + unsigned line; + unsigned col; + + SourceLoc() : line(0), col(0) {} + + bool operator==(const SourceLoc &b) const { + return function == b.function && filename == b.filename && + line == b.line && col == b.col; + } + + bool operator!=(const SourceLoc &b) const { return !(*this == b); } + }; + + // In which binary object this trace is located. + std::string object_filename; + + // The function in the object that contain the trace. This is not the same + // as source.function which can be an function inlined in object_function. + std::string object_function; + + // The source location of this trace. It is possible for filename to be + // empty and for line/col to be invalid (value 0) if this information + // couldn't be deduced, for example if there is no debug information in the + // binary object. + SourceLoc source; + + // An optionals list of "inliners". All the successive sources location + // from where the source location of the trace (the attribute right above) + // is inlined. It is especially useful when you compiled with optimization. + typedef std::vector source_locs_t; + source_locs_t inliners; + + ResolvedTrace() : Trace() {} + ResolvedTrace(const Trace &mini_trace) : Trace(mini_trace) {} +}; + +/*************** STACK TRACE ***************/ + +// default implemention. +template class StackTraceImpl { +public: + size_t size() const { return 0; } + Trace operator[](size_t) const { return Trace(); } + size_t load_here(size_t = 0) { return 0; } + size_t load_from(void *, size_t = 0, void * = nullptr, void * = nullptr) { + return 0; + } + size_t thread_id() const { return 0; } + void skip_n_firsts(size_t) {} +}; + +class StackTraceImplBase { +public: + StackTraceImplBase() + : _thread_id(0), _skip(0), _context(nullptr), _error_addr(nullptr) {} + + size_t thread_id() const { return _thread_id; } + + void skip_n_firsts(size_t n) { _skip = n; } + +protected: + void load_thread_info() { +#ifdef BACKWARD_SYSTEM_LINUX +#ifndef __ANDROID__ + _thread_id = static_cast(syscall(SYS_gettid)); +#else + _thread_id = static_cast(gettid()); +#endif + if (_thread_id == static_cast(getpid())) { + // If the thread is the main one, let's hide that. + // I like to keep little secret sometimes. + _thread_id = 0; + } +#elif defined(BACKWARD_SYSTEM_DARWIN) + _thread_id = reinterpret_cast(pthread_self()); + if (pthread_main_np() == 1) { + // If the thread is the main one, let's hide that. + _thread_id = 0; + } +#endif + } + + void set_context(void *context) { _context = context; } + void *context() const { return _context; } + + void set_error_addr(void *error_addr) { _error_addr = error_addr; } + void *error_addr() const { return _error_addr; } + + size_t skip_n_firsts() const { return _skip; } + +private: + size_t _thread_id; + size_t _skip; + void *_context; + void *_error_addr; +}; + +class StackTraceImplHolder : public StackTraceImplBase { +public: + size_t size() const { + return (_stacktrace.size() >= skip_n_firsts()) + ? _stacktrace.size() - skip_n_firsts() + : 0; + } + Trace operator[](size_t idx) const { + if (idx >= size()) { + return Trace(); + } + return Trace(_stacktrace[idx + skip_n_firsts()], idx); + } + void *const *begin() const { + if (size()) { + return &_stacktrace[skip_n_firsts()]; + } + return nullptr; + } + +protected: + std::vector _stacktrace; +}; + +#if BACKWARD_HAS_UNWIND == 1 + +namespace details { + +template class Unwinder { +public: + size_t operator()(F &f, size_t depth) { + _f = &f; + _index = -1; + _depth = depth; + _Unwind_Backtrace(&this->backtrace_trampoline, this); + if (_index == -1) { + // _Unwind_Backtrace has failed to obtain any backtraces + return 0; + } else { + return static_cast(_index); + } + } + +private: + F *_f; + ssize_t _index; + size_t _depth; + + static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context *ctx, + void *self) { + return (static_cast(self))->backtrace(ctx); + } + + _Unwind_Reason_Code backtrace(_Unwind_Context *ctx) { + if (_index >= 0 && static_cast(_index) >= _depth) + return _URC_END_OF_STACK; + + int ip_before_instruction = 0; + uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); + + if (!ip_before_instruction) { + // calculating 0-1 for unsigned, looks like a possible bug to sanitizers, + // so let's do it explicitly: + if (ip == 0) { + ip = std::numeric_limits::max(); // set it to 0xffff... (as + // from casting 0-1) + } else { + ip -= 1; // else just normally decrement it (no overflow/underflow will + // happen) + } + } + + if (_index >= 0) { // ignore first frame. + (*_f)(static_cast(_index), reinterpret_cast(ip)); + } + _index += 1; + return _URC_NO_REASON; + } +}; + +template size_t unwind(F f, size_t depth) { + Unwinder unwinder; + return unwinder(f, depth); +} + +} // namespace details + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_thread_info(); + set_context(context); + set_error_addr(error_addr); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth); + size_t trace_cnt = details::unwind(callback(*this), depth); + _stacktrace.resize(trace_cnt); + skip_n_firsts(0); + return size(); + } + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + struct callback { + StackTraceImpl &self; + callback(StackTraceImpl &_self) : self(_self) {} + + void operator()(size_t idx, void *addr) { self._stacktrace[idx] = addr; } + }; +}; + +#elif BACKWARD_HAS_LIBUNWIND == 1 + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + __attribute__((noinline)) size_t load_here(size_t depth = 32, + void *_context = nullptr, + void *_error_addr = nullptr) { + set_context(_context); + set_error_addr(_error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + + int result = 0; + + unw_context_t ctx; + size_t index = 0; + + // Add the tail call. If the Instruction Pointer is the crash address it + // means we got a bad function pointer dereference, so we "unwind" the + // bad pointer manually by using the return address pointed to by the + // Stack Pointer as the Instruction Pointer and letting libunwind do + // the rest + + if (context()) { + ucontext_t *uctx = reinterpret_cast(context()); +#ifdef REG_RIP // x86_64 + if (uctx->uc_mcontext.gregs[REG_RIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_RIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_RSP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(REG_EIP) // x86_32 + if (uctx->uc_mcontext.gregs[REG_EIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_EIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_ESP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(__arm__) + // libunwind uses its own context type for ARM unwinding. + // Copy the registers from the signal handler's context so we can + // unwind + unw_getcontext(&ctx); + ctx.regs[UNW_ARM_R0] = uctx->uc_mcontext.arm_r0; + ctx.regs[UNW_ARM_R1] = uctx->uc_mcontext.arm_r1; + ctx.regs[UNW_ARM_R2] = uctx->uc_mcontext.arm_r2; + ctx.regs[UNW_ARM_R3] = uctx->uc_mcontext.arm_r3; + ctx.regs[UNW_ARM_R4] = uctx->uc_mcontext.arm_r4; + ctx.regs[UNW_ARM_R5] = uctx->uc_mcontext.arm_r5; + ctx.regs[UNW_ARM_R6] = uctx->uc_mcontext.arm_r6; + ctx.regs[UNW_ARM_R7] = uctx->uc_mcontext.arm_r7; + ctx.regs[UNW_ARM_R8] = uctx->uc_mcontext.arm_r8; + ctx.regs[UNW_ARM_R9] = uctx->uc_mcontext.arm_r9; + ctx.regs[UNW_ARM_R10] = uctx->uc_mcontext.arm_r10; + ctx.regs[UNW_ARM_R11] = uctx->uc_mcontext.arm_fp; + ctx.regs[UNW_ARM_R12] = uctx->uc_mcontext.arm_ip; + ctx.regs[UNW_ARM_R13] = uctx->uc_mcontext.arm_sp; + ctx.regs[UNW_ARM_R14] = uctx->uc_mcontext.arm_lr; + ctx.regs[UNW_ARM_R15] = uctx->uc_mcontext.arm_pc; + + // If we have crashed in the PC use the LR instead, as this was + // a bad function dereference + if (reinterpret_cast(error_addr()) == + uctx->uc_mcontext.arm_pc) { + ctx.regs[UNW_ARM_R15] = + uctx->uc_mcontext.arm_lr - sizeof(unsigned long); + } + _stacktrace[index] = reinterpret_cast(ctx.regs[UNW_ARM_R15]); + ++index; +#elif defined(__APPLE__) && defined(__x86_64__) + unw_getcontext(&ctx); + // OS X's implementation of libunwind uses its own context object + // so we need to convert the passed context to libunwind's format + // (information about the data layout taken from unw_getcontext.s + // in Apple's libunwind source + ctx.data[0] = uctx->uc_mcontext->__ss.__rax; + ctx.data[1] = uctx->uc_mcontext->__ss.__rbx; + ctx.data[2] = uctx->uc_mcontext->__ss.__rcx; + ctx.data[3] = uctx->uc_mcontext->__ss.__rdx; + ctx.data[4] = uctx->uc_mcontext->__ss.__rdi; + ctx.data[5] = uctx->uc_mcontext->__ss.__rsi; + ctx.data[6] = uctx->uc_mcontext->__ss.__rbp; + ctx.data[7] = uctx->uc_mcontext->__ss.__rsp; + ctx.data[8] = uctx->uc_mcontext->__ss.__r8; + ctx.data[9] = uctx->uc_mcontext->__ss.__r9; + ctx.data[10] = uctx->uc_mcontext->__ss.__r10; + ctx.data[11] = uctx->uc_mcontext->__ss.__r11; + ctx.data[12] = uctx->uc_mcontext->__ss.__r12; + ctx.data[13] = uctx->uc_mcontext->__ss.__r13; + ctx.data[14] = uctx->uc_mcontext->__ss.__r14; + ctx.data[15] = uctx->uc_mcontext->__ss.__r15; + ctx.data[16] = uctx->uc_mcontext->__ss.__rip; + + // If the IP is the same as the crash address we have a bad function + // dereference The caller's address is pointed to by %rsp, so we + // dereference that value and set it to be the next frame's IP. + if (uctx->uc_mcontext->__ss.__rip == + reinterpret_cast<__uint64_t>(error_addr())) { + ctx.data[16] = + *reinterpret_cast<__uint64_t *>(uctx->uc_mcontext->__ss.__rsp); + } + _stacktrace[index] = reinterpret_cast(ctx.data[16]); + ++index; +#elif defined(__APPLE__) + unw_getcontext(&ctx) + // TODO: Convert the ucontext_t to libunwind's unw_context_t like + // we do in 64 bits + if (ctx.uc_mcontext->__ss.__eip == + reinterpret_cast(error_addr())) { + ctx.uc_mcontext->__ss.__eip = ctx.uc_mcontext->__ss.__esp; + } + _stacktrace[index] = + reinterpret_cast(ctx.uc_mcontext->__ss.__eip); + ++index; +#endif + } + + unw_cursor_t cursor; + if (context()) { +#if defined(UNW_INIT_SIGNAL_FRAME) + result = unw_init_local2(&cursor, &ctx, UNW_INIT_SIGNAL_FRAME); +#else + result = unw_init_local(&cursor, &ctx); +#endif + } else { + unw_getcontext(&ctx); + ; + result = unw_init_local(&cursor, &ctx); + } + + if (result != 0) + return 1; + + unw_word_t ip = 0; + + while (index <= depth && unw_step(&cursor) > 0) { + result = unw_get_reg(&cursor, UNW_REG_IP, &ip); + if (result == 0) { + _stacktrace[index] = reinterpret_cast(--ip); + ++index; + } + } + --index; + + _stacktrace.resize(index + 1); + skip_n_firsts(0); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i]); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_HAS_BACKTRACE) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(context); + set_error_addr(error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); + _stacktrace.resize(trace_cnt); + skip_n_firsts(1); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i] + 1); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_SYSTEM_WINDOWS) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + // We have to load the machine type from the image info + // So we first initialize the resolver, and it tells us this info + void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; } + void set_context(CONTEXT *ctx) { ctx_ = ctx; } + void set_thread_handle(HANDLE handle) { thd_ = handle; } + + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(static_cast(context)); + set_error_addr(error_addr); + CONTEXT localCtx; // used when no context is provided + + if (depth == 0) { + return 0; + } + + if (!ctx_) { + ctx_ = &localCtx; + RtlCaptureContext(ctx_); + } + + if (!thd_) { + thd_ = GetCurrentThread(); + } + + HANDLE process = GetCurrentProcess(); + + STACKFRAME64 s; + memset(&s, 0, sizeof(STACKFRAME64)); + + // TODO: 32 bit context capture + s.AddrStack.Mode = AddrModeFlat; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrPC.Mode = AddrModeFlat; +#ifdef _M_X64 + s.AddrPC.Offset = ctx_->Rip; + s.AddrStack.Offset = ctx_->Rsp; + s.AddrFrame.Offset = ctx_->Rbp; +#else + s.AddrPC.Offset = ctx_->Eip; + s.AddrStack.Offset = ctx_->Esp; + s.AddrFrame.Offset = ctx_->Ebp; +#endif + + if (!machine_type_) { +#ifdef _M_X64 + machine_type_ = IMAGE_FILE_MACHINE_AMD64; +#else + machine_type_ = IMAGE_FILE_MACHINE_I386; +#endif + } + + for (;;) { + // NOTE: this only works if PDBs are already loaded! + SetLastError(0); + if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + break; + + if (s.AddrReturn.Offset == 0) + break; + + _stacktrace.push_back(reinterpret_cast(s.AddrPC.Offset)); + + if (size() >= depth) + break; + } + + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + DWORD machine_type_ = 0; + HANDLE thd_ = 0; + CONTEXT *ctx_ = nullptr; +}; + +#endif + +class StackTrace : public StackTraceImpl {}; + +/*************** TRACE RESOLVER ***************/ + +class TraceResolverImplBase { +public: + virtual ~TraceResolverImplBase() {} + + virtual void load_addresses(void *const*addresses, int address_count) { + (void)addresses; + (void)address_count; + } + + template void load_stacktrace(ST &st) { + load_addresses(st.begin(), static_cast(st.size())); + } + + virtual ResolvedTrace resolve(ResolvedTrace t) { return t; } + +protected: + std::string demangle(const char *funcname) { + return _demangler.demangle(funcname); + } + +private: + details::demangler _demangler; +}; + +template class TraceResolverImpl; + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +template <> class TraceResolverImpl + : public TraceResolverImplBase {}; + +#endif + +#ifdef BACKWARD_SYSTEM_LINUX + +class TraceResolverLinuxBase : public TraceResolverImplBase { +public: + TraceResolverLinuxBase() + : argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) {} + std::string resolve_exec_path(Dl_info &symbol_info) const { + // mutates symbol_info.dli_fname to be filename to open and returns filename + // to display + if (symbol_info.dli_fname == argv0_) { + // dladdr returns argv[0] in dli_fname for symbols contained in + // the main executable, which is not a valid path if the + // executable was found by a search of the PATH environment + // variable; In that case, we actually open /proc/self/exe, which + // is always the actual executable (even if it was deleted/replaced!) + // but display the path that /proc/self/exe links to. + // However, this right away reduces probability of successful symbol + // resolution, because libbfd may try to find *.debug files in the + // same dir, in case symbols are stripped. As a result, it may try + // to find a file /proc/self/.debug, which obviously does + // not exist. /proc/self/exe is a last resort. First load attempt + // should go for the original executable file path. + symbol_info.dli_fname = "/proc/self/exe"; + return exec_path_; + } else { + return symbol_info.dli_fname; + } + } + +private: + std::string argv0_; + std::string exec_path_; + + static std::string get_argv0() { + std::string argv0; + std::ifstream ifs("/proc/self/cmdline"); + std::getline(ifs, argv0, '\0'); + return argv0; + } + + static std::string read_symlink(std::string const &symlink_path) { + std::string path; + path.resize(100); + + while (true) { + ssize_t len = + ::readlink(symlink_path.c_str(), &*path.begin(), path.size()); + if (len < 0) { + return ""; + } + if (static_cast(len) == path.size()) { + path.resize(path.size() * 2); + } else { + path.resize(static_cast(len)); + break; + } + } + + return path; + } +}; + +template class TraceResolverLinuxImpl; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + char *filename = _symbols[trace.idx]; + char *funcname = filename; + while (*funcname && *funcname != '(') { + funcname += 1; + } + trace.object_filename.assign(filename, + funcname); // ok even if funcname is the ending + // \0 (then we assign entire string) + + if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) + funcname += 1; + char *funcname_end = funcname; + while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { + funcname_end += 1; + } + *funcname_end = '\0'; + trace.object_function = this->demangle(funcname); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +#endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +#if BACKWARD_HAS_BFD == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _bfd_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + Dl_info symbol_info; + + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + if (!dladdr(trace.addr, &symbol_info)) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + bfd_fileobject *fobj; + // Before rushing to resolution need to ensure the executable + // file still can be used. For that compare inode numbers of + // what is stored by the executable's file path, and in the + // dli_fname, which not necessarily equals to the executable. + // It can be a shared library, or /proc/self/exe, and in the + // latter case has drawbacks. See the exec path resolution for + // details. In short - the dli object should be used only as + // the last resort. + // If inode numbers are equal, it is known dli_fname and the + // executable file are the same. This is guaranteed by Linux, + // because if the executable file is changed/deleted, it will + // be done in a new inode. The old file will be preserved in + // /proc/self/exe, and may even have inode 0. The latter can + // happen if the inode was actually reused, and the file was + // kept only in the main memory. + // + struct stat obj_stat; + struct stat dli_stat; + if (stat(trace.object_filename.c_str(), &obj_stat) == 0 && + stat(symbol_info.dli_fname, &dli_stat) == 0 && + obj_stat.st_ino == dli_stat.st_ino) { + // The executable file, and the shared object containing the + // address are the same file. Safe to use the original path. + // this is preferable. Libbfd will search for stripped debug + // symbols in the same directory. + fobj = load_object_with_bfd(trace.object_filename); + } else{ + // The original object file was *deleted*! The only hope is + // that the debug symbols are either inside the shared + // object file, or are in the same directory, and this is + // not /proc/self/exe. + fobj = nullptr; + } + if (fobj == nullptr || !fobj->handle) { + fobj = load_object_with_bfd(symbol_info.dli_fname); + if (!fobj->handle) { + return trace; + } + } + + find_sym_result *details_selected; // to be filled. + + // trace.addr is the next instruction to be executed after returning + // from the nested stack frame. In C++ this usually relate to the next + // statement right after the function call that leaded to a new stack + // frame. This is not usually what you want to see when printing out a + // stacktrace... + find_sym_result details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + details_selected = &details_call_site; + +#if BACKWARD_HAS_UNWIND == 0 + // ...this is why we also try to resolve the symbol that is right + // before the return address. If we are lucky enough, we will get the + // line of the function that was called. But if the code is optimized, + // we might get something absolutely not related since the compiler + // can reschedule the return address with inline functions and + // tail-call optimization (among other things that I don't even know + // or cannot even dream about with my tiny limited brain). + find_sym_result details_adjusted_call_site = find_symbol_details( + fobj, (void *)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); + + // In debug mode, we should always get the right thing(TM). + if (details_call_site.found && details_adjusted_call_site.found) { + // Ok, we assume that details_adjusted_call_site is a better estimation. + details_selected = &details_adjusted_call_site; + trace.addr = (void *)(uintptr_t(trace.addr) - 1); + } + + if (details_selected == &details_call_site && details_call_site.found) { + // we have to re-resolve the symbol in order to reset some + // internal state in BFD... so we can call backtrace_inliners + // thereafter... + details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + } +#endif // BACKWARD_HAS_UNWIND + + if (details_selected->found) { + if (details_selected->filename) { + trace.source.filename = details_selected->filename; + } + trace.source.line = details_selected->line; + + if (details_selected->funcname) { + // this time we get the name of the function where the code is + // located, instead of the function were the address is + // located. In short, if the code was inlined, we get the + // function corresponding to the code. Else we already got in + // trace.function. + trace.source.function = demangle(details_selected->funcname); + + if (!symbol_info.dli_sname) { + // for the case dladdr failed to find the symbol name of + // the function, we might as well try to put something + // here. + trace.object_function = trace.source.function; + } + } + + // Maybe the source of the trace got inlined inside the function + // (trace.source.function). Let's see if we can get all the inlined + // calls along the way up to the initial call site. + trace.inliners = backtrace_inliners(fobj, *details_selected); + +#if 0 + if (trace.inliners.size() == 0) { + // Maybe the trace was not inlined... or maybe it was and we + // are lacking the debug information. Let's try to make the + // world better and see if we can get the line number of the + // function (trace.source.function) now. + // + // We will get the location of where the function start (to be + // exact: the first instruction that really start the + // function), not where the name of the function is defined. + // This can be quite far away from the name of the function + // btw. + // + // If the source of the function is the same as the source of + // the trace, we cannot say if the trace was really inlined or + // not. However, if the filename of the source is different + // between the function and the trace... we can declare it as + // an inliner. This is not 100% accurate, but better than + // nothing. + + if (symbol_info.dli_saddr) { + find_sym_result details = find_symbol_details(fobj, + symbol_info.dli_saddr, + symbol_info.dli_fbase); + + if (details.found) { + ResolvedTrace::SourceLoc diy_inliner; + diy_inliner.line = details.line; + if (details.filename) { + diy_inliner.filename = details.filename; + } + if (details.funcname) { + diy_inliner.function = demangle(details.funcname); + } else { + diy_inliner.function = trace.source.function; + } + if (diy_inliner != trace.source) { + trace.inliners.push_back(diy_inliner); + } + } + } + } +#endif + } + + return trace; + } + +private: + bool _bfd_loaded; + + typedef details::handle > + bfd_handle_t; + + typedef details::handle bfd_symtab_t; + + struct bfd_fileobject { + bfd_handle_t handle; + bfd_vma base_addr; + bfd_symtab_t symtab; + bfd_symtab_t dynamic_symtab; + }; + + typedef details::hashtable::type fobj_bfd_map_t; + fobj_bfd_map_t _fobj_bfd_map; + + bfd_fileobject *load_object_with_bfd(const std::string &filename_object) { + using namespace details; + + if (!_bfd_loaded) { + using namespace details; + bfd_init(); + _bfd_loaded = true; + } + + fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); + if (it != _fobj_bfd_map.end()) { + return &it->second; + } + + // this new object is empty for now. + bfd_fileobject *r = &_fobj_bfd_map[filename_object]; + + // we do the work temporary in this one; + bfd_handle_t bfd_handle; + + int fd = open(filename_object.c_str(), O_RDONLY); + bfd_handle.reset(bfd_fdopenr(filename_object.c_str(), "default", fd)); + if (!bfd_handle) { + close(fd); + return r; + } + + if (!bfd_check_format(bfd_handle.get(), bfd_object)) { + return r; // not an object? You lose. + } + + if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { + return r; // that's what happen when you forget to compile in debug. + } + + ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get()); + + ssize_t dyn_symtab_storage_size = + bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); + + if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { + return r; // weird, is the file is corrupted? + } + + bfd_symtab_t symtab, dynamic_symtab; + ssize_t symcount = 0, dyn_symcount = 0; + + if (symtab_storage_size > 0) { + symtab.reset(static_cast( + malloc(static_cast(symtab_storage_size)))); + symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get()); + } + + if (dyn_symtab_storage_size > 0) { + dynamic_symtab.reset(static_cast( + malloc(static_cast(dyn_symtab_storage_size)))); + dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(), + dynamic_symtab.get()); + } + + if (symcount <= 0 && dyn_symcount <= 0) { + return r; // damned, that's a stripped file that you got there! + } + + r->handle = move(bfd_handle); + r->symtab = move(symtab); + r->dynamic_symtab = move(dynamic_symtab); + return r; + } + + struct find_sym_result { + bool found; + const char *filename; + const char *funcname; + unsigned int line; + }; + + struct find_sym_context { + TraceResolverLinuxImpl *self; + bfd_fileobject *fobj; + void *addr; + void *base_addr; + find_sym_result result; + }; + + find_sym_result find_symbol_details(bfd_fileobject *fobj, void *addr, + void *base_addr) { + find_sym_context context; + context.self = this; + context.fobj = fobj; + context.addr = addr; + context.base_addr = base_addr; + context.result.found = false; + bfd_map_over_sections(fobj->handle.get(), &find_in_section_trampoline, + static_cast(&context)); + return context.result; + } + + static void find_in_section_trampoline(bfd *, asection *section, void *data) { + find_sym_context *context = static_cast(data); + context->self->find_in_section( + reinterpret_cast(context->addr), + reinterpret_cast(context->base_addr), context->fobj, section, + context->result); + } + + void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject *fobj, + asection *section, find_sym_result &result) { + if (result.found) + return; + +#ifdef bfd_get_section_flags + if ((bfd_get_section_flags(fobj->handle.get(), section) & SEC_ALLOC) == 0) +#else + if ((bfd_section_flags(section) & SEC_ALLOC) == 0) +#endif + return; // a debug section is never loaded automatically. + +#ifdef bfd_get_section_vma + bfd_vma sec_addr = bfd_get_section_vma(fobj->handle.get(), section); +#else + bfd_vma sec_addr = bfd_section_vma(section); +#endif +#ifdef bfd_get_section_size + bfd_size_type size = bfd_get_section_size(section); +#else + bfd_size_type size = bfd_section_size(section); +#endif + + // are we in the boundaries of the section? + if (addr < sec_addr || addr >= sec_addr + size) { + addr -= base_addr; // oops, a relocated object, lets try again... + if (addr < sec_addr || addr >= sec_addr + size) { + return; + } + } + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + if (!result.found && fobj->symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->symtab.get(), addr - sec_addr, + &result.filename, &result.funcname, &result.line); + } + + if (!result.found && fobj->dynamic_symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->dynamic_symtab.get(), + addr - sec_addr, &result.filename, &result.funcname, &result.line); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + } + + ResolvedTrace::source_locs_t + backtrace_inliners(bfd_fileobject *fobj, find_sym_result previous_result) { + // This function can be called ONLY after a SUCCESSFUL call to + // find_symbol_details. The state is global to the bfd_handle. + ResolvedTrace::source_locs_t results; + while (previous_result.found) { + find_sym_result result; + result.found = bfd_find_inliner_info(fobj->handle.get(), &result.filename, + &result.funcname, &result.line); + + if (result + .found) /* and not ( + cstrings_eq(previous_result.filename, + result.filename) and + cstrings_eq(previous_result.funcname, result.funcname) + and result.line == previous_result.line + )) */ + { + ResolvedTrace::SourceLoc src_loc; + src_loc.line = result.line; + if (result.filename) { + src_loc.filename = result.filename; + } + if (result.funcname) { + src_loc.function = demangle(result.funcname); + } + results.push_back(src_loc); + } + previous_result = result; + } + return results; + } + + bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } +}; +#endif // BACKWARD_HAS_BFD == 1 + +#if BACKWARD_HAS_DW == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + using namespace details; + + Dwarf_Addr trace_addr = reinterpret_cast(trace.addr); + + if (!_dwfl_handle_initialized) { + // initialize dwfl... + _dwfl_cb.reset(new Dwfl_Callbacks); + _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; + _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; + _dwfl_cb->debuginfo_path = 0; + + _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); + _dwfl_handle_initialized = true; + + if (!_dwfl_handle) { + return trace; + } + + // ...from the current process. + dwfl_report_begin(_dwfl_handle.get()); + int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid()); + dwfl_report_end(_dwfl_handle.get(), NULL, NULL); + if (r < 0) { + return trace; + } + } + + if (!_dwfl_handle) { + return trace; + } + + // find the module (binary object) that contains the trace's address. + // This is not using any debug information, but the addresses ranges of + // all the currently loaded binary object. + Dwfl_Module *mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); + if (mod) { + // now that we found it, lets get the name of it, this will be the + // full path to the running binary or one of the loaded library. + const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); + if (module_name) { + trace.object_filename = module_name; + } + // We also look after the name of the symbol, equal or before this + // address. This is found by walking the symtab. We should get the + // symbol corresponding to the function (mangled) containing the + // address. If the code corresponding to the address was inlined, + // this is the name of the out-most inliner function. + const char *sym_name = dwfl_module_addrname(mod, trace_addr); + if (sym_name) { + trace.object_function = demangle(sym_name); + } + } + + // now let's get serious, and find out the source location (file and + // line number) of the address. + + // This function will look in .debug_aranges for the address and map it + // to the location of the compilation unit DIE in .debug_info and + // return it. + Dwarf_Addr mod_bias = 0; + Dwarf_Die *cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); + +#if 1 + if (!cudie) { + // Sadly clang does not generate the section .debug_aranges, thus + // dwfl_module_addrdie will fail early. Clang doesn't either set + // the lowpc/highpc/range info for every compilation unit. + // + // So in order to save the world: + // for every compilation unit, we will iterate over every single + // DIEs. Normally functions should have a lowpc/highpc/range, which + // we will use to infer the compilation unit. + + // note that this is probably badly inefficient. + while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { + Dwarf_Die die_mem; + Dwarf_Die *fundie = + find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); + if (fundie) { + break; + } + } + } +#endif + +//#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE +#ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE + if (!cudie) { + // If it's still not enough, lets dive deeper in the shit, and try + // to save the world again: for every compilation unit, we will + // load the corresponding .debug_line section, and see if we can + // find our address in it. + + Dwarf_Addr cfi_bias; + Dwarf_CFI *cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); + + Dwarf_Addr bias; + while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { + if (dwarf_getsrc_die(cudie, trace_addr - bias)) { + + // ...but if we get a match, it might be a false positive + // because our (address - bias) might as well be valid in a + // different compilation unit. So we throw our last card on + // the table and lookup for the address into the .eh_frame + // section. + + handle frame; + dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); + if (frame) { + break; + } + } + } + } +#endif + + if (!cudie) { + return trace; // this time we lost the game :/ + } + + // Now that we have a compilation unit DIE, this function will be able + // to load the corresponding section in .debug_line (if not already + // loaded) and hopefully find the source location mapped to our + // address. + Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); + + if (srcloc) { + const char *srcfile = dwarf_linesrc(srcloc, 0, 0); + if (srcfile) { + trace.source.filename = srcfile; + } + int line = 0, col = 0; + dwarf_lineno(srcloc, &line); + dwarf_linecol(srcloc, &col); + trace.source.line = static_cast(line); + trace.source.col = static_cast(col); + } + + deep_first_search_by_pc(cudie, trace_addr - mod_bias, + inliners_search_cb(trace)); + if (trace.source.function.size() == 0) { + // fallback. + trace.source.function = trace.object_function; + } + + return trace; + } + +private: + typedef details::handle > + dwfl_handle_t; + details::handle > + _dwfl_cb; + dwfl_handle_t _dwfl_handle; + bool _dwfl_handle_initialized; + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die *die) { + switch (dwarf_tag(die)) { + const char *name; + case DW_TAG_subprogram: + if ((name = dwarf_diename(die))) { + trace.source.function = name; + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + Dwarf_Attribute attr_mem; + + if ((name = dwarf_diename(die))) { + sloc.function = name; + } + if ((name = die_call_file(die))) { + sloc.filename = name; + } + + Dwarf_Word line = 0, col = 0; + dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line); + dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col); + sloc.line = static_cast(line); + sloc.col = static_cast(col); + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + inliners_search_cb(ResolvedTrace &t) : trace(t) {} + }; + + static bool die_has_pc(Dwarf_Die *die, Dwarf_Addr pc) { + Dwarf_Addr low, high; + + // continuous range + if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) { + if (dwarf_lowpc(die, &low) != 0) { + return false; + } + if (dwarf_highpc(die, &high) != 0) { + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); + Dwarf_Word value; + if (dwarf_formudata(attr, &value) != 0) { + return false; + } + high = low + value; + } + return pc >= low && pc < high; + } + + // non-continuous range. + Dwarf_Addr base; + ptrdiff_t offset = 0; + while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { + if (pc >= low && pc < high) { + return true; + } + } + return false; + } + + static Dwarf_Die *find_fundie_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + Dwarf_Die *result) { + if (dwarf_child(parent_die, result) != 0) { + return 0; + } + + Dwarf_Die *die = result; + do { + switch (dwarf_tag(die)) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(die, pc)) { + return result; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, + // function are not necessarily at the first level, but + // might be nested inside a namespace, structure etc. + Dwarf_Die die_mem; + Dwarf_Die *indie = find_fundie_by_pc(die, pc, &die_mem); + if (indie) { + *result = die_mem; + return result; + } + } + } while (dwarf_siblingof(die, result) == 0); + return 0; + } + + template + static bool deep_first_search_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + CB cb) { + Dwarf_Die die_mem; + if (dwarf_child(parent_die, &die_mem) != 0) { + return false; + } + + bool branch_has_pc = false; + Dwarf_Die *die = &die_mem; + do { + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(die, pc, cb); + } + if (!branch_has_pc) { + branch_has_pc = die_has_pc(die, pc); + } + if (branch_has_pc) { + cb(die); + } + } while (dwarf_siblingof(die, &die_mem) == 0); + return branch_has_pc; + } + + static const char *die_call_file(Dwarf_Die *die) { + Dwarf_Attribute attr_mem; + Dwarf_Word file_idx = 0; + + dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); + + if (file_idx == 0) { + return 0; + } + + Dwarf_Die die_mem; + Dwarf_Die *cudie = dwarf_diecu(die, &die_mem, 0, 0); + if (!cudie) { + return 0; + } + + Dwarf_Files *files = 0; + size_t nfiles; + dwarf_getsrcfiles(cudie, &files, &nfiles); + if (!files) { + return 0; + } + + return dwarf_filesrc(files, file_idx, 0, 0); + } +}; +#endif // BACKWARD_HAS_DW == 1 + +#if BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwarf_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + + Dl_info symbol_info; + int dladdr_result = 0; +#if defined(__GLIBC__) + link_map *link_map; + // We request the link map so we can get information about offsets + dladdr_result = + dladdr1(trace.addr, &symbol_info, reinterpret_cast(&link_map), + RTLD_DL_LINKMAP); +#else + // Android doesn't have dladdr1. Don't use the linker map. + dladdr_result = dladdr(trace.addr, &symbol_info); +#endif + if (!dladdr_result) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + // + // And in link_map: + // .l_addr: + // difference between the address in the ELF file and the address + // in memory + // l_name: + // absolute pathname where the object was found + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + dwarf_fileobject &fobj = load_object_with_dwarf(symbol_info.dli_fname); + if (!fobj.dwarf_handle) { + return trace; // sad, we couldn't load the object :( + } + +#if defined(__GLIBC__) + // Convert the address to a module relative one by looking at + // the module's loading address in the link map + Dwarf_Addr address = reinterpret_cast(trace.addr) - + reinterpret_cast(link_map->l_addr); +#else + Dwarf_Addr address = reinterpret_cast(trace.addr); +#endif + + if (trace.object_function.empty()) { + symbol_cache_t::iterator it = fobj.symbol_cache.lower_bound(address); + + if (it != fobj.symbol_cache.end()) { + if (it->first != address) { + if (it != fobj.symbol_cache.begin()) { + --it; + } + } + trace.object_function = demangle(it->second.c_str()); + } + } + + // Get the Compilation Unit DIE for the address + Dwarf_Die die = find_die(fobj, address); + + if (!die) { + return trace; // this time we lost the game :/ + } + + // libdwarf doesn't give us direct access to its objects, it always + // allocates a copy for the caller. We keep that copy alive in a cache + // and we deallocate it later when it's no longer required. + die_cache_entry &die_object = get_die_cache(fobj, die); + if (die_object.isEmpty()) + return trace; // We have no line section for this DIE + + die_linemap_t::iterator it = die_object.line_section.lower_bound(address); + + if (it != die_object.line_section.end()) { + if (it->first != address) { + if (it == die_object.line_section.begin()) { + // If we are on the first item of the line section + // but the address does not match it means that + // the address is below the range of the DIE. Give up. + return trace; + } else { + --it; + } + } + } else { + return trace; // We didn't find the address. + } + + // Get the Dwarf_Line that the address points to and call libdwarf + // to get source file, line and column info. + Dwarf_Line line = die_object.line_buffer[it->second]; + Dwarf_Error error = DW_DLE_NE; + + char *filename; + if (dwarf_linesrc(line, &filename, &error) == DW_DLV_OK) { + trace.source.filename = std::string(filename); + dwarf_dealloc(fobj.dwarf_handle.get(), filename, DW_DLA_STRING); + } + + Dwarf_Unsigned number = 0; + if (dwarf_lineno(line, &number, &error) == DW_DLV_OK) { + trace.source.line = number; + } else { + trace.source.line = 0; + } + + if (dwarf_lineoff_b(line, &number, &error) == DW_DLV_OK) { + trace.source.col = number; + } else { + trace.source.col = 0; + } + + std::vector namespace_stack; + deep_first_search_by_pc(fobj, die, address, namespace_stack, + inliners_search_cb(trace, fobj, die)); + + dwarf_dealloc(fobj.dwarf_handle.get(), die, DW_DLA_DIE); + + return trace; + } + +public: + static int close_dwarf(Dwarf_Debug dwarf) { + return dwarf_finish(dwarf, NULL); + } + +private: + bool _dwarf_loaded; + + typedef details::handle > + dwarf_file_t; + + typedef details::handle > + dwarf_elf_t; + + typedef details::handle > + dwarf_handle_t; + + typedef std::map die_linemap_t; + + typedef std::map die_specmap_t; + + struct die_cache_entry { + die_specmap_t spec_section; + die_linemap_t line_section; + Dwarf_Line *line_buffer; + Dwarf_Signed line_count; + Dwarf_Line_Context line_context; + + inline bool isEmpty() { + return line_buffer == NULL || line_count == 0 || line_context == NULL || + line_section.empty(); + } + + die_cache_entry() : line_buffer(0), line_count(0), line_context(0) {} + + ~die_cache_entry() { + if (line_context) { + dwarf_srclines_dealloc_b(line_context); + } + } + }; + + typedef std::map die_cache_t; + + typedef std::map symbol_cache_t; + + struct dwarf_fileobject { + dwarf_file_t file_handle; + dwarf_elf_t elf_handle; + dwarf_handle_t dwarf_handle; + symbol_cache_t symbol_cache; + + // Die cache + die_cache_t die_cache; + die_cache_entry *current_cu; + }; + + typedef details::hashtable::type + fobj_dwarf_map_t; + fobj_dwarf_map_t _fobj_dwarf_map; + + static bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } + + dwarf_fileobject &load_object_with_dwarf(const std::string &filename_object) { + + if (!_dwarf_loaded) { + // Set the ELF library operating version + // If that fails there's nothing we can do + _dwarf_loaded = elf_version(EV_CURRENT) != EV_NONE; + } + + fobj_dwarf_map_t::iterator it = _fobj_dwarf_map.find(filename_object); + if (it != _fobj_dwarf_map.end()) { + return it->second; + } + + // this new object is empty for now + dwarf_fileobject &r = _fobj_dwarf_map[filename_object]; + + dwarf_file_t file_handle; + file_handle.reset(open(filename_object.c_str(), O_RDONLY)); + if (file_handle.get() < 0) { + return r; + } + + // Try to get an ELF handle. We need to read the ELF sections + // because we want to see if there is a .gnu_debuglink section + // that points to a split debug file + dwarf_elf_t elf_handle; + elf_handle.reset(elf_begin(file_handle.get(), ELF_C_READ, NULL)); + if (!elf_handle) { + return r; + } + + const char *e_ident = elf_getident(elf_handle.get(), 0); + if (!e_ident) { + return r; + } + + // Get the number of sections + // We use the new APIs as elf_getshnum is deprecated + size_t shdrnum = 0; + if (elf_getshdrnum(elf_handle.get(), &shdrnum) == -1) { + return r; + } + + // Get the index to the string section + size_t shdrstrndx = 0; + if (elf_getshdrstrndx(elf_handle.get(), &shdrstrndx) == -1) { + return r; + } + + std::string debuglink; + // Iterate through the ELF sections to try to get a gnu_debuglink + // note and also to cache the symbol table. + // We go the preprocessor way to avoid having to create templated + // classes or using gelf (which might throw a compiler error if 64 bit + // is not supported +#define ELF_GET_DATA(ARCH) \ + Elf_Scn *elf_section = 0; \ + Elf_Data *elf_data = 0; \ + Elf##ARCH##_Shdr *section_header = 0; \ + Elf_Scn *symbol_section = 0; \ + size_t symbol_count = 0; \ + size_t symbol_strings = 0; \ + Elf##ARCH##_Sym *symbol = 0; \ + const char *section_name = 0; \ + \ + while ((elf_section = elf_nextscn(elf_handle.get(), elf_section)) != NULL) { \ + section_header = elf##ARCH##_getshdr(elf_section); \ + if (section_header == NULL) { \ + return r; \ + } \ + \ + if ((section_name = elf_strptr(elf_handle.get(), shdrstrndx, \ + section_header->sh_name)) == NULL) { \ + return r; \ + } \ + \ + if (cstrings_eq(section_name, ".gnu_debuglink")) { \ + elf_data = elf_getdata(elf_section, NULL); \ + if (elf_data && elf_data->d_size > 0) { \ + debuglink = \ + std::string(reinterpret_cast(elf_data->d_buf)); \ + } \ + } \ + \ + switch (section_header->sh_type) { \ + case SHT_SYMTAB: \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + break; \ + \ + /* We use .dynsyms as a last resort, we prefer .symtab */ \ + case SHT_DYNSYM: \ + if (!symbol_section) { \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + } \ + break; \ + } \ + } \ + \ + if (symbol_section && symbol_count && symbol_strings) { \ + elf_data = elf_getdata(symbol_section, NULL); \ + symbol = reinterpret_cast(elf_data->d_buf); \ + for (size_t i = 0; i < symbol_count; ++i) { \ + int type = ELF##ARCH##_ST_TYPE(symbol->st_info); \ + if (type == STT_FUNC && symbol->st_value > 0) { \ + r.symbol_cache[symbol->st_value] = std::string( \ + elf_strptr(elf_handle.get(), symbol_strings, symbol->st_name)); \ + } \ + ++symbol; \ + } \ + } + + if (e_ident[EI_CLASS] == ELFCLASS32) { + ELF_GET_DATA(32) + } else if (e_ident[EI_CLASS] == ELFCLASS64) { + // libelf might have been built without 64 bit support +#if __LIBELF64 + ELF_GET_DATA(64) +#endif + } + + if (!debuglink.empty()) { + // We have a debuglink section! Open an elf instance on that + // file instead. If we can't open the file, then return + // the elf handle we had already opened. + dwarf_file_t debuglink_file; + debuglink_file.reset(open(debuglink.c_str(), O_RDONLY)); + if (debuglink_file.get() > 0) { + dwarf_elf_t debuglink_elf; + debuglink_elf.reset(elf_begin(debuglink_file.get(), ELF_C_READ, NULL)); + + // If we have a valid elf handle, return the new elf handle + // and file handle and discard the original ones + if (debuglink_elf) { + elf_handle = move(debuglink_elf); + file_handle = move(debuglink_file); + } + } + } + + // Ok, we have a valid ELF handle, let's try to get debug symbols + Dwarf_Debug dwarf_debug; + Dwarf_Error error = DW_DLE_NE; + dwarf_handle_t dwarf_handle; + + int dwarf_result = dwarf_elf_init(elf_handle.get(), DW_DLC_READ, NULL, NULL, + &dwarf_debug, &error); + + // We don't do any special handling for DW_DLV_NO_ENTRY specially. + // If we get an error, or the file doesn't have debug information + // we just return. + if (dwarf_result != DW_DLV_OK) { + return r; + } + + dwarf_handle.reset(dwarf_debug); + + r.file_handle = move(file_handle); + r.elf_handle = move(elf_handle); + r.dwarf_handle = move(dwarf_handle); + + return r; + } + + die_cache_entry &get_die_cache(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + // Get the die offset, we use it as the cache key + Dwarf_Off die_offset; + if (dwarf_dieoffset(die, &die_offset, &error) != DW_DLV_OK) { + die_offset = 0; + } + + die_cache_t::iterator it = fobj.die_cache.find(die_offset); + + if (it != fobj.die_cache.end()) { + fobj.current_cu = &it->second; + return it->second; + } + + die_cache_entry &de = fobj.die_cache[die_offset]; + fobj.current_cu = &de; + + Dwarf_Addr line_addr; + Dwarf_Small table_count; + + // The addresses in the line section are not fully sorted (they might + // be sorted by block of code belonging to the same file), which makes + // it necessary to do so before searching is possible. + // + // As libdwarf allocates a copy of everything, let's get the contents + // of the line section and keep it around. We also create a map of + // program counter to line table indices so we can search by address + // and get the line buffer index. + // + // To make things more difficult, the same address can span more than + // one line, so we need to keep the index pointing to the first line + // by using insert instead of the map's [ operator. + + // Get the line context for the DIE + if (dwarf_srclines_b(die, 0, &table_count, &de.line_context, &error) == + DW_DLV_OK) { + // Get the source lines for this line context, to be deallocated + // later + if (dwarf_srclines_from_linecontext(de.line_context, &de.line_buffer, + &de.line_count, + &error) == DW_DLV_OK) { + + // Add all the addresses to our map + for (int i = 0; i < de.line_count; i++) { + if (dwarf_lineaddr(de.line_buffer[i], &line_addr, &error) != + DW_DLV_OK) { + line_addr = 0; + } + de.line_section.insert(std::pair(line_addr, i)); + } + } + } + + // For each CU, cache the function DIEs that contain the + // DW_AT_specification attribute. When building with -g3 the function + // DIEs are separated in declaration and specification, with the + // declaration containing only the name and parameters and the + // specification the low/high pc and other compiler attributes. + // + // We cache those specifications so we don't skip over the declarations, + // because they have no pc, and we can do namespace resolution for + // DWARF function names. + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Die current_die = 0; + if (dwarf_child(die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_subprogram || + tag_value == DW_TAG_inlined_subroutine) { + + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(current_die, DW_AT_specification, &has_attr, + &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_specification, &attr_mem, + &error) == DW_DLV_OK) { + Dwarf_Off spec_offset = 0; + if (dwarf_formref(attr_mem, &spec_offset, &error) == + DW_DLV_OK) { + Dwarf_Off spec_die_offset; + if (dwarf_dieoffset(current_die, &spec_die_offset, &error) == + DW_DLV_OK) { + de.spec_section[spec_offset] = spec_die_offset; + } + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + return de; + } + + static Dwarf_Die get_referenced_die(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Attribute attr_mem; + + Dwarf_Die found_die = NULL; + if (dwarf_attr(die, attr, &attr_mem, &error) == DW_DLV_OK) { + Dwarf_Off offset; + int result = 0; + if (global) { + result = dwarf_global_formref(attr_mem, &offset, &error); + } else { + result = dwarf_formref(attr_mem, &offset, &error); + } + + if (result == DW_DLV_OK) { + if (dwarf_offdie(dwarf, offset, &found_die, &error) != DW_DLV_OK) { + found_die = NULL; + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + return found_die; + } + + static std::string get_referenced_die_name(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + std::string value; + + Dwarf_Die found_die = get_referenced_die(dwarf, die, attr, global); + + if (found_die) { + char *name; + if (dwarf_diename(found_die, &name, &error) == DW_DLV_OK) { + if (name) { + value = std::string(name); + } + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, found_die, DW_DLA_DIE); + } + + return value; + } + + // Returns a spec DIE linked to the passed one. The caller should + // deallocate the DIE + static Dwarf_Die get_spec_die(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Off die_offset; + if (fobj.current_cu && + dwarf_die_CU_offset(die, &die_offset, &error) == DW_DLV_OK) { + die_specmap_t::iterator it = + fobj.current_cu->spec_section.find(die_offset); + + // If we have a DIE that completes the current one, check if + // that one has the pc we are looking for + if (it != fobj.current_cu->spec_section.end()) { + Dwarf_Die spec_die = 0; + if (dwarf_offdie(dwarf, it->second, &spec_die, &error) == DW_DLV_OK) { + return spec_die; + } + } + } + + // Maybe we have an abstract origin DIE with the function information? + return get_referenced_die(fobj.dwarf_handle.get(), die, + DW_AT_abstract_origin, true); + } + + static bool die_has_pc(dwarf_fileobject &fobj, Dwarf_Die die, Dwarf_Addr pc) { + Dwarf_Addr low_pc = 0, high_pc = 0; + Dwarf_Half high_pc_form = 0; + Dwarf_Form_Class return_class; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + bool has_lowpc = false; + bool has_highpc = false; + bool has_ranges = false; + + if (dwarf_lowpc(die, &low_pc, &error) == DW_DLV_OK) { + // If we have a low_pc check if there is a high pc. + // If we don't have a high pc this might mean we have a base + // address for the ranges list or just an address. + has_lowpc = true; + + if (dwarf_highpc_b(die, &high_pc, &high_pc_form, &return_class, &error) == + DW_DLV_OK) { + // We do have a high pc. In DWARF 4+ this is an offset from the + // low pc, but in earlier versions it's an absolute address. + + has_highpc = true; + // In DWARF 2/3 this would be a DW_FORM_CLASS_ADDRESS + if (return_class == DW_FORM_CLASS_CONSTANT) { + high_pc = low_pc + high_pc; + } + + // We have low and high pc, check if our address + // is in that range + return pc >= low_pc && pc < high_pc; + } + } else { + // Reset the low_pc, in case dwarf_lowpc failing set it to some + // undefined value. + low_pc = 0; + } + + // Check if DW_AT_ranges is present and search for the PC in the + // returned ranges list. We always add the low_pc, as it not set it will + // be 0, in case we had a DW_AT_low_pc and DW_AT_ranges pair + bool result = false; + + Dwarf_Attribute attr; + if (dwarf_attr(die, DW_AT_ranges, &attr, &error) == DW_DLV_OK) { + + Dwarf_Off offset; + if (dwarf_global_formref(attr, &offset, &error) == DW_DLV_OK) { + Dwarf_Ranges *ranges; + Dwarf_Signed ranges_count = 0; + Dwarf_Unsigned byte_count = 0; + + if (dwarf_get_ranges_a(dwarf, offset, die, &ranges, &ranges_count, + &byte_count, &error) == DW_DLV_OK) { + has_ranges = ranges_count != 0; + for (int i = 0; i < ranges_count; i++) { + if (ranges[i].dwr_addr1 != 0 && + pc >= ranges[i].dwr_addr1 + low_pc && + pc < ranges[i].dwr_addr2 + low_pc) { + result = true; + break; + } + } + dwarf_ranges_dealloc(dwarf, ranges, ranges_count); + } + } + } + + // Last attempt. We might have a single address set as low_pc. + if (!result && low_pc != 0 && pc == low_pc) { + result = true; + } + + // If we don't have lowpc, highpc and ranges maybe this DIE is a + // declaration that relies on a DW_AT_specification DIE that happens + // later. Use the specification cache we filled when we loaded this CU. + if (!result && (!has_lowpc && !has_highpc && !has_ranges)) { + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (spec_die) { + result = die_has_pc(fobj, spec_die, pc); + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + } + } + + return result; + } + + static void get_type(Dwarf_Debug dwarf, Dwarf_Die die, std::string &type) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Die child = 0; + if (dwarf_child(die, &child, &error) == DW_DLV_OK) { + get_type(dwarf, child, type); + } + + if (child) { + type.insert(0, "::"); + dwarf_dealloc(dwarf, child, DW_DLA_DIE); + } + + char *name; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + type.insert(0, std::string(name)); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + type.insert(0, ""); + } + } + + static std::string get_type_by_signature(Dwarf_Debug dwarf, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Sig8 signature; + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(die, DW_AT_signature, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formsig8(attr_mem, &signature, &error) != DW_DLV_OK) { + return std::string(""); + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + + Dwarf_Unsigned next_cu_header; + Dwarf_Sig8 tu_signature; + std::string result; + bool found = false; + + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, &tu_signature, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (strncmp(signature.signature, tu_signature.signature, 8) == 0) { + Dwarf_Die type_cu_die = 0; + if (dwarf_siblingof_b(dwarf, 0, 0, &type_cu_die, &error) == DW_DLV_OK) { + Dwarf_Die child_die = 0; + if (dwarf_child(type_cu_die, &child_die, &error) == DW_DLV_OK) { + get_type(dwarf, child_die, result); + found = !result.empty(); + dwarf_dealloc(dwarf, child_die, DW_DLA_DIE); + } + dwarf_dealloc(dwarf, type_cu_die, DW_DLA_DIE); + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Unfortunately, libdwarf's + // next_cu_header API keeps its own iterator per Dwarf_Debug + // that can't be reset. We need to keep fetching elements until + // the end. + } + } else { + // If we couldn't resolve the type just print out the signature + std::ostringstream string_stream; + string_stream << "<0x" << std::hex << std::setfill('0'); + for (int i = 0; i < 8; ++i) { + string_stream << std::setw(2) << std::hex + << (int)(unsigned char)(signature.signature[i]); + } + string_stream << ">"; + result = string_stream.str(); + } + return result; + } + + struct type_context_t { + bool is_const; + bool is_typedef; + bool has_type; + bool has_name; + std::string text; + + type_context_t() + : is_const(false), is_typedef(false), has_type(false), has_name(false) { + } + }; + + // Types are resolved from right to left: we get the variable name first + // and then all specifiers (like const or pointer) in a chain of DW_AT_type + // DIEs. Call this function recursively until we get a complete type + // string. + static void set_parameter_string(dwarf_fileobject &fobj, Dwarf_Die die, + type_context_t &context) { + char *name; + Dwarf_Error error = DW_DLE_NE; + + // typedefs contain also the base type, so we skip it and only + // print the typedef name + if (!context.is_typedef) { + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + if (!context.text.empty()) { + context.text.insert(0, " "); + } + context.text.insert(0, std::string(name)); + dwarf_dealloc(fobj.dwarf_handle.get(), name, DW_DLA_STRING); + } + } else { + context.is_typedef = false; + context.has_type = true; + if (context.is_const) { + context.text.insert(0, "const "); + context.is_const = false; + } + } + + bool next_type_is_const = false; + bool is_keyword = true; + + Dwarf_Half tag = 0; + Dwarf_Bool has_attr = 0; + if (dwarf_tag(die, &tag, &error) == DW_DLV_OK) { + switch (tag) { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + context.has_type = true; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == + DW_DLV_OK) { + // If we have a signature it means the type is defined + // in .debug_types, so we need to load the DIE pointed + // at by the signature and resolve it + if (has_attr) { + std::string type = + get_type_by_signature(fobj.dwarf_handle.get(), die); + if (context.is_const) + type.insert(0, "const "); + + if (!context.text.empty()) + context.text.insert(0, " "); + context.text.insert(0, type); + } + + // Treat enums like typedefs, and skip printing its + // base type + context.is_typedef = (tag == DW_TAG_enumeration_type); + } + break; + case DW_TAG_const_type: + next_type_is_const = true; + break; + case DW_TAG_pointer_type: + context.text.insert(0, "*"); + break; + case DW_TAG_reference_type: + context.text.insert(0, "&"); + break; + case DW_TAG_restrict_type: + context.text.insert(0, "restrict "); + break; + case DW_TAG_rvalue_reference_type: + context.text.insert(0, "&&"); + break; + case DW_TAG_volatile_type: + context.text.insert(0, "volatile "); + break; + case DW_TAG_typedef: + // Propagate the const-ness to the next type + // as typedefs are linked to its base type + next_type_is_const = context.is_const; + context.is_typedef = true; + context.has_type = true; + break; + case DW_TAG_base_type: + context.has_type = true; + break; + case DW_TAG_formal_parameter: + context.has_name = true; + break; + default: + is_keyword = false; + break; + } + } + + if (!is_keyword && context.is_const) { + context.text.insert(0, "const "); + } + + context.is_const = next_type_is_const; + + Dwarf_Die ref = + get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_type, true); + if (ref) { + set_parameter_string(fobj, ref, context); + dwarf_dealloc(fobj.dwarf_handle.get(), ref, DW_DLA_DIE); + } + + if (!context.has_type && context.has_name) { + context.text.insert(0, "void "); + context.has_type = true; + } + } + + // Resolve the function return type and parameters + static void set_function_parameters(std::string &function_name, + std::vector &ns, + dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Die current_die = 0; + std::string parameters; + bool has_spec = true; + // Check if we have a spec DIE. If we do we use it as it contains + // more information, like parameter names. + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (!spec_die) { + has_spec = false; + spec_die = die; + } + + std::vector::const_iterator it = ns.begin(); + std::string ns_name; + for (it = ns.begin(); it < ns.end(); ++it) { + ns_name.append(*it).append("::"); + } + + if (!ns_name.empty()) { + function_name.insert(0, ns_name); + } + + // See if we have a function return type. It can be either on the + // current die or in its spec one (usually true for inlined functions) + std::string return_type = + get_referenced_die_name(dwarf, die, DW_AT_type, true); + if (return_type.empty()) { + return_type = get_referenced_die_name(dwarf, spec_die, DW_AT_type, true); + } + if (!return_type.empty()) { + return_type.append(" "); + function_name.insert(0, return_type); + } + + if (dwarf_child(spec_die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_formal_parameter) { + // Ignore artificial (ie, compiler generated) parameters + bool is_artificial = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_artificial, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + is_artificial = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!is_artificial) { + type_context_t context; + set_parameter_string(fobj, current_die, context); + + if (parameters.empty()) { + parameters.append("("); + } else { + parameters.append(", "); + } + parameters.append(context.text); + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + if (parameters.empty()) + parameters = "("; + parameters.append(")"); + + // If we got a spec DIE we need to deallocate it + if (has_spec) + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + + function_name.append(parameters); + } + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die die, std::vector &ns) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Half tag_value; + Dwarf_Attribute attr_mem; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + dwarf_tag(die, &tag_value, &error); + + switch (tag_value) { + char *name; + case DW_TAG_subprogram: + if (!trace.source.function.empty()) + break; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + trace.source.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a function name in this DIE. + // Check if there is a referenced non-defining + // declaration. + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + if (trace.source.function.empty()) { + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_specification, true); + } + } + + // Append the function parameters, if available + set_function_parameters(trace.source.function, ns, fobj, die); + + // If the object function name is empty, it's possible that + // there is no dynamic symbol table (maybe the executable + // was stripped or not built with -rdynamic). See if we have + // a DWARF linkage name to use instead. We try both + // linkage_name and MIPS_linkage_name because the MIPS tag + // was the unofficial one until it was adopted in DWARF4. + // Old gcc versions generate MIPS_linkage_name + if (trace.object_function.empty()) { + details::demangler demangler; + + if (dwarf_attr(die, DW_AT_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + if (dwarf_attr(die, DW_AT_MIPS_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + break; + } + } + + char *linkage; + if (dwarf_formstring(attr_mem, &linkage, &error) == DW_DLV_OK) { + trace.object_function = demangler.demangle(linkage); + dwarf_dealloc(dwarf, linkage, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + sloc.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a name for this inlined DIE, it could + // be that there is an abstract origin instead. + // Get the DW_AT_abstract_origin value, which is a + // reference to the source DIE and try to get its name + sloc.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + } + + set_function_parameters(sloc.function, ns, fobj, die); + + std::string file = die_call_file(dwarf, die, cu_die); + if (!file.empty()) + sloc.filename = file; + + Dwarf_Unsigned number = 0; + if (dwarf_attr(die, DW_AT_call_line, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.line = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (dwarf_attr(die, DW_AT_call_column, &attr_mem, &error) == + DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.col = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + dwarf_fileobject &fobj; + Dwarf_Die cu_die; + inliners_search_cb(ResolvedTrace &t, dwarf_fileobject &f, Dwarf_Die c) + : trace(t), fobj(f), cu_die(c) {} + }; + + static Dwarf_Die find_fundie_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + Dwarf_Die result) { + Dwarf_Die current_die = 0; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return NULL; + } + + for (;;) { + Dwarf_Die sibling_die = 0; + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + switch (tag_value) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(fobj, current_die, pc)) { + return current_die; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, functions are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + Dwarf_Die die_mem = 0; + Dwarf_Die indie = find_fundie_by_pc(fobj, current_die, pc, die_mem); + if (indie) { + result = die_mem; + return result; + } + } + + int res = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (res == DW_DLV_ERROR) { + return NULL; + } else if (res == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + return NULL; + } + + template + static bool deep_first_search_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + std::vector &ns, CB cb) { + Dwarf_Die current_die = 0; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return false; + } + + bool branch_has_pc = false; + bool has_namespace = false; + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag; + if (dwarf_tag(current_die, &tag, &error) == DW_DLV_OK) { + if (tag == DW_TAG_namespace || tag == DW_TAG_class_type) { + char *ns_name = NULL; + if (dwarf_diename(current_die, &ns_name, &error) == DW_DLV_OK) { + if (ns_name) { + ns.push_back(std::string(ns_name)); + } else { + ns.push_back(""); + } + dwarf_dealloc(dwarf, ns_name, DW_DLA_STRING); + } else { + ns.push_back(""); + } + has_namespace = true; + } + } + + bool declaration = false; + Dwarf_Attribute attr_mem; + if (tag != DW_TAG_class_type && + dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(fobj, current_die, pc, ns, cb); + } + + if (!branch_has_pc) { + branch_has_pc = die_has_pc(fobj, current_die, pc); + } + + if (branch_has_pc) { + cb(current_die, ns); + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + return false; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + if (has_namespace) { + has_namespace = false; + ns.pop_back(); + } + current_die = sibling_die; + } + + if (has_namespace) { + ns.pop_back(); + } + return branch_has_pc; + } + + static std::string die_call_file(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Die cu_die) { + Dwarf_Attribute attr_mem; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Unsigned file_index; + + std::string file; + + if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &file_index, &error) != DW_DLV_OK) { + file_index = 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + + if (file_index == 0) { + return file; + } + + char **srcfiles = 0; + Dwarf_Signed file_count = 0; + if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) { + if (file_count > 0 && file_index <= static_cast(file_count)) { + file = std::string(srcfiles[file_index - 1]); + } + + // Deallocate all strings! + for (int i = 0; i < file_count; ++i) { + dwarf_dealloc(dwarf, srcfiles[i], DW_DLA_STRING); + } + dwarf_dealloc(dwarf, srcfiles, DW_DLA_LIST); + } + } + return file; + } + + Dwarf_Die find_die(dwarf_fileobject &fobj, Dwarf_Addr addr) { + // Let's get to work! First see if we have a debug_aranges section so + // we can speed up the search + + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Arange *aranges; + Dwarf_Signed arange_count; + + Dwarf_Die returnDie; + bool found = false; + if (dwarf_get_aranges(dwarf, &aranges, &arange_count, &error) != + DW_DLV_OK) { + aranges = NULL; + } + + if (aranges) { + // We have aranges. Get the one where our address is. + Dwarf_Arange arange; + if (dwarf_get_arange(aranges, arange_count, addr, &arange, &error) == + DW_DLV_OK) { + + // We found our address. Get the compilation-unit DIE offset + // represented by the given address range. + Dwarf_Off cu_die_offset; + if (dwarf_get_cu_die_offset(arange, &cu_die_offset, &error) == + DW_DLV_OK) { + // Get the DIE at the offset returned by the aranges search. + // We set is_info to 1 to specify that the offset is from + // the .debug_info section (and not .debug_types) + int dwarf_result = + dwarf_offdie_b(dwarf, cu_die_offset, 1, &returnDie, &error); + + found = dwarf_result == DW_DLV_OK; + } + dwarf_dealloc(dwarf, arange, DW_DLA_ARANGE); + } + } + + if (found) + return returnDie; // The caller is responsible for freeing the die + + // The search for aranges failed. Try to find our address by scanning + // all compilation units. + Dwarf_Unsigned next_cu_header; + Dwarf_Half tag = 0; + returnDie = 0; + + while (!found && + dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (returnDie) + dwarf_dealloc(dwarf, returnDie, DW_DLA_DIE); + + if (dwarf_siblingof(dwarf, 0, &returnDie, &error) == DW_DLV_OK) { + if ((dwarf_tag(returnDie, &tag, &error) == DW_DLV_OK) && + tag == DW_TAG_compile_unit) { + if (die_has_pc(fobj, returnDie, addr)) { + found = true; + } + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return returnDie; + + // We couldn't find any compilation units with ranges or a high/low pc. + // Try again by looking at all DIEs in all compilation units. + Dwarf_Die cudie; + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + if (dwarf_siblingof(dwarf, 0, &cudie, &error) == DW_DLV_OK) { + Dwarf_Die die_mem = 0; + Dwarf_Die resultDie = find_fundie_by_pc(fobj, cudie, addr, die_mem); + + if (resultDie) { + found = true; + break; + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return cudie; + + // We failed. + return NULL; + } +}; +#endif // BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverImpl + : public TraceResolverLinuxImpl {}; + +#endif // BACKWARD_SYSTEM_LINUX + +#ifdef BACKWARD_SYSTEM_DARWIN + +template class TraceResolverDarwinImpl; + +template <> +class TraceResolverDarwinImpl + : public TraceResolverImplBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + // parse: + // + + char *filename = _symbols[trace.idx]; + + // skip " " + while (*filename && *filename != ' ') + filename++; + while (*filename == ' ') + filename++; + + // find start of from end ( may contain a space) + char *p = filename + strlen(filename) - 1; + // skip to start of " + " + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + char *funcname_end = p + 1; + + // skip to start of "" + while (p > filename && *p != ' ') + p--; + char *funcname = p + 1; + + // skip to start of " " + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + + // skip "", handling the case where it contains a + char *filename_end = p + 1; + if (p == filename) { + // something went wrong, give up + filename_end = filename + strlen(filename); + funcname = filename_end; + } + trace.object_filename.assign( + filename, filename_end); // ok even if filename_end is the ending \0 + // (then we assign entire string) + + if (*funcname) { // if it's not end of string + *funcname_end = '\0'; + + trace.object_function = this->demangle(funcname); + trace.object_function += " "; + trace.object_function += (funcname_end + 1); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +template <> +class TraceResolverImpl + : public TraceResolverDarwinImpl {}; + +#endif // BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +// Load all symbol info +// Based on: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 + +struct module_data { + std::string image_name; + std::string module_name; + void *base_address; + DWORD load_size; +}; + +class get_mod_info { + HANDLE process; + static const int buffer_length = 4096; + +public: + get_mod_info(HANDLE h) : process(h) {} + + module_data operator()(HMODULE module) { + module_data ret; + char temp[buffer_length]; + MODULEINFO mi; + + GetModuleInformation(process, module, &mi, sizeof(mi)); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameExA(process, module, temp, sizeof(temp)); + ret.image_name = temp; + GetModuleBaseNameA(process, module, temp, sizeof(temp)); + ret.module_name = temp; + std::vector img(ret.image_name.begin(), ret.image_name.end()); + std::vector mod(ret.module_name.begin(), ret.module_name.end()); + SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, + ret.load_size); + return ret; + } +}; + +template <> class TraceResolverImpl + : public TraceResolverImplBase { +public: + TraceResolverImpl() { + + HANDLE process = GetCurrentProcess(); + + std::vector modules; + DWORD cbNeeded; + std::vector module_handles(1); + SymInitialize(process, NULL, false); + DWORD symOptions = SymGetOptions(); + symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; + SymSetOptions(symOptions); + EnumProcessModules(process, &module_handles[0], + static_cast(module_handles.size() * sizeof(HMODULE)), + &cbNeeded); + module_handles.resize(cbNeeded / sizeof(HMODULE)); + EnumProcessModules(process, &module_handles[0], + static_cast(module_handles.size() * sizeof(HMODULE)), + &cbNeeded); + std::transform(module_handles.begin(), module_handles.end(), + std::back_inserter(modules), get_mod_info(process)); + void *base = modules[0].base_address; + IMAGE_NT_HEADERS *h = ImageNtHeader(base); + image_type = h->FileHeader.Machine; + } + + static const int max_sym_len = 255; + struct symbol_t { + SYMBOL_INFO sym; + char buffer[max_sym_len]; + } sym; + + DWORD64 displacement; + + ResolvedTrace resolve(ResolvedTrace t) override { + HANDLE process = GetCurrentProcess(); + + char name[256]; + + memset(&sym, 0, sizeof(sym)); + sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO); + sym.sym.MaxNameLen = max_sym_len; + + if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) { + // TODO: error handling everywhere + char* lpMsgBuf; + DWORD dw = GetLastError(); + + if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&lpMsgBuf, 0, NULL)) { + std::fprintf(stderr, "%s\n", lpMsgBuf); + LocalFree(lpMsgBuf); + } + + // abort(); + } + UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE); + + DWORD offset = 0; + IMAGEHLP_LINE line; + if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) { + t.object_filename = line.FileName; + t.source.filename = line.FileName; + t.source.line = line.LineNumber; + t.source.col = offset; + } + + t.source.function = name; + t.object_filename = ""; + t.object_function = name; + + return t; + } + + DWORD machine_type() const { return image_type; } + +private: + DWORD image_type; +}; + +#endif + +class TraceResolver : public TraceResolverImpl {}; + +/*************** CODE SNIPPET ***************/ + +class SourceFile { +public: + typedef std::vector > lines_t; + + SourceFile() {} + SourceFile(const std::string &path) { + // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains + // a colon-separated list of path prefixes. Try prepending each + // to the given path until a valid file is found. + const std::vector &prefixes = get_paths_from_env_variable(); + for (size_t i = 0; i < prefixes.size(); ++i) { + // Double slashes (//) should not be a problem. + std::string new_path = prefixes[i] + '/' + path; + _file.reset(new std::ifstream(new_path.c_str())); + if (is_open()) + break; + } + // 2. If no valid file found then fallback to opening the path as-is. + if (!_file || !is_open()) { + _file.reset(new std::ifstream(path.c_str())); + } + } + bool is_open() const { return _file->is_open(); } + + lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) { + using namespace std; + // This function make uses of the dumbest algo ever: + // 1) seek(0) + // 2) read lines one by one and discard until line_start + // 3) read line one by one until line_start + line_count + // + // If you are getting snippets many time from the same file, it is + // somewhat a waste of CPU, feel free to benchmark and propose a + // better solution ;) + + _file->clear(); + _file->seekg(0); + string line; + unsigned line_idx; + + for (line_idx = 1; line_idx < line_start; ++line_idx) { + std::getline(*_file, line); + if (!*_file) { + return lines; + } + } + + // think of it like a lambda in C++98 ;) + // but look, I will reuse it two times! + // What a good boy am I. + struct isspace { + bool operator()(char c) { return std::isspace(c); } + }; + + bool started = false; + for (; line_idx < line_start + line_count; ++line_idx) { + getline(*_file, line); + if (!*_file) { + return lines; + } + if (!started) { + if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end()) + continue; + started = true; + } + lines.push_back(make_pair(line_idx, line)); + } + + lines.erase( + std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(), + lines.end()); + return lines; + } + + lines_t get_lines(unsigned line_start, unsigned line_count) { + lines_t lines; + return get_lines(line_start, line_count, lines); + } + + // there is no find_if_not in C++98, lets do something crappy to + // workaround. + struct not_isspace { + bool operator()(char c) { return !std::isspace(c); } + }; + // and define this one here because C++98 is not happy with local defined + // struct passed to template functions, fuuuu. + struct not_isempty { + bool operator()(const lines_t::value_type &p) { + return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) == + p.second.end()); + } + }; + + void swap(SourceFile &b) { _file.swap(b._file); } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(SourceFile &&from) : _file(nullptr) { swap(from); } + SourceFile &operator=(SourceFile &&from) { + swap(from); + return *this; + } +#else + explicit SourceFile(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + SourceFile &operator=(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + + // Allow adding to paths gotten from BACKWARD_CXX_SOURCE_PREFIXES after loading the + // library; this can be useful when the library is loaded when the locations are unknown + // Warning: Because this edits the static paths variable, it is *not* intrinsiclly thread safe + static void add_paths_to_env_variable_impl(const std::string & to_add) { + get_mutable_paths_from_env_variable().push_back(to_add); + } + +private: + details::handle > + _file; + + static std::vector get_paths_from_env_variable_impl() { + std::vector paths; + const char *prefixes_str = std::getenv("BACKWARD_CXX_SOURCE_PREFIXES"); + if (prefixes_str && prefixes_str[0]) { + paths = details::split_source_prefixes(prefixes_str); + } + return paths; + } + + static std::vector &get_mutable_paths_from_env_variable() { + static volatile std::vector paths = get_paths_from_env_variable_impl(); + return const_cast&>(paths); + } + + static const std::vector &get_paths_from_env_variable() { + return get_mutable_paths_from_env_variable(); + } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(const SourceFile &) = delete; + SourceFile &operator=(const SourceFile &) = delete; +#endif +}; + +class SnippetFactory { +public: + typedef SourceFile::lines_t lines_t; + + lines_t get_snippet(const std::string &filename, unsigned line_start, + unsigned context_size) { + + SourceFile &src_file = get_src_file(filename); + unsigned start = line_start - context_size / 2; + return src_file.get_lines(start, context_size); + } + + lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a, + const std::string &filename_b, unsigned line_b, + unsigned context_size) { + SourceFile &src_file_a = get_src_file(filename_a); + SourceFile &src_file_b = get_src_file(filename_b); + + lines_t lines = + src_file_a.get_lines(line_a - context_size / 4, context_size / 2); + src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines); + return lines; + } + + lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a, + unsigned line_b, unsigned context_size) { + SourceFile &src_file = get_src_file(filename); + + using std::max; + using std::min; + unsigned a = min(line_a, line_b); + unsigned b = max(line_a, line_b); + + if ((b - a) < (context_size / 3)) { + return src_file.get_lines((a + b - context_size + 1) / 2, context_size); + } + + lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2); + src_file.get_lines(b - context_size / 4, context_size / 2, lines); + return lines; + } + +private: + typedef details::hashtable::type src_files_t; + src_files_t _src_files; + + SourceFile &get_src_file(const std::string &filename) { + src_files_t::iterator it = _src_files.find(filename); + if (it != _src_files.end()) { + return it->second; + } + SourceFile &new_src_file = _src_files[filename]; + new_src_file = SourceFile(filename); + return new_src_file; + } +}; + +/*************** PRINTER ***************/ + +namespace ColorMode { +enum type { automatic, never, always }; +} + +class cfile_streambuf : public std::streambuf { +public: + cfile_streambuf(FILE *_sink) : sink(_sink) {} + int_type underflow() override { return traits_type::eof(); } + int_type overflow(int_type ch) override { + if (traits_type::not_eof(ch) && fputc(ch, sink) != EOF) { + return ch; + } + return traits_type::eof(); + } + + std::streamsize xsputn(const char_type *s, std::streamsize count) override { + return static_cast( + fwrite(s, sizeof *s, static_cast(count), sink)); + } + +#ifdef BACKWARD_ATLEAST_CXX11 +public: + cfile_streambuf(const cfile_streambuf &) = delete; + cfile_streambuf &operator=(const cfile_streambuf &) = delete; +#else +private: + cfile_streambuf(const cfile_streambuf &); + cfile_streambuf &operator=(const cfile_streambuf &); +#endif + +private: + FILE *sink; + std::vector buffer; +}; + +#ifdef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 33, purple = 35, reset = 39 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &os) : _os(os), _reset(false), _enabled(false) {} + + void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; } + + void activate(ColorMode::type mode, FILE *fp) { activate(mode, fileno(fp)); } + + void set_color(Color::type ccode) { + if (!_enabled) + return; + + // I assume that the terminal can handle basic colors. Seriously I + // don't want to deal with all the termcap shit. + _os << "\033[" << static_cast(ccode) << "m"; + _reset = (ccode != Color::reset); + } + + ~Colorize() { + if (_reset) { + set_color(Color::reset); + } + } + +private: + void activate(ColorMode::type mode, int fd) { + activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always + : mode); + } + + std::ostream &_os; + bool _reset; + bool _enabled; +}; + +#else // ndef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 0, purple = 0, reset = 0 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &) {} + void activate(ColorMode::type) {} + void activate(ColorMode::type, FILE *) {} + void set_color(Color::type) {} +}; + +#endif // BACKWARD_SYSTEM_LINUX + +class Printer { +public: + bool snippet; + ColorMode::type color_mode; + bool address; + bool object; + int inliner_context_size; + int trace_context_size; + bool reverse; + + Printer() + : snippet(true), color_mode(ColorMode::automatic), address(false), + object(false), inliner_context_size(5), trace_context_size(7), + reverse(true) {} + + template FILE *print(ST &st, FILE *fp = stderr) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(st, os, colorize); + return fp; + } + + template std::ostream &print(ST &st, std::ostream &os) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(st, os, colorize); + return os; + } + + template + FILE *print(IT begin, IT end, FILE *fp = stderr, size_t thread_id = 0) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(begin, end, os, thread_id, colorize); + return fp; + } + + template + std::ostream &print(IT begin, IT end, std::ostream &os, + size_t thread_id = 0) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(begin, end, os, thread_id, colorize); + return os; + } + + TraceResolver const &resolver() const { return _resolver; } + +private: + TraceResolver _resolver; + SnippetFactory _snippets; + + template + void print_stacktrace(ST &st, std::ostream &os, Colorize &colorize) { + print_header(os, st.thread_id()); + _resolver.load_stacktrace(st); + if ( reverse ) { + for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { + print_trace(os, _resolver.resolve(st[trace_idx - 1]), colorize); + } + } else { + for (size_t trace_idx = 0; trace_idx < st.size(); ++trace_idx) { + print_trace(os, _resolver.resolve(st[trace_idx]), colorize); + } + } + } + + template + void print_stacktrace(IT begin, IT end, std::ostream &os, size_t thread_id, + Colorize &colorize) { + print_header(os, thread_id); + for (; begin != end; ++begin) { + print_trace(os, *begin, colorize); + } + } + + void print_header(std::ostream &os, size_t thread_id) { + os << "Stack trace (most recent call last)"; + if (thread_id) { + os << " in thread " << thread_id; + } + os << ":\n"; + } + + void print_trace(std::ostream &os, const ResolvedTrace &trace, + Colorize &colorize) { + os << "#" << std::left << std::setw(2) << trace.idx << std::right; + bool already_indented = true; + + if (!trace.source.filename.size() || object) { + os << " Object \"" << trace.object_filename << "\", at " << trace.addr + << ", in " << trace.object_function << "\n"; + already_indented = false; + } + + for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0; + --inliner_idx) { + if (!already_indented) { + os << " "; + } + const ResolvedTrace::SourceLoc &inliner_loc = + trace.inliners[inliner_idx - 1]; + print_source_loc(os, " | ", inliner_loc); + if (snippet) { + print_snippet(os, " | ", inliner_loc, colorize, Color::purple, + inliner_context_size); + } + already_indented = false; + } + + if (trace.source.filename.size()) { + if (!already_indented) { + os << " "; + } + print_source_loc(os, " ", trace.source, trace.addr); + if (snippet) { + print_snippet(os, " ", trace.source, colorize, Color::yellow, + trace_context_size); + } + } + } + + void print_snippet(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + Colorize &colorize, Color::type color_code, + int context_size) { + using namespace std; + typedef SnippetFactory::lines_t lines_t; + + lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line, + static_cast(context_size)); + + for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) { + if (it->first == source_loc.line) { + colorize.set_color(color_code); + os << indent << ">"; + } else { + os << indent << " "; + } + os << std::setw(4) << it->first << ": " << it->second << "\n"; + if (it->first == source_loc.line) { + colorize.set_color(Color::reset); + } + } + } + + void print_source_loc(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + void *addr = nullptr) { + os << indent << "Source \"" << source_loc.filename << "\", line " + << source_loc.line << ", in " << source_loc.function; + + if (address && addr != nullptr) { + os << " [" << addr << "]"; + } + os << "\n"; + } +}; + +/*************** SIGNALS HANDLING ***************/ + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +class SignalHandling { +public: + static std::vector make_default_signals() { + const int posix_signals[] = { + // Signals for which the default action is "Core". + SIGABRT, // Abort signal from abort(3) + SIGBUS, // Bus error (bad memory access) + SIGFPE, // Floating point exception + SIGILL, // Illegal Instruction + SIGIOT, // IOT trap. A synonym for SIGABRT + SIGQUIT, // Quit from keyboard + SIGSEGV, // Invalid memory reference + SIGSYS, // Bad argument to routine (SVr4) + SIGTRAP, // Trace/breakpoint trap + SIGXCPU, // CPU time limit exceeded (4.2BSD) + SIGXFSZ, // File size limit exceeded (4.2BSD) +#if defined(BACKWARD_SYSTEM_DARWIN) + SIGEMT, // emulation instruction executed +#endif + }; + return std::vector(posix_signals, + posix_signals + + sizeof posix_signals / sizeof posix_signals[0]); + } + + SignalHandling(const std::vector &posix_signals = make_default_signals()) + : _loaded(false) { + bool success = true; + + const size_t stack_size = 1024 * 1024 * 8; + _stack_content.reset(static_cast(malloc(stack_size))); + if (_stack_content) { + stack_t ss; + ss.ss_sp = _stack_content.get(); + ss.ss_size = stack_size; + ss.ss_flags = 0; + if (sigaltstack(&ss, nullptr) < 0) { + success = false; + } + } else { + success = false; + } + + for (size_t i = 0; i < posix_signals.size(); ++i) { + struct sigaction action; + memset(&action, 0, sizeof action); + action.sa_flags = + static_cast(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); + sigfillset(&action.sa_mask); + sigdelset(&action.sa_mask, posix_signals[i]); +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +#endif + action.sa_sigaction = &sig_handler; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + int r = sigaction(posix_signals[i], &action, nullptr); + if (r < 0) + success = false; + } + + _loaded = success; + } + + bool loaded() const { return _loaded; } + + static void handleSignal(int, siginfo_t *info, void *_ctx) { + ucontext_t *uctx = static_cast(_ctx); + + StackTrace st; + void *error_addr = nullptr; +#ifdef REG_RIP // x86_64 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); +#elif defined(REG_EIP) // x86_32 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); +#elif defined(__arm__) + error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); +#elif defined(__aarch64__) + #if defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__pc); + #else + error_addr = reinterpret_cast(uctx->uc_mcontext.pc); + #endif +#elif defined(__mips__) + error_addr = reinterpret_cast( + reinterpret_cast(&uctx->uc_mcontext)->sc_pc); +#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) + error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); +#elif defined(__riscv) + error_addr = reinterpret_cast(uctx->uc_mcontext.__gregs[REG_PC]); +#elif defined(__s390x__) + error_addr = reinterpret_cast(uctx->uc_mcontext.psw.addr); +#elif defined(__APPLE__) && defined(__x86_64__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); +#elif defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__eip); +#else +#warning ":/ sorry, ain't know no nothing none not of your architecture!" +#endif + if (error_addr) { + st.load_from(error_addr, 32, reinterpret_cast(uctx), + info->si_addr); + } else { + st.load_here(32, reinterpret_cast(uctx), info->si_addr); + } + + Printer printer; + printer.address = true; + printer.print(st, stderr); + +#if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || \ + (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) + psiginfo(info, nullptr); +#else + (void)info; +#endif + } + +private: + details::handle _stack_content; + bool _loaded; + +#ifdef __GNUC__ + __attribute__((noreturn)) +#endif + static void + sig_handler(int signo, siginfo_t *info, void *_ctx) { + handleSignal(signo, info, _ctx); + + // try to forward the signal. + raise(info->si_signo); + + // terminate the process immediately. + puts("watf? exit"); + _exit(EXIT_FAILURE); + } +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) + : reporter_thread_([]() { + /* We handle crashes in a utility thread: + backward structures and some Windows functions called here + need stack space, which we do not have when we encounter a + stack overflow. + To support reporting stack traces during a stack overflow, + we create a utility thread at startup, which waits until a + crash happens or the program exits normally. */ + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::running; }); + } + if (crashed() == crash_status::crashed) { + handle_stacktrace(skip_recs()); + } + { + std::unique_lock lk(mtx()); + crashed() = crash_status::ending; + } + cv().notify_one(); + }) { + SetUnhandledExceptionFilter(crash_handler); + + signal(SIGABRT, signal_handler); + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + + std::set_terminate(&terminator); +#ifndef BACKWARD_ATLEAST_CXX17 + std::set_unexpected(&terminator); +#endif + _set_purecall_handler(&terminator); + _set_invalid_parameter_handler(&invalid_parameter_handler); + } + bool loaded() const { return true; } + + ~SignalHandling() { + { + std::unique_lock lk(mtx()); + crashed() = crash_status::normal_exit; + } + + cv().notify_one(); + + reporter_thread_.join(); + } + +private: + static CONTEXT *ctx() { + static CONTEXT data; + return &data; + } + + enum class crash_status { running, crashed, normal_exit, ending }; + + static crash_status &crashed() { + static crash_status data; + return data; + } + + static std::mutex &mtx() { + static std::mutex data; + return data; + } + + static std::condition_variable &cv() { + static std::condition_variable data; + return data; + } + + static HANDLE &thread_handle() { + static HANDLE handle; + return handle; + } + + std::thread reporter_thread_; + + // TODO: how not to hardcode these? + static const constexpr int signal_skip_recs = +#ifdef __clang__ + // With clang, RtlCaptureContext also captures the stack frame of the + // current function Below that, there are 3 internal Windows functions + 4 +#else + // With MSVC cl, RtlCaptureContext misses the stack frame of the current + // function The first entries during StackWalk are the 3 internal Windows + // functions + 3 +#endif + ; + + static int &skip_recs() { + static int data; + return data; + } + + static inline void terminator() { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void signal_handler(int) { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void __cdecl invalid_parameter_handler(const wchar_t *, + const wchar_t *, + const wchar_t *, + unsigned int, + uintptr_t) { + crash_handler(signal_skip_recs); + abort(); + } + + NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) { + // The exception info supplies a trace from exactly where the issue was, + // no need to skip records + crash_handler(0, info->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + + NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) { + + if (ct == nullptr) { + RtlCaptureContext(ctx()); + } else { + memcpy(ctx(), ct, sizeof(CONTEXT)); + } + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &thread_handle(), 0, FALSE, + DUPLICATE_SAME_ACCESS); + + skip_recs() = skip; + + { + std::unique_lock lk(mtx()); + crashed() = crash_status::crashed; + } + + cv().notify_one(); + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::crashed; }); + } + } + + static void handle_stacktrace(int skip_frames = 0) { + // printer creates the TraceResolver, which can supply us a machine type + // for stack walking. Without this, StackTrace can only guess using some + // macros. + // StackTrace also requires that the PDBs are already loaded, which is done + // in the constructor of TraceResolver + Printer printer; + + StackTrace st; + st.set_machine_type(printer.resolver().machine_type()); + st.set_thread_handle(thread_handle()); + st.load_here(32 + skip_frames, ctx()); + st.skip_n_firsts(skip_frames); + + printer.address = true; + printer.print(st, std::cerr); + } +}; + +#endif // BACKWARD_SYSTEM_WINDOWS + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) {} + bool init() { return false; } + bool loaded() { return false; } +}; + +#endif // BACKWARD_SYSTEM_UNKNOWN + +} // namespace backward + +#endif /* H_GUARD */ diff --git a/uml/main_sequence_diagram.yml b/uml/main_sequence_diagram.yml new file mode 100644 index 00000000..2f88eaa2 --- /dev/null +++ b/uml/main_sequence_diagram.yml @@ -0,0 +1,27 @@ +type: sequence +# Group free functions into one participant per file +combine_free_functions_into_file_participants: true +# Do not generate method or function arguments +generate_method_arguments: none +# Parse only 1 translation unit for this diagram +glob: + - src/main.cc +include: + # Only include entities and call expressions from the project directory + paths: + - src +exclude: + # Exclude calls to config options + elements: + - clanguml::config::option + # Exclude entities and call expressions from irrelevant files + paths: + - src/util/util.h +using_namespace: + - clanguml +plantuml: + before: + - 'title clang-uml main function sequence diagram' +# Use clang-uml main function as entry point for this diagram +start_from: + - function: main(int,const char **) \ No newline at end of file diff --git a/uml/sequence_diagram_visitor_sequence_diagram.yml b/uml/sequence_diagram_visitor_sequence_diagram.yml index bcbbe923..531fe592 100644 --- a/uml/sequence_diagram_visitor_sequence_diagram.yml +++ b/uml/sequence_diagram_visitor_sequence_diagram.yml @@ -1,13 +1,26 @@ type: sequence +# Group free functions into one participant per file +combine_free_functions_into_file_participants: true +# Do not generate method or function arguments +generate_method_arguments: none glob: - src/sequence_diagram/visitor/*.cc - src/sequence_diagram/model/*.cc + - src/common/visitor/*.cc + - src/common/model/*.cc include: namespaces: - - clanguml::sequence_diagram::visitor - - clanguml::sequence_diagram::model + - clanguml + paths: + - . +exclude: + namespaces: + - clanguml::model::tvl + - clanguml::decorators + paths: + - src/common/model/source_location.h using_namespace: - - clanguml::sequence_diagram::visitor + - clanguml plantuml: before: - 'title clang-uml sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl sequence diagram' diff --git a/util/check_formatting.sh b/util/check_formatting.sh new file mode 100755 index 00000000..2bd1170a --- /dev/null +++ b/util/check_formatting.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +## +## util/check_formatting.sh +## +## Copyright (c) 2021-2023 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. +## + +shopt -s globstar + +include_file_list() { + cat .clang-format-include | grep "^\+" | awk '{print $2}' \ + | tr [:space:] '\n' | sort | uniq +} + +ignore_file_list() { + echo $(cat .clang-format-include | grep "^\-" | awk '{print $2}') \ + $(git ls-files --others --exclude-standard --ignored) \ + | tr [:space:] '\n' | sort | uniq +} + +valid_ignore_file_list() { + echo $(include_file_list) $(ignore_file_list) \ + | tr [:space:] '\n' | sort | uniq -d +} + +effective_file_list() { + echo $(include_file_list) $(valid_ignore_file_list) \ + | tr [:space:] '\n' | sort | uniq -u +} + +GIT_STATUS=$(git diff-index --quiet HEAD --) + +EFFECTIVE_FILE_LIST=$(effective_file_list) + +if [[ ${#EFFECTIVE_FILE_LIST[@]} -eq 0 ]]; then + echo ".clang-format-include patterns did not match any files." + exit 0 +else + clang-format-12 --dry-run --Werror ${EFFECTIVE_FILE_LIST} +fi \ No newline at end of file diff --git a/util/format_svg.py b/util/format_svg.py index 5b6abf3f..32904cea 100755 --- a/util/format_svg.py +++ b/util/format_svg.py @@ -3,7 +3,7 @@ ## ## util/format_svg.py ## -## Copyright (c) 2021-2022 Bartek Kryza +## Copyright (c) 2021-2023 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/util/generate_test_case.py b/util/generate_test_case.py index ba2e1bc3..44b99c53 100755 --- a/util/generate_test_case.py +++ b/util/generate_test_case.py @@ -3,7 +3,7 @@ ## ## util/generate_test_case.py ## -## Copyright (c) 2021-2022 Bartek Kryza +## Copyright (c) 2021-2023 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/util/generate_test_cases_docs.py b/util/generate_test_cases_docs.py index e64c494e..782fa4d2 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-2022 Bartek Kryza +## Copyright (c) 2021-2023 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/util/templates/test_cases/test_case.h b/util/templates/test_cases/test_case.h index f70fdce8..34a5811e 100644 --- a/util/templates/test_cases/test_case.h +++ b/util/templates/test_cases/test_case.h @@ -1,7 +1,7 @@ /** * tests/{{ name }}/test_case.h * - * Copyright (c) 2021-2022 Bartek Kryza + * Copyright (c) 2021-2023 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.