diff --git a/.clang-tidy b/.clang-tidy index a9964efa..5aa17178 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -7,7 +7,9 @@ Checks: >- -android*, -bugprone-branch-clone, -bugprone-exception-escape, + -bugprone-easily-swappable-parameters, -clang-analyzer-alpha.*, + -clang-analyzer-core.StackAddressEscape, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-macro-usage, -cppcoreguidelines-pro-bounds-pointer-arithmetic, @@ -16,6 +18,7 @@ Checks: >- -cppcoreguidelines-non-private-member-variables-in-classes, -cert-env33-c, -cert-err58-cpp, + -cert-dcl58-cpp, -fuchsia*, -hicpp-no-array-decay, -hicpp-special-member-functions, @@ -30,17 +33,21 @@ Checks: >- -llvm-namespace-comment, -misc-no-recursion, -misc-non-private-member-variables-in-classes, + -misc-const-correctness, -modernize-use-nodiscard, -modernize-use-trailing-return-type, + -modernize-concat-nested-namespaces, -mpi*, -objc*, -openmp*, -readability-inconsistent-declaration-parameter-name, + -readability-identifier-length, -readability-identifier-naming, -readability-redundant-smartptr-get, -readability-convert-member-functions-to-static, -readability-function-cognitive-complexity, -readability-const-return-type, + -readability-simplify-boolean-expr, -darwin*, -zircon* WarningsAsErrors: '*' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 59cddc83..46ee8834 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,24 +22,24 @@ on: jobs: build-ubuntu: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repository uses: actions/checkout@v2 - name: Update package database run: sudo apt -y update - name: Install deps - run: sudo apt -y install git make pkg-config 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 libunwind-dev libdw-dev + run: sudo apt -y install git make pkg-config gcc-11 g++-11 ccache cmake libyaml-cpp-dev llvm-15 clang-15 libclang-15-dev libclang-cpp15-dev clang-format-15 lcov zlib1g-dev libunwind-dev libdw-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 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 11 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 11 - 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 + NUMPROC=2 CMAKE_CXX_FLAGS="--coverage -fno-inline" CMAKE_EXE_LINKER_FLAGS="-lgcov --coverage" LLVM_VERSION=15 make test - name: Run coverage run: | lcov -c -d debug -o coverage.info diff --git a/CHANGELOG.md b/CHANGELOG.md index e9300d77..1731e69e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # CHANGELOG + * Added initial support for C++20 concept rendering (#96) * Added support for plain C11 translation units (#97) * Added 'row' and 'column' layout hints for aligning elements (#90) * Added 'together' layout hint for grouping elements (#43) diff --git a/Makefile b/Makefile index 033da801..8135a982 100644 --- a/Makefile +++ b/Makefile @@ -114,15 +114,15 @@ init_compile_commands: debug .PHONY: clang-format clang-format: - docker run --rm -v $(CURDIR):/root/sources bkryza/clang-format-check:1.3 + docker run --rm -v $(CURDIR):/root/sources bkryza/clang-format-check:1.4 .PHONY: format format: - docker run --rm -v $(CURDIR):/root/sources bkryza/clang-format-check:1.3 + docker run --rm -v $(CURDIR):/root/sources bkryza/clang-format-check:1.4 .PHONY: debug_tidy tidy: debug_tidy - run-clang-tidy-12 -p debug_tidy ./src + run-clang-tidy-15 -p debug_tidy ./src .PHONY: check-formatting check-formatting: diff --git a/README.md b/README.md index da7caf82..d19c1935 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Main features supported so far include: * Optional package generation from namespaces * Interactive links to online code to classes, methods and class fields in SVG diagrams * Support for plain C99/C11 code (struct and units relationships) + * C++20 concept constraints * **Sequence diagram generation** * Generation of sequence diagram from specific method or function * Generation of loop and conditional statements diff --git a/docs/test_cases.md b/docs/test_cases.md index 8cd81348..90628a8c 100644 --- a/docs/test_cases.md +++ b/docs/test_cases.md @@ -54,7 +54,10 @@ * [t00053](./test_cases/t00053.md) - Test case for `together` layout hint in class diagram * [t00054](./test_cases/t00054.md) - Test case for `together` layout hint in class diagram with rendered namespaces * [t00055](./test_cases/t00055.md) - Test case for `row` and `column` layout hints + * [t00056](./test_cases/t00056.md) - Basic C++20 concepts test case * [t00057](./test_cases/t00057.md) - Test case C99/C11 translation units with structs and unions + * [t00058](./test_cases/t00058.md) - Test case for concepts with variadic parameters and type aliases + * [t00059](./test_cases/t00059.md) - Non-virtual abstract factory pattern using concepts test case ## Sequence diagrams * [t20001](./test_cases/t20001.md) - Basic sequence diagram test case * [t20002](./test_cases/t20002.md) - Free function sequence diagram test case diff --git a/docs/test_cases/t00002_class.svg b/docs/test_cases/t00002_class.svg index fed2ad75..de1de2bc 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_class.svg b/docs/test_cases/t00003_class.svg index 7fdf8d89..49c2403f 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_class.svg b/docs/test_cases/t00004_class.svg index 494c200d..b89b82e5 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_class.svg b/docs/test_cases/t00005_class.svg index 6781140d..d3aafd19 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_class.svg b/docs/test_cases/t00006_class.svg index ae8be5fc..4e105753 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] - + - + k : K *[20] - + - + lm : std::vector<std::pair<L,M>> - + - + ns : std::tuple<N,NN,NNN> diff --git a/docs/test_cases/t00007_class.svg b/docs/test_cases/t00007_class.svg index 16f7cdac..3bd0c033 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_class.svg b/docs/test_cases/t00008_class.svg index 81ebee3e..7efd2707 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_class.svg b/docs/test_cases/t00009_class.svg index 2477223a..29d753db 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_class.svg b/docs/test_cases/t00010_class.svg index f8a12895..7d2b82e4 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_class.svg b/docs/test_cases/t00011_class.svg index 0e642e2f..a666def9 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_class.svg b/docs/test_cases/t00012_class.svg index 23f8c2c6..e0c40a2a 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_class.svg b/docs/test_cases/t00013_class.svg index 8f7cc241..e765fa19 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_class.svg b/docs/test_cases/t00014_class.svg index fadc852c..9e65c781 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_class.svg b/docs/test_cases/t00015_class.svg index b7991dfc..77195a8c 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_class.svg b/docs/test_cases/t00016_class.svg index c131bcca..15d53ba8 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_class.svg b/docs/test_cases/t00017_class.svg index 702027e9..9babd43d 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_class.svg b/docs/test_cases/t00018_class.svg index e52ca1d4..1bcb6047 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 de819334..e4864a43 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_class.svg b/docs/test_cases/t00020_class.svg index eab19cef..e61adb2e 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_class.svg b/docs/test_cases/t00021_class.svg index d54b98c2..be979eff 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_class.svg b/docs/test_cases/t00022_class.svg index 2be92818..91e625fa 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_class.svg b/docs/test_cases/t00023_class.svg index 83fb64a3..422f6f57 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_class.svg b/docs/test_cases/t00024_class.svg index 5de76fea..eb299e92 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_class.svg b/docs/test_cases/t00025_class.svg index 4764d75b..d9cc377c 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_class.svg b/docs/test_cases/t00026_class.svg index d1474e9e..e1e0137b 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_class.svg b/docs/test_cases/t00027_class.svg index 284a38a1..033311c7 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 ff82e908..8fa31e61 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,65 +105,65 @@ 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. - + R contains an instance of A. - + Reference to C. diff --git a/docs/test_cases/t00029_class.svg b/docs/test_cases/t00029_class.svg index 34adf119..3484b5d5 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 d842d30c..ab96ab99 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 b3da4a05..119e1871 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 70377ef2..f35d0696 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 bc010275..3c8e2cff 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 11c317bc..22463255 100644 --- a/docs/test_cases/t00034_class.svg +++ b/docs/test_cases/t00034_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Void @@ -21,8 +21,8 @@ operator!=(const Void & ) const : bool - - + + lift_void @@ -31,16 +31,16 @@ - - + + lift_void - - + + lift_void @@ -49,8 +49,8 @@ - - + + drop_void @@ -59,16 +59,16 @@ - - + + drop_void - - + + drop_void @@ -77,33 +77,33 @@ - - + + A - - + + R - + - + la : lift_void_t<A> * - + - + lv : lift_void_t<void> * diff --git a/docs/test_cases/t00035_class.svg b/docs/test_cases/t00035_class.svg index 86ae1585..388755ce 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_class.svg b/docs/test_cases/t00036_class.svg index 0b4ba3e1..1402ed40 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 e3ab9328..3e4b4702 100644 --- a/docs/test_cases/t00037_class.svg +++ b/docs/test_cases/t00037_class.svg @@ -1,6 +1,6 @@ - + @@ -9,98 +9,98 @@ - - + + ST - + - - dimensions : ST::(anonymous_620) + + dimensions : ST::(anonymous_662) - + - - units : ST::(anonymous_739) + + units : ST::(anonymous_792) - - + + ST::(dimensions) - + - + t : double - + - + x : double - + - + y : double - + - + z : double - - + + ST::(units) - + - + c : double - + - + h : double - - + + A - + - + st : ST diff --git a/docs/test_cases/t00038_class.svg b/docs/test_cases/t00038_class.svg index 9ec9e376..c7e47307 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_class.svg b/docs/test_cases/t00039_class.svg index e1696dc3..8bc65c3d 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 4595bbe1..78e40dfa 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 cf91342b..cd1a9fa2 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 587e0db6..f4847467 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 589b7fa1..7f7ab48a 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 1e534f38..93b73721 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_class.svg b/docs/test_cases/t00045_class.svg index 5b594df7..c321cb5f 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_class.svg b/docs/test_cases/t00046_class.svg index 3e488507..1336fdc4 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_class.svg b/docs/test_cases/t00047_class.svg index c15a2320..87deabc1 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 1dccdc4e..9e6e861d 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_class.svg b/docs/test_cases/t00049_class.svg index 2d87b9c8..83ee46e5 100644 --- a/docs/test_cases/t00049_class.svg +++ b/docs/test_cases/t00049_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,17 +18,17 @@ T - + - + a : T get_a() : T & - + A @@ -36,7 +36,7 @@ intmap - + A @@ -44,7 +44,7 @@ thestring - + A @@ -52,32 +52,32 @@ string_vector - - + + R - + - + a_string : A<thestring> - + - + a_vector_string : A<string_vector> - + - + a_int_map : A<intmap> diff --git a/docs/test_cases/t00050_class.svg b/docs/test_cases/t00050_class.svg index ced715bf..90f01157 100644 --- a/docs/test_cases/t00050_class.svg +++ b/docs/test_cases/t00050_class.svg @@ -1,6 +1,6 @@ - + @@ -9,40 +9,40 @@ - - + + A - - + + B - - + + C - - + + utils::D - - + + E @@ -52,8 +52,8 @@ E3 - - + + F @@ -61,44 +61,44 @@ T,V,int N - + - + t : T[N] - + - + v : V - - + + G - - + + 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. @@ -125,50 +125,50 @@ 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 diff --git a/docs/test_cases/t00051_class.svg b/docs/test_cases/t00051_class.svg index d875d75e..c58b32db 100644 --- a/docs/test_cases/t00051_class.svg +++ b/docs/test_cases/t00051_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + B @@ -18,18 +18,18 @@ F,FF - + - + f_ : F - + - + ff_ : FF @@ -39,8 +39,8 @@ f() : void ff() : void - - + + B<(lambda at ../../tests/t00051/t00051.cc:43:18),(lambda at ../../tests/t00051/t00051.cc:43:27)> @@ -53,22 +53,22 @@ ff() : void - + - + f_ : (lambda at ../../tests/t00051/t00051.cc:43:18) - + - + ff_ : (lambda at ../../tests/t00051/t00051.cc:43:27) - - + + A @@ -83,8 +83,8 @@ get_function() : (lambda at ../../tests/t00051/t00051.cc:48:16) - - + + A::custom_thread1 @@ -93,8 +93,8 @@ custom_thread1<Function,Args...>(Function && f, Args &&... args) : void - - + + A::custom_thread2 diff --git a/docs/test_cases/t00052_class.svg b/docs/test_cases/t00052_class.svg index 848ed036..990482ec 100644 --- a/docs/test_cases/t00052_class.svg +++ b/docs/test_cases/t00052_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -21,8 +21,8 @@ aa<F,Q>(F && f, Q q) : void - - + + B @@ -35,8 +35,8 @@ bb<F>(F && f, T t) : T - - + + C @@ -47,7 +47,7 @@ c<P>(P p) : T - + B @@ -55,7 +55,7 @@ int - + C @@ -63,32 +63,32 @@ int - - + + R - + - + a : A - + - + b : B<int> - + - + c : C<int> diff --git a/docs/test_cases/t00053_class.svg b/docs/test_cases/t00053_class.svg index b87f1ea0..2f46085f 100644 --- a/docs/test_cases/t00053_class.svg +++ b/docs/test_cases/t00053_class.svg @@ -1,6 +1,6 @@ - + @@ -9,72 +9,72 @@ - - + + A - - + + C - - + + E - - + + F - - + + a - - + + c - - + + e - - + + f - - + + h @@ -82,8 +82,8 @@ hhh - - + + j @@ -91,56 +91,56 @@ jjj - - + + b - - + + d - - + + g - - + + B - - + + D - - + + G - - + + i diff --git a/docs/test_cases/t00054_class.svg b/docs/test_cases/t00054_class.svg index 38f12d4f..b9aff610 100644 --- a/docs/test_cases/t00054_class.svg +++ b/docs/test_cases/t00054_class.svg @@ -1,6 +1,6 @@ - + @@ -9,116 +9,116 @@ - + detail - + detail2 - + detail3 - + detail4 - - + + d - - + + a - - + + c - - + + e - - + + C - - + + F - - + + D - - + + E - - + + A - - + + B - - + + f - - + + G - - + + h @@ -127,8 +127,8 @@ hhh - - + + i @@ -137,8 +137,8 @@ iii - - + + j @@ -147,16 +147,16 @@ jjj - - + + b - - + + g diff --git a/docs/test_cases/t00055_class.svg b/docs/test_cases/t00055_class.svg index 78f71227..8f1d011f 100644 --- a/docs/test_cases/t00055_class.svg +++ b/docs/test_cases/t00055_class.svg @@ -1,6 +1,6 @@ - + @@ -9,80 +9,80 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - - + + G - - + + H - - + + I - - + + J diff --git a/docs/test_cases/t00056.md b/docs/test_cases/t00056.md new file mode 100644 index 00000000..81bd1a60 --- /dev/null +++ b/docs/test_cases/t00056.md @@ -0,0 +1,124 @@ +# t00056 - Basic C++20 concepts test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t00056_class: + type: class + glob: + - ../../tests/t00056/t00056.cc + include: + namespaces: + - clanguml::t00056 + using_namespace: + - clanguml::t00056 +``` +## Source code +File t00056.cc +```cpp +#include + +namespace clanguml { +namespace t00056 { + +template +concept greater_than_simple = sizeof(T) > sizeof(L); + +template +concept greater_than_with_requires = requires(T l, P r) +{ + sizeof(l) > sizeof(r); +}; + +// Constraint expression +template +concept max_four_bytes = sizeof(T) <= 4; + +// Simple requirement +template +concept iterable = requires(T container) +{ + container.begin(); + container.end(); +}; + +// Type requirement +template +concept has_value_type = requires +{ + typename T::value_type; +}; + +template +concept convertible_to_string = max_four_bytes && requires(T s) +{ + std::string{s}; + { + std::to_string(s) + } + noexcept; + { + std::to_string(s) + } -> std::same_as; +}; + +// Compound requirement +// ... + +// Combined concept +template +concept iterable_with_value_type = iterable && has_value_type; + +template +concept iterable_or_small_value_type = + iterable_with_value_type || max_four_bytes; + +// Simple type constraint +template struct A { + T a; +}; + +// Requires constant expression +template +requires iterable_or_small_value_type +struct B { + T b; +}; + +// Anonymous concept requirement (TODO) +template +requires requires(T t) +{ + --t; + t--; +} +struct C { + T c; +}; + +template +requires max_four_bytes && max_four_bytes +struct D { +}; + +template +requires greater_than_with_requires +struct E { + T1 e1; + T2 e2; + T3 e3; +}; + +template +requires greater_than_simple +struct F { + T1 f1; + T2 f2; + T3 f3; +}; +} +} +``` +## Generated UML diagrams +![t00056_class](./t00056_class.svg "Basic C++20 concepts test case") diff --git a/docs/test_cases/t00056_class.svg b/docs/test_cases/t00056_class.svg new file mode 100644 index 00000000..6fa41ae7 --- /dev/null +++ b/docs/test_cases/t00056_class.svg @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + «concept» + greater_than_simple + + T,L + + + + + + + + «concept» + greater_than_with_requires + + T,P + + (T l,P r) + + sizeof (l) > sizeof (r) + + + + + + «concept» + max_four_bytes + + T + + + + + + + + «concept» + iterable + + T + + (T container) + + container.begin() + container.end() + + + + + + «concept» + has_value_type + + T + + () + + typename T::value_type + + + + + + «concept» + convertible_to_string + + T + + (T s) + + std::string{s} + {std::to_string(s)} noexcept + {std::to_string(s)} -> std::same_as<std::string> + + + + + + «concept» + iterable_with_value_type + + T + + + + + + + + «concept» + iterable_or_small_value_type + + T + + + + + + + + A + + max_four_bytes T + + + + + + + + a : T + + + + + + + B + + T + + + + + + + + b : T + + + + + + + C + + convertible_to_string T + + + + + + + + c : T + + + + + + + D + + iterable T1,T2,iterable T3,T4,T5 + + + + + + + + E + + T1,T2,T3 + + + + + + + + e1 : T1 + + + + + + + e2 : T2 + + + + + + + e3 : T3 + + + + + + + F + + T1,T2,T3 + + + + + + + + f1 : T1 + + + + + + + f2 : T2 + + + + + + + f3 : T3 + + + + + T + + + T + + + T + + + T + + + T + + + T + + + T + + + T + + + T1 + + + T3 + + + T2 + + + T5 + + + T1,T3 + + + T1,T3 + + diff --git a/docs/test_cases/t00057_class.svg b/docs/test_cases/t00057_class.svg index 0f097a4e..21f5c8b7 100644 --- a/docs/test_cases/t00057_class.svg +++ b/docs/test_cases/t00057_class.svg @@ -1,6 +1,6 @@ - + @@ -9,210 +9,210 @@ - - + + t00057_A - + - + a1 : int - - + + t00057_B - + - + b1 : int - - + + t00057_C - + - + c1 : int - - + + «union» t00057_D - + - + d1 : int - + - + d2 : float - - + + t00057_E - + - + e : int - + - + coordinates : t00057_E::(anonymous_739) - + - + height : t00057_E::(anonymous_807) - - + + t00057_E::(coordinates) - + - + x : int - + - + y : int - - + + «union» t00057_E::(height) - + - + z : int - + - + t : double - - + + t00057_R - + - + a : struct t00057_A - + - + b : t00057_B - + - + c : struct t00057_C * - + - + d : union t00057_D - + - + e : struct t00057_E * - + - + f : struct t00057_F * - - + + t00057_F - + - + f1 : int diff --git a/docs/test_cases/t00058.md b/docs/test_cases/t00058.md new file mode 100644 index 00000000..375c0580 --- /dev/null +++ b/docs/test_cases/t00058.md @@ -0,0 +1,69 @@ +# t00058 - Test case for concepts with variadic parameters and type aliases +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t00058_class: + type: class + glob: + - ../../tests/t00058/t00058.cc + include: + namespaces: + - clanguml::t00058 + using_namespace: + - clanguml::t00058 + plantuml: + after: + - '{{ alias("same_as_first_type") }} ..> {{ alias("first_type") }}' +``` +## Source code +File t00058.cc +```cpp +#include +#include +#include + +// Based on a blog post: +// https://andreasfertig.blog/2020/08/cpp20-concepts-testing-constrained-functions/ + +namespace clanguml { +namespace t00058 { + +template struct first_type { + using type = T; +}; + +template +using first_type_t = typename first_type::type; + +// TODO: Dependency of this concept on first_type<> template does not currently +// work due to the fact that I don't know how to extract that information +// from clang::DependentNameType to which first_type_t<> resolves to... +template +concept same_as_first_type = std::is_same_v, + std::remove_cvref_t>>; + +template +requires same_as_first_type +struct A { + std::vector a; +}; + +template +requires same_as_first_type +struct B { + std::vector b; + P bb; +}; + +struct R { + A aa; + B> bb; +}; + +} +} +``` +## Generated UML diagrams +![t00058_class](./t00058_class.svg "Test case for concepts with variadic parameters and type aliases") diff --git a/docs/test_cases/t00058_class.svg b/docs/test_cases/t00058_class.svg new file mode 100644 index 00000000..a96f7359 --- /dev/null +++ b/docs/test_cases/t00058_class.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + first_type + + T,Args... + + + + + + + + «concept» + same_as_first_type + + T,Args... + + + + + + + + A + + T,Args... + + + + + + + + a : std::vector<T> + + + + + + + B + + T,P,Args... + + + + + + + + b : std::vector<T> + + + + + + + bb : P + + + + + + A + + int,int,double,std::string + + + + + + A + + int,int + + + + + + B + + int,std::string,int,double,A<int,int> + + + + + + + R + + + + + + + + aa : A<int,int,double,std::string> + + + + + + + bb : B<int,std::string,int,double,A<int,int>> + + + + + T,Args... + + + T,Args... + + + + + + + + + + + + aa + + + + bb + + + + diff --git a/docs/test_cases/t00059.md b/docs/test_cases/t00059.md new file mode 100644 index 00000000..a7a3d826 --- /dev/null +++ b/docs/test_cases/t00059.md @@ -0,0 +1,85 @@ +# t00059 - Non-virtual abstract factory pattern using concepts test case +## Config +```yaml +compilation_database_dir: .. +output_directory: puml +diagrams: + t00059_class: + type: class + glob: + - ../../tests/t00059/t00059.cc + include: + namespaces: + - clanguml::t00059 + using_namespace: + - clanguml::t00059 +``` +## Source code +File t00059.cc +```cpp +#include + +namespace clanguml { +namespace t00059 { + +template +concept fruit_c = requires(T t) +{ + T{}; + t.get_name(); +}; + +template +concept apple_c = fruit_c && requires(T t) +{ + t.get_sweetness(); +}; + +template +concept orange_c = fruit_c && requires(T t) +{ + t.get_bitterness(); +}; + +class gala_apple { +public: + auto get_name() const -> std::string { return "gala"; } + auto get_sweetness() const -> float { return 0.8; } +}; + +class empire_apple { +public: + auto get_name() const -> std::string { return "empire"; } + auto get_sweetness() const -> float { return 0.6; } +}; + +class lima_orange { +public: + auto get_name() const -> std::string { return "lima"; } + auto get_bitterness() const -> float { return 0.8; } +}; + +class valencia_orange { +public: + auto get_name() const -> std::string { return "valencia"; } + auto get_bitterness() const -> float { return 0.6; } +}; + +template class fruit_factory { +public: + auto create_apple() const -> TA { return TA{}; } + auto create_orange() const -> TO { return TO{}; } +}; + +using fruit_factory_1 = fruit_factory; +using fruit_factory_2 = fruit_factory; + +struct R { + fruit_factory_1 factory_1; + fruit_factory_2 factory_2; +}; +} +} +``` +## Generated UML diagrams +![t00059_class](./t00059_class.svg "Non-virtual abstract factory pattern using concepts test case") diff --git a/docs/test_cases/t00059_class.svg b/docs/test_cases/t00059_class.svg new file mode 100644 index 00000000..686b1314 --- /dev/null +++ b/docs/test_cases/t00059_class.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + «concept» + fruit_c + + T + + (T t) + + T{} + t.get_name() + + + + + + «concept» + apple_c + + T + + (T t) + + t.get_sweetness() + + + + + + «concept» + orange_c + + T + + (T t) + + t.get_bitterness() + + + + + + gala_apple + + + + get_name() const : std::string + + get_sweetness() const : float + + + + + + empire_apple + + + + get_name() const : std::string + + get_sweetness() const : float + + + + + + lima_orange + + + + get_name() const : std::string + + get_bitterness() const : float + + + + + + valencia_orange + + + + get_name() const : std::string + + get_bitterness() const : float + + + + + + fruit_factory + + apple_c TA,orange_c TO + + + + create_apple() const : TA + + create_orange() const : TO + + + + + fruit_factory + + gala_apple,valencia_orange + + + + + + fruit_factory + + empire_apple,lima_orange + + + + + + + R + + + + + + + + factory_1 : fruit_factory_1 + + + + + + + factory_2 : fruit_factory_2 + + + + + T + + + T + + + TA + + + TO + + + + + + + + + + + + + + + + factory_1 + + + + factory_2 + + diff --git a/docs/test_cases/t20001_sequence.svg b/docs/test_cases/t20001_sequence.svg index 61419be5..64e43a33 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 45dbcf89..504130cc 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 d049ac81..3155fa49 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 579fe95e..504fa091 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 9cfc2063..fd6f2658 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.md b/docs/test_cases/t20006.md index 3c16a989..6f766f95 100644 --- a/docs/test_cases/t20006.md +++ b/docs/test_cases/t20006.md @@ -55,7 +55,7 @@ template struct BB { void bb1(T t, std::string f) { aa_->aa2(t); } void bb2(T t, std::string f) { aa_->aa1(t); } - BB(AA *aa) + BB(AA *aa) : aa_{aa} { } @@ -67,7 +67,7 @@ template struct BB { void bb1(T t, float f) { bb2(t, f); } void bb2(T t, float f) { aa_.aa2(t); } - BB(AA &aa) + BB(AA &aa) : aa_{aa} { } diff --git a/docs/test_cases/t20006_sequence.svg b/docs/test_cases/t20006_sequence.svg index 80c7580c..2e52fe71 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 33eba696..a3447279 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 158155f3..85cf32aa 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 4d0124de..f3e8df2d 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 23bb3c55..492394bb 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 c27ccb03..728be388 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 bb5014d3..b3a7a096 100644 --- a/docs/test_cases/t20012_sequence.svg +++ b/docs/test_cases/t20012_sequence.svg @@ -1,6 +1,6 @@ - + @@ -9,31 +9,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -43,122 +43,122 @@ - - + + tmain() - + tmain() - - + + tmain()::(lambda ../../tests/t20012/t20012.cc:66:20) - + tmain()::(lambda ../../tests/t20012/t20012.cc:66:20) - - + + A - + A - - + + B - + B - - + + tmain()::(lambda ../../tests/t20012/t20012.cc:79:20) - + tmain()::(lambda ../../tests/t20012/t20012.cc:79:20) - - + + C - + C - - + + R<R::(lambda ../../tests/t20012/t20012.cc:85:9)> - + R<R::(lambda ../../tests/t20012/t20012.cc:85:9)> - - + + tmain()::(lambda ../../tests/t20012/t20012.cc:85:9) - + tmain()::(lambda ../../tests/t20012/t20012.cc:85:9) - - + + D - + D - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + operator()() - + a() - + aa() - + aaa() - + b() - + bb() - + @@ -167,67 +167,67 @@ - + operator()() - + c() - + cc() - + ccc() - + operator()() - + a() - + aa() - + aaa() - + b() - + bb() - + @@ -238,29 +238,29 @@ - + r() - + operator()() - + c() - + cc() - + @@ -269,7 +269,7 @@ - + add5(int) diff --git a/docs/test_cases/t20013_sequence.svg b/docs/test_cases/t20013_sequence.svg index e69d0c18..32deb5e6 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 859251c8..39900a6d 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 516d2c73..7b8c45fe 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 19b9acba..191a6b84 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_sequence.svg b/docs/test_cases/t20017_sequence.svg index a6ffafab..6a76d0e3 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 fb8e401e..c3302ba3 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 3c3c17c3..d5cd8141 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 f47ce566..53a160ec 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 0ea8dab9..475e4ef1 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 bb8a34ff..e19277fa 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 a7285f0f..0fdbc64c 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 d974cb5d..a86a873d 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 e9867d35..eca1ae5f 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 d704e9b3..ea4e74b2 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 9d5e4d0e..11ca0c04 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 68a9704b..79b466a6 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 1a1962d3..4b5fae8a 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 84feaea0..98a7666e 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 077febb6..beb66c14 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 81205730..fc4b6776 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 f15d820b..f9930318 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 73a6aff9..34f9f4f3 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 d6821d3b..573ce3b1 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 2e95773d..3e5dd3b9 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 4defcf1a..88731cc0 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/t30009_package.svg b/docs/test_cases/t30009_package.svg index f4b24d7a..ed0eb6c9 100644 --- a/docs/test_cases/t30009_package.svg +++ b/docs/test_cases/t30009_package.svg @@ -1,6 +1,6 @@ - + @@ -9,53 +9,53 @@ - - + + One - - + + Two - - + + B - - + + D - - + + A - - + + C - - + + A - - + + B - - + + C - - + + D diff --git a/docs/test_cases/t40001_include.svg b/docs/test_cases/t40001_include.svg index 5f9b11fd..45f1727c 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_include.svg b/docs/test_cases/t40002_include.svg index 83549e0a..f7a34272 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_include.svg b/docs/test_cases/t40003_include.svg index ee093fe9..c8618335 100644 --- a/docs/test_cases/t40003_include.svg +++ b/docs/test_cases/t40003_include.svg @@ -1,6 +1,6 @@ - + @@ -9,66 +9,66 @@ - + src - + dependants - + dependencies - + include - + dependants - + dependencies - - + + t1.cc - - + + t2.cc - - + + t3.h - - + + t2.h - - + + t1.h - - + + t3.h - - + + t2.h - - + + t1.h - - + + t5.h diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index 8b18db8b..17e2e296 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -102,6 +102,23 @@ void generator::generate_alias(const enum_ &e, std::ostream &ostr) const m_generated_aliases.emplace(e.alias()); } +void generator::generate_alias(const concept_ &c, std::ostream &ostr) const +{ + print_debug(c, ostr); + + if (m_config.generate_packages()) + ostr << "class" + << " \"" << c.name(); + else + ostr << "class" + << " \"" << render_name(c.full_name()); + + ostr << "\" as " << c.alias() << '\n'; + + // Register the added alias + m_generated_aliases.emplace(c.alias()); +} + void generator::generate(const class_ &c, std::ostream &ostr) const { namespace plantuml_common = clanguml::common::generators::plantuml; @@ -279,6 +296,39 @@ void generator::generate(const class_ &c, std::ostream &ostr) const generate_member_notes(ostr, method, c.alias()); } +void generator::generate(const concept_ &c, std::ostream &ostr) const +{ + std::string class_type{"class"}; + + ostr << class_type << " " << c.alias() << " <>"; + + if (m_config.generate_links) { + common_generator::generate_link(ostr, c); + } + + if (!c.style().empty()) + ostr << " " << c.style(); + + ostr << " {" << '\n'; + + // TODO: add option to enable/disable this + if (c.requires_parameters().size() + c.requires_statements().size() > 0) { + std::vector parameters; + parameters.reserve(c.requires_parameters().size()); + for (const auto &p : c.requires_parameters()) { + parameters.emplace_back(p.to_string(m_config.using_namespace())); + } + + ostr << fmt::format("({})\n", fmt::join(parameters, ",")); + + ostr << "..\n"; + + ostr << fmt::format("{}\n", fmt::join(c.requires_statements(), "\n")); + } + + ostr << "}" << '\n'; +} + void generator::generate_member_notes(std::ostream &ostr, const class_element &member, const std::string &alias) const { @@ -309,6 +359,11 @@ void generator::generate_relationships(std::ostream &ostr) const generate_relationships(*enm, ostr); } } + else if (auto *cpt = dynamic_cast(p.get()); cpt) { + if (m_model.should_include(*cpt)) { + generate_relationships(*cpt, ostr); + } + } } } @@ -409,6 +464,82 @@ void generator::generate_relationships( ostr << all_relations_str.str(); } +void generator::generate_relationships( + const concept_ &c, std::ostream &ostr) const +{ + namespace plantuml_common = clanguml::common::generators::plantuml; + + // + // Process relationships + // + std::set rendered_relations; + + std::stringstream all_relations_str; + std::set unique_relations; + + for (const auto &r : c.relationships()) { + if (!m_model.should_include(r.type())) + continue; + + LOG_DBG("== Processing relationship {}", + plantuml_common::to_plantuml(r.type(), r.style())); + + std::stringstream relstr; + clanguml::common::id_t destination{0}; + try { + destination = r.destination(); + + std::string puml_relation; + if (!r.multiplicity_source().empty()) + puml_relation += "\"" + r.multiplicity_source() + "\" "; + + puml_relation += plantuml_common::to_plantuml(r.type(), r.style()); + + if (!r.multiplicity_destination().empty()) + puml_relation += " \"" + r.multiplicity_destination() + "\""; + + std::string target_alias; + try { + target_alias = m_model.to_alias(destination); + } + catch (...) { + LOG_DBG("Failed to find alias to {}", destination); + continue; + } + + if (m_generated_aliases.find(target_alias) == + m_generated_aliases.end()) + continue; + + relstr << c.alias() << " " << puml_relation << " " << target_alias; + + if (!r.label().empty()) { + relstr << " : " << plantuml_common::to_plantuml(r.access()) + << r.label(); + rendered_relations.emplace(r.label()); + } + + if (unique_relations.count(relstr.str()) == 0) { + unique_relations.emplace(relstr.str()); + + relstr << '\n'; + + LOG_DBG("=== Adding relation {}", relstr.str()); + + all_relations_str << relstr.str(); + } + } + catch (error::uml_alias_missing &e) { + LOG_DBG("=== Skipping {} relation from {} to {} due " + "to: {}", + plantuml_common::to_plantuml(r.type(), r.style()), + c.full_name(), destination, e.what()); + } + } + + ostr << all_relations_str.str(); +} + void generator::generate(const enum_ &e, std::ostream &ostr) const { ostr << "enum " << e.alias(); @@ -534,6 +665,20 @@ void generator::generate(const package &p, std::ostream &ostr) const } } } + else if (auto *cpt = dynamic_cast(subpackage.get()); cpt) { + if (m_model.should_include(*subpackage)) { + auto together_group = + m_config.get_together_group(cpt->full_name(false)); + if (together_group) { + together_group_stack_.group_together( + together_group.value(), cpt); + } + else { + generate_alias(*cpt, ostr); + generate(*cpt, ostr); + } + } + } } if (m_config.generate_packages()) { @@ -552,6 +697,10 @@ void generator::generate(const package &p, std::ostream &ostr) const generate_alias(*enm, ostr); generate(*enm, ostr); } + if (auto *cpt = dynamic_cast(e); cpt) { + generate_alias(*cpt, ostr); + generate(*cpt, ostr); + } } ostr << "}\n"; @@ -589,6 +738,12 @@ void generator::generate_relationships( dynamic_cast(*subpackage), ostr); } } + else if (dynamic_cast(subpackage.get()) != nullptr) { + if (m_model.should_include(*subpackage)) { + generate_relationships( + dynamic_cast(*subpackage), ostr); + } + } } } @@ -648,6 +803,20 @@ void generator::generate_top_level_elements(std::ostream &ostr) const } } } + else if (auto *cpt = dynamic_cast(p.get()); cpt) { + if (m_model.should_include(*cpt)) { + auto together_group = + m_config.get_together_group(cpt->full_name(false)); + if (together_group) { + together_group_stack_.group_together( + together_group.value(), cpt); + } + else { + generate_alias(*cpt, ostr); + generate(*cpt, ostr); + } + } + } } } @@ -666,6 +835,10 @@ void generator::generate_groups(std::ostream &ostr) const generate_alias(*enm, ostr); generate(*enm, ostr); } + if (auto *cpt = dynamic_cast(e); cpt) { + generate_alias(*cpt, ostr); + generate(*cpt, ostr); + } } ostr << "}\n"; diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.h b/src/class_diagram/generators/plantuml/class_diagram_generator.h index 063a286b..4929c7c1 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.h +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.h @@ -18,6 +18,7 @@ #pragma once #include "class_diagram/model/class.h" +#include "class_diagram/model/concept.h" #include "class_diagram/model/diagram.h" #include "class_diagram/model/enum.h" #include "class_diagram/visitor/translation_unit_visitor.h" @@ -47,6 +48,7 @@ using common_generator = using clanguml::class_diagram::model::class_; using clanguml::class_diagram::model::class_element; +using clanguml::class_diagram::model::concept_; using clanguml::class_diagram::model::enum_; using clanguml::common::model::access_t; using clanguml::common::model::package; @@ -65,6 +67,8 @@ public: void generate_alias(const enum_ &e, std::ostream &ostr) const; + void generate_alias(const concept_ &c, std::ostream &ostr) const; + void generate(const class_ &c, std::ostream &ostr) const; void generate_top_level_elements(std::ostream &ostr) const; @@ -77,6 +81,10 @@ public: void generate_relationships(const enum_ &c, std::ostream &ostr) const; + void generate(const concept_ &c, std::ostream &ostr) const; + + void generate_relationships(const concept_ &c, std::ostream &ostr) const; + void generate(const package &p, std::ostream &ostr) const; void generate_relationships(const package &p, std::ostream &ostr) const; diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 1968f85f..09eb7a66 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -126,7 +126,7 @@ int class_::calculate_template_specialization_match( { int res{}; - std::string left = name_and_ns(); + const std::string left = name_and_ns(); // TODO: handle variadic templates if ((name_and_ns() != full_name) || (templates().size() != other.templates().size())) { diff --git a/src/class_diagram/model/class_element.h b/src/class_diagram/model/class_element.h index fa6e53fc..d4161278 100644 --- a/src/class_diagram/model/class_element.h +++ b/src/class_diagram/model/class_element.h @@ -32,6 +32,8 @@ public: class_element( common::model::access_t scope, std::string name, std::string type); + virtual ~class_element() = default; + common::model::access_t access() const; std::string name() const; std::string type() const; diff --git a/src/class_diagram/model/class_member.h b/src/class_diagram/model/class_member.h index 4b760293..4a0a80cb 100644 --- a/src/class_diagram/model/class_member.h +++ b/src/class_diagram/model/class_member.h @@ -28,7 +28,7 @@ public: class_member(common::model::access_t access, const std::string &name, const std::string &type); - virtual ~class_member() = default; + ~class_member() override = default; bool is_relationship() const; void is_relationship(bool is_relationship); diff --git a/src/class_diagram/model/class_method.h b/src/class_diagram/model/class_method.h index d2e2b81e..51a6d4c2 100644 --- a/src/class_diagram/model/class_method.h +++ b/src/class_diagram/model/class_method.h @@ -34,7 +34,7 @@ public: class_method(common::model::access_t access, const std::string &name, const std::string &type); - virtual ~class_method() = default; + ~class_method() override = default; bool is_pure_virtual() const; void is_pure_virtual(bool is_pure_virtual); diff --git a/src/class_diagram/model/concept.cc b/src/class_diagram/model/concept.cc new file mode 100644 index 00000000..f090f9aa --- /dev/null +++ b/src/class_diagram/model/concept.cc @@ -0,0 +1,93 @@ +/** + * src/class_diagram/model/concept.cc + * + * 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. + */ + +#include "concept.h" +#include "method_parameter.h" + +#include + +namespace clanguml::class_diagram::model { + +concept_::concept_(const common::model::namespace_ &using_namespace) + : element{using_namespace} +{ +} + +bool operator==(const concept_ &l, const concept_ &r) +{ + return l.id() == r.id(); +} + +std::string concept_::full_name_no_ns() const +{ + using namespace clanguml::util; + + std::ostringstream ostr; + + ostr << name(); + + render_template_params(ostr, using_namespace(), false); + + return ostr.str(); +} + +std::string concept_::full_name(bool relative) const +{ + using namespace clanguml::util; + using clanguml::common::model::namespace_; + + std::ostringstream ostr; + + ostr << name_and_ns(); + + render_template_params(ostr, using_namespace(), relative); + + std::string res; + + if (relative) + res = using_namespace().relative(ostr.str()); + else + res = ostr.str(); + + if (res.empty()) + return "<>"; + + return res; +} + +void concept_::add_parameter(method_parameter mp) +{ + requires_parameters_.emplace_back(std::move(mp)); +} + +const std::vector &concept_::requires_parameters() const +{ + return requires_parameters_; +} + +void concept_::add_statement(std::string stmt) +{ + requires_statements_.emplace_back(std::move(stmt)); +} + +const std::vector &concept_::requires_statements() const +{ + return requires_statements_; +} + +} // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/concept.h b/src/class_diagram/model/concept.h new file mode 100644 index 00000000..36e23675 --- /dev/null +++ b/src/class_diagram/model/concept.h @@ -0,0 +1,71 @@ +/** + * src/class_diagram/model/concept.h + * + * 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. + */ +#pragma once + +#include "class_diagram/model/method_parameter.h" +#include "common/model/element.h" +#include "common/model/stylable_element.h" +#include "common/model/template_parameter.h" +#include "common/model/template_trait.h" +#include "common/types.h" + +#include +#include + +namespace clanguml::class_diagram::model { + +struct requires_expression { + common::model::template_parameter parameter; + std::vector requirements; +}; + +class concept_ : public common::model::element, + public common::model::stylable_element, + public common::model::template_trait { +public: + concept_(const common::model::namespace_ &using_namespace); + + concept_(const concept_ &) = delete; + concept_(concept_ &&) noexcept = default; + concept_ &operator=(const concept_ &) = delete; + concept_ &operator=(concept_ &&) = delete; + + std::string type_name() const override { return "concept"; } + + friend bool operator==(const concept_ &l, const concept_ &r); + + std::string full_name(bool relative = true) const override; + + std::string full_name_no_ns() const override; + + void add_parameter(method_parameter mp); + + const std::vector &requires_parameters() const; + + void add_statement(std::string stmt); + + const std::vector &requires_statements() const; + +private: + std::vector requires_expression_; + + std::vector requires_parameters_; + + std::vector requires_statements_; +}; +} // namespace clanguml::class_diagram::model \ No newline at end of file diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 2a365684..56b37016 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -32,6 +32,11 @@ const common::reference_vector &diagram::classes() const const common::reference_vector &diagram::enums() const { return enums_; } +const common::reference_vector &diagram::concepts() const +{ + return concepts_; +} + common::model::diagram_t diagram::type() const { return common::model::diagram_t::kClass; @@ -48,6 +53,11 @@ common::optional_ref diagram::get( res = get_enum(full_name); + if (res.has_value()) + return res; + + res = get_concept(full_name); + return res; } @@ -63,6 +73,11 @@ common::optional_ref diagram::get( res = get_enum(id); + if (res.has_value()) + return res; + + res = get_concept(id); + return res; } @@ -78,6 +93,12 @@ bool diagram::has_enum(const enum_ &e) const [&e](const auto &ee) { return ee.get().full_name() == e.full_name(); }); } +bool diagram::has_concept(const concept_ &c) const +{ + return std::any_of(concepts_.cbegin(), concepts_.cend(), + [&c](const auto &cc) { return cc.get() == c; }); +} + common::optional_ref diagram::get_class(const std::string &name) const { for (const auto &c : classes_) { @@ -126,6 +147,32 @@ common::optional_ref diagram::get_enum( return {}; } +common::optional_ref diagram::get_concept( + const std::string &name) const +{ + for (const auto &c : concepts_) { + const auto full_name = c.get().full_name(false); + + if (full_name == name) { + return {c}; + } + } + + return {}; +} + +common::optional_ref diagram::get_concept( + clanguml::common::model::diagram_element::id_t id) const +{ + for (const auto &c : concepts_) { + if (c.get().id() == id) { + return {c}; + } + } + + return {}; +} + bool diagram::add_package(std::unique_ptr &&p) { LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true)); @@ -173,8 +220,8 @@ bool diagram::add_class(std::unique_ptr &&c) } } catch (const std::runtime_error &e) { - LOG_WARN("Cannot add template specialization {} with id {} due to: {}", - name, id, e.what()); + LOG_WARN( + "Cannot add concept {} with id {} due to: {}", name, id, e.what()); return false; } @@ -207,6 +254,55 @@ bool diagram::add_enum(std::unique_ptr &&e) return false; } +bool diagram::add_concept(std::unique_ptr &&c) +{ + const auto base_name = c->name(); + const auto full_name = c->full_name(false); + + LOG_DBG("Adding concept: {}::{}, {}", c->get_namespace().to_string(), + base_name, full_name); + + if (util::contains(base_name, "::")) + throw std::runtime_error("Name cannot contain namespace: " + base_name); + + if (util::contains(base_name, "*")) + throw std::runtime_error("Name cannot contain *: " + base_name); + + const auto ns = c->get_relative_namespace(); + auto name = base_name; + auto name_with_ns = c->name_and_ns(); + auto name_and_ns = ns | name; + auto &cc = *c; + auto id = cc.id(); + + try { + if (!has_concept(cc)) { + if (add_element(ns, std::move(c))) + concepts_.push_back(std::ref(cc)); + + const auto &el = get_element(name_and_ns).value(); + + if ((el.name() != name) || !(el.get_relative_namespace() == ns)) + throw std::runtime_error( + "Invalid element stored in the diagram tree"); + + LOG_DBG("Added concept {} ({} - [{}])", base_name, full_name, id); + + return true; + } + } + catch (const std::runtime_error &e) { + LOG_WARN( + "Cannot add concept {} with id {} due to: {}", name, id, e.what()); + return false; + } + + LOG_DBG("Concept {} ({} - [{}]) already in the model", base_name, full_name, + id); + + return false; +} + void diagram::get_parents( clanguml::common::reference_set &parents) const { @@ -257,6 +353,11 @@ std::string diagram::to_alias( return e.get().alias(); } + for (const auto &c : concepts_) { + if (c.get().id() == id) + return c.get().alias(); + } + throw error::uml_alias_missing(fmt::format("Missing alias for {}", id)); } diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 61ffee1a..cb0e39b5 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -22,6 +22,7 @@ #include "common/model/nested_trait.h" #include "common/model/package.h" #include "common/types.h" +#include "concept.h" #include "enum.h" #include @@ -54,10 +55,14 @@ public: const common::reference_vector &enums() const; + const common::reference_vector &concepts() const; + bool has_class(const class_ &c) const; bool has_enum(const enum_ &e) const; + bool has_concept(const concept_ &e) const; + common::optional_ref get_class(const std::string &name) const; common::optional_ref get_class( @@ -68,10 +73,17 @@ public: common::optional_ref get_enum( clanguml::common::model::diagram_element::id_t id) const; + common::optional_ref get_concept(const std::string &name) const; + + common::optional_ref get_concept( + clanguml::common::model::diagram_element::id_t id) const; + bool add_class(std::unique_ptr &&c); bool add_enum(std::unique_ptr &&e); + bool add_concept(std::unique_ptr &&e); + bool add_package(std::unique_ptr &&p); std::string to_alias( @@ -90,6 +102,8 @@ private: common::reference_vector classes_; common::reference_vector enums_; + + common::reference_vector concepts_; }; } // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/method_parameter.cc b/src/class_diagram/model/method_parameter.cc index f072344c..e6a66a60 100644 --- a/src/class_diagram/model/method_parameter.cc +++ b/src/class_diagram/model/method_parameter.cc @@ -22,6 +22,14 @@ namespace clanguml::class_diagram::model { +method_parameter::method_parameter( + std::string type, std::string name, std::string default_value) + : type_{std::move(type)} + , name_{std::move(name)} + , default_value_{std::move(default_value)} +{ +} + void method_parameter::set_type(const std::string &type) { type_ = type; } std::string method_parameter::type() const { return type_; } @@ -43,10 +51,14 @@ std::string method_parameter::to_string( using namespace clanguml::util; auto type_ns = using_namespace.relative(common::model::namespace_{type()}.to_string()); - if (default_value().empty()) - return fmt::format("{} {}", type_ns, name()); - return fmt::format("{} {} = {}", type_ns, name(), default_value()); + auto name_ns = + using_namespace.relative(common::model::namespace_{name()}.to_string()); + + if (default_value().empty()) + return fmt::format("{} {}", type_ns, name_ns); + + return fmt::format("{} {} = {}", type_ns, name_ns, 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 acd7ca73..2709dcc6 100644 --- a/src/class_diagram/model/method_parameter.h +++ b/src/class_diagram/model/method_parameter.h @@ -27,6 +27,10 @@ namespace clanguml::class_diagram::model { class method_parameter : public common::model::decorated_element { public: + method_parameter() = default; + method_parameter( + std::string type, std::string name, std::string default_value = {}); + void set_type(const std::string &type); std::string type() const; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 62ddcafa..dfec7160 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -19,6 +19,7 @@ #include "translation_unit_visitor.h" #include "common/clang_utils.h" +#include #include #include #include @@ -206,11 +207,11 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( // Process template specialization bases process_class_bases(cls, template_specialization); - if (get_ast_local_id(cls->getSpecializedTemplate()->getID()).has_value()) + const auto maybe_id = + get_ast_local_id(cls->getSpecializedTemplate()->getID()); + if (maybe_id.has_value()) template_specialization.add_relationship( - {relationship_t::kInstantiation, - get_ast_local_id(cls->getSpecializedTemplate()->getID()) - .value()}); + {relationship_t::kInstantiation, maybe_id.value()}); if (diagram_.should_include(template_specialization)) { const auto name = template_specialization.full_name(false); @@ -284,7 +285,7 @@ bool translation_unit_visitor::VisitClassTemplateDecl( // 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(*cls, *c_ptr, *c_ptr); const auto cls_full_name = c_ptr->full_name(false); const auto id = common::to_id(cls_full_name); @@ -293,6 +294,16 @@ bool translation_unit_visitor::VisitClassTemplateDecl( set_ast_local_id(cls->getID(), id); + constexpr auto kMaxConstraintCount = 24U; + llvm::SmallVector constraints{}; + if (cls->hasAssociatedConstraints()) { + cls->getAssociatedConstraints(constraints); + } + + for (const auto *expr : constraints) { + find_relationships_in_constraint_expression(*c_ptr, expr); + } + if (!cls->getTemplatedDecl()->isCompleteDefinition()) { forward_declarations_.emplace(id, std::move(c_ptr)); return true; @@ -316,7 +327,7 @@ bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec) if (source_manager().isInSystemHeader(rec->getSourceRange().getBegin())) return true; - if (clang::dyn_cast_or_null(rec)) + if (clang::dyn_cast_or_null(rec) != nullptr) // This is handled by VisitCXXRecordDecl() return true; @@ -367,13 +378,297 @@ bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec) return true; } +bool translation_unit_visitor::TraverseConceptDecl(clang::ConceptDecl *cpt) +{ + // Skip system headers + if (source_manager().isInSystemHeader(cpt->getSourceRange().getBegin())) + return true; + + if (!diagram().should_include(cpt->getQualifiedNameAsString())) + return true; + + LOG_DBG("= Visiting concept (isType: {}) declaration {} at {}", + cpt->isTypeConcept(), cpt->getQualifiedNameAsString(), + cpt->getLocation().printToString(source_manager())); + + auto concept_model = create_concept_declaration(cpt); + + if (!concept_model) + return true; + + const auto concept_id = concept_model->id(); + + set_ast_local_id(cpt->getID(), concept_id); + + process_template_parameters(*cpt, *concept_model); + + constexpr auto kMaxConstraintCount = 24U; + llvm::SmallVector constraints{}; + if (cpt->hasAssociatedConstraints()) { + cpt->getAssociatedConstraints(constraints); + } + + for (const auto *expr : constraints) { + find_relationships_in_constraint_expression(*concept_model, expr); + } + + if (cpt->getConstraintExpr() != nullptr) { + process_constraint_requirements( + cpt, cpt->getConstraintExpr(), *concept_model); + + find_relationships_in_constraint_expression( + *concept_model, cpt->getConstraintExpr()); + } + + if (diagram_.should_include(*concept_model)) { + LOG_DBG("Adding concept {} with id {}", concept_model->full_name(false), + concept_model->id()); + + diagram_.add_concept(std::move(concept_model)); + } + else { + LOG_DBG("Skipping concept {} with id {}", concept_model->full_name(), + concept_model->id()); + } + + return true; +} + +void translation_unit_visitor::process_constraint_requirements( + const clang::ConceptDecl *cpt, const clang::Expr *expr, + model::concept_ &concept_model) const +{ + if (const auto *constraint = llvm::dyn_cast(expr); + constraint) { + + auto constraint_source = common::to_string(constraint); + + LOG_DBG("== Processing constraint: '{}'", constraint_source); + + for (const auto *requirement : constraint->getRequirements()) { + LOG_DBG("== Processing requirement: '{}'", requirement->getKind()); + } + + // process 'requires (...)' declaration + for (const auto *decl : constraint->getBody()->decls()) { + if (const auto *parm_var_decl = + llvm::dyn_cast(decl); + parm_var_decl) { + parm_var_decl->getQualifiedNameAsString(); + + auto param_name = parm_var_decl->getQualifiedNameAsString(); + auto param_type = common::to_string( + parm_var_decl->getType(), cpt->getASTContext()); + + LOG_DBG("=== Processing parameter variable declaration: {}, {}", + param_name, param_type); + + concept_model.add_parameter( + {std::move(param_type), std::move(param_name)}); + } + else { + LOG_DBG("=== Processing some other concept declaration: {}", + decl->getID()); + } + } + + // process concept body requirements '{ }' if any + for (const auto *req : constraint->getRequirements()) { + if (req->getKind() == clang::concepts::Requirement::RK_Simple) { + const auto *simple_req = + llvm::dyn_cast(req); + + if (simple_req != nullptr) { + util::apply_if_not_null( + simple_req->getExpr(), [&concept_model](const auto *e) { + auto simple_expr = common::to_string(e); + + LOG_DBG("=== Processing expression requirement: {}", + simple_expr); + + concept_model.add_statement(std::move(simple_expr)); + }); + } + } + else if (req->getKind() == clang::concepts::Requirement::RK_Type) { + util::apply_if_not_null( + llvm::dyn_cast(req), + [&concept_model, cpt](const auto *t) { + auto type_name = common::to_string( + t->getType()->getType(), cpt->getASTContext()); + + LOG_DBG( + "=== Processing type requirement: {}", type_name); + + concept_model.add_statement(std::move(type_name)); + }); + } + else if (req->getKind() == + clang::concepts::Requirement::RK_Nested) { + const auto *nested_req = + llvm::dyn_cast(req); + + if (nested_req != nullptr) { + util::apply_if_not_null( + nested_req->getConstraintExpr(), [](const auto *e) { + LOG_DBG("=== Processing nested requirement: {}", + common::to_string(e)); + }); + } + } + else if (req->getKind() == + clang::concepts::Requirement::RK_Compound) { + const auto *compound_req = + llvm::dyn_cast(req); + + if (compound_req != nullptr) { + const auto *compound_expr_ptr = compound_req->getExpr(); + + if (compound_expr_ptr != nullptr) { + auto compound_expr = + common::to_string(compound_expr_ptr); + + auto req_return_type = + compound_req->getReturnTypeRequirement(); + + if (!req_return_type.isEmpty()) { + compound_expr = + fmt::format("{{{}}} -> {}", compound_expr, + common::to_string( + req_return_type.getTypeConstraint())); + } + else if (compound_req->hasNoexceptRequirement()) { + compound_expr = + fmt::format("{{{}}} noexcept", compound_expr); + } + + LOG_DBG("=== Processing compound requirement: {}", + compound_expr); + + concept_model.add_statement(std::move(compound_expr)); + } + } + } + } + } + else if (const auto *binop = llvm::dyn_cast(expr); + binop) { + process_constraint_requirements(cpt, binop->getLHS(), concept_model); + process_constraint_requirements(cpt, binop->getRHS(), concept_model); + } + else if (const auto *unop = llvm::dyn_cast(expr); + unop) { + process_constraint_requirements(cpt, unop->getSubExpr(), concept_model); + } +} + +void translation_unit_visitor::find_relationships_in_constraint_expression( + clanguml::common::model::element &c, const clang::Expr *expr) +{ + if (expr == nullptr) + return; + found_relationships_t relationships; + + common::if_dyn_cast(expr, [&](const auto *ul) { + for (const auto ta : ul->template_arguments()) { + find_relationships(ta.getArgument().getAsType(), relationships, + relationship_t::kConstraint); + } + }); + + common::if_dyn_cast( + expr, [&](const auto *cs) { + process_concept_specialization_relationships(c, cs); + }); + + common::if_dyn_cast(expr, [&](const auto *re) { + // TODO + }); + + common::if_dyn_cast(expr, [&](const auto *op) { + find_relationships_in_constraint_expression(c, op->getLHS()); + find_relationships_in_constraint_expression(c, op->getRHS()); + }); + + common::if_dyn_cast(expr, [&](const auto *op) { + find_relationships_in_constraint_expression(c, op->getSubExpr()); + }); + + for (const auto &[type_element_id, relationship_type] : relationships) { + if (type_element_id != c.id() && + (relationship_type != relationship_t::kNone)) { + + relationship r{relationship_type, type_element_id}; + + c.add_relationship(std::move(r)); + } + } +} + +void translation_unit_visitor::process_concept_specialization_relationships( + common::model::element &c, + const clang::ConceptSpecializationExpr *concept_specialization) +{ + + if (const auto *cpt = concept_specialization->getNamedConcept(); + should_include(cpt)) { + + const auto cpt_name = cpt->getNameAsString(); + + const auto maybe_id = get_ast_local_id(cpt->getID()); + if (!maybe_id) + return; + + const auto target_id = maybe_id.value(); + + std::vector constrained_template_params; + + size_t argument_index{}; + for (const auto ta : concept_specialization->getTemplateArguments()) { + + if (ta.getKind() == clang::TemplateArgument::Type) { + auto type_name = + common::to_string(ta.getAsType(), cpt->getASTContext()); + extract_constrained_template_param_name(concept_specialization, + cpt, constrained_template_params, argument_index, + type_name); + } + else if (ta.getKind() == clang::TemplateArgument::Pack) { + if (!ta.getPackAsArray().empty() && + ta.getPackAsArray().front().isPackExpansion()) { + const auto &pack_head = + ta.getPackAsArray().front().getAsType(); + auto type_name = + common::to_string(pack_head, cpt->getASTContext()); + extract_constrained_template_param_name( + concept_specialization, cpt, + constrained_template_params, argument_index, type_name); + } + } + else { + auto type_name = + common::to_string(ta.getAsType(), cpt->getASTContext()); + LOG_DBG( + "=== Unsupported concept type parameter: {}", type_name); + } + argument_index++; + } + + if (!constrained_template_params.empty()) + c.add_relationship( + {relationship_t::kConstraint, target_id, access_t::kNone, + fmt::format( + "{}", fmt::join(constrained_template_params, ","))}); + } +} + bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) { // Skip system headers if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) return true; - if (!diagram().should_include(cls->getQualifiedNameAsString())) + if (!should_include(cls)) return true; LOG_DBG("= Visiting class declaration {} at {}", @@ -440,19 +735,46 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) return true; } +std::unique_ptr +translation_unit_visitor::create_concept_declaration(clang::ConceptDecl *cpt) +{ + assert(cpt != nullptr); + + if (!should_include(cpt)) + return {}; + + auto concept_ptr{ + std::make_unique(config_.using_namespace())}; + auto &concept_model = *concept_ptr; + + auto ns = common::get_template_namespace(*cpt); + + concept_model.set_name(cpt->getNameAsString()); + concept_model.set_namespace(ns); + concept_model.set_id(common::to_id(concept_model.full_name(false))); + + process_comment(*cpt, concept_model); + set_source_location(*cpt, concept_model); + + if (concept_model.skip()) + return {}; + + concept_model.set_style(concept_model.style_spec()); + + return concept_ptr; +} + std::unique_ptr translation_unit_visitor::create_record_declaration( clang::RecordDecl *rec) { assert(rec != nullptr); + if (!should_include(rec)) + return {}; + auto record_ptr{std::make_unique(config_.using_namespace())}; auto &record = *record_ptr; - auto qualified_name = rec->getQualifiedNameAsString(); - - if (!diagram().should_include(qualified_name)) - return {}; - process_record_parent(rec, record, namespace_{}); if (!record.is_nested()) { @@ -481,16 +803,13 @@ std::unique_ptr translation_unit_visitor::create_class_declaration( { assert(cls != nullptr); + if (!should_include(cls)) + return {}; + auto c_ptr{std::make_unique(config_.using_namespace())}; auto &c = *c_ptr; - // TODO: refactor to method get_qualified_name() - auto qualified_name = cls->getQualifiedNameAsString(); - - if (!diagram().should_include(qualified_name)) - return {}; - - auto ns = common::get_tag_namespace(*cls); + auto ns{common::get_tag_namespace(*cls)}; process_record_parent(cls, c, ns); @@ -531,7 +850,8 @@ void translation_unit_visitor::process_record_parent( // regular class id_opt = get_ast_local_id(local_id); - // If not, check if the parent template declaration is in the model + // If not, check if the parent template declaration is in the + // model if (!id_opt) { if (parent_record_decl->getDescribedTemplate() != nullptr) { local_id = @@ -600,9 +920,10 @@ void translation_unit_visitor::process_class_declaration( bool translation_unit_visitor::process_template_parameters( const clang::TemplateDecl &template_declaration, - common::model::template_trait &c) + common::model::template_trait &c, + common::optional_ref templated_element) { - LOG_DBG("Processing class {} template parameters...", + LOG_DBG("Processing {} template parameters...", common::get_qualified_name(template_declaration)); if (template_declaration.getTemplateParameters() == nullptr) @@ -621,6 +942,25 @@ bool translation_unit_visitor::process_template_parameters( ct.set_default_value(""); ct.is_variadic(template_type_parameter->isParameterPack()); + if (template_type_parameter->getTypeConstraint() != nullptr) { + util::apply_if_not_null( + template_type_parameter->getTypeConstraint() + ->getNamedConcept(), + [this, &ct, &templated_element]( + const clang::ConceptDecl *named_concept) mutable { + ct.set_concept_constraint( + named_concept->getQualifiedNameAsString()); + if (templated_element && + should_include(named_concept)) { + templated_element.value().add_relationship( + {relationship_t::kConstraint, + get_ast_local_id(named_concept->getID()) + .value(), + access_t::kNone, ct.name()}); + } + }); + } + c.add_template(std::move(ct)); } else if (clang::dyn_cast_or_null( @@ -665,7 +1005,7 @@ void translation_unit_visitor::process_template_record_containment( { assert(record.getParent()->isRecord()); - const auto *parent = record.getParent(); //->getOuterLexicalRecordContext(); + const auto *parent = record.getParent(); if (parent != nullptr) { if (const auto *record_decl = @@ -903,9 +1243,7 @@ void translation_unit_visitor::process_friend( // TODO: handle template friend } else if (friend_type->getAs() != nullptr) { - const auto friend_type_name = - friend_type->getAsRecordDecl()->getQualifiedNameAsString(); - if (diagram().should_include(friend_type_name)) { + if (should_include(friend_type->getAsRecordDecl())) { relationship r{relationship_t::kFriendship, common::to_id(*friend_type->getAsRecordDecl()), common::access_specifier_to_access_t(f.getAccess()), @@ -1033,11 +1371,7 @@ void translation_unit_visitor:: const auto &template_specialization_model = diagram().classes().back(); - const auto template_field_decl_name = - deduced_auto_decl->getQualifiedNameAsString(); - - if (diagram().should_include(template_field_decl_name)) { - + if (should_include(deduced_auto_decl)) { relationship r{relationship_t::kDependency, template_specialization_model.get().id()}; @@ -1121,6 +1455,16 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, type->getAs(); if (type_instantiation_decl != nullptr) { + // If this template should be included in the diagram + // add it - and then process recursively its arguments + if (should_include(type_instantiation_decl->getTemplateName() + .getAsTemplateDecl())) { + relationships.emplace_back( + type_instantiation_decl->getTemplateName() + .getAsTemplateDecl() + ->getID(), + relationship_hint); + } for (const auto &template_argument : *type_instantiation_decl) { const auto template_argument_kind = template_argument.getKind(); if (template_argument_kind == @@ -1164,7 +1508,7 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, } } } - else if (type->getAsCXXRecordDecl()) { + else if (type->getAsCXXRecordDecl() != nullptr) { const auto target_id = common::to_id(*type->getAsCXXRecordDecl()); relationships.emplace_back(target_id, relationship_hint); result = true; @@ -1289,33 +1633,30 @@ void translation_unit_visitor:: const std::set & /*template_parameter_names*/, const clang::TemplateSpecializationType &template_instantiation_type) { - const auto template_field_decl_name = - template_instantiation_type.getTemplateName() - .getAsTemplateDecl() - ->getQualifiedNameAsString(); + if (!should_include( + template_instantiation_type.getTemplateName().getAsTemplateDecl())) + return; auto template_specialization_ptr = - build_template_instantiation(template_instantiation_type); + build_template_instantiation(template_instantiation_type, &c); - if (diagram().should_include(template_field_decl_name)) { - if (template_instantiation_type.isDependentType()) { - if (template_specialization_ptr) { - relationship r{relationship_t::kDependency, - template_specialization_ptr->id()}; + if (template_instantiation_type.isDependentType()) { + if (template_specialization_ptr) { + relationship r{ + relationship_t::kDependency, template_specialization_ptr->id()}; - c.add_relationship(std::move(r)); - } + c.add_relationship(std::move(r)); } - else { - if (template_specialization_ptr) { - relationship r{relationship_t::kDependency, - template_specialization_ptr->id()}; + } + else { + if (template_specialization_ptr) { + relationship r{ + relationship_t::kDependency, template_specialization_ptr->id()}; - if (!diagram().has_element(template_specialization_ptr->id())) - diagram().add_class(std::move(template_specialization_ptr)); + if (!diagram().has_element(template_specialization_ptr->id())) + diagram().add_class(std::move(template_specialization_ptr)); - c.add_relationship(std::move(r)); - } + c.add_relationship(std::move(r)); } } } @@ -2189,14 +2530,15 @@ bool translation_unit_visitor::build_template_instantiation_add_base_classes( } } - if (add_template_argument_as_base_class && ct.id()) { + const auto maybe_id = ct.id(); + if (add_template_argument_as_base_class && maybe_id) { LOG_DBG("Adding template argument as base class '{}'", ct.to_string({}, false)); class_parent cp; cp.set_access(access_t::kPublic); cp.set_name(ct.to_string({}, false)); - cp.set_id(ct.id().value()); + cp.set_id(maybe_id.value()); tinst.add_parent(std::move(cp)); } @@ -2395,16 +2737,27 @@ void translation_unit_visitor::resolve_local_to_global_ids() 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(); - if (get_ast_local_id(maybe_local_id)) { + const auto maybe_id = get_ast_local_id(rel.destination()); + if (maybe_id) { LOG_DBG("= Resolved instantiation destination from local " "id {} to global id {}", - maybe_local_id, *get_ast_local_id(maybe_local_id)); - rel.set_destination(*get_ast_local_id(maybe_local_id)); + rel.destination(), *maybe_id); + rel.set_destination(*maybe_id); } } } } + for (const auto &cpt : diagram().concepts()) { + for (auto &rel : cpt.get().relationships()) { + const auto maybe_id = get_ast_local_id(rel.destination()); + if (maybe_id) { + LOG_DBG("= Resolved instantiation destination from local " + "id {} to global id {}", + rel.destination(), *maybe_id); + rel.set_destination(*maybe_id); + } + } + } } void translation_unit_visitor::finalize() @@ -2440,4 +2793,38 @@ translation_unit_visitor::get_ast_local_id(int64_t local_id) const return local_ast_id_map_.at(local_id); } + +void translation_unit_visitor::extract_constrained_template_param_name( + const clang::ConceptSpecializationExpr *concept_specialization, + const clang::ConceptDecl *cpt, + std::vector &constrained_template_params, + size_t argument_index, std::string &type_name) const +{ + const auto full_declaration_text = common::get_source_text_raw( + concept_specialization->getSourceRange(), source_manager()); + + if (!full_declaration_text.empty()) { + // Handle typename constraint in requires clause + if (type_name.find("type-parameter-") == 0) { + const auto concept_declaration_text = full_declaration_text.substr( + full_declaration_text.find(cpt->getNameAsString()) + + cpt->getNameAsString().size() + 1); + + auto template_params = common::parse_unexposed_template_params( + concept_declaration_text, [](const auto &t) { return t; }); + + if (template_params.size() > argument_index) + type_name = template_params[argument_index].to_string( + config().using_namespace(), false); + } + constrained_template_params.push_back(type_name); + } +} + +bool translation_unit_visitor::should_include(const clang::NamedDecl *decl) +{ + return decl != nullptr && + diagram().should_include(decl->getQualifiedNameAsString()); +} + } // 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 d275c2e8..351ac8a1 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -18,6 +18,7 @@ #pragma once #include "class_diagram/model/class.h" +#include "class_diagram/model/concept.h" #include "class_diagram/model/diagram.h" #include "common/model/enums.h" #include "common/model/template_trait.h" @@ -83,6 +84,8 @@ public: virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls); + virtual bool TraverseConceptDecl(clang::ConceptDecl *cpt); + /** * @brief Get diagram model reference * @@ -108,12 +111,17 @@ public: void finalize(); private: + bool should_include(const clang::NamedDecl *decl); + std::unique_ptr create_class_declaration(clang::CXXRecordDecl *cls); std::unique_ptr create_record_declaration(clang::RecordDecl *rec); + std::unique_ptr + create_concept_declaration(clang::ConceptDecl *cpt); + void process_class_declaration(const clang::CXXRecordDecl &cls, clanguml::class_diagram::model::class_ &c); @@ -134,7 +142,8 @@ private: bool process_template_parameters( const clang::TemplateDecl &template_declaration, - clanguml::common::model::template_trait &t); + clanguml::common::model::template_trait &t, + common::optional_ref templated_element = {}); void process_template_specialization_argument( const clang::ClassTemplateSpecializationDecl *cls, @@ -242,6 +251,9 @@ private: const std::set &template_parameter_names, const clang::TemplateSpecializationType &template_instantiation_type); + void find_relationships_in_constraint_expression( + clanguml::common::model::element &c, const clang::Expr *expr); + void process_unexposed_template_specialization_parameters( const std::string &tspec, clanguml::common::model::template_parameter &tp, @@ -258,6 +270,18 @@ private: bool simplify_system_template(common::model::template_parameter &ct, const std::string &full_name) const; + void process_constraint_requirements(const clang::ConceptDecl *cpt, + const clang::Expr *expr, model::concept_ &concept_model) const; + + void process_concept_specialization_relationships(common::model::element &c, + const clang::ConceptSpecializationExpr *concept_specialization); + + void extract_constrained_template_param_name( + const clang::ConceptSpecializationExpr *concept_specialization, + const clang::ConceptDecl *cpt, + std::vector &constrained_template_params, + size_t argument_index, std::string &type_name) const; + /// Store the mapping from local clang entity id (obtained using /// getID()) method to clang-uml global id void set_ast_local_id( diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index ce94c1ff..33b088f5 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -90,6 +90,14 @@ model::namespace_ get_tag_namespace(const clang::TagDecl &declaration) return ns; } +model::namespace_ get_template_namespace(const clang::TemplateDecl &declaration) +{ + model::namespace_ ns{declaration.getQualifiedNameAsString()}; + ns.pop_back(); + + return ns; +} + std::string get_tag_name(const clang::TagDecl &declaration) { auto base_name = declaration.getNameAsString(); @@ -176,7 +184,7 @@ std::string to_string(const clang::RecordType &type, std::string to_string(const clang::Expr *expr) { - clang::LangOptions lang_options; + const clang::LangOptions lang_options; std::string result; llvm::raw_string_ostream ostream(result); expr->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options)); @@ -186,7 +194,7 @@ std::string to_string(const clang::Expr *expr) std::string to_string(const clang::Stmt *stmt) { - clang::LangOptions lang_options; + const clang::LangOptions lang_options; std::string result; llvm::raw_string_ostream ostream(result); stmt->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options)); @@ -220,6 +228,21 @@ std::string to_string(const clang::FunctionTemplateDecl *decl) fmt::join(template_parameters, ","), ""); } +std::string to_string(const clang::TypeConstraint *tc) +{ + if (tc == nullptr) + return {}; + + const clang::PrintingPolicy print_policy( + tc->getNamedConcept()->getASTContext().getLangOpts()); + + std::string ostream_buf; + llvm::raw_string_ostream ostream{ostream_buf}; + tc->print(ostream, print_policy); + + return ostream.str(); +} + std::string get_source_text_raw( clang::SourceRange range, const clang::SourceManager &sm) { @@ -231,7 +254,7 @@ std::string get_source_text_raw( std::string get_source_text( clang::SourceRange range, const clang::SourceManager &sm) { - clang::LangOptions lo; + const clang::LangOptions lo; auto start_loc = sm.getSpellingLoc(range.getBegin()); auto last_token_loc = sm.getSpellingLoc(range.getEnd()); @@ -363,8 +386,7 @@ std::vector parse_unexposed_template_params( nested_params_str, ns_resolve, depth + 1); if (nested_params.empty()) - nested_params.emplace_back( - template_parameter{nested_params_str}); + nested_params.emplace_back(nested_params_str); it = bracket_match_end - 1; } diff --git a/src/common/clang_utils.h b/src/common/clang_utils.h index 6e40ee6d..4cc0d547 100644 --- a/src/common/clang_utils.h +++ b/src/common/clang_utils.h @@ -72,6 +72,9 @@ template std::string get_qualified_name(const T &declaration) model::namespace_ get_tag_namespace(const clang::TagDecl &declaration); +model::namespace_ get_template_namespace( + const clang::TemplateDecl &declaration); + std::optional get_enclosing_namespace( const clang::DeclContext *decl); @@ -87,6 +90,8 @@ std::string to_string(const clang::Stmt *stmt); std::string to_string(const clang::FunctionTemplateDecl *decl); +std::string to_string(const clang::TypeConstraint *tc); + std::string get_source_text_raw( clang::SourceRange range, const clang::SourceManager &sm); @@ -144,4 +149,16 @@ std::vector parse_unexposed_template_params( const std::string ¶ms, const std::function &ns_resolve, int depth = 0); + +template +void if_dyn_cast(P pointer, F &&func) +{ + if (pointer == nullptr) + return; + + if (const auto *dyn_cast_value = clang::dyn_cast(pointer); + dyn_cast_value) { + std::forward(func)(dyn_cast_value); + } +} } // namespace clanguml::common diff --git a/src/common/generators/nested_element_stack.h b/src/common/generators/nested_element_stack.h index e8005561..c18770c9 100644 --- a/src/common/generators/nested_element_stack.h +++ b/src/common/generators/nested_element_stack.h @@ -27,7 +27,6 @@ template class nested_element_stack { public: nested_element_stack(bool is_flat) : is_flat_{is_flat} - , current_level_{0} { current_level_groups_.push_back({}); } @@ -66,7 +65,7 @@ public: private: bool is_flat_; - uint32_t current_level_; + uint32_t current_level_{0}; std::vector>> current_level_groups_; }; diff --git a/src/common/generators/plantuml/generator.cc b/src/common/generators/plantuml/generator.cc index e83e8de9..c384ddd2 100644 --- a/src/common/generators/plantuml/generator.cc +++ b/src/common/generators/plantuml/generator.cc @@ -37,6 +37,8 @@ std::string to_plantuml(relationship_t r, const std::string &style) return style.empty() ? "<.." : fmt::format("<.[{}].", style); case relationship_t::kDependency: return style.empty() ? "..>" : fmt::format(".[{}].>", style); + case relationship_t::kConstraint: + return style.empty() ? "..>" : fmt::format(".[{}].>", style); case relationship_t::kAlias: return style.empty() ? ".." : fmt::format(".[{}].", style); default: diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index 6f548d19..6fd4d0cc 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -212,8 +212,9 @@ inja::json generator::element_context(const E &e) const ctx["element"]["source"]["line"] = e.line(); } - if (e.comment().has_value()) { - ctx["element"]["comment"] = e.comment().value(); + const auto maybe_comment = e.comment(); + if (maybe_comment) { + ctx["element"]["comment"] = maybe_comment.value(); } return ctx; diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index dc14a503..a4bd4ec2 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -492,12 +492,12 @@ void diagram_filter::init_filters(const config::diagram &c) std::vector dependencies; for (auto &&path : c.include().dependants) { - std::filesystem::path dep_path{path}; + const std::filesystem::path dep_path{path}; dependants.emplace_back(dep_path.lexically_normal().string()); } for (auto &&path : c.include().dependencies) { - std::filesystem::path dep_path{path}; + const std::filesystem::path dep_path{path}; dependencies.emplace_back(dep_path.lexically_normal().string()); } diff --git a/src/common/model/element.cc b/src/common/model/element.cc index 9ff0b19e..57a0994c 100644 --- a/src/common/model/element.cc +++ b/src/common/model/element.cc @@ -45,8 +45,8 @@ inja::json element::context() const ctx["alias"] = alias(); ctx["full_name"] = full_name(false); ctx["namespace"] = get_namespace().to_string(); - if (comment().has_value()) { - ctx["comment"] = comment().value(); + if (const auto maybe_comment = comment(); maybe_comment.has_value()) { + ctx["comment"] = maybe_comment.value(); } return ctx; diff --git a/src/common/model/enums.cc b/src/common/model/enums.cc index a0b66ffc..feeb740c 100644 --- a/src/common/model/enums.cc +++ b/src/common/model/enums.cc @@ -46,6 +46,8 @@ std::string to_string(relationship_t r) return "dependency"; case relationship_t::kAlias: return "alias"; + case relationship_t::kConstraint: + return "constraint"; default: assert(false); return ""; diff --git a/src/common/model/enums.h b/src/common/model/enums.h index e8c0779b..399c812f 100644 --- a/src/common/model/enums.h +++ b/src/common/model/enums.h @@ -23,7 +23,7 @@ namespace clanguml::common::model { enum class diagram_t { kClass, kSequence, kPackage, kInclude }; -enum class access_t { kPublic, kProtected, kPrivate }; +enum class access_t { kPublic, kProtected, kPrivate, kNone }; enum class relationship_t { kNone, @@ -36,7 +36,8 @@ enum class relationship_t { kInstantiation, kFriendship, kAlias, - kDependency + kDependency, + kConstraint }; /// Types of sequence diagram activity elements diff --git a/src/common/model/path.h b/src/common/model/path.h index cda01bd5..5bf0d030 100644 --- a/src/common/model/path.h +++ b/src/common/model/path.h @@ -45,7 +45,10 @@ public: std::copy(begin, end, std::back_inserter(path_)); } - path(const path &right) { path_ = right.path_; } + path(const path &right) + : path_{right.path_} + { + } path &operator=(const path &right) = default; diff --git a/src/common/model/template_parameter.cc b/src/common/model/template_parameter.cc index ed276b6c..61e58d30 100644 --- a/src/common/model/template_parameter.cc +++ b/src/common/model/template_parameter.cc @@ -138,6 +138,8 @@ std::string template_parameter::to_string( { using clanguml::common::model::namespace_; + assert(!(!type().empty() && concept_constraint().has_value())); + std::string res; if (!type().empty()) { if (!relative) @@ -146,9 +148,21 @@ std::string template_parameter::to_string( res += namespace_{type()}.relative_to(using_namespace).to_string(); } + const auto &maybe_concept_constraint = concept_constraint(); + + if (maybe_concept_constraint) { + if (!relative) + res += namespace_{maybe_concept_constraint.value()}.to_string(); + else + res += namespace_{maybe_concept_constraint.value()} + .relative_to(using_namespace) + .to_string(); + } + if (!name().empty()) { - if (!type().empty()) + if (!type().empty() || maybe_concept_constraint) res += " "; + if (!relative) res += namespace_{name()}.to_string(); else @@ -158,6 +172,7 @@ std::string template_parameter::to_string( // Render nested template params if (!template_params_.empty()) { std::vector params; + params.reserve(template_params_.size()); for (const auto &template_param : template_params_) { params.push_back( template_param.to_string(using_namespace, relative)); @@ -186,8 +201,9 @@ bool template_parameter::find_nested_relationships( // If this type argument should be included in the relationship // just add it and skip recursion (e.g. this is a user defined type) if (should_include(name())) { - if (id()) { - nested_relationships.emplace_back(id().value(), hint); + const auto maybe_id = id(); + if (maybe_id) { + nested_relationships.emplace_back(maybe_id.value(), hint); added_aggregation_relationship = (hint == common::model::relationship_t::kAggregation); } @@ -196,11 +212,10 @@ bool template_parameter::find_nested_relationships( // interested what is stored inside it else { for (const auto &template_argument : template_params()) { - if (should_include(template_argument.name()) && - template_argument.id()) { + const auto maybe_id = template_argument.id(); + if (should_include(template_argument.name()) && maybe_id) { - nested_relationships.emplace_back( - template_argument.id().value(), hint); + nested_relationships.emplace_back(maybe_id.value(), hint); added_aggregation_relationship = (hint == common::model::relationship_t::kAggregation); @@ -216,4 +231,14 @@ bool template_parameter::find_nested_relationships( return added_aggregation_relationship; } +void template_parameter::set_concept_constraint(std::string constraint) +{ + concept_constraint_ = std::move(constraint); +} + +const std::optional &template_parameter::concept_constraint() const +{ + return concept_constraint_; +} + } // namespace clanguml::common::model diff --git a/src/common/model/template_parameter.h b/src/common/model/template_parameter.h index 3ca0f173..b9ce70eb 100644 --- a/src/common/model/template_parameter.h +++ b/src/common/model/template_parameter.h @@ -100,6 +100,9 @@ public: const std::function &should_include) const; + void set_concept_constraint(std::string constraint); + const std::optional &concept_constraint() const; + private: /// Represents the type of non-type template parameters /// e.g. 'int' @@ -125,6 +128,10 @@ private: /// Whether the argument specializes argument pack from parent template bool is_pack_{false}; + /// Stores optional fully qualified name of constraint for this template + /// parameter + std::optional concept_constraint_; + // Nested template parameters std::vector template_params_; diff --git a/src/common/model/template_trait.cc b/src/common/model/template_trait.cc index 878c02ce..31e7ad2e 100644 --- a/src/common/model/template_trait.cc +++ b/src/common/model/template_trait.cc @@ -27,7 +27,6 @@ std::ostream &template_trait::render_template_params(std::ostream &ostr, if (!templates_.empty()) { std::vector tnames; - std::vector tnames_simplified; std::transform(templates_.cbegin(), templates_.cend(), std::back_inserter(tnames), diff --git a/src/common/types.h b/src/common/types.h index 7c9b18fd..5122afff 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -33,23 +33,38 @@ public: optional_ref() = default; - optional_ref(T *value) { value_ = value; } + optional_ref(T *value) + : value_{value} + { + } - optional_ref(const T *value) { value_ = value; } + optional_ref(const T *value) + : value_{value} + { + } - optional_ref(T &value) { value_ = &value; } + optional_ref(T &value) + : value_{&value} + { + } - optional_ref(const T &value) { value_ = &value; } + optional_ref(const T &value) + : value_{&value} + { + } - optional_ref(optional_ref &right) { value_ = right.get(); } + optional_ref(optional_ref &right) + : value_{right.get()} + { + } template || std::is_same_v>> optional_ref(const V &t) + : value_{t.get()} { - value_ = t.get(); } template || std::is_same_v>> optional_ref(V &&t) + : value_{t.get()} { - value_ = t.get(); t.reset(); } template >> optional_ref(const std::reference_wrapper &t) + : value_{&t.get()} { - value_ = &t.get(); } optional_ref &operator=(const optional_ref &right) diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index e4712597..8faa8b8a 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -53,6 +53,8 @@ public: explicit translation_unit_visitor( clang::SourceManager &sm, const clanguml::config::diagram &config); + virtual ~translation_unit_visitor() = default; + /** * @brief Get clang::SourceManager * @return Reference to @link clang::SourceManager used by this translation diff --git a/src/decorators/decorators.cc b/src/decorators/decorators.cc index d113b67e..6864d2ed 100644 --- a/src/decorators/decorators.cc +++ b/src/decorators/decorators.cc @@ -185,7 +185,7 @@ std::vector> parse( documentation_block, "\\" + clanguml_tag, "@" + clanguml_tag); documentation_block = util::trim(documentation_block); - std::string_view block_view{documentation_block}; + const std::string_view block_view{documentation_block}; auto pos = block_view.find("@" + clanguml_tag + "{"); while (pos < documentation_block.size()) { diff --git a/src/include_diagram/visitor/translation_unit_visitor.h b/src/include_diagram/visitor/translation_unit_visitor.h index 54cc2081..942db8f8 100644 --- a/src/include_diagram/visitor/translation_unit_visitor.h +++ b/src/include_diagram/visitor/translation_unit_visitor.h @@ -47,6 +47,8 @@ public: clanguml::include_diagram::model::diagram &diagram, const clanguml::config::include_diagram &config); + ~include_visitor() override = default; + #if LLVM_VERSION_MAJOR > 14 void InclusionDirective(clang::SourceLocation hash_loc, const clang::Token &include_tok, clang::StringRef file_name, diff --git a/src/main.cc b/src/main.cc index 58dc0d2c..4c968022 100644 --- a/src/main.cc +++ b/src/main.cc @@ -49,7 +49,6 @@ backward::SignalHandling sh; // NOLINT #endif using namespace clanguml; -using config::config; /** * Print the program version and basic information diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index cf68dcb7..d7e01d25 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -27,12 +27,10 @@ namespace clanguml::package_diagram::visitor { -using clanguml::common::model::access_t; using clanguml::common::model::namespace_; using clanguml::common::model::package; using clanguml::common::model::relationship; using clanguml::common::model::relationship_t; -using clanguml::package_diagram::model::diagram; translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, clanguml::package_diagram::model::diagram &diagram, diff --git a/src/package_diagram/visitor/translation_unit_visitor.h b/src/package_diagram/visitor/translation_unit_visitor.h index 49729a35..eb6a1243 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.h +++ b/src/package_diagram/visitor/translation_unit_visitor.h @@ -45,6 +45,8 @@ public: clanguml::package_diagram::model::diagram &diagram, const clanguml::config::package_diagram &config); + ~translation_unit_visitor() override = default; + virtual bool VisitNamespaceDecl(clang::NamespaceDecl *ns); virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls); diff --git a/src/sequence_diagram/model/participant.cc b/src/sequence_diagram/model/participant.cc index aa1ef0c4..5de94d30 100644 --- a/src/sequence_diagram/model/participant.cc +++ b/src/sequence_diagram/model/participant.cc @@ -265,7 +265,7 @@ std::string function_template::message_name(message_render_mode mode) const std::ostringstream s; render_template_params(s, using_namespace(), true); - std::string template_params = s.str(); + const std::string template_params = s.str(); if (mode == message_render_mode::no_arguments) { return fmt::format( diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 6f5cdf3b..758afcfc 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -1098,9 +1098,10 @@ bool translation_unit_visitor::process_class_template_method_call_expression( 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()); + if (const auto maybe_id = + get_unique_id(template_declaration->getID()); + maybe_id.has_value()) + diagram().add_active_participant(maybe_id.value()); } } else { @@ -1137,12 +1138,13 @@ bool translation_unit_visitor::process_function_call_expression( std::unique_ptr f_ptr; - if (!get_unique_id(callee_function->getID()).has_value()) { + const auto maybe_id = get_unique_id(callee_function->getID()); + if (!maybe_id.has_value()) { // This is hopefully not an interesting call... m.set_to(callee_function->getID()); } else { - m.set_to(get_unique_id(callee_function->getID()).value()); + m.set_to(maybe_id.value()); } m.set_message_name(callee_name.substr(0, callee_name.size() - 2)); @@ -1168,10 +1170,11 @@ bool translation_unit_visitor::process_unresolved_lookup_call_expression( const auto *ftd = clang::dyn_cast_or_null(decl); - if (!get_unique_id(ftd->getID()).has_value()) + const auto maybe_id = get_unique_id(ftd->getID()); + if (!maybe_id.has_value()) m.set_to(ftd->getID()); else { - m.set_to(get_unique_id(ftd->getID()).value()); + m.set_to(maybe_id.value()); } break; @@ -1748,14 +1751,11 @@ void translation_unit_visitor::process_template_specialization_argument( } else if ((arg.getAsType()->getAsCXXRecordDecl() != nullptr) && arg.getAsType()->getAsCXXRecordDecl()->isLambda()) { - if (get_unique_id(arg.getAsType()->getAsCXXRecordDecl()->getID()) - .has_value()) { - argument.set_name(get_participant( - get_unique_id( - arg.getAsType()->getAsCXXRecordDecl()->getID()) - .value()) - .value() - .full_name(false)); + const auto maybe_id = + get_unique_id(arg.getAsType()->getAsCXXRecordDecl()->getID()); + if (maybe_id.has_value()) { + argument.set_name( + get_participant(maybe_id.value()).value().full_name(false)); } else { const auto type_name = diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index c67a1d75..9429c5eb 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -42,6 +42,8 @@ public: clanguml::sequence_diagram::model::diagram &diagram, const clanguml::config::sequence_diagram &config); + ~translation_unit_visitor() override = default; + bool shouldVisitTemplateInstantiations(); bool VisitCallExpr(clang::CallExpr *expr); diff --git a/src/util/util.cc b/src/util/util.cc index 16a8b0cc..94f1f5fd 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -53,7 +53,7 @@ std::string get_process_output(const std::string &command) std::string result; #if defined(__linux) || defined(__unix) || defined(__APPLE__) - std::unique_ptr pipe( + const std::unique_ptr pipe( popen(command.c_str(), "r"), pclose); #elif defined(_WIN32) std::unique_ptr pipe( @@ -147,13 +147,13 @@ std::string get_git_toplevel_dir() std::string ltrim(const std::string &s) { - size_t start = s.find_first_not_of(WHITESPACE); + const size_t start = s.find_first_not_of(WHITESPACE); return (start == std::string::npos) ? "" : s.substr(start); } std::string rtrim(const std::string &s) { - size_t end = s.find_last_not_of(WHITESPACE); + const size_t end = s.find_last_not_of(WHITESPACE); return (end == std::string::npos) ? "" : s.substr(0, end + 1); } @@ -210,7 +210,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(R"((@A\([^\).]+\)))"); + const std::regex alias_regex(R"((@A\([^\).]+\)))"); auto alias_it = std::sregex_iterator(input.begin(), input.end(), alias_regex); @@ -219,8 +219,8 @@ bool find_element_alias( if (alias_it == end_it) return false; - std::smatch match = *alias_it; - std::string alias = match.str().substr(3, match.str().size() - 4); + const std::smatch &match = *alias_it; + const std::string alias = match.str().substr(3, match.str().size() - 4); std::get<0>(result) = alias; std::get<1>(result) = match.position(); diff --git a/src/util/util.h b/src/util/util.h index 96e39fbf..15c12335 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -27,6 +27,31 @@ #include #include +#define LOG_ERROR(fmt__, ...) \ + spdlog::get("console")->error( \ + fmt::runtime(std::string("[{}:{}] ") + fmt__), FILENAME_, __LINE__, \ + ##__VA_ARGS__) + +#define LOG_WARN(fmt__, ...) \ + spdlog::get("console")->warn( \ + fmt::runtime(std::string("[{}:{}] ") + fmt__), FILENAME_, __LINE__, \ + ##__VA_ARGS__) + +#define LOG_INFO(fmt__, ...) \ + spdlog::get("console")->info( \ + fmt::runtime(std::string("[{}:{}] ") + fmt__), FILENAME_, __LINE__, \ + ##__VA_ARGS__) + +#define LOG_DBG(fmt__, ...) \ + spdlog::get("console")->debug( \ + fmt::runtime(std::string("[{}:{}] ") + fmt__), FILENAME_, __LINE__, \ + ##__VA_ARGS__) + +#define LOG_TRACE(fmt__, ...) \ + spdlog::get("console")->trace( \ + fmt::runtime(std::string("[{}:{}] ") + fmt__), FILENAME_, __LINE__, \ + ##__VA_ARGS__) + namespace clanguml::util { std::string ltrim(const std::string &s); @@ -36,26 +61,6 @@ std::string trim(const std::string &s); #define FILENAME_ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#define LOG_ERROR(fmt__, ...) \ - 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__) - -#define LOG_INFO(fmt__, ...) \ - 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__) - -#define LOG_TRACE(fmt__, ...) \ - spdlog::get("console")->trace( \ - std::string("[{}:{}] ") + fmt__, FILENAME_, __LINE__, ##__VA_ARGS__) - /** * @brief Setup spdlog logger. * @@ -237,6 +242,39 @@ void for_each_if(const T &collection, C &&cond, F &&func) }); } +template +void apply_if_not_null(const T *pointer, F &&func, FElse &&func_else) +{ + if (pointer != nullptr) { + std::forward(func)(pointer); + } + else { + std::forward(func_else)(); + } +} + +template +void apply_if_not_null(const T *pointer, F &&func) +{ + apply_if_not_null(pointer, std::forward(func), []() {}); +} + +template +void apply_if(const bool condition, F &&func, FElse &&func_else) +{ + if (condition) { + std::forward(func)(); + } + else { + std::forward(func_else)(); + } +} + +template void apply_if(const bool condition, F &&func) +{ + apply_if(condition, std::forward(func), []() {}); +} + std::size_t hash_seed(std::size_t seed); /** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f9bf22c5..daf64ccb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,6 +3,8 @@ file(GLOB_RECURSE TEST_CASE_SOURCES t*/*.cc t*/*.c t*/src/*.c) file(GLOB_RECURSE TEST_CASE_CONFIGS t*/.clang-uml) file(GLOB_RECURSE TEST_CONFIG_YMLS test_config_data/*.yml) +set(TEST_CASES_REQUIRING_CXX20 t00056 t00058 t00059) + set(CLANG_UML_TEST_LIBRARIES clang-umllib ${YAML_CPP_LIBRARIES} @@ -13,6 +15,23 @@ if(MSVC) list(APPEND CLANG_UML_TEST_LIBRARIES "Version.lib") endif(MSVC) +list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 SUPPORTS_CXX_STD_20) + +if(SUPPORTS_CXX_STD_20 EQUAL -1 + OR ${LLVM_PACKAGE_VERSION} VERSION_LESS "14.0") + set(ENABLE_CXX_STD_20_TEST_CASES 0) + foreach(CXX20_TC ${TEST_CASES_REQUIRING_CXX20}) + list(FILTER TEST_CASE_SOURCES + EXCLUDE + REGEX ".*${CXX20_TC}.*") + list(FILTER TEST_CASE_CONFIGS + EXCLUDE + REGEX ".*${CXX20_TC}.*") + endforeach() +else() + set(ENABLE_CXX_STD_20_TEST_CASES 1) +endif() + set(TEST_CASES test_util test_model @@ -27,12 +46,16 @@ foreach(TEST_NAME ${TEST_CASES}) ${TEST_NAME}.cc $<$:${TEST_CASE_SOURCES}> catch.h) - target_compile_features(${TEST_NAME} PRIVATE cxx_std_17) + target_compile_features(${TEST_NAME} PRIVATE + $) + target_compile_definitions(${TEST_NAME} PRIVATE + $<$:ENABLE_CXX_STD_20_TEST_CASES>) target_compile_options(${TEST_NAME} PRIVATE + $<$: $<$,$>: -Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable - -Wno-attributes -Wno-nonnull> - $<$:/MP /W1 /bigobj /wd4624>) + -Wno-attributes -Wno-nonnull -Wno-deprecated-enum-enum-conversion> + $<$:/MP /W1 /bigobj /wd4624>>) target_link_libraries(${TEST_NAME} PRIVATE ${CLANG_UML_TEST_LIBRARIES}) endforeach() diff --git a/tests/catch.h b/tests/catch.h index 90d8fe9a..c3237aad 100644 --- a/tests/catch.h +++ b/tests/catch.h @@ -532,8 +532,7 @@ unsigned int rngSeed(); // We need a dummy global operator<< so we can bring it into Catch namespace // later -struct Catch_global_namespace_dummy { -}; +struct Catch_global_namespace_dummy { }; std::ostream &operator<<(std::ostream &, Catch_global_namespace_dummy); namespace Catch { @@ -914,17 +913,15 @@ constexpr auto operator"" _catch_sr( N #define INTERNAL_CATCH_TYPE_GEN \ - template struct TypeList { \ - }; \ + template struct TypeList { }; \ template \ - constexpr auto get_wrapper() noexcept->TypeList \ + constexpr auto get_wrapper() noexcept -> TypeList \ { \ return {}; \ } \ - template > { -}; +struct is_related : std::is_same, Decay> { }; /// We need to reinvent std::function because every piece of code that might add /// overhead in a measurement context needs to have consistent performance @@ -8698,8 +8668,8 @@ public: } // namespace TestCaseTracking using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; using TestCaseTracking::SectionTracker; +using TestCaseTracking::TrackerContext; } // namespace Catch @@ -8886,8 +8856,8 @@ double outlier_variance(Estimate mean, Estimate stddev, int n) return (nc / n) * (sb2 - nc * sg2); }; - return (std::min)(var_out(1), - var_out((std::min)(c_max(0.), c_max(mg_min)))) / + return (std::min)( + var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; } @@ -9844,8 +9814,7 @@ class Columns { public: class iterator { friend Columns; - struct EndTag { - }; + struct EndTag { }; std::vector const &m_columns; std::vector m_iterators; @@ -9995,8 +9964,7 @@ namespace detail { // Traits for extracting arg and return type of lambdas (for single argument // lambdas) template -struct UnaryLambdaTraits : UnaryLambdaTraits { -}; +struct UnaryLambdaTraits : UnaryLambdaTraits { }; template struct UnaryLambdaTraits { @@ -10805,7 +10773,7 @@ struct Help : Opt { return ParserResult::ok(ParseResultType::ShortCircuitAll); }) { - static_cast (*this)( + static_cast(*this)( "display usage information")["-?"]["-h"]["--help"] .optional(); } @@ -15277,8 +15245,8 @@ int Session::runInternal() // the return value to 255 prevents false negative when some multiple // of 256 tests has failed return (std::min)(MaxExitCode, - (std::max)(totals.error, - static_cast(totals.assertions.failed))); + (std::max)( + totals.error, static_cast(totals.assertions.failed))); } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) catch (std::exception &ex) @@ -16409,8 +16377,8 @@ std::string const &SectionTracker::trimmedName() const } // namespace TestCaseTracking using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; using TestCaseTracking::SectionTracker; +using TestCaseTracking::TrackerContext; } // namespace Catch @@ -18283,10 +18251,8 @@ struct ColumnInfo { int width; Justification justification; }; -struct ColumnBreak { -}; -struct RowBreak { -}; +struct ColumnBreak { }; +struct RowBreak { }; class Duration { enum class Unit { diff --git a/tests/t00004/t00004.cc b/tests/t00004/t00004.cc index 74881853..594bf354 100644 --- a/tests/t00004/t00004.cc +++ b/tests/t00004/t00004.cc @@ -14,8 +14,7 @@ public: public: enum class Lights { Green, Yellow, Red }; - class AAA { - }; + class AAA { }; }; void foo2() const { } @@ -26,8 +25,7 @@ public: T t; class AA { - class AAA { - }; + class AAA { }; enum class CCC { CCC_1, CCC_2 }; }; @@ -46,8 +44,7 @@ class D { public: enum class AA { AA_1, AA_2, AA_3 }; - class DD { - }; + class DD { }; }; } diff --git a/tests/t00005/t00005.cc b/tests/t00005/t00005.cc index 995b729f..e4c6d6af 100644 --- a/tests/t00005/t00005.cc +++ b/tests/t00005/t00005.cc @@ -1,37 +1,26 @@ namespace clanguml { namespace t00005 { -class A { -}; +class A { }; -class B { -}; +class B { }; -class C { -}; +class C { }; -class D { -}; +class D { }; -class E { -}; +class E { }; -class F { -}; +class F { }; -class G { -}; +class G { }; -class H { -}; +class H { }; -class I { -}; +class I { }; -class J { -}; +class J { }; -class K { -}; +class K { }; class R { public: diff --git a/tests/t00006/t00006.cc b/tests/t00006/t00006.cc index d6f80352..25ec9575 100644 --- a/tests/t00006/t00006.cc +++ b/tests/t00006/t00006.cc @@ -4,53 +4,37 @@ namespace clanguml { namespace t00006 { -class A { -}; +class A { }; -class B { -}; +class B { }; -class C { -}; +class C { }; -class D { -}; +class D { }; -class E { -}; +class E { }; -class F { -}; +class F { }; -class G { -}; +class G { }; -class H { -}; +class H { }; -class I { -}; +class I { }; -class J { -}; +class J { }; -class K { -}; +class K { }; -class L { -}; +class L { }; -class M { -}; +class M { }; -class N { -}; +class N { }; -class NN { -}; +class NN { }; -class NNN { -}; +class NNN { }; template class custom_container { public: diff --git a/tests/t00007/t00007.cc b/tests/t00007/t00007.cc index af9c1813..52635dd2 100644 --- a/tests/t00007/t00007.cc +++ b/tests/t00007/t00007.cc @@ -2,14 +2,11 @@ namespace clanguml { namespace t00007 { -class A { -}; +class A { }; -class B { -}; +class B { }; -class C { -}; +class C { }; class R { public: diff --git a/tests/t00011/t00011.cc b/tests/t00011/t00011.cc index e8b03e3c..0c4192d5 100644 --- a/tests/t00011/t00011.cc +++ b/tests/t00011/t00011.cc @@ -1,6 +1,5 @@ namespace external { -class C { -}; +class C { }; } namespace clanguml { diff --git a/tests/t00015/t00015.cc b/tests/t00015/t00015.cc index 7b5bcd71..a34db437 100644 --- a/tests/t00015/t00015.cc +++ b/tests/t00015/t00015.cc @@ -3,30 +3,25 @@ namespace t00015 { namespace ns1 { inline namespace ns2_v1_0_0 { -class A { -}; +class A { }; } namespace ns2_v0_9_0 { -class [[deprecated]] A { -}; +class [[deprecated]] A { }; } namespace { -class Anon final : public A { -}; +class Anon final : public A { }; } } // namespace ns1 namespace ns3 { namespace ns1::ns2 { -class Anon : public t00015::ns1::A { -}; +class Anon : public t00015::ns1::A { }; } -class B : public ns1::ns2::Anon { -}; +class B : public ns1::ns2::Anon { }; } } // namespace t00015 } // namespace clanguml diff --git a/tests/t00017/t00017.cc b/tests/t00017/t00017.cc index e5d30b17..b038c556 100644 --- a/tests/t00017/t00017.cc +++ b/tests/t00017/t00017.cc @@ -2,38 +2,27 @@ namespace clanguml { namespace t00017 { -class A { -}; +class A { }; -class B { -}; +class B { }; -class C { -}; +class C { }; -class D { -}; +class D { }; -class E { -}; +class E { }; -class F { -}; +class F { }; -class G { -}; +class G { }; -class H { -}; +class H { }; -class I { -}; +class I { }; -class J { -}; +class J { }; -class K { -}; +class K { }; class R { explicit R(int &some_int, C &cc, const E &ee, F &&ff, I *&ii) diff --git a/tests/t00028/t00028.cc b/tests/t00028/t00028.cc index e452c836..b48f5ab4 100644 --- a/tests/t00028/t00028.cc +++ b/tests/t00028/t00028.cc @@ -5,25 +5,21 @@ namespace clanguml { namespace t00028 { /// \uml{note[top] A class note.} -class A { -}; +class A { }; /// \uml{note[] B class note.} -class B { -}; +class B { }; /// /// @uml{note:t00028_class[bottom] C class note.} /// This is class C. -class C { -}; +class C { }; /// \uml{note /// D /// class /// note.} -class D { -}; +class D { }; /// \uml{note E template class note.} template class E { @@ -31,8 +27,7 @@ template class E { }; /// \uml{note:other_diagram[left] G class note.} -class G { -}; +class G { }; /// @uml{note[ bottom ] F enum note.} enum class F { one, two, three }; diff --git a/tests/t00029/t00029.cc b/tests/t00029/t00029.cc index b56162bb..3e7badee 100644 --- a/tests/t00029/t00029.cc +++ b/tests/t00029/t00029.cc @@ -4,12 +4,10 @@ namespace clanguml { namespace t00029 { -class A { -}; +class A { }; /// \uml{skip} -class B { -}; +class B { }; template class C { T param; @@ -25,17 +23,13 @@ enum class E { one, two, three }; /// \uml{skip} enum class F { red, green, blue }; -class G1 { -}; +class G1 { }; -class G2 { -}; +class G2 { }; -class G3 { -}; +class G3 { }; -class G4 { -}; +class G4 { }; struct R { G1 g1; diff --git a/tests/t00030/t00030.cc b/tests/t00030/t00030.cc index 7799cd04..0193421e 100644 --- a/tests/t00030/t00030.cc +++ b/tests/t00030/t00030.cc @@ -4,20 +4,15 @@ namespace clanguml { namespace t00030 { -class A { -}; +class A { }; -class B { -}; +class B { }; -class C { -}; +class C { }; -class D { -}; +class D { }; -class E { -}; +class E { }; struct R { /// @uml{association[]} diff --git a/tests/t00031/t00031.cc b/tests/t00031/t00031.cc index 9e3d1cc0..96bb8dde 100644 --- a/tests/t00031/t00031.cc +++ b/tests/t00031/t00031.cc @@ -5,8 +5,7 @@ namespace clanguml { namespace t00031 { /// @uml{style[#back:lightgreen|yellow;header:blue/red]} -class A { -}; +class A { }; /// @uml{style[#line.dotted:blue]} enum B { one, two, three }; @@ -16,8 +15,7 @@ template class C { T ttt; }; -class D { -}; +class D { }; struct R { /// @uml{style[#red,dashed,thickness=2]} diff --git a/tests/t00032/t00032.cc b/tests/t00032/t00032.cc index c080048e..abf02084 100644 --- a/tests/t00032/t00032.cc +++ b/tests/t00032/t00032.cc @@ -4,11 +4,9 @@ namespace clanguml { namespace t00032 { -struct Base { -}; +struct Base { }; -struct TBase { -}; +struct TBase { }; struct A { void operator()() { } diff --git a/tests/t00034/t00034.cc b/tests/t00034/t00034.cc index 0ab412c7..4b50c66e 100644 --- a/tests/t00034/t00034.cc +++ b/tests/t00034/t00034.cc @@ -40,8 +40,7 @@ template <> struct drop_void { template using drop_void_t = typename drop_void::type; -struct A { -}; +struct A { }; struct R { lift_void_t *la; diff --git a/tests/t00035/t00035.cc b/tests/t00035/t00035.cc index dbc01d47..50fefebb 100644 --- a/tests/t00035/t00035.cc +++ b/tests/t00035/t00035.cc @@ -1,20 +1,15 @@ namespace clanguml { namespace t00035 { -struct Top { -}; +struct Top { }; -struct Left { -}; +struct Left { }; -struct Center { -}; +struct Center { }; -struct Bottom { -}; +struct Bottom { }; -struct Right { -}; +struct Right { }; } // namespace t00035 } // namespace clanguml diff --git a/tests/t00036/t00036.cc b/tests/t00036/t00036.cc index 2ba646b7..cb0ec2fa 100644 --- a/tests/t00036/t00036.cc +++ b/tests/t00036/t00036.cc @@ -25,8 +25,7 @@ namespace ns2 { namespace ns22 { // TODO: Fix for incomplete struct C declaration "struct C;" -struct C { -}; +struct C { }; } } diff --git a/tests/t00038/t00038.cc b/tests/t00038/t00038.cc index b6ba3b3a..b792a06e 100644 --- a/tests/t00038/t00038.cc +++ b/tests/t00038/t00038.cc @@ -7,12 +7,10 @@ namespace thirdparty { namespace ns1 { enum class color_t { red, green, blue }; -struct E { -}; +struct E { }; } // namespace ns1 namespace ns2 { -struct F { -}; +struct F { }; } // namespace ns2 } // namespace thirdparty @@ -21,41 +19,33 @@ namespace t00038 { enum class property_t { property_a, property_b, property_c }; -struct A { -}; -struct B { -}; -struct C { -}; +struct A { }; +struct B { }; +struct C { }; struct key_t { std::string key; }; -template struct map { -}; +template struct map { }; using namespace thirdparty::ns1; -template <> struct map> : E { -}; +template <> struct map> : E { }; template <> struct map> : A { -}; + clanguml::t00038::property_t::property_a>> : A { }; template <> struct map>> - : B { -}; + : B { }; template <> struct map>>> - : C { -}; + : C { }; } // namespace t00038 } // namespace clanguml diff --git a/tests/t00039/t00039.cc b/tests/t00039/t00039.cc index d6f2c11f..c8391124 100644 --- a/tests/t00039/t00039.cc +++ b/tests/t00039/t00039.cc @@ -1,50 +1,38 @@ #include namespace clanguml::t00039 { -struct B { -}; +struct B { }; -struct C { -}; +struct C { }; -struct D { -}; +struct D { }; -struct E { -}; +struct E { }; namespace ns1 { -struct BB : public B { -}; +struct BB : public B { }; } // namespace ns1 -struct CD : public C, public D { -}; +struct CD : public C, public D { }; -struct DE : public D, public E { -}; +struct DE : public D, public E { }; -struct CDE : public C, public D, public E { -}; +struct CDE : public C, public D, public E { }; -struct A { -}; +struct A { }; -struct AA : public A { -}; +struct AA : public A { }; struct AAA : public AA { B *b; }; namespace ns2 { -struct AAAA : public virtual AAA { -}; +struct AAAA : public virtual AAA { }; } // namespace ns2 namespace detail { -struct AA : public A { -}; +struct AA : public A { }; } // namespace detail namespace ns3 { diff --git a/tests/t00040/t00040.cc b/tests/t00040/t00040.cc index 21b76dd2..b94d7af1 100644 --- a/tests/t00040/t00040.cc +++ b/tests/t00040/t00040.cc @@ -1,7 +1,6 @@ namespace clanguml::t00040 { -struct B { -}; +struct B { }; struct A { public: diff --git a/tests/t00041/t00041.cc b/tests/t00041/t00041.cc index a65f5474..5204c843 100644 --- a/tests/t00041/t00041.cc +++ b/tests/t00041/t00041.cc @@ -1,16 +1,12 @@ namespace clanguml::t00041 { -struct B { -}; +struct B { }; -struct A { -}; +struct A { }; -class AA : public A { -}; +class AA : public A { }; -struct R { -}; +struct R { }; struct RR; @@ -18,15 +14,12 @@ struct D { RR *rr; }; -struct E { -}; +struct E { }; -struct F { -}; +struct F { }; namespace detail { -struct G { -}; +struct G { }; } // namespace detail struct RR : public R { @@ -35,18 +28,14 @@ struct RR : public R { detail::G *g; }; -struct RRR : public RR { -}; +struct RRR : public RR { }; namespace ns1 { -struct N { -}; +struct N { }; -struct NN : public N { -}; +struct NN : public N { }; -struct NM : public N { -}; +struct NM : public N { }; } } // namespace clanguml::t00041 diff --git a/tests/t00043/t00043.cc b/tests/t00043/t00043.cc index a0a2a04d..d3a8a7c1 100644 --- a/tests/t00043/t00043.cc +++ b/tests/t00043/t00043.cc @@ -1,8 +1,7 @@ namespace clanguml::t00043 { namespace dependants { -struct A { -}; +struct A { }; struct B { void b(A *a) { } @@ -25,17 +24,14 @@ struct E { void e(D *d) { } }; -struct F { -}; +struct F { }; } // namespace dependants namespace dependencies { -struct G { -}; +struct G { }; -struct GG { -}; +struct GG { }; struct H { void h(G *g) { } diff --git a/tests/t00044/t00044.cc b/tests/t00044/t00044.cc index 67ff645b..33110d68 100644 --- a/tests/t00044/t00044.cc +++ b/tests/t00044/t00044.cc @@ -21,8 +21,7 @@ private: }; template -class signal_handler { -}; +class signal_handler { }; template sink(signal_handler &) diff --git a/tests/t00045/t00045.cc b/tests/t00045/t00045.cc index 09e3167d..c9622c88 100644 --- a/tests/t00045/t00045.cc +++ b/tests/t00045/t00045.cc @@ -1,11 +1,8 @@ -class A { -}; +class A { }; -class AA { -}; +class AA { }; -class AAA { -}; +class AAA { }; template class AAAA { T t; @@ -13,28 +10,21 @@ template class AAAA { namespace ns1 { -class A { -}; +class A { }; namespace ns2 { -class A { -}; +class A { }; -class B : public A { -}; +class B : public A { }; -class C : public ns1::A { -}; +class C : public ns1::A { }; -class D : public ns1::ns2::A { -}; +class D : public ns1::ns2::A { }; -class E : public ::A { -}; +class E : public ::A { }; -class AAA { -}; +class AAA { }; class R { public: diff --git a/tests/t00046/t00046.cc b/tests/t00046/t00046.cc index 6a26a248..6185aa5b 100644 --- a/tests/t00046/t00046.cc +++ b/tests/t00046/t00046.cc @@ -1,33 +1,25 @@ #include #include -class A { -}; +class A { }; -class AA { -}; +class AA { }; namespace ns1 { -class A { -}; +class A { }; namespace ns2 { -class A { -}; +class A { }; -class B : public A { -}; +class B : public A { }; -class C : public ns1::A { -}; +class C : public ns1::A { }; -class D : public ns1::ns2::A { -}; +class D : public ns1::ns2::A { }; -class E : public ::A { -}; +class E : public ::A { }; class R { public: diff --git a/tests/t00050/t00050.cc b/tests/t00050/t00050.cc index 00cf7580..488b659a 100644 --- a/tests/t00050/t00050.cc +++ b/tests/t00050/t00050.cc @@ -1,14 +1,12 @@ namespace clanguml { /// Vivamus integer non suscipit taciti mus -class A { -}; +class A { }; namespace t00050 { /// Lorem ipsum dolor sit -class A { -}; +class A { }; /** * \brief Lorem ipsum @@ -23,8 +21,7 @@ class A { * \todo 2. Write tests * \todo 3. Implement */ -class B { -}; +class B { }; /// \brief Long comment example /// @@ -53,8 +50,7 @@ class B { /// 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. -class C { -}; +class C { }; /// Mollis pretium lorem primis namespace utils { @@ -66,8 +62,7 @@ namespace utils { /// tellus ligula porttitor metus. /// /// \todo Implement... -class D { -}; +class D { }; } // namespace utils @@ -93,11 +88,9 @@ template class F { /// This is an intermediate description of class G. /// /// This is a long description of class G. -class G { -}; +class G { }; -class NoComment { -}; +class NoComment { }; } // namespace t00050 } // namespace clanguml \ No newline at end of file diff --git a/tests/t00053/t00053.cc b/tests/t00053/t00053.cc index c93be191..5fac4af8 100644 --- a/tests/t00053/t00053.cc +++ b/tests/t00053/t00053.cc @@ -1,34 +1,20 @@ namespace clanguml { namespace t00053 { -struct a { -}; -struct b { -}; -struct c { -}; -struct d { -}; -struct e { -}; -struct f { -}; -struct g { -}; +struct a { }; +struct b { }; +struct c { }; +struct d { }; +struct e { }; +struct f { }; +struct g { }; -struct A { -}; -struct B { -}; -struct C { -}; -struct D { -}; -struct E { -}; -struct F { -}; -struct G { -}; +struct A { }; +struct B { }; +struct C { }; +struct D { }; +struct E { }; +struct F { }; +struct G { }; enum class h { hhh }; enum class i { iii }; diff --git a/tests/t00054/t00054.cc b/tests/t00054/t00054.cc index 4f8080be..08c401a9 100644 --- a/tests/t00054/t00054.cc +++ b/tests/t00054/t00054.cc @@ -1,42 +1,28 @@ namespace clanguml { namespace t00054 { -struct a { -}; -struct b { -}; +struct a { }; +struct b { }; namespace detail { -struct c { -}; -struct d { -}; -struct e { -}; +struct c { }; +struct d { }; +struct e { }; } // namespace detail -struct f { -}; -struct g { -}; +struct f { }; +struct g { }; -struct A { -}; -struct B { -}; +struct A { }; +struct B { }; namespace detail2 { -struct C { -}; +struct C { }; namespace detail3 { -struct D { -}; -struct E { -}; +struct D { }; +struct E { }; } // namespace detail3 -struct F { -}; +struct F { }; } // namespace detail2 -struct G { -}; +struct G { }; namespace detail4 { enum class h { hhh }; diff --git a/tests/t00055/t00055.cc b/tests/t00055/t00055.cc index fb20f94f..16e9756a 100644 --- a/tests/t00055/t00055.cc +++ b/tests/t00055/t00055.cc @@ -1,24 +1,14 @@ namespace clanguml { namespace t00055 { -struct A { -}; -struct B { -}; -struct C { -}; -struct D { -}; -struct E { -}; -struct F { -}; -struct G { -}; -struct H { -}; -struct I { -}; -struct J { -}; +struct A { }; +struct B { }; +struct C { }; +struct D { }; +struct E { }; +struct F { }; +struct G { }; +struct H { }; +struct I { }; +struct J { }; } } \ No newline at end of file diff --git a/tests/t00056/.clang-uml b/tests/t00056/.clang-uml new file mode 100644 index 00000000..64345fc7 --- /dev/null +++ b/tests/t00056/.clang-uml @@ -0,0 +1,12 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00056_class: + type: class + glob: + - ../../tests/t00056/t00056.cc + include: + namespaces: + - clanguml::t00056 + using_namespace: + - clanguml::t00056 \ No newline at end of file diff --git a/tests/t00056/t00056.cc b/tests/t00056/t00056.cc new file mode 100644 index 00000000..92d982de --- /dev/null +++ b/tests/t00056/t00056.cc @@ -0,0 +1,93 @@ +#include + +namespace clanguml { +namespace t00056 { + +template +concept greater_than_simple = sizeof(T) > sizeof(L); + +template +concept greater_than_with_requires = + requires(T l, P r) { sizeof(l) > sizeof(r); }; + +// Constraint expression +template +concept max_four_bytes = sizeof(T) <= 4; + +// Simple requirement +template +concept iterable = requires(T container) { + container.begin(); + container.end(); + }; + +// Type requirement +template +concept has_value_type = requires { typename T::value_type; }; + +template +concept convertible_to_string = + max_four_bytes && requires(T s) { + std::string{s}; + { + std::to_string(s) + } noexcept; + { + std::to_string(s) + } -> std::same_as; + }; + +// Compound requirement +// ... + +// Combined concept +template +concept iterable_with_value_type = iterable && has_value_type; + +template +concept iterable_or_small_value_type = + iterable_with_value_type || max_four_bytes; + +// Simple type constraint +template struct A { + T a; +}; + +// Requires constant expression +template + requires iterable_or_small_value_type +struct B { + T b; +}; + +// Anonymous concept requirement (TODO) +template + requires requires(T t) { + --t; + t--; + } +struct C { + T c; +}; + +template + requires max_four_bytes && max_four_bytes +struct D { }; + +template + requires greater_than_with_requires +struct E { + T1 e1; + T2 e2; + T3 e3; +}; + +template + requires greater_than_simple +struct F { + T1 f1; + T2 f2; + T3 f3; +}; +} +} \ No newline at end of file diff --git a/tests/t00056/test_case.h b/tests/t00056/test_case.h new file mode 100644 index 00000000..a1cce9f9 --- /dev/null +++ b/tests/t00056/test_case.h @@ -0,0 +1,117 @@ +/** + * tests/t00056/test_case.h + * + * 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. + */ + +TEST_CASE("t00056", "[test-case][class]") +{ + auto [config, db] = load_config("t00056"); + + auto diagram = config.diagrams["t00056_class"]; + + REQUIRE(diagram->name == "t00056_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00056_class"); + + auto puml = generate_class_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + // Check if all classes exist + REQUIRE_THAT(puml, IsConcept(_A("greater_than_simple"))); + REQUIRE_THAT(puml, IsConcept(_A("greater_than_with_requires"))); + REQUIRE_THAT(puml, IsConcept(_A("max_four_bytes"))); + REQUIRE_THAT(puml, IsConcept(_A("iterable"))); + REQUIRE_THAT(puml, IsConcept(_A("has_value_type"))); + REQUIRE_THAT(puml, IsConcept(_A("convertible_to_string"))); + REQUIRE_THAT(puml, IsConcept(_A("iterable_with_value_type"))); + REQUIRE_THAT(puml, IsConcept(_A("iterable_or_small_value_type"))); + + REQUIRE_THAT(puml, + IsConceptRequirement( + _A("greater_than_with_requires"), "sizeof (l) > sizeof (r)")); + + REQUIRE_THAT( + puml, IsConceptRequirement(_A("iterable"), "container.begin()")); + REQUIRE_THAT( + puml, IsConceptRequirement(_A("iterable"), "container.end()")); + +#ifdef _MSC_VER + REQUIRE_THAT(puml, + IsConceptRequirement( + _A("convertible_to_string"), "std::string({s})")); +#else + REQUIRE_THAT(puml, + IsConceptRequirement(_A("convertible_to_string"), "std::string{s}")); +#endif + REQUIRE_THAT(puml, + IsConceptRequirement( + _A("convertible_to_string"), "{std::to_string(s)} noexcept")); + REQUIRE_THAT(puml, + IsConceptRequirement(_A("convertible_to_string"), + "{std::to_string(s)} -> std::same_as")); + + // Check if class templates exist + REQUIRE_THAT(puml, IsClassTemplate("A", "max_four_bytes T")); + REQUIRE_THAT(puml, IsClassTemplate("B", "T")); + REQUIRE_THAT(puml, IsClassTemplate("C", "convertible_to_string T")); + REQUIRE_THAT( + puml, IsClassTemplate("D", "iterable T1,T2,iterable T3,T4,T5")); + REQUIRE_THAT(puml, IsClassTemplate("E", "T1,T2,T3")); + REQUIRE_THAT(puml, IsClassTemplate("F", "T1,T2,T3")); + + // Check if all relationships exist + REQUIRE_THAT(puml, + IsConstraint(_A("A"), _A("max_four_bytes"), "T")); + + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("max_four_bytes"), "T2")); + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("max_four_bytes"), "T5")); + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("iterable"), "T1")); + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("iterable"), "T3")); + + REQUIRE_THAT(puml, + IsConstraint( + _A("iterable_with_value_type"), _A("has_value_type"), "T")); + + REQUIRE_THAT(puml, + IsConstraint(_A("iterable_or_small_value_type"), + _A("max_four_bytes"), "T")); + REQUIRE_THAT(puml, + IsConstraint(_A("iterable_or_small_value_type"), + _A("iterable_with_value_type"), "T")); + + REQUIRE_THAT(puml, + IsConstraint( + _A("E"), _A("greater_than_with_requires"), "T1,T3")); + + REQUIRE_THAT(puml, + IsConstraint( + _A("F"), _A("greater_than_simple"), "T1,T3")); + + save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/t00058/.clang-uml b/tests/t00058/.clang-uml new file mode 100644 index 00000000..dfc6afb4 --- /dev/null +++ b/tests/t00058/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00058_class: + type: class + glob: + - ../../tests/t00058/t00058.cc + include: + namespaces: + - clanguml::t00058 + using_namespace: + - clanguml::t00058 + plantuml: + after: + - '{{ alias("same_as_first_type") }} ..> {{ alias("first_type") }}' \ No newline at end of file diff --git a/tests/t00058/t00058.cc b/tests/t00058/t00058.cc new file mode 100644 index 00000000..e0ca24ee --- /dev/null +++ b/tests/t00058/t00058.cc @@ -0,0 +1,44 @@ +#include +#include +#include + +// Based on a blog post: +// https://andreasfertig.blog/2020/08/cpp20-concepts-testing-constrained-functions/ + +namespace clanguml { +namespace t00058 { + +template struct first_type { + using type = T; +}; + +template +using first_type_t = typename first_type::type; + +// TODO: Dependency of this concept on first_type<> template does not currently +// work due to the fact that I don't know how to extract that information +// from clang::DependentNameType to which first_type_t<> resolves to... +template +concept same_as_first_type = std::is_same_v, + std::remove_cvref_t>>; + +template + requires same_as_first_type +struct A { + std::vector a; +}; + +template + requires same_as_first_type +struct B { + std::vector b; + P bb; +}; + +struct R { + A aa; + B> bb; +}; + +} +} \ No newline at end of file diff --git a/tests/t00058/test_case.h b/tests/t00058/test_case.h new file mode 100644 index 00000000..bcfea0f3 --- /dev/null +++ b/tests/t00058/test_case.h @@ -0,0 +1,69 @@ +/** + * tests/t00058/test_case.h + * + * 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. + */ + +TEST_CASE("t00058", "[test-case][class]") +{ + auto [config, db] = load_config("t00058"); + + auto diagram = config.diagrams["t00058_class"]; + + REQUIRE(diagram->name == "t00058_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00058_class"); + + auto puml = generate_class_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsClassTemplate("A", "int,int,double,std::string")); + REQUIRE_THAT( + puml, IsClassTemplate("B", "int,std::string,int,double,A")); + + REQUIRE_THAT(puml, IsConcept(_A("same_as_first_type"))); + + REQUIRE_THAT(puml, + IsConstraint(_A("A"), _A("same_as_first_type"), + "T,Args...")); + + REQUIRE_THAT(puml, + IsConstraint(_A("B"), _A("same_as_first_type"), + "T,Args...")); + + REQUIRE_THAT(puml, + IsAggregation(_A("R"), _A("A"), "+aa")); + REQUIRE_THAT(puml, + IsAggregation( + _A("R"), _A("B>"), "+bb")); + + REQUIRE_THAT(puml, + IsInstantiation( + _A("A"), _A("A"))); + REQUIRE_THAT(puml, + IsInstantiation(_A("B"), + _A("B>"))); + + REQUIRE_THAT(puml, + IsDependency( + _A("same_as_first_type"), _A("first_type"))); + + save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/t00059/.clang-uml b/tests/t00059/.clang-uml new file mode 100644 index 00000000..94adde8e --- /dev/null +++ b/tests/t00059/.clang-uml @@ -0,0 +1,12 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00059_class: + type: class + glob: + - ../../tests/t00059/t00059.cc + include: + namespaces: + - clanguml::t00059 + using_namespace: + - clanguml::t00059 \ No newline at end of file diff --git a/tests/t00059/t00059.cc b/tests/t00059/t00059.cc new file mode 100644 index 00000000..314f1111 --- /dev/null +++ b/tests/t00059/t00059.cc @@ -0,0 +1,56 @@ +#include + +namespace clanguml { +namespace t00059 { + +template +concept fruit_c = requires(T t) { + T{}; + t.get_name(); + }; + +template +concept apple_c = fruit_c && requires(T t) { t.get_sweetness(); }; + +template +concept orange_c = fruit_c && requires(T t) { t.get_bitterness(); }; + +class gala_apple { +public: + auto get_name() const -> std::string { return "gala"; } + auto get_sweetness() const -> float { return 0.8; } +}; + +class empire_apple { +public: + auto get_name() const -> std::string { return "empire"; } + auto get_sweetness() const -> float { return 0.6; } +}; + +class lima_orange { +public: + auto get_name() const -> std::string { return "lima"; } + auto get_bitterness() const -> float { return 0.8; } +}; + +class valencia_orange { +public: + auto get_name() const -> std::string { return "valencia"; } + auto get_bitterness() const -> float { return 0.6; } +}; + +template class fruit_factory { +public: + auto create_apple() const -> TA { return TA{}; } + auto create_orange() const -> TO { return TO{}; } +}; + +using fruit_factory_1 = fruit_factory; +using fruit_factory_2 = fruit_factory; + +struct R { + fruit_factory_1 factory_1; + fruit_factory_2 factory_2; +}; +} +} \ No newline at end of file diff --git a/tests/t00059/test_case.h b/tests/t00059/test_case.h new file mode 100644 index 00000000..56426b3f --- /dev/null +++ b/tests/t00059/test_case.h @@ -0,0 +1,87 @@ +/** + * tests/t00059/test_case.h + * + * 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. + */ + +TEST_CASE("t00059", "[test-case][class]") +{ + auto [config, db] = load_config("t00059"); + + auto diagram = config.diagrams["t00059_class"]; + + REQUIRE(diagram->name == "t00059_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00059_class"); + + auto puml = generate_class_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + REQUIRE_THAT(puml, IsConcept(_A("fruit_c"))); + REQUIRE_THAT(puml, IsConcept(_A("apple_c"))); + REQUIRE_THAT(puml, IsConcept(_A("orange_c"))); + + REQUIRE_THAT(puml, IsConstraint(_A("apple_c"), _A("fruit_c"), "T")); + REQUIRE_THAT(puml, IsConstraint(_A("orange_c"), _A("fruit_c"), "T")); + + REQUIRE_THAT( + puml, IsConceptRequirement(_A("apple_c"), "t.get_sweetness()")); + REQUIRE_THAT( + puml, IsConceptRequirement(_A("apple_c"), "t.get_bitterness()")); + + REQUIRE_THAT(puml, IsClass(_A("gala_apple"))); + REQUIRE_THAT(puml, IsClass(_A("empire_apple"))); + REQUIRE_THAT(puml, IsClass(_A("valencia_orange"))); + REQUIRE_THAT(puml, IsClass(_A("lima_orange"))); + REQUIRE_THAT(puml, IsClass(_A("R"))); + + REQUIRE_THAT( + puml, IsClassTemplate("fruit_factory", "apple_c TA,orange_c TO")); + + REQUIRE_THAT(puml, + IsDependency( + _A("fruit_factory"), _A("gala_apple"))); + REQUIRE_THAT(puml, + IsDependency(_A("fruit_factory"), + _A("valencia_orange"))); + + REQUIRE_THAT(puml, + IsDependency( + _A("fruit_factory"), _A("empire_apple"))); + REQUIRE_THAT(puml, + IsDependency( + _A("fruit_factory"), _A("lima_orange"))); + + REQUIRE_THAT(puml, + IsAggregation(_A("R"), _A("fruit_factory"), + "+factory_1")); + REQUIRE_THAT(puml, + IsAggregation(_A("R"), _A("fruit_factory"), + "+factory_2")); + + REQUIRE_THAT(puml, + IsInstantiation(_A("fruit_factory"), + _A("fruit_factory"))); + REQUIRE_THAT(puml, + IsInstantiation(_A("fruit_factory"), + _A("fruit_factory"))); + + save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/t20006/t20006.cc b/tests/t20006/t20006.cc index f76e9d54..0ea7d8c2 100644 --- a/tests/t20006/t20006.cc +++ b/tests/t20006/t20006.cc @@ -34,7 +34,7 @@ template struct BB { void bb1(T t, std::string f) { aa_->aa2(t); } void bb2(T t, std::string f) { aa_->aa1(t); } - BB(AA *aa) + BB(AA *aa) : aa_{aa} { } @@ -46,7 +46,7 @@ template struct BB { void bb1(T t, float f) { bb2(t, f); } void bb2(T t, float f) { aa_.aa2(t); } - BB(AA &aa) + BB(AA &aa) : aa_{aa} { } diff --git a/tests/t30002/t30002.cc b/tests/t30002/t30002.cc index 159b6d41..864cd485 100644 --- a/tests/t30002/t30002.cc +++ b/tests/t30002/t30002.cc @@ -8,72 +8,55 @@ namespace clanguml { namespace t30002 { namespace A::AA { namespace A1 { -struct CA { -}; +struct CA { }; } namespace A2 { -struct CB { -}; +struct CB { }; } namespace A3 { -struct CC { -}; +struct CC { }; } namespace A4 { -struct CD { -}; +struct CD { }; } namespace A5 { -struct CE { -}; +struct CE { }; } namespace A6 { -struct CF { -}; +struct CF { }; } namespace A7 { -struct CG { -}; +struct CG { }; } namespace A8 { -struct CH { -}; +struct CH { }; } namespace A9 { -struct CI { -}; +struct CI { }; } namespace A10 { -struct CJ { -}; +struct CJ { }; } namespace A11 { -struct CK { -}; +struct CK { }; } namespace A12 { -struct CL { -}; +struct CL { }; } namespace A13 { -struct CM { -}; +struct CM { }; } namespace A14 { -struct CN { -}; +struct CN { }; } namespace A15 { -struct CO { -}; +struct CO { }; } namespace A16 { -struct CP { -}; +struct CP { }; } namespace A17 { -struct CR { -}; +struct CR { }; } } namespace B::BB::BBB { diff --git a/tests/t30003/t30003.cc b/tests/t30003/t30003.cc index b83621a8..134e675d 100644 --- a/tests/t30003/t30003.cc +++ b/tests/t30003/t30003.cc @@ -3,30 +3,25 @@ namespace t30003 { namespace ns1 { namespace ns2_v1_0_0 { -class A { -}; +class A { }; } namespace [[deprecated]] ns2_v0_9_0 { -class A { -}; +class A { }; } namespace { -class Anon final { -}; +class Anon final { }; } } namespace [[deprecated]] ns3 { namespace ns1::ns2 { -class Anon : public t30003::ns1::ns2_v1_0_0::A { -}; +class Anon : public t30003::ns1::ns2_v1_0_0::A { }; } -class B : public ns1::ns2::Anon { -}; +class B : public ns1::ns2::Anon { }; } } } \ No newline at end of file diff --git a/tests/t30005/t30005.cc b/tests/t30005/t30005.cc index f87fad0a..43fc1947 100644 --- a/tests/t30005/t30005.cc +++ b/tests/t30005/t30005.cc @@ -2,8 +2,7 @@ namespace clanguml { namespace t30005 { namespace A::AA::AAA { -struct C1 { -}; +struct C1 { }; } namespace B::BB::BBB { diff --git a/tests/t30006/t30006.cc b/tests/t30006/t30006.cc index d9778875..db5583d8 100644 --- a/tests/t30006/t30006.cc +++ b/tests/t30006/t30006.cc @@ -2,8 +2,7 @@ namespace clanguml { namespace t30006 { namespace B { -struct BB { -}; +struct BB { }; } /// \uml{note[top] Top A note.} @@ -14,8 +13,7 @@ struct A1 { } namespace C { -struct CC { -}; +struct CC { }; } /// \uml{note[bottom] Bottom A note.} diff --git a/tests/t30007/t30007.cc b/tests/t30007/t30007.cc index e79461ab..a325d063 100644 --- a/tests/t30007/t30007.cc +++ b/tests/t30007/t30007.cc @@ -2,8 +2,7 @@ namespace clanguml { namespace t30007 { namespace B { -struct BB { -}; +struct BB { }; } /// \uml{note[top] Compare layout with t30006.} @@ -16,8 +15,7 @@ struct A1 { } namespace C { -struct CC { -}; +struct CC { }; } /// \uml{note[bottom] Bottom A note.} diff --git a/tests/t30008/t30008.cc b/tests/t30008/t30008.cc index c7eca1df..7ccffca5 100644 --- a/tests/t30008/t30008.cc +++ b/tests/t30008/t30008.cc @@ -5,8 +5,7 @@ namespace dependants { namespace X { } namespace A { -struct AA { -}; +struct AA { }; } namespace B { struct BB { @@ -26,8 +25,7 @@ namespace Y { } namespace D { -struct DD { -}; +struct DD { }; } namespace E { struct EE { diff --git a/tests/test_cases.cc b/tests/test_cases.cc index a1d4d618..fa277f29 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -255,8 +255,14 @@ using namespace clanguml::test::matchers; #include "t00053/test_case.h" #include "t00054/test_case.h" #include "t00055/test_case.h" +#if defined(ENABLE_CXX_STD_20_TEST_CASES) +#include "t00056/test_case.h" +#endif #include "t00057/test_case.h" - +#if defined(ENABLE_CXX_STD_20_TEST_CASES) +#include "t00058/test_case.h" +#include "t00059/test_case.h" +#endif /// /// Sequence diagram tests /// @@ -340,4 +346,4 @@ int main(int argc, char *argv[]) clanguml::util::setup_logging(debug_log ? 3 : 1); return session.run(); -} \ No newline at end of file +} diff --git a/tests/test_cases.h b/tests/test_cases.h index 99894176..d520d6d0 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -82,26 +82,19 @@ template constexpr bool has_type() noexcept return (std::is_same_v || ... || false); } -struct Public { -}; +struct Public { }; -struct Protected { -}; +struct Protected { }; -struct Private { -}; +struct Private { }; -struct Abstract { -}; +struct Abstract { }; -struct Static { -}; +struct Static { }; -struct Const { -}; +struct Const { }; -struct Default { -}; +struct Default { }; struct HasCallWithResultMatcher : ContainsMatcher { HasCallWithResultMatcher( @@ -281,6 +274,13 @@ ContainsMatcher IsClassTemplate(std::string const &str, fmt::format("class \"{}<{}>\"", str, tmplt), caseSensitivity)); } +ContainsMatcher IsConcept(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher( + CasedString("class " + str + " <>", caseSensitivity)); +} + ContainsMatcher IsEnum(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { @@ -334,11 +334,13 @@ ContainsMatcher IsAssociation(std::string const &from, std::string const &to, if (!label.empty()) { format_string += " : {}"; return ContainsMatcher(CasedString( - fmt::format(format_string, from, to, label), caseSensitivity)); + fmt::format(fmt::runtime(format_string), from, to, label), + caseSensitivity)); } else return ContainsMatcher( - CasedString(fmt::format(format_string, from, to), caseSensitivity)); + CasedString(fmt::format(fmt::runtime(format_string), from, to), + caseSensitivity)); } ContainsMatcher IsComposition(std::string const &from, std::string const &to, @@ -357,8 +359,9 @@ ContainsMatcher IsComposition(std::string const &from, std::string const &to, format_string += " {} : {}"; - return ContainsMatcher(CasedString( - fmt::format(format_string, from, to, label), caseSensitivity)); + return ContainsMatcher( + CasedString(fmt::format(fmt::runtime(format_string), from, to, label), + caseSensitivity)); } ContainsMatcher IsAggregation(std::string const &from, std::string const &to, @@ -377,8 +380,9 @@ ContainsMatcher IsAggregation(std::string const &from, std::string const &to, format_string += " {} : {}"; - return ContainsMatcher(CasedString( - fmt::format(format_string, from, to, label), caseSensitivity)); + return ContainsMatcher( + CasedString(fmt::format(fmt::runtime(format_string), from, to, label), + caseSensitivity)); } ContainsMatcher IsAggregationWithStyle(std::string const &from, @@ -422,6 +426,25 @@ ContainsMatcher IsDependency(std::string const &from, std::string const &to, CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); } +ContainsMatcher IsConstraint(std::string const &from, std::string const &to, + std::string const &label = {}, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + if (label.empty()) + return ContainsMatcher( + CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); + else + return ContainsMatcher(CasedString( + fmt::format("{} ..> {} : {}", from, to, label), caseSensitivity)); +} + +ContainsMatcher IsConceptRequirement(std::string const &cpt, + std::string const &requirement, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher(CasedString(requirement, caseSensitivity)); +} + ContainsMatcher IsLayoutHint(std::string const &from, std::string const &hint, std::string const &to, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 0cbdbea1..6466cf82 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -162,9 +162,18 @@ test_cases: - name: t00055 title: Test case for `row` and `column` layout hints description: + - name: t00056 + title: Basic C++20 concepts test case + description: - name: t00057 title: Test case C99/C11 translation units with structs and unions description: + - name: t00058 + title: Test case for concepts with variadic parameters and type aliases + description: + - name: t00059 + title: Non-virtual abstract factory pattern using concepts test case + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case diff --git a/util/check_formatting.sh b/util/check_formatting.sh index 2bd1170a..eca40d8a 100755 --- a/util/check_formatting.sh +++ b/util/check_formatting.sh @@ -49,5 +49,5 @@ 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} + clang-format-15 --dry-run --Werror ${EFFECTIVE_FILE_LIST} fi \ No newline at end of file diff --git a/util/generate_test_case.py b/util/generate_test_case.py index 44b99c53..f6c4653b 100755 --- a/util/generate_test_case.py +++ b/util/generate_test_case.py @@ -31,6 +31,12 @@ CLASS_DIAGRAM_TEST_CASE_EXAMPLES = """ // Check if class templates exist //REQUIRE_THAT(puml, IsClassTemplate("A", "T,P,CMP,int N")); + // Check concepts + //REQUIRE_THAT(puml, IsConcept(_A("AConcept"))); + //REQUIRE_THAT(puml, + // IsConceptRequirement( + // _A("AConcept"), "sizeof (T) > sizeof (P)")); + // Check if all enums exist //REQUIRE_THAT(puml, IsEnum(_A("Lights"))); @@ -49,8 +55,8 @@ CLASS_DIAGRAM_TEST_CASE_EXAMPLES = """ // Check if all relationships exist //REQUIRE_THAT(puml, IsAssociation(_A("D"), _A("A"), "-as")); //REQUIRE_THAT(puml, IsDependency(_A("R"), _A("B"))); - //REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("D"))); - //REQUIRE_THAT(puml, IsComposition(_A("R"), _A("D"))); + //REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("D"), "-ag")); + //REQUIRE_THAT(puml, IsComposition(_A("R"), _A("D"), "-ac")); //REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F"), _A("F"))); """