diff --git a/.clanguml-sequence.example b/.clanguml-sequence.example index 4e7de5b4..0ec4ba89 100644 --- a/.clanguml-sequence.example +++ b/.clanguml-sequence.example @@ -10,11 +10,12 @@ diagrams: start_from: - usr: "c:@F@main#I#**1C#" include: - namespace: + namespaces: - clanguml exclude: - namespace: + namespaces: - std + - CLI plantuml: before: - "' main test sequence diagram" diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f2a08f4..146a1ed2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ find_package(yaml-cpp REQUIRED) message(STATUS "Checking for libclang...") find_package(LibClang REQUIRED) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3 ${LIBCLANG_CXXFLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fno-limit-debug-info ${LIBCLANG_CXXFLAGS}") # Thirdparty sources set(THIRDPARTY_HEADERS_DIR ${PROJECT_SOURCE_DIR}/thirdparty/) diff --git a/src/config/config.h b/src/config/config.h index ea8913ad..b6d21001 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -18,13 +18,41 @@ struct plantuml { std::vector after; }; +struct filter { + std::vector namespaces; +}; + struct diagram { virtual ~diagram() = default; std::string name; std::vector glob; std::vector using_namespace; + + filter include; + filter exclude; + plantuml puml; + + bool should_include(const std::string &name) const + { + for (const auto &ex : exclude.namespaces) { + if (name.find(ex) == 0) + return false; + } + + // If no inclusive namespaces are provided, + // allow all + if (include.namespaces.empty()) + return true; + + for (const auto &in : include.namespaces) { + if (name.find(in) == 0) + return true; + } + + return false; + } }; struct source_location { @@ -34,7 +62,7 @@ struct source_location { // std::variant requires unique types, so we cannot add // marker here, we need sth like boost::mp_unique // type function - using variant = std::variant; + using variant = std::variant; }; enum class class_scopes { public_, protected_, private_ }; @@ -84,6 +112,7 @@ config load(const std::string &config_file); namespace YAML { using clanguml::config::class_diagram; using clanguml::config::config; +using clanguml::config::filter; using clanguml::config::plantuml; using clanguml::config::sequence_diagram; using clanguml::config::source_location; @@ -140,6 +169,18 @@ template <> struct convert { } }; +// +// filter Yaml decoder +// +template <> struct convert { + static bool decode(const Node &node, filter &rhs) + { + if (node["namespaces"]) + rhs.namespaces = node["namespaces"].as(); + return true; + } +}; + // // sequence_diagram Yaml decoder // @@ -151,6 +192,12 @@ template <> struct convert { rhs.glob = node["glob"].as>(); rhs.puml = node["plantuml"].as(); + if (node["include"]) + rhs.include = node["include"].as(); + + if (node["exclude"]) + rhs.exclude = node["exclude"].as(); + if (node["start_from"]) rhs.start_from = node["start_from"].as(); return true; @@ -176,21 +223,21 @@ template <> struct convert { for (const auto &d : diagrams) { const auto diagram_type = d.second["type"].as(); + auto name = d.first.as(); if (diagram_type == "class") { - rhs.diagrams[d.first.as()] = - std::make_shared( - d.second.as()); + rhs.diagrams[name] = std::make_shared( + d.second.as()); } if (diagram_type == "sequence") { - rhs.diagrams[d.first.as()] = - std::make_shared( - d.second.as()); + rhs.diagrams[name] = std::make_shared( + d.second.as()); } else { spdlog::warn( "Diagrams of type {} are not supported at the moment... ", diagram_type); } + rhs.diagrams[name]->name = name; } return true; diff --git a/src/cx/compilation_database.cc b/src/cx/compilation_database.cc index 4d988777..286a24bf 100644 --- a/src/cx/compilation_database.cc +++ b/src/cx/compilation_database.cc @@ -14,7 +14,13 @@ compilation_database::compilation_database(CXCompilationDatabase &&d) compilation_database::~compilation_database() { - clang_CompilationDatabase_dispose(m_database); + //clang_CompilationDatabase_dispose(m_database); +} + +compilation_database::compilation_database(compilation_database &&d) + : m_database{std::move(d.m_database)} + , m_index{std::move(d.m_index)} +{ } compilation_database compilation_database::from_directory( diff --git a/src/cx/compilation_database.h b/src/cx/compilation_database.h index 91971ebc..e8623857 100644 --- a/src/cx/compilation_database.h +++ b/src/cx/compilation_database.h @@ -15,6 +15,8 @@ public: compilation_database(CXCompilationDatabase &&d); ~compilation_database(); + compilation_database(compilation_database &&d); + CXCompilationDatabase &db(); CXIndex &index(); diff --git a/src/main.cc b/src/main.cc index c1118133..d9997770 100644 --- a/src/main.cc +++ b/src/main.cc @@ -52,8 +52,7 @@ int main(int argc, const char *argv[]) spdlog::info("Loading compilation database from {} directory", config.compilation_database_dir); - auto db = - compilation_database::from_directory(config.compilation_database_dir); + auto db = compilation_database::from_directory(config.compilation_database_dir); for (const auto &[name, diagram] : config.diagrams) { using clanguml::config::class_diagram; @@ -82,5 +81,6 @@ int main(int argc, const char *argv[]) ofs.close(); } + return 0; } diff --git a/src/puml/sequence_diagram_generator.h b/src/puml/sequence_diagram_generator.h index cec82abc..deabd388 100644 --- a/src/puml/sequence_diagram_generator.h +++ b/src/puml/sequence_diagram_generator.h @@ -156,7 +156,7 @@ clanguml::model::sequence_diagram::diagram generate( spdlog::debug("Cursor name: {}", clang_getCString(clang_getCursorDisplayName(cursor))); - clanguml::visitor::sequence_diagram::tu_context ctx(d); + clanguml::visitor::sequence_diagram::tu_context ctx(d, diagram); auto res = clang_visitChildren(cursor, clanguml::visitor::sequence_diagram::translation_unit_visitor, &ctx); diff --git a/src/uml/sequence_diagram_visitor.h b/src/uml/sequence_diagram_visitor.h index 12eb6c91..eb3e4dc2 100644 --- a/src/uml/sequence_diagram_visitor.h +++ b/src/uml/sequence_diagram_visitor.h @@ -21,14 +21,16 @@ using clanguml::model::sequence_diagram::message; using clanguml::model::sequence_diagram::message_t; struct tu_context { - tu_context(diagram &d_) + tu_context(diagram &d_, const clanguml::config::sequence_diagram &config_) : d{d_} + , config{config_} { } std::vector namespace_; cx::cursor current_method; clanguml::model::sequence_diagram::diagram &d; + const clanguml::config::sequence_diagram &config; }; static enum CXChildVisitResult translation_unit_visitor( @@ -71,20 +73,21 @@ static enum CXChildVisitResult translation_unit_visitor( std::string file{clang_getCString(clang_getFileName(f))}; auto &d = ctx->d; + auto &config = ctx->config; if (referenced.kind() == CXCursor_CXXMethod) { - if (true/*sp_name.find("clanguml::") == 0 || - clang_Location_isFromMainFile(cursor.location())*/) { + if (config.should_include(sp_name)) { // Get calling object std::string caller{}; if (ctx->current_method.semantic_parent() .is_translation_unit() || ctx->current_method.semantic_parent().is_namespace()) { - caller = - ctx->current_method.semantic_parent().fully_qualified() + + caller = ctx->current_method.semantic_parent() + .fully_qualified() + "::" + ctx->current_method.spelling() + "()"; } else { - caller = ctx->current_method.semantic_parent().fully_qualified(); + caller = ctx->current_method.semantic_parent() + .fully_qualified(); } auto caller_usr = ctx->current_method.usr(); diff --git a/src/util/util.h b/src/util/util.h index 753abd10..0eaa3644 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -10,6 +10,9 @@ std::string namespace_relative( const std::vector &namespaces, const std::string &n) { for (const auto &ns : namespaces) { + if(ns.empty()) + continue; + if (n == ns) return ""; diff --git a/tests/t00001/.clanguml b/tests/t00001/.clanguml index 543d0dd0..876b6a07 100644 --- a/tests/t00001/.clanguml +++ b/tests/t00001/.clanguml @@ -6,10 +6,10 @@ diagrams: glob: - ../../tests/t00001/t00001.cc include: - namespace: + namespaces: - clanguml::t00001 exclude: - namespace: + namespaces: - clanguml::t00001::detail using_namespace: - clanguml::t00001 diff --git a/tests/test_cases.cc b/tests/test_cases.cc index f160dcb1..f6bbf03b 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -9,31 +9,89 @@ #include "uml/sequence_diagram_visitor.h" #include "catch.h" + #include #include #include +using Catch::Matchers::Contains; +using Catch::Matchers::EndsWith; +using Catch::Matchers::StartsWith; +using Catch::Matchers::VectorContains; +using clanguml::cx::compilation_database; + +auto load_config(const std::string &test_name) +{ + auto config = clanguml::config::load(test_name + "/.clanguml"); + + auto db = clanguml::cx::compilation_database::from_directory( + config.compilation_database_dir); + + return std::make_pair(std::move(config), std::move(db)); +} + +auto generate_sequence_diagram(compilation_database &db, + std::shared_ptr diagram) +{ + auto diagram_model = + clanguml::generators::sequence_diagram::generate(db, diagram->name, + dynamic_cast(*diagram)); + + return diagram_model; +} + +auto generate_class_diagram(compilation_database &db, + std::shared_ptr diagram) +{ + auto diagram_model = + clanguml::generators::class_diagram::generate(db, diagram->name, + dynamic_cast(*diagram)); + + return diagram_model; +} + +std::string generate_sequence_puml( + std::shared_ptr config, + clanguml::model::sequence_diagram::diagram &model) +{ + using namespace clanguml::generators::sequence_diagram::puml; + + std::stringstream ss; + + ss << generator( + dynamic_cast(*config), model); + + return ss.str(); +} + TEST_CASE("Test t00001", "[unit-test]") { spdlog::set_level(spdlog::level::debug); - // REQUIRE(2 + 5 == 7); - std::cout << "RUNNING TEST IN " << std::filesystem::current_path() - << std::endl; - - auto config = clanguml::config::load("t00001/.clanguml"); - - auto db = clanguml::cx::compilation_database::from_directory( - config.compilation_database_dir); + auto [config, db] = load_config("t00001"); auto diagram = config.diagrams["t00001_sequence"]; - auto diagram_model = - clanguml::generators::sequence_diagram::generate(db, "t00001_sequence", - dynamic_cast(*diagram)); - REQUIRE(diagram_model.name == "t00001_sequence"); + REQUIRE(diagram->include.namespaces.size() == 1); + REQUIRE_THAT(diagram->include.namespaces, + VectorContains(std::string{"clanguml::t00001"})); - auto generator = clanguml::generators::sequence_diagram::puml::generator( - dynamic_cast(*diagram), diagram_model); - std::cout << generator; + REQUIRE(diagram->exclude.namespaces.size() == 1); + REQUIRE_THAT(diagram->exclude.namespaces, + VectorContains(std::string{"clanguml::t00001::detail"})); + + REQUIRE(diagram->should_include("clanguml::t00001::A")); + REQUIRE(!diagram->should_include("clanguml::t00001::detail::C")); + REQUIRE(!diagram->should_include("std::vector")); + + REQUIRE(diagram->name == "t00001_sequence"); + + auto model = generate_sequence_diagram(db, diagram); + + REQUIRE(model.name == "t00001_sequence"); + + auto puml = generate_sequence_puml(diagram, model); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); }