First passing test cases using libtooling visitor

This commit is contained in:
Bartek Kryza
2022-07-17 11:48:00 +02:00
parent 0f91316fd3
commit 88a87edc42
30 changed files with 883 additions and 2808 deletions

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ _deps
lib/
bin/
*.swp
*.bak
/puml/
/debug/
/release/

View File

@@ -32,7 +32,7 @@ message(STATUS "Checking for yaml-cpp...")
find_package(yaml-cpp REQUIRED)
message(STATUS "Checking for libclang...")
set(LLVM_PREFERRED_VERSION 12.0.0)
set(LLVM_PREFERRED_VERSION 14.0.0)
# Add
# -DLLVM_CONFIG_PATH=/path/to/llvm-config
# to use custom LLVM version
@@ -60,8 +60,18 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
OUTPUT_VARIABLE GCC_STDDEF_INCLUDE)
message(STATUS "FOUND GCC STDDEF INCLUDE ${GCC_STDDEF_INCLUDE}")
include_directories(${GCC_STDDEF_INCLUDE})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${GCC_STDDEF_INCLUDE}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${GCC_STDDEF_INCLUDE} -Wno-unused-parameter ")
endif()
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
link_directories(/usr/lib/llvm-14/lib)
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CLANG_UML_INSTALL_INCLUDE_DIR})
include_directories(${YAML_CPP_INCLUDE_DIR})
include_directories(${UML_HEADERS_DIR})
@@ -77,10 +87,42 @@ file(GLOB_RECURSE SOURCES src/*.cc include/*.h)
set(MAIN_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc)
list(REMOVE_ITEM SOURCES ${MAIN_SOURCE_FILE})
set(LIBTOOLING_LIBS
clangFrontend
clangSerialization
clangDriver
clangParse
clangSema
clangAnalysis
clangAST
clangBasic
clangEdit
clangLex
clangTooling
LLVMipo
LLVMScalarOpts
LLVMInstCombine
LLVMTransformUtils
LLVMAnalysis
LLVMTarget
LLVMOption # Support
LLVMMCParser # MC, Support
LLVMMC # Object, Support
LLVMObject # BitReader, Core, Support
LLVMBitReader # Core, Support
LLVMCore # Support
LLVMSupport)
add_library(clang-umllib OBJECT ${SOURCES})
add_executable(clang-uml ${MAIN_SOURCE_FILE})
target_link_libraries(clang-uml ${LIBCLANG_LIBRARIES} ${YAML_CPP_LIBRARIES} cppast clang-umllib Threads::Threads)
target_link_libraries(clang-uml
${LIBCLANG_LIBRARIES}
${YAML_CPP_LIBRARIES}
${LIBTOOLING_LIBS}
cppast
clang-umllib
Threads::Threads)
target_compile_features(clang-uml PRIVATE cxx_std_17)

View File

@@ -26,8 +26,6 @@
#include "config/config.h"
#include "util/util.h"
#include <cppast/cpp_entity_index.hpp>
#include <cppast/libclang_parser.hpp>
#include <glob/glob.hpp>
#include <filesystem>

File diff suppressed because it is too large Load Diff

View File

@@ -18,26 +18,27 @@
#pragma once
#include "class_diagram/model/diagram.h"
#include "class_diagram/visitor/translation_unit_context.h"
//#include "class_diagram/visitor/translation_unit_context.h"
#include "common/model/enums.h"
#include "config/config.h"
#include <clang-c/CXCompilationDatabase.h>
#include <clang-c/Index.h>
#include <cppast/cpp_friend.hpp>
#include <cppast/cpp_function_template.hpp>
#include <cppast/cpp_member_function.hpp>
#include <cppast/cpp_member_variable.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_type.hpp>
#include <cppast/visitor.hpp>
#include <type_safe/reference.hpp>
//#include <clang-c/CXCompilationDatabase.h>
//#include <clang-c/Index.h>
//#include <cppast/cpp_friend.hpp>
//#include <cppast/cpp_function_template.hpp>
//#include <cppast/cpp_member_function.hpp>
//#include <cppast/cpp_member_variable.hpp>
//#include <cppast/cpp_template.hpp>
//#include <cppast/cpp_template_parameter.hpp>
//#include <cppast/cpp_type.hpp>
//#include <cppast/visitor.hpp>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Basic/SourceManager.h>
#include <class_diagram/model/class.h>
#include <common/model/enums.h>
#include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_type_alias.hpp>
#include <type_safe/reference.hpp>
//#include <cppast/cpp_alias_template.hpp>
//#include <cppast/cpp_type_alias.hpp>
#include <deque>
#include <functional>
#include <map>
@@ -47,13 +48,88 @@
namespace clanguml::class_diagram::visitor {
using found_relationships_t =
std::vector<std::pair<std::string, common::model::relationship_t>>;
std::vector<std::pair<clanguml::common::model::element::id_t,
common::model::relationship_t>>;
// class nested_template_relationships {
//
// std::vector<std::unique_ptr<nested_template_relationships>> children;
//};
class translation_unit_visitor
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
public:
explicit translation_unit_visitor(clang::SourceManager &sm,
clanguml::class_diagram::model::diagram &diagram,
const clanguml::config::class_diagram &config);
virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *d);
virtual bool VisitEnumDecl(clang::EnumDecl *e);
// virtual bool VisitVarDecl(clang::VarDecl *variable_declaration);
clanguml::class_diagram::model::diagram &diagram() { return diagram_; }
// void operator()();
private:
void process_class_bases(const clang::CXXRecordDecl *cls,
clanguml::class_diagram::model::class_ &c) const;
void process_class_children(const clang::CXXRecordDecl *cls,
clanguml::class_diagram::model::class_ &c);
void process_record_containment(const clang::TagDecl &record,
clanguml::common::model::element &c) const;
void process_method(const clang::CXXMethodDecl &mf,
clanguml::class_diagram::model::class_ &c);
void process_static_field(const clang::VarDecl &field_declaration,
clanguml::class_diagram::model::class_ &c);
void process_field(const clang::FieldDecl &field_declaration,
clanguml::class_diagram::model::class_ &c);
void process_function_parameter(const clang::ParmVarDecl &param,
clanguml::class_diagram::model::class_method &method,
clanguml::class_diagram::model::class_ &c,
const std::set<std::string> &template_parameter_names = {});
bool find_relationships(const clang::QualType &type, &,
clanguml::common::model::relationship_t relationship_hint);
void add_relationships(clanguml::class_diagram::model::class_ &c,
const found_relationships_t &relationships,
clanguml::common::model::access_t access,
std::optional<std::string> label);
void set_source_location(const clang::Decl &decl,
clanguml::common::model::source_location &element);
template <typename ClangDecl>
void process_comment(
const ClangDecl &decl, clanguml::common::model::decorated_element &e)
{
const auto *comment =
decl.getASTContext().getRawCommentForDeclNoCache(&decl);
if (comment != nullptr) {
e.set_comment(comment->getBriefText(decl.getASTContext()));
// if (mf.location().has_value()) {
// e.set_file(mf.location().value().file);
// e.set_line(mf.location().value().line);
// }
e.add_decorators(decorators::parse(e.comment().value()));
}
}
clang::SourceManager &source_manager_;
// Reference to the output diagram model
clanguml::class_diagram::model::diagram &diagram_;
// Reference to class diagram config
const clanguml::config::class_diagram &config_;
};
/*
class translation_unit_visitor {
public:
translation_unit_visitor(cppast::cpp_entity_index &idx,
@@ -106,8 +182,8 @@ public:
clanguml::class_diagram::model::class_ &c,
cppast::cpp_access_specifier_kind as);
void process_function_parameter(const cppast::cpp_function_parameter &param,
clanguml::class_diagram::model::class_method &m,
void process_function_parameter(const cppast::cpp_function_parameter
&param, clanguml::class_diagram::model::class_method &m,
clanguml::class_diagram::model::class_ &c,
const std::set<std::string> &template_parameter_names = {});
@@ -139,28 +215,27 @@ public:
void process_type_alias_template(const cppast::cpp_alias_template &at);
void process_class_children(const cppast::cpp_class &cls, model::class_ &c);
void process_class_children(const cppast::cpp_class &cls, model::class_
&c);
void process_class_bases(
const cppast::cpp_class &cls, model::class_ &c) const;
void process_unexposed_template_specialization_parameters(
const type_safe::optional_ref<const cppast::cpp_template_specialization>
&tspec,
model::class_ &c) const;
const type_safe::optional_ref<const
cppast::cpp_template_specialization> &tspec, model::class_ &c) const;
void process_exposed_template_specialization_parameters(
const type_safe::optional_ref<const cppast::cpp_template_specialization>
&tspec,
model::class_ &c);
const type_safe::optional_ref<const
cppast::cpp_template_specialization> &tspec, model::class_ &c);
void process_scope_template_parameters(
model::class_ &c, const cppast::cpp_scope_name &scope);
bool process_template_parameters(const cppast::cpp_class &cls,
model::class_ &c,
const type_safe::optional_ref<const cppast::cpp_template_specialization>
&tspec);
const type_safe::optional_ref<const
cppast::cpp_template_specialization> &tspec);
void process_class_containment(
const cppast::cpp_class &cls, model::class_ &c) const;
@@ -169,19 +244,17 @@ private:
std::unique_ptr<clanguml::class_diagram::model::class_>
build_template_instantiation(
const cppast::cpp_template_instantiation_type &t,
std::optional<clanguml::class_diagram::model::class_ *> parent = {});
std::optional<clanguml::class_diagram::model::class_ *> parent =
{});
/**
* Try to resolve a type instance into a type referenced through an alias.
* If t does not represent an alias, returns t.
*/
const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t);
const cppast::cpp_type &resolve_alias_template(
const cppast::cpp_type &type);
bool find_relationships_in_array(
found_relationships_t &relationships, const cppast::cpp_type &t) const;
found_relationships_t &relationships, const cppast::cpp_type &t)
const;
bool find_relationships_in_pointer(const cppast::cpp_type &t_,
found_relationships_t &relationships,
@@ -196,8 +269,8 @@ private:
common::model::relationship_t &relationship_type,
const cppast::cpp_type &t) const;
bool find_relationships_in_template_instantiation(const cppast::cpp_type &t,
const std::string &fn, found_relationships_t &relationships,
bool find_relationships_in_template_instantiation(const cppast::cpp_type
&t, const std::string &fn, found_relationships_t &relationships,
common::model::relationship_t relationship_type) const;
bool find_relationships_in_unexposed_template_params(
@@ -207,13 +280,13 @@ private:
void build_template_instantiation_primary_template(
const cppast::cpp_template_instantiation_type &t,
clanguml::class_diagram::model::class_ &tinst,
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
std::optional<clanguml::class_diagram::model::class_ *> &parent,
std::string &full_template_name) const;
std::deque<std::tuple<std::string, int, bool>>
&template_base_params, std::optional<clanguml::class_diagram::model::class_
*> &parent, std::string &full_template_name) const;
void build_template_instantiation_process_type_argument(
const std::optional<clanguml::class_diagram::model::class_ *> &parent,
model::class_ &tinst, const cppast::cpp_type &targ_type,
const std::optional<clanguml::class_diagram::model::class_ *>
&parent, model::class_ &tinst, const cppast::cpp_type &targ_type,
class_diagram::model::template_parameter &ct);
void build_template_instantiation_process_expression_argument(
@@ -221,18 +294,19 @@ private:
model::template_parameter &ct) const;
bool build_template_instantiation_add_base_classes(model::class_ &tinst,
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
int arg_index, bool variadic_params,
const model::template_parameter &ct) const;
std::deque<std::tuple<std::string, int, bool>>
&template_base_params, int arg_index, bool variadic_params, const
model::template_parameter &ct) const;
void process_function_parameter_find_relationships_in_template(
model::class_ &c, const std::set<std::string> &template_parameter_names,
const cppast::cpp_type &t);
model::class_ &c, const std::set<std::string>
&template_parameter_names, const cppast::cpp_type &t);
// ctx allows to track current visitor context, e.g. current namespace
translation_unit_context ctx;
bool simplify_system_template(
model::template_parameter &parameter, const std::string &basicString);
model::template_parameter &parameter, const std::string
&basicString);
bool add_nested_template_relationships(
const cppast::cpp_member_variable &mv, model::class_ &c,
@@ -242,4 +316,5 @@ private:
common::model::relationship_t &decorator_rtype,
std::string &decorator_rmult);
};
*/
}

