diff --git a/src/puml/class_diagram_generator.h b/src/puml/class_diagram_generator.h index ad974b35..bd6bc799 100644 --- a/src/puml/class_diagram_generator.h +++ b/src/puml/class_diagram_generator.h @@ -92,6 +92,8 @@ public: return "..|>"; case relationship_t::kFriendship: return "<.."; + case relationship_t::kDependency: + return "..>"; default: return ""; } diff --git a/src/uml/class_diagram_model.h b/src/uml/class_diagram_model.h index 3828faad..05250b11 100644 --- a/src/uml/class_diagram_model.h +++ b/src/uml/class_diagram_model.h @@ -45,7 +45,8 @@ enum class relationship_t { kOwnership, kAssociation, kInstantiation, - kFriendship + kFriendship, + kDependency }; class element { diff --git a/src/uml/class_diagram_visitor.h b/src/uml/class_diagram_visitor.h index b581345b..0d4d807c 100644 --- a/src/uml/class_diagram_visitor.h +++ b/src/uml/class_diagram_visitor.h @@ -65,6 +65,7 @@ template struct element_visitor_context { tu_context *ctx; T &element; + class_ *parent_class{}; diagram &d; }; @@ -220,26 +221,26 @@ static enum CXChildVisitResult method_parameter_visitor( spdlog::debug( "Analyzing method parameter: {}, {}", cursor, cursor.type()); + auto t = cursor.type(); method_parameter mp; mp.name = cursor.spelling(); - mp.type = cursor.type().spelling(); + mp.type = t.spelling(); mp.default_value = cursor.default_value(); ctx->element.parameters.emplace_back(std::move(mp)); - /* TODO: handle dependency relationships based - * on method arguments - * - if (ctx->ctx->config.should_include( - cursor.type().referenced().fully_qualified())) { + if (t.is_relationship() && + ctx->ctx->config.should_include(t.referenced().spelling()) && + (t.referenced().spelling() != ctx->parent_class->name)) { - class_relationship r; - r.type = relationship_t::kDependency; - r.destination = cursor.type().referenced().usr(); + class_relationship r; + r.type = relationship_t::kDependency; + r.destination = t.referenced().spelling(); - ctx->element.relationships.emplace_back(std::move(r)); + assert(ctx->parent_class != nullptr); + + ctx->parent_class->relationships.emplace_back(std::move(r)); } - */ ret = CXChildVisit_Continue; } break; @@ -448,6 +449,7 @@ static enum CXChildVisitResult class_visitor( auto method_ctx = element_visitor_context(ctx->d, m); method_ctx.ctx = ctx->ctx; + method_ctx.parent_class = &ctx->element; clang_visitChildren( cursor.get(), method_parameter_visitor, &method_ctx); diff --git a/tests/t00013/.clanguml b/tests/t00013/.clanguml new file mode 100644 index 00000000..578a0fee --- /dev/null +++ b/tests/t00013/.clanguml @@ -0,0 +1,12 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00013_class: + type: class + glob: + - ../../tests/t00013/t00013.cc + using_namespace: + - clanguml::t00013 + include: + namespaces: + - clanguml::t00013 diff --git a/tests/t00013/t00013.cc b/tests/t00013/t00013.cc new file mode 100644 index 00000000..7928d5fb --- /dev/null +++ b/tests/t00013/t00013.cc @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +namespace clanguml { +namespace t00013 { + +struct A { + int a; +}; + +struct B { + int b; +}; + +struct C { + int c; +}; + +class R; + +struct D { + int d; + void print(R *r) {} +}; + +class R { +public: + int get_a(A *a) { return a->a; } + int get_b(B &b) { return b.b; } + // TODO: int get_b(const B &b) { return b.b; } + int get_c(C c) { return c.c; } + int get_d(D &&d) { return d.d; } +}; +} +} diff --git a/tests/t00013/test_case.h b/tests/t00013/test_case.h new file mode 100644 index 00000000..a4261c09 --- /dev/null +++ b/tests/t00013/test_case.h @@ -0,0 +1,57 @@ +/** + * tests/t00013/test_case.cc + * + * Copyright (c) 2021 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("t00013", "[test-case][class]") +{ + auto [config, db] = load_config("t00013"); + + auto diagram = config.diagrams["t00013_class"]; + + REQUIRE(diagram->name == "t00013_class"); + + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t00013"})); + + REQUIRE(diagram->exclude.namespaces.size() == 0); + + REQUIRE(diagram->should_include("clanguml::t00013::A")); + REQUIRE(diagram->should_include("clanguml::t00013::B")); + + auto model = generate_class_diagram(db, diagram); + + REQUIRE(model.name == "t00013_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, IsClass(_A("A"))); + REQUIRE_THAT(puml, IsClass(_A("B"))); + REQUIRE_THAT(puml, IsClass(_A("C"))); + REQUIRE_THAT(puml, IsClass(_A("D"))); + REQUIRE_THAT(puml, IsDependency(_A("R"), _A("A"))); + REQUIRE_THAT(puml, IsDependency(_A("R"), _A("B"))); + REQUIRE_THAT(puml, IsDependency(_A("R"), _A("C"))); + REQUIRE_THAT(puml, IsDependency(_A("R"), _A("D"))); + REQUIRE_THAT(puml, IsDependency(_A("D"), _A("R"))); + + save_puml( + "./" + config.output_directory + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 6759e3dd..5a632d91 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -15,8 +15,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -//#define CATCH_CONFIG_MAIN - #include "test_cases.h" #include @@ -105,6 +103,7 @@ using clanguml::test::matchers::IsBaseClass; using clanguml::test::matchers::IsClass; using clanguml::test::matchers::IsClassTemplate; using clanguml::test::matchers::IsComposition; +using clanguml::test::matchers::IsDependency; using clanguml::test::matchers::IsEnum; using clanguml::test::matchers::IsField; using clanguml::test::matchers::IsFriend; @@ -129,6 +128,7 @@ using clanguml::test::matchers::Static; #include "t00010/test_case.h" #include "t00011/test_case.h" #include "t00012/test_case.h" +#include "t00013/test_case.h" // // Sequence diagram tests diff --git a/tests/test_cases.h b/tests/test_cases.h index 45b17aec..130a15c7 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -304,6 +304,13 @@ ContainsMatcher IsInstantiation(std::string const &from, std::string const &to, CasedString(fmt::format("{} ..|> {}", to, from), caseSensitivity)); } +ContainsMatcher IsDependency(std::string const &from, std::string const &to, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher( + CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); +} + ContainsMatcher IsMethod(std::string const &name, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) {