View File

@@ -22,7 +22,10 @@
#include "util/error.h"
#include "util/util.h"
#include <cppast/libclang_parser.hpp>
//#include <cppast/libclang_parser.hpp>
#include <clang/Tooling/CompilationDatabase.h>
#include <clang/Tooling/Tooling.h>
#include <clang/Frontend/CompilerInstance.h>
#include <glob/glob.hpp>
#include <inja/inja.hpp>
@@ -225,10 +228,76 @@ void generator<C, D>::generate_link(std::ostream &ostr, const E &e) const
ostr << "]]";
}
template <typename DiagramModel, typename DiagramConfig,
typename TranslationUnitVisitor>
class diagram_ast_consumer : public clang::ASTConsumer {
TranslationUnitVisitor visitor_;
public:
explicit diagram_ast_consumer(clang::CompilerInstance &ci,
DiagramModel &diagram, const DiagramConfig &config)
: visitor_{ci.getSourceManager(), diagram, config}
{
}
virtual void HandleTranslationUnit(clang::ASTContext &ast_context)
{
// const auto* tud = ast_context.getTranslationUnitDecl();
//// tud->dump();
visitor_.TraverseDecl(ast_context.getTranslationUnitDecl());
}
};
template <typename DiagramModel, typename DiagramConfig,
typename DiagramVisitor>
class diagram_fronted_action : public clang::ASTFrontendAction {
public:
explicit diagram_fronted_action(
DiagramModel &diagram, const DiagramConfig &config)
: diagram_{diagram}
, config_{config}
{
}
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &CI, clang::StringRef file)
{
return std::make_unique<
diagram_ast_consumer<DiagramModel, DiagramConfig, DiagramVisitor>>(
CI, diagram_, config_);
}
private:
DiagramModel &diagram_;
const DiagramConfig &config_;
};
template <typename DiagramModel, typename DiagramConfig,
typename DiagramVisitor>
class diagram_action_visitor_factory
: public clang::tooling::FrontendActionFactory {
public:
explicit diagram_action_visitor_factory(
DiagramModel &diagram, const DiagramConfig &config)
: diagram_{diagram}
, config_{config}
{
}
std::unique_ptr<clang::FrontendAction> create() override
{
return std::make_unique<diagram_fronted_action<DiagramModel,
DiagramConfig, DiagramVisitor>>(diagram_, config_);
}
private:
DiagramModel &diagram_;
const DiagramConfig &config_;
};
template <typename DiagramModel, typename DiagramConfig,
typename DiagramVisitor>
std::unique_ptr<DiagramModel> generate(
const cppast::libclang_compilation_database &db, const std::string &name,
const clang::tooling::CompilationDatabase &db, const std::string &name,
DiagramConfig &config, bool verbose = false)
{
LOG_INFO("Generating diagram {}.puml", name);
@@ -247,6 +316,15 @@ std::unique_ptr<DiagramModel> generate(
std::back_inserter(translation_units));
}
// DiagramVisitor visitor(db, *diagram, config);
clang::tooling::ClangTool clang_tool(db, translation_units);
auto action_factory =
std::make_unique<diagram_action_visitor_factory<DiagramModel,
DiagramConfig, DiagramVisitor>>(*diagram, config);
clang_tool.run(action_factory.get());
/*
cppast::cpp_entity_index idx;
auto logger =
verbose ? cppast::default_logger() : cppast::default_quiet_logger();
@@ -258,6 +336,7 @@ std::unique_ptr<DiagramModel> generate(
cppast::parse_files(parser, translation_units, db);
for (auto &file : parser.files())
ctx(file);
*/
diagram->set_complete(true);

View File

@@ -29,6 +29,10 @@ element::element(const namespace_ &using_namespace)
{
}
element::id_t element::id() const { return id_; }
void element::set_id(element::id_t id) { id_ = id; }
void element::set_using_namespaces(const namespace_ &un)
{
using_namespace_ = un;

View File

@@ -34,10 +34,16 @@ namespace clanguml::common::model {
class element : public diagram_element, public source_location {
public:
using id_t = int64_t;
element(const namespace_ &using_namespace);
virtual ~element() = default;
id_t id() const;
void set_id(id_t id);
std::string name_and_ns() const
{
auto ns = ns_ | name();
@@ -71,6 +77,7 @@ public:
inja::json context() const override;
private:
id_t id_;
namespace_ ns_;
namespace_ using_namespace_;
};

View File

@@ -18,186 +18,17 @@
#include "translation_unit_visitor.h"
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_preprocessor.hpp>
#include <filesystem>
namespace clanguml::include_diagram::visitor {
translation_unit_visitor::translation_unit_visitor(
cppast::cpp_entity_index &idx,
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
clanguml::include_diagram::model::diagram &diagram,
const clanguml::config::include_diagram &config)
: ctx{idx, diagram, config}
: source_manager_{sm}
, diagram_{diagram}
, config_{config}
{
}
void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
{
assert(file.kind() == cppast::cpp_entity_kind::file_t);
process_source_file(static_cast<const cppast::cpp_file &>(file));
cppast::visit(file,
[&, this](const cppast::cpp_entity &e, cppast::visitor_info /*info*/) {
if (e.kind() == cppast::cpp_entity_kind::include_directive_t) {
const auto &inc =
static_cast<const cppast::cpp_include_directive &>(e);
process_include_directive(inc);
}
});
}
void translation_unit_visitor::process_include_directive(
const cppast::cpp_include_directive &include_directive)
{
using common::model::relationship;
using common::model::source_file;
using common::model::source_file_t;
assert(ctx.get_current_file().has_value());
LOG_DBG("Processing include directive {} in file {}",
include_directive.full_path(), ctx.get_current_file().value().name());
auto include_path = std::filesystem::path(include_directive.full_path());
// Make sure the file_path is absolute with respect to the
// filesystem, and in normal form
if (include_path.is_relative()) {
include_path = ctx.config().base_directory() / include_path;
}
include_path = include_path.lexically_normal();
if (ctx.diagram().should_include(source_file{include_path})) {
process_internal_header(include_directive, include_path);
}
else if (ctx.config().generate_system_headers() &&
include_directive.include_kind() == cppast::cpp_include_kind::system) {
process_external_system_header(include_directive);
}
else {
LOG_DBG("Skipping include directive to file {}", include_path.string());
}
}
void translation_unit_visitor::process_internal_header(
const cppast::cpp_include_directive &include_directive,
const std::filesystem::path &include_path)
{
// Relativize the path with respect to relative_to config option
auto relative_include_path = include_path;
if (ctx.config().relative_to) {
const std::filesystem::path relative_to{ctx.config().relative_to()};
relative_include_path =
std::filesystem::relative(include_path, relative_to);
}
// Check if this source file is already registered in the diagram,
// if not add it
auto diagram_path =
common::model::source_file{relative_include_path}.full_path();
if (!ctx.diagram().get_element(diagram_path).has_value()) {
ctx.diagram().add_file(std::make_unique<common::model::source_file>(
relative_include_path));
}
auto &include_file = ctx.diagram().get_element(diagram_path).value();
include_file.set_type(common::model::source_file_t::kHeader);
// Add relationship from the currently parsed source file to this
// include file
auto relationship_type = common::model::relationship_t::kAssociation;
if (include_directive.include_kind() == cppast::cpp_include_kind::system)
relationship_type = common::model::relationship_t::kDependency;
ctx.get_current_file().value().add_relationship(
common::model::relationship{relationship_type, include_file.alias()});
include_file.set_file(
std::filesystem::absolute(include_directive.full_path())
.lexically_normal()
.string());
include_file.set_line(0);
}
void translation_unit_visitor::process_external_system_header(
const cppast::cpp_include_directive &include_directive)
{
auto f = std::make_unique<common::model::source_file>();
f->set_name(include_directive.name());
f->set_type(common::model::source_file_t::kHeader);
if (!ctx.diagram().add_element(std::move(f)))
LOG_DBG("Include {} already in the model", include_directive.name());
auto dependency_relationship = common::model::relationship{
common::model::relationship_t::kDependency,
ctx.diagram().get_element(include_directive.name()).value().alias()};
ctx.get_current_file().value().add_relationship(
std::move(dependency_relationship));
}
void translation_unit_visitor::process_source_file(const cppast::cpp_file &file)
{
using common::model::relationship;
using common::model::source_file;
using common::model::source_file_t;
LOG_DBG("Processing source file {}", file.name());
auto file_path = std::filesystem::path(file.name());
// Make sure the file_path is absolute with respect to the
// filesystem, and in normal form
if (file_path.is_relative()) {
file_path = ctx.config().base_directory() / file_path;
}
file_path = file_path.lexically_normal();
if (ctx.diagram().should_include(source_file{file_path})) {
// Relativize the path with respect to relative_to config option
auto relative_file_path = file_path;
if (ctx.config().relative_to) {
const std::filesystem::path relative_to{ctx.config().relative_to()};
relative_file_path =
std::filesystem::relative(file_path, relative_to);
}
// Check if this source file is already registered in the diagram,
// if not add it
auto diagram_path = source_file{relative_file_path}.full_path();
if (!ctx.diagram().get_element(diagram_path).has_value()) {
ctx.diagram().add_file(
std::make_unique<source_file>(relative_file_path));
}
auto &source_file = ctx.diagram().get_element(diagram_path).value();
const std::string implementation_suffix_prefix{".c"};
if (file_path.has_extension() &&
util::starts_with(
file_path.extension().string(), implementation_suffix_prefix)) {
source_file.set_type(source_file_t::kImplementation);
}
else
source_file.set_type(source_file_t::kHeader);
source_file.set_file(
std::filesystem::absolute(file.name()).lexically_normal().string());
source_file.set_line(0);
ctx.set_current_file(type_safe::opt_ref(source_file));
}
else {
LOG_DBG("Skipping source file {}", file_path.string());
}
}
}

View File

@@ -23,11 +23,8 @@
#include "include_diagram/model/diagram.h"
#include "include_diagram/visitor/translation_unit_context.h"
#include <clang-c/CXCompilationDatabase.h>
#include <clang-c/Index.h>
#include <cppast/cpp_preprocessor.hpp>
#include <cppast/visitor.hpp>
#include <type_safe/reference.hpp>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Basic/SourceManager.h>
#include <functional>
#include <map>
@@ -36,28 +33,22 @@
namespace clanguml::include_diagram::visitor {
class translation_unit_visitor {
class translation_unit_visitor
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
public:
translation_unit_visitor(cppast::cpp_entity_index &idx,
translation_unit_visitor(clang::SourceManager &sm,
clanguml::include_diagram::model::diagram &diagram,
const clanguml::config::include_diagram &config);
void operator()(const cppast::cpp_entity &file);
private:
void process_include_directive(
const cppast::cpp_include_directive &include_directive);
clang::SourceManager &source_manager_;
void process_source_file(const cppast::cpp_file &file);
// Reference to the output diagram model
clanguml::include_diagram::model::diagram &diagram_;
void process_external_system_header(
const cppast::cpp_include_directive &include_directive);
void process_internal_header(
const cppast::cpp_include_directive &include_directive,
const std::filesystem::path &include_path);
// ctx allows to track current visitor context, e.g. current namespace
translation_unit_context ctx;
// Reference to class diagram config
const clanguml::config::include_diagram &config_;
};
}

View File

@@ -21,17 +21,6 @@
#include "common/model/namespace.h"
#include "cx/util.h"
#include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_array_type.hpp>
#include <cppast/cpp_class_template.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_enum.hpp>
#include <cppast/cpp_member_function.hpp>
#include <cppast/cpp_member_variable.hpp>
#include <cppast/cpp_namespace.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_type_alias.hpp>
#include <cppast/cpp_variable.hpp>
#include <spdlog/spdlog.h>
#include <deque>
@@ -45,467 +34,13 @@ using clanguml::common::model::relationship;
using clanguml::common::model::relationship_t;
using clanguml::package_diagram::model::diagram;
namespace detail {
bool is_constructor(const cppast::cpp_entity &e)
{
return dynamic_cast<const cppast::cpp_constructor *>(
dynamic_cast<const cppast::cpp_function_base *>(&e)) != nullptr;
}
}
translation_unit_visitor::translation_unit_visitor(
cppast::cpp_entity_index &idx,
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
clanguml::package_diagram::model::diagram &diagram,
const clanguml::config::package_diagram &config)
: ctx{idx, diagram, config}
: source_manager_{sm}
, diagram_{diagram}
, config_{config}
{
}
void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
{
cppast::visit(file,
[&, this](const cppast::cpp_entity &e, cppast::visitor_info info) {
auto name = e.name();
if (e.kind() == cppast::cpp_entity_kind::namespace_t) {
if (info.event ==
cppast::visitor_info::container_entity_enter) {
LOG_DBG("========== Visiting '{}' - {}", e.name(),
cppast::to_string(e.kind()));
const auto &ns_declaration =
static_cast<const cppast::cpp_namespace &>(e);
if (!ns_declaration.is_anonymous() &&
!ns_declaration.is_inline()) {
auto package_parent = ctx.get_namespace();
auto package_path = package_parent | e.name();
auto usn = ctx.config().using_namespace();
auto p = std::make_unique<package>(usn);
package_path = package_path.relative_to(usn);
if (e.location().has_value()) {
p->set_file(e.location().value().file);
p->set_line(e.location().value().line);
}
p->set_name(e.name());
p->set_namespace(package_parent);
if (ctx.diagram().should_include(*p)) {
if (ns_declaration.comment().has_value()) {
p->set_comment(
ns_declaration.comment().value());
p->add_decorators(decorators::parse(
ns_declaration.comment().value()));
}
p->set_style(p->style_spec());
for (const auto &attr :
ns_declaration.attributes()) {
if (attr.kind() ==
cppast::cpp_attribute_kind::deprecated) {
p->set_deprecated(true);
break;
}
}
if (!p->skip()) {
auto rns = p->get_relative_namespace();
ctx.diagram().add_package(std::move(p));
ctx.set_current_package(
ctx.diagram().get_element<package>(
package_path));
}
}
ctx.push_namespace(e.name());
}
}
else {
LOG_DBG("========== Leaving '{}' - {}", e.name(),
cppast::to_string(e.kind()));
const auto &ns_declaration =
static_cast<const cppast::cpp_namespace &>(e);
if (!ns_declaration.is_anonymous() &&
!ns_declaration.is_inline())
ctx.pop_namespace();
}
}
else if (e.kind() == cppast::cpp_entity_kind::namespace_alias_t) {
auto &na = static_cast<const cppast::cpp_namespace_alias &>(e);
for (const auto &alias_target :
na.target().get(ctx.entity_index())) {
auto full_ns = cx::util::full_name(ctx.get_namespace(), na);
ctx.add_namespace_alias(full_ns, alias_target);
}
}
else if (e.kind() ==
cppast::cpp_entity_kind::class_template_specialization_t) {
LOG_DBG("========== Visiting '{}' - {}",
cx::util::full_name(ctx.get_namespace(), e),
cppast::to_string(e.kind()));
auto &tspec = static_cast<
const cppast::cpp_class_template_specialization &>(e);
process_class_declaration(
tspec.class_(), type_safe::ref(tspec));
}
else if (e.kind() == cppast::cpp_entity_kind::class_t) {
LOG_DBG("========== Visiting '{}' - {}",
cx::util::full_name(ctx.get_namespace(), e),
cppast::to_string(e.kind()));
auto &cls = static_cast<const cppast::cpp_class &>(e);
if (cppast::get_definition(ctx.entity_index(), cls)) {
auto &clsdef = static_cast<const cppast::cpp_class &>(
cppast::get_definition(ctx.entity_index(), cls)
.value());
if (&cls != &clsdef) {
LOG_DBG("Forward declaration of class {} - skipping...",
cls.name());
return;
}
}
process_class_declaration(cls);
}
else if (e.kind() == cppast::cpp_entity_kind::function_t) {
LOG_DBG("========== Visiting '{}' - {}",
cx::util::full_name(ctx.get_namespace(), e),
cppast::to_string(e.kind()));
auto &f = static_cast<const cppast::cpp_function &>(e);
process_function(f);
}
else if (e.kind() == cppast::cpp_entity_kind::function_template_t) {
LOG_DBG("========== Visiting '{}' - {}",
cx::util::full_name(ctx.get_namespace(), e),
cppast::to_string(e.kind()));
auto &function_template =
static_cast<const cppast::cpp_function_template &>(e);
auto &f = static_cast<const cppast::cpp_function &>(
function_template.function());
process_function(f, detail::is_constructor(f));
}
else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) {
LOG_DBG("========== Visiting '{}' - {}",
cx::util::full_name(ctx.get_namespace(), e),
cppast::to_string(e.kind()));
auto &ta = static_cast<const cppast::cpp_type_alias &>(e);
type_alias t;
t.set_alias(cx::util::full_name(ctx.get_namespace(), ta));
t.set_underlying_type(cx::util::full_name(ta.underlying_type(),
ctx.entity_index(), cx::util::is_inside_class(e)));
ctx.add_type_alias(cx::util::full_name(ctx.get_namespace(), ta),
type_safe::ref(ta.underlying_type()));
}
else if (e.kind() == cppast::cpp_entity_kind::alias_template_t) {
LOG_DBG("========== Visiting '{}' - {}",
cx::util::full_name(ctx.get_namespace(), e),
cppast::to_string(e.kind()));
}
});
}
void translation_unit_visitor::process_class_declaration(
const cppast::cpp_class &cls,
type_safe::optional_ref<
const cppast::cpp_template_specialization> /*tspec*/)
{
auto current_package = ctx.get_current_package();
if (!current_package)
return;
std::vector<std::pair<std::string, relationship_t>> relationships;
// Process class elements
for (auto &child : cls) {
auto name = child.name();
if (child.kind() == cppast::cpp_entity_kind::member_variable_t) {
auto &mv = static_cast<const cppast::cpp_member_variable &>(child);
find_relationships(
mv.type(), relationships, relationship_t::kDependency);
}
else if (child.kind() == cppast::cpp_entity_kind::variable_t) {
auto &mv = static_cast<const cppast::cpp_variable &>(child);
find_relationships(
mv.type(), relationships, relationship_t::kDependency);
}
else if (child.kind() == cppast::cpp_entity_kind::constructor_t) {
auto &mc = static_cast<const cppast::cpp_function &>(child);
process_function(mc, true);
}
else if (child.kind() == cppast::cpp_entity_kind::destructor_t) {
// Skip - destructor won't have any interesting candidates
// for relationships
}
else if (child.kind() == cppast::cpp_entity_kind::member_function_t) {
auto &mf = static_cast<const cppast::cpp_member_function &>(child);
for (const auto &param : mf.parameters())
find_relationships(
param.type(), relationships, relationship_t::kDependency);
find_relationships(
mf.return_type(), relationships, relationship_t::kDependency);
}
else if (child.kind() == cppast::cpp_entity_kind::function_t) {
auto &mf = static_cast<const cppast::cpp_function &>(child);
for (const auto &param : mf.parameters())
find_relationships(
param.type(), relationships, relationship_t::kDependency);
}
else if (child.kind() == cppast::cpp_entity_kind::function_template_t) {
auto &tm = static_cast<const cppast::cpp_function_template &>(child)
.function();
for (const auto &param : tm.parameters())
find_relationships(
param.type(), relationships, relationship_t::kDependency);
if (tm.kind() == cppast::cpp_entity_kind::member_function_t)
find_relationships(
static_cast<const cppast::cpp_member_function &>(tm)
.return_type(),
relationships, relationship_t::kDependency);
}
else if (child.kind() == cppast::cpp_entity_kind::constructor_t) {
auto &mc = static_cast<const cppast::cpp_constructor &>(child);
for (const auto &param : mc.parameters())
find_relationships(
param.type(), relationships, relationship_t::kDependency);
}
else {
LOG_DBG("Found some other class child: {} ({})", child.name(),
cppast::to_string(child.kind()));
}
}
// Process class bases
for (auto &base : cls.bases()) {
find_relationships(
base.type(), relationships, relationship_t::kDependency);
}
for (const auto &dependency : relationships) {
auto destination = common::model::namespace_{std::get<0>(dependency)};
if (!ctx.get_namespace().starts_with(destination) &&
!destination.starts_with(ctx.get_namespace())) {
relationship r{
relationship_t::kDependency, std::get<0>(dependency)};
current_package.value().add_relationship(std::move(r));
}
}
}
void translation_unit_visitor::process_function(
const cppast::cpp_function &f, bool skip_return_type)
{
std::vector<std::pair<std::string, relationship_t>> relationships;
auto current_package = ctx.get_current_package();
if (!current_package)
return;
for (const auto &param : f.parameters())
find_relationships(
param.type(), relationships, relationship_t::kDependency);
if (!skip_return_type) {
find_relationships(
f.return_type(), relationships, relationship_t::kDependency);
}
for (const auto &dependency : relationships) {
auto destination = common::model::namespace_{std::get<0>(dependency)};
if (!ctx.get_namespace().starts_with(destination) &&
!destination.starts_with(ctx.get_namespace())) {
relationship r{
relationship_t::kDependency, std::get<0>(dependency)};
current_package.value().add_relationship(std::move(r));
}
}
}
bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
std::vector<std::pair<std::string, common::model::relationship_t>>
&relationships,
relationship_t relationship_hint)
{
bool found{false};
if (t_.kind() == cppast::cpp_type_kind::template_parameter_t)
return false;
const auto fn = cx::util::full_name(
resolve_alias(cppast::remove_cv(t_)), ctx.entity_index(), false);
auto t_ns = common::model::namespace_{fn};
auto t_name = t_ns.name();
t_ns.pop_back();
const auto &t_raw = resolve_alias(cppast::remove_cv(t_));
if (t_raw.kind() == cppast::cpp_type_kind::user_defined_t) {
auto t_raw_ns = cx::util::ns(t_raw, ctx.entity_index());
const auto &type_entities =
static_cast<const cppast::cpp_user_defined_type &>(t_raw)
.entity()
.get(ctx.entity_index());
if (type_entities.size() > 0) {
const auto &type_entity = type_entities[0];
const auto &t_raw_ns = cx::util::entity_ns(type_entity.get());
const auto &t_raw_ns_final = cx::util::ns(t_raw_ns.value()) +
"::" + cx::util::full_name({}, t_raw_ns.value());
t_ns = common::model::namespace_{t_raw_ns_final};
}
}
std::vector<std::string> possible_matches;
possible_matches.push_back(t_ns.to_string());
const auto fn_ns = cx::util::ns(cppast::remove_cv(t_), ctx.entity_index());
LOG_DBG("Finding relationships for type {}, {}, {}", cppast::to_string(t_),
cppast::to_string(t_.kind()), fn);
relationship_t relationship_type = relationship_hint;
const auto &t = cppast::remove_cv(cx::util::unreferenced(t_));
if (t.kind() == cppast::cpp_type_kind::array_t) {
auto &a = static_cast<const cppast::cpp_array_type &>(t);
found = find_relationships(
a.value_type(), relationships, relationship_t::kDependency);
return found;
}
auto name = cppast::to_string(t);
if (t_.kind() == cppast::cpp_type_kind::pointer_t) {
auto &p = static_cast<const cppast::cpp_pointer_type &>(t_);
auto rt = relationship_t::kAssociation;
if (relationship_hint == relationship_t::kDependency)
rt = relationship_hint;
found = find_relationships(p.pointee(), relationships, rt);
}
else if (t_.kind() == cppast::cpp_type_kind::reference_t) {
auto &r = static_cast<const cppast::cpp_reference_type &>(t_);
auto rt = relationship_t::kAssociation;
if (r.reference_kind() == cppast::cpp_reference::cpp_ref_rvalue) {
rt = relationship_t::kAggregation;
}
if (relationship_hint == relationship_t::kDependency)
rt = relationship_hint;
found = find_relationships(r.referee(), relationships, rt);
}
if (cppast::remove_cv(t_).kind() == cppast::cpp_type_kind::user_defined_t) {
LOG_DBG("User defined type: {} | {}", cppast::to_string(t_),
cppast::to_string(t_.canonical()));
// Check if t_ has an alias in the alias index
if (ctx.has_type_alias(fn)) {
LOG_DBG("Found relationship in alias of {} | {}", fn,
cppast::to_string(ctx.get_type_alias(fn).get()));
found = find_relationships(
ctx.get_type_alias(fn).get(), relationships, relationship_type);
if (found)
return found;
}
for (const auto &pm : possible_matches) {
relationships.emplace_back(pm, relationship_t::kDependency);
}
}
else if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
const auto &tinst =
static_cast<const cppast::cpp_template_instantiation_type &>(t);
if (!tinst.arguments_exposed()) {
LOG_DBG("Template instantiation {} has no exposed arguments", name);
return found;
}
const auto args = tinst.arguments().value();
// Try to match common containers
// TODO: Refactor to a separate class with configurable
// container list
if (name.find("std::unique_ptr") == 0) {
found = find_relationships(args[0u].type().value(), relationships,
relationship_t::kDependency);
}
else if (name.find("std::shared_ptr") == 0) {
found = find_relationships(args[0u].type().value(), relationships,
relationship_t::kDependency);
}
else if (name.find("std::weak_ptr") == 0) {
found = find_relationships(args[0u].type().value(), relationships,
relationship_t::kDependency);
}
else if (name.find("std::vector") == 0) {
found = find_relationships(args[0u].type().value(), relationships,
relationship_t::kDependency);
}
else if (ctx.diagram().should_include(t_ns, t_name)) {
LOG_DBG("User defined template instantiation: {} | {}",
cppast::to_string(t_), cppast::to_string(t_.canonical()));
relationships.emplace_back(
cppast::to_string(t), relationship_t::kDependency);
// Check if t_ has an alias in the alias index
if (ctx.has_type_alias(fn)) {
LOG_DBG("Find relationship in alias of {} | {}", fn,
cppast::to_string(ctx.get_type_alias(fn).get()));
found = find_relationships(ctx.get_type_alias(fn).get(),
relationships, relationship_type);
if (found)
return found;
}
return found;
}
else {
for (const auto &arg : args) {
if (arg.type()) {
found = find_relationships(
arg.type().value(), relationships, relationship_type);
}
}
}
}
return found;
}
const cppast::cpp_type &translation_unit_visitor::resolve_alias(
const cppast::cpp_type &type)
{
const auto &raw_type = cppast::remove_cv(cx::util::unreferenced(type));
const auto type_full_name =
cx::util::full_name(raw_type, ctx.entity_index(), false);
if (ctx.has_type_alias(type_full_name)) {
return ctx.get_type_alias_final(raw_type).get();
}
return type;
}
}

View File

@@ -21,17 +21,8 @@
#include "package_diagram/model/diagram.h"
#include "package_diagram/visitor/translation_unit_context.h"
#include <clang-c/CXCompilationDatabase.h>
#include <clang-c/Index.h>
#include <cppast/cpp_friend.hpp>
#include <cppast/cpp_function_template.hpp>
#include <cppast/cpp_member_function.hpp>
#include <cppast/cpp_member_variable.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_type.hpp>
#include <cppast/visitor.hpp>
#include <type_safe/reference.hpp>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Basic/SourceManager.h>
#include <common/model/enums.h>
#include <common/model/package.h>
@@ -42,34 +33,21 @@
namespace clanguml::package_diagram::visitor {
class translation_unit_visitor {
class translation_unit_visitor
: public clang::RecursiveASTVisitor<translation_unit_visitor>{
public:
translation_unit_visitor(cppast::cpp_entity_index &idx,
translation_unit_visitor(clang::SourceManager &sm,
clanguml::package_diagram::model::diagram &diagram,
const clanguml::config::package_diagram &config);
void operator()(const cppast::cpp_entity &file);
void process_class_declaration(const cppast::cpp_class &cls,
type_safe::optional_ref<const cppast::cpp_template_specialization>
tspec = nullptr);
void process_function(
const cppast::cpp_function &f, bool skip_return_type = false);
bool find_relationships(const cppast::cpp_type &t_,
std::vector<std::pair<std::string, common::model::relationship_t>>
&relationships,
common::model::relationship_t relationship_hint);
private:
/**
* Try to resolve a type instance into a type referenced through an alias.
* If t does not represent an alias, returns t.
*/
const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t);
clang::SourceManager &source_manager_;
// ctx allows to track current visitor context, e.g. current namespace
translation_unit_context ctx;
// Reference to the output diagram model
clanguml::package_diagram::model::diagram &diagram_;
// Reference to class diagram config
const clanguml::config::package_diagram &config_;
};
}

View File

@@ -22,20 +22,20 @@
#include "cx/util.h"
#include "translation_unit_context.h"
#include <cppast/cpp_function.hpp>
#include <cppast/cpp_member_function.hpp>
#include <cppast/visitor.hpp>
namespace clanguml::sequence_diagram::visitor {
translation_unit_visitor::translation_unit_visitor(
cppast::cpp_entity_index &idx,
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
clanguml::sequence_diagram::model::diagram &diagram,
const clanguml::config::sequence_diagram &config)
: ctx{idx, diagram, config}
: source_manager_{sm}
, diagram_{diagram}
, config_{config}
{
}
/*
void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
{
using clanguml::common::model::message_t;
@@ -138,7 +138,7 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
using cppast::cpp_member_function_call;
using cppast::visitor_info;
cppast::visit(file, [&, this](const cpp_entity &e, visitor_info /*info*/) {
cppast::visit(file, [&, this](const cpp_entity &e, visitor_info) {
if (e.kind() == cpp_entity_kind::function_t) {
const auto &function = static_cast<const cpp_function &>(e);
process_activities(function);
@@ -149,4 +149,5 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
}
});
}
*/
}

View File

@@ -21,22 +21,26 @@
#include "sequence_diagram/model/diagram.h"
#include "sequence_diagram/visitor/translation_unit_context.h"
#include <cppast/cpp_function.hpp>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Basic/SourceManager.h>
namespace clanguml::sequence_diagram::visitor {
class translation_unit_visitor {
class translation_unit_visitor
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
public:
translation_unit_visitor(cppast::cpp_entity_index &idx,
translation_unit_visitor(clang::SourceManager &sm,
clanguml::sequence_diagram::model::diagram &diagram,
const clanguml::config::sequence_diagram &config);
void operator()(const cppast::cpp_entity &file);
private:
void process_activities(const cppast::cpp_function &e);
clang::SourceManager &source_manager_;
// ctx allows to track current visitor context, e.g. current namespace
translation_unit_context ctx;
// Reference to the output diagram model
clanguml::sequence_diagram::model::diagram &diagram_;
// Reference to class diagram config
const clanguml::config::sequence_diagram &config_;
};
}

View File

@@ -15,6 +15,7 @@ file(GLOB_RECURSE TEST_CONFIG_YMLS test_config_data/*.yml)
set(CLANG_UML_TEST_LIBRARIES
${LIBCLANG_LIBRARIES}
${YAML_CPP_LIBRARIES}
${LIBTOOLING_LIBS}
clang-umllib
cppast
Threads::Threads)

View File

@@ -31,7 +31,7 @@ TEST_CASE("t00002", "[test-case][class]")
REQUIRE(diagram->exclude().namespaces.size() == 0);
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00002_class");

View File

@@ -21,7 +21,7 @@ public:
auto auto_method() { return 1; }
auto double_int(const int i) { return 2 * i; }
auto sum(const double a, const double b) { return a + b; }
auto sum(const double a, const double b) { return a_ + b_ + c_; }
auto default_int(int i = 12) { return i + 10; }
std::string default_string(int i, std::string s = "abc")
@@ -49,7 +49,7 @@ private:
void private_method() { }
int private_member;
int a, b, c;
int a_, b_, c_;
};
int A::static_int = 1;

View File

@@ -28,7 +28,7 @@ TEST_CASE("t00003", "[test-case][class]")
REQUIRE(diagram->exclude().namespaces.size() == 0);
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00003_class");
REQUIRE(model->should_include(std::string("clanguml::t00003::A")));
@@ -54,11 +54,11 @@ TEST_CASE("t00003", "[test-case][class]")
REQUIRE_THAT(puml, (IsField<Protected>("protected_member", "int")));
REQUIRE_THAT(puml, (IsField<Private>("private_member", "int")));
REQUIRE_THAT(
puml, (IsField<Public, Static>("auto_member", "unsigned long const")));
puml, (IsField<Public, Static>("auto_member", "const unsigned long")));
REQUIRE_THAT(puml, (IsField<Private>("a", "int")));
REQUIRE_THAT(puml, (IsField<Private>("b", "int")));
REQUIRE_THAT(puml, (IsField<Private>("c", "int")));
REQUIRE_THAT(puml, (IsField<Private>("a_", "int")));
REQUIRE_THAT(puml, (IsField<Private>("b_", "int")));
REQUIRE_THAT(puml, (IsField<Private>("c_", "int")));
save_puml(
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);

View File

@@ -27,7 +27,7 @@ TEST_CASE("t00004", "[test-case][class]")
REQUIRE(diagram->include().namespaces.size() == 1);
REQUIRE(diagram->exclude().namespaces.size() == 0);
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00004_class");
REQUIRE(!model->should_include("std::vector"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00005", "[test-case][class]")
REQUIRE(diagram->name == "t00005_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00005_class");
REQUIRE(model->should_include("clanguml::t00005::A"));
@@ -51,8 +51,8 @@ TEST_CASE("t00005", "[test-case][class]")
REQUIRE_THAT(puml, IsClass(_A("R")));
REQUIRE_THAT(puml, (IsField<Public>("some_int", "int")));
REQUIRE_THAT(puml, (IsField<Public>("some_int_pointer", "int*")));
REQUIRE_THAT(puml, (IsField<Public>("some_int_pointer_pointer", "int**")));
REQUIRE_THAT(puml, (IsField<Public>("some_int_pointer", "int *")));
REQUIRE_THAT(puml, (IsField<Public>("some_int_pointer_pointer", "int **")));
REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("A"), "+a"));
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("B"), "+b"));

View File

@@ -24,7 +24,7 @@ TEST_CASE("t00006", "[test-case][class]")
REQUIRE(diagram->name == "t00006_class");
auto model = generate_class_diagram(db, diagram);
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00006_class");

View File

@@ -30,18 +30,25 @@ void inject_diagram_options(std::shared_ptr<clanguml::config::diagram> diagram)
diagram->generate_links.set(links_config);
}
std::pair<clanguml::config::config, cppast::libclang_compilation_database>
std::pair<clanguml::config::config,
std::unique_ptr<clang::tooling::CompilationDatabase>>
load_config(const std::string &test_name)
{
auto config = clanguml::config::load(test_name + "/.clang-uml");
cppast::libclang_compilation_database db(config.compilation_database_dir());
std::string err{};
auto compilation_database =
clang::tooling::CompilationDatabase::autoDetectFromDirectory(
config.compilation_database_dir(), err);
return std::make_pair(std::move(config), std::move(db));
if(!err.empty())
throw std::runtime_error{err};
return std::make_pair(std::move(config), std::move(compilation_database));
}
std::unique_ptr<clanguml::sequence_diagram::model::diagram>
generate_sequence_diagram(cppast::libclang_compilation_database &db,
generate_sequence_diagram(clang::tooling::CompilationDatabase &db,
std::shared_ptr<clanguml::config::diagram> diagram)
{
using diagram_config = clanguml::config::sequence_diagram;
@@ -59,7 +66,7 @@ generate_sequence_diagram(cppast::libclang_compilation_database &db,
}
std::unique_ptr<clanguml::class_diagram::model::diagram> generate_class_diagram(
cppast::libclang_compilation_database &db,
clang::tooling::CompilationDatabase &db,
std::shared_ptr<clanguml::config::diagram> diagram)
{
using diagram_config = clanguml::config::class_diagram;
@@ -77,7 +84,7 @@ std::unique_ptr<clanguml::class_diagram::model::diagram> generate_class_diagram(
}
std::unique_ptr<clanguml::package_diagram::model::diagram>
generate_package_diagram(cppast::libclang_compilation_database &db,
generate_package_diagram(clang::tooling::CompilationDatabase &db,
std::shared_ptr<clanguml::config::diagram> diagram)
{
using diagram_config = clanguml::config::package_diagram;
@@ -93,7 +100,7 @@ generate_package_diagram(cppast::libclang_compilation_database &db,
}
std::unique_ptr<clanguml::include_diagram::model::diagram>
generate_include_diagram(cppast::libclang_compilation_database &db,
generate_include_diagram(clang::tooling::CompilationDatabase &db,
std::shared_ptr<clanguml::config::diagram> diagram)
{
using diagram_config = clanguml::config::include_diagram;
@@ -188,76 +195,76 @@ using namespace clanguml::test::matchers;
#include "t00004/test_case.h"
#include "t00005/test_case.h"
#include "t00006/test_case.h"
#include "t00007/test_case.h"
#include "t00008/test_case.h"
#include "t00009/test_case.h"
#include "t00010/test_case.h"
#include "t00011/test_case.h"
#include "t00012/test_case.h"
#include "t00013/test_case.h"
#include "t00014/test_case.h"
#include "t00015/test_case.h"
#include "t00016/test_case.h"
#include "t00017/test_case.h"
#include "t00018/test_case.h"
#include "t00019/test_case.h"
#include "t00020/test_case.h"
#include "t00021/test_case.h"
#include "t00022/test_case.h"
#include "t00023/test_case.h"
#include "t00024/test_case.h"
#include "t00025/test_case.h"
#include "t00026/test_case.h"
#include "t00027/test_case.h"
#include "t00028/test_case.h"
#include "t00029/test_case.h"
#include "t00030/test_case.h"
#include "t00031/test_case.h"
#include "t00032/test_case.h"
#include "t00033/test_case.h"
#include "t00034/test_case.h"
#include "t00035/test_case.h"
#include "t00036/test_case.h"
#include "t00037/test_case.h"
#include "t00038/test_case.h"
#include "t00039/test_case.h"
#include "t00040/test_case.h"
#include "t00041/test_case.h"
#include "t00042/test_case.h"
#include "t00043/test_case.h"
#include "t00044/test_case.h"
#include "t00045/test_case.h"
#include "t00046/test_case.h"
//#include "t00007/test_case.h"
//#include "t00008/test_case.h"
//#include "t00009/test_case.h"
//#include "t00010/test_case.h"
//#include "t00011/test_case.h"
//#include "t00012/test_case.h"
//#include "t00013/test_case.h"
//#include "t00014/test_case.h"
//#include "t00015/test_case.h"
//#include "t00016/test_case.h"
//#include "t00017/test_case.h"
//#include "t00018/test_case.h"
//#include "t00019/test_case.h"
//#include "t00020/test_case.h"
//#include "t00021/test_case.h"
//#include "t00022/test_case.h"
//#include "t00023/test_case.h"
//#include "t00024/test_case.h"
//#include "t00025/test_case.h"
//#include "t00026/test_case.h"
//#include "t00027/test_case.h"
//#include "t00028/test_case.h"
//#include "t00029/test_case.h"
//#include "t00030/test_case.h"
//#include "t00031/test_case.h"
//#include "t00032/test_case.h"
//#include "t00033/test_case.h"
//#include "t00034/test_case.h"
//#include "t00035/test_case.h"
//#include "t00036/test_case.h"
//#include "t00037/test_case.h"
//#include "t00038/test_case.h"
//#include "t00039/test_case.h"
//#include "t00040/test_case.h"
//#include "t00041/test_case.h"
//#include "t00042/test_case.h"
//#include "t00043/test_case.h"
//#include "t00044/test_case.h"
//#include "t00045/test_case.h"
//#include "t00046/test_case.h"
//
// Sequence diagram tests
////
//// Sequence diagram tests
////
//#include "t20001/test_case.h"
//#include "t20002/test_case.h"
//
#include "t20001/test_case.h"
#include "t20002/test_case.h"
////
//// Package diagram tests
////
//#include "t30001/test_case.h"
//#include "t30002/test_case.h"
//#include "t30003/test_case.h"
//#include "t30004/test_case.h"
//#include "t30005/test_case.h"
//#include "t30006/test_case.h"
//#include "t30007/test_case.h"
//#include "t30008/test_case.h"
//
// Package diagram tests
////
//// Include diagram tests
////
//#include "t40001/test_case.h"
//#include "t40002/test_case.h"
//#include "t40003/test_case.h"
//
#include "t30001/test_case.h"
#include "t30002/test_case.h"
#include "t30003/test_case.h"
#include "t30004/test_case.h"
#include "t30005/test_case.h"
#include "t30006/test_case.h"
#include "t30007/test_case.h"
#include "t30008/test_case.h"
//
// Include diagram tests
//
#include "t40001/test_case.h"
#include "t40002/test_case.h"
#include "t40003/test_case.h"
//
// Other tests (e.g. configuration file)
//
#include "t90000/test_case.h"
////
//// Other tests (e.g. configuration file)
////
//#include "t90000/test_case.h"
//
// Main test function

View File

@@ -37,6 +37,9 @@
#include "catch.h"
#include <clang/Tooling/Tooling.h>
#include <clang/Tooling/CompilationDatabase.h>
#include <algorithm>
#include <complex>
#include <filesystem>
@@ -49,7 +52,7 @@ using Catch::Matchers::StartsWith;
using Catch::Matchers::VectorContains;
using namespace clanguml::util;
std::pair<clanguml::config::config, cppast::libclang_compilation_database>
std::pair<clanguml::config::config, std::unique_ptr<clang::tooling::CompilationDatabase>>
load_config(const std::string &test_name);
std::string generate_sequence_puml(
@@ -399,6 +402,9 @@ ContainsMatcher IsMethod(std::string const &name,
if constexpr (has_type<Abstract, Ts...>())
pattern += " = 0";
if constexpr (has_type<Default, Ts...>())
pattern += " = default";
pattern += " : " + type;
return ContainsMatcher(CasedString(pattern, caseSensitivity));