First passing test cases using libtooling visitor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,6 +14,7 @@ _deps
|
|||||||
lib/
|
lib/
|
||||||
bin/
|
bin/
|
||||||
*.swp
|
*.swp
|
||||||
|
*.bak
|
||||||
/puml/
|
/puml/
|
||||||
/debug/
|
/debug/
|
||||||
/release/
|
/release/
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ message(STATUS "Checking for yaml-cpp...")
|
|||||||
find_package(yaml-cpp REQUIRED)
|
find_package(yaml-cpp REQUIRED)
|
||||||
|
|
||||||
message(STATUS "Checking for libclang...")
|
message(STATUS "Checking for libclang...")
|
||||||
set(LLVM_PREFERRED_VERSION 12.0.0)
|
set(LLVM_PREFERRED_VERSION 14.0.0)
|
||||||
# Add
|
# Add
|
||||||
# -DLLVM_CONFIG_PATH=/path/to/llvm-config
|
# -DLLVM_CONFIG_PATH=/path/to/llvm-config
|
||||||
# to use custom LLVM version
|
# to use custom LLVM version
|
||||||
@@ -60,8 +60,18 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|||||||
OUTPUT_VARIABLE GCC_STDDEF_INCLUDE)
|
OUTPUT_VARIABLE GCC_STDDEF_INCLUDE)
|
||||||
message(STATUS "FOUND GCC STDDEF INCLUDE ${GCC_STDDEF_INCLUDE}")
|
message(STATUS "FOUND GCC STDDEF INCLUDE ${GCC_STDDEF_INCLUDE}")
|
||||||
include_directories(${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()
|
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(${CLANG_UML_INSTALL_INCLUDE_DIR})
|
||||||
include_directories(${YAML_CPP_INCLUDE_DIR})
|
include_directories(${YAML_CPP_INCLUDE_DIR})
|
||||||
include_directories(${UML_HEADERS_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)
|
set(MAIN_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc)
|
||||||
list(REMOVE_ITEM SOURCES ${MAIN_SOURCE_FILE})
|
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_library(clang-umllib OBJECT ${SOURCES})
|
||||||
|
|
||||||
add_executable(clang-uml ${MAIN_SOURCE_FILE})
|
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)
|
target_compile_features(clang-uml PRIVATE cxx_std_17)
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,6 @@
|
|||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
#include <cppast/cpp_entity_index.hpp>
|
|
||||||
#include <cppast/libclang_parser.hpp>
|
|
||||||
#include <glob/glob.hpp>
|
#include <glob/glob.hpp>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,26 +18,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "class_diagram/model/diagram.h"
|
#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 "common/model/enums.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
#include <clang-c/CXCompilationDatabase.h>
|
//#include <clang-c/CXCompilationDatabase.h>
|
||||||
#include <clang-c/Index.h>
|
//#include <clang-c/Index.h>
|
||||||
#include <cppast/cpp_friend.hpp>
|
//#include <cppast/cpp_friend.hpp>
|
||||||
#include <cppast/cpp_function_template.hpp>
|
//#include <cppast/cpp_function_template.hpp>
|
||||||
#include <cppast/cpp_member_function.hpp>
|
//#include <cppast/cpp_member_function.hpp>
|
||||||
#include <cppast/cpp_member_variable.hpp>
|
//#include <cppast/cpp_member_variable.hpp>
|
||||||
#include <cppast/cpp_template.hpp>
|
//#include <cppast/cpp_template.hpp>
|
||||||
#include <cppast/cpp_template_parameter.hpp>
|
//#include <cppast/cpp_template_parameter.hpp>
|
||||||
#include <cppast/cpp_type.hpp>
|
//#include <cppast/cpp_type.hpp>
|
||||||
#include <cppast/visitor.hpp>
|
//#include <cppast/visitor.hpp>
|
||||||
#include <type_safe/reference.hpp>
|
#include <clang/AST/RecursiveASTVisitor.h>
|
||||||
|
#include <clang/Basic/SourceManager.h>
|
||||||
#include <class_diagram/model/class.h>
|
#include <class_diagram/model/class.h>
|
||||||
#include <common/model/enums.h>
|
#include <common/model/enums.h>
|
||||||
#include <cppast/cpp_alias_template.hpp>
|
#include <type_safe/reference.hpp>
|
||||||
#include <cppast/cpp_type_alias.hpp>
|
//#include <cppast/cpp_alias_template.hpp>
|
||||||
|
//#include <cppast/cpp_type_alias.hpp>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -47,13 +48,88 @@
|
|||||||
namespace clanguml::class_diagram::visitor {
|
namespace clanguml::class_diagram::visitor {
|
||||||
|
|
||||||
using found_relationships_t =
|
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 {
|
class translation_unit_visitor
|
||||||
//
|
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
|
||||||
// std::vector<std::unique_ptr<nested_template_relationships>> children;
|
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 ¶m,
|
||||||
|
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 {
|
class translation_unit_visitor {
|
||||||
public:
|
public:
|
||||||
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
||||||
@@ -106,8 +182,8 @@ public:
|
|||||||
clanguml::class_diagram::model::class_ &c,
|
clanguml::class_diagram::model::class_ &c,
|
||||||
cppast::cpp_access_specifier_kind as);
|
cppast::cpp_access_specifier_kind as);
|
||||||
|
|
||||||
void process_function_parameter(const cppast::cpp_function_parameter ¶m,
|
void process_function_parameter(const cppast::cpp_function_parameter
|
||||||
clanguml::class_diagram::model::class_method &m,
|
¶m, clanguml::class_diagram::model::class_method &m,
|
||||||
clanguml::class_diagram::model::class_ &c,
|
clanguml::class_diagram::model::class_ &c,
|
||||||
const std::set<std::string> &template_parameter_names = {});
|
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_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(
|
void process_class_bases(
|
||||||
const cppast::cpp_class &cls, model::class_ &c) const;
|
const cppast::cpp_class &cls, model::class_ &c) const;
|
||||||
|
|
||||||
void process_unexposed_template_specialization_parameters(
|
void process_unexposed_template_specialization_parameters(
|
||||||
const type_safe::optional_ref<const cppast::cpp_template_specialization>
|
const type_safe::optional_ref<const
|
||||||
&tspec,
|
cppast::cpp_template_specialization> &tspec, model::class_ &c) const;
|
||||||
model::class_ &c) const;
|
|
||||||
|
|
||||||
void process_exposed_template_specialization_parameters(
|
void process_exposed_template_specialization_parameters(
|
||||||
const type_safe::optional_ref<const cppast::cpp_template_specialization>
|
const type_safe::optional_ref<const
|
||||||
&tspec,
|
cppast::cpp_template_specialization> &tspec, model::class_ &c);
|
||||||
model::class_ &c);
|
|
||||||
|
|
||||||
void process_scope_template_parameters(
|
void process_scope_template_parameters(
|
||||||
model::class_ &c, const cppast::cpp_scope_name &scope);
|
model::class_ &c, const cppast::cpp_scope_name &scope);
|
||||||
|
|
||||||
bool process_template_parameters(const cppast::cpp_class &cls,
|
bool process_template_parameters(const cppast::cpp_class &cls,
|
||||||
model::class_ &c,
|
model::class_ &c,
|
||||||
const type_safe::optional_ref<const cppast::cpp_template_specialization>
|
const type_safe::optional_ref<const
|
||||||
&tspec);
|
cppast::cpp_template_specialization> &tspec);
|
||||||
|
|
||||||
void process_class_containment(
|
void process_class_containment(
|
||||||
const cppast::cpp_class &cls, model::class_ &c) const;
|
const cppast::cpp_class &cls, model::class_ &c) const;
|
||||||
@@ -169,19 +244,17 @@ private:
|
|||||||
std::unique_ptr<clanguml::class_diagram::model::class_>
|
std::unique_ptr<clanguml::class_diagram::model::class_>
|
||||||
build_template_instantiation(
|
build_template_instantiation(
|
||||||
const cppast::cpp_template_instantiation_type &t,
|
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(const cppast::cpp_type &t);
|
||||||
|
|
||||||
const cppast::cpp_type &resolve_alias_template(
|
const cppast::cpp_type &resolve_alias_template(
|
||||||
const cppast::cpp_type &type);
|
const cppast::cpp_type &type);
|
||||||
|
|
||||||
bool find_relationships_in_array(
|
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_,
|
bool find_relationships_in_pointer(const cppast::cpp_type &t_,
|
||||||
found_relationships_t &relationships,
|
found_relationships_t &relationships,
|
||||||
@@ -196,8 +269,8 @@ private:
|
|||||||
common::model::relationship_t &relationship_type,
|
common::model::relationship_t &relationship_type,
|
||||||
const cppast::cpp_type &t) const;
|
const cppast::cpp_type &t) const;
|
||||||
|
|
||||||
bool find_relationships_in_template_instantiation(const cppast::cpp_type &t,
|
bool find_relationships_in_template_instantiation(const cppast::cpp_type
|
||||||
const std::string &fn, found_relationships_t &relationships,
|
&t, const std::string &fn, found_relationships_t &relationships,
|
||||||
common::model::relationship_t relationship_type) const;
|
common::model::relationship_t relationship_type) const;
|
||||||
|
|
||||||
bool find_relationships_in_unexposed_template_params(
|
bool find_relationships_in_unexposed_template_params(
|
||||||
@@ -207,13 +280,13 @@ private:
|
|||||||
void build_template_instantiation_primary_template(
|
void build_template_instantiation_primary_template(
|
||||||
const cppast::cpp_template_instantiation_type &t,
|
const cppast::cpp_template_instantiation_type &t,
|
||||||
clanguml::class_diagram::model::class_ &tinst,
|
clanguml::class_diagram::model::class_ &tinst,
|
||||||
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
|
std::deque<std::tuple<std::string, int, bool>>
|
||||||
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
&template_base_params, std::optional<clanguml::class_diagram::model::class_
|
||||||
std::string &full_template_name) const;
|
*> &parent, std::string &full_template_name) const;
|
||||||
|
|
||||||
void build_template_instantiation_process_type_argument(
|
void build_template_instantiation_process_type_argument(
|
||||||
const std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
const std::optional<clanguml::class_diagram::model::class_ *>
|
||||||
model::class_ &tinst, const cppast::cpp_type &targ_type,
|
&parent, model::class_ &tinst, const cppast::cpp_type &targ_type,
|
||||||
class_diagram::model::template_parameter &ct);
|
class_diagram::model::template_parameter &ct);
|
||||||
|
|
||||||
void build_template_instantiation_process_expression_argument(
|
void build_template_instantiation_process_expression_argument(
|
||||||
@@ -221,18 +294,19 @@ private:
|
|||||||
model::template_parameter &ct) const;
|
model::template_parameter &ct) const;
|
||||||
|
|
||||||
bool build_template_instantiation_add_base_classes(model::class_ &tinst,
|
bool build_template_instantiation_add_base_classes(model::class_ &tinst,
|
||||||
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
|
std::deque<std::tuple<std::string, int, bool>>
|
||||||
int arg_index, bool variadic_params,
|
&template_base_params, int arg_index, bool variadic_params, const
|
||||||
const model::template_parameter &ct) const;
|
model::template_parameter &ct) const;
|
||||||
|
|
||||||
void process_function_parameter_find_relationships_in_template(
|
void process_function_parameter_find_relationships_in_template(
|
||||||
model::class_ &c, const std::set<std::string> &template_parameter_names,
|
model::class_ &c, const std::set<std::string>
|
||||||
const cppast::cpp_type &t);
|
&template_parameter_names, const cppast::cpp_type &t);
|
||||||
|
|
||||||
// ctx allows to track current visitor context, e.g. current namespace
|
// ctx allows to track current visitor context, e.g. current namespace
|
||||||
translation_unit_context ctx;
|
translation_unit_context ctx;
|
||||||
bool simplify_system_template(
|
bool simplify_system_template(
|
||||||
model::template_parameter ¶meter, const std::string &basicString);
|
model::template_parameter ¶meter, const std::string
|
||||||
|
&basicString);
|
||||||
|
|
||||||
bool add_nested_template_relationships(
|
bool add_nested_template_relationships(
|
||||||
const cppast::cpp_member_variable &mv, model::class_ &c,
|
const cppast::cpp_member_variable &mv, model::class_ &c,
|
||||||
@@ -242,4 +316,5 @@ private:
|
|||||||
common::model::relationship_t &decorator_rtype,
|
common::model::relationship_t &decorator_rtype,
|
||||||
std::string &decorator_rmult);
|
std::string &decorator_rmult);
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,10 @@
|
|||||||
#include "util/error.h"
|
#include "util/error.h"
|
||||||
#include "util/util.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 <glob/glob.hpp>
|
||||||
#include <inja/inja.hpp>
|
#include <inja/inja.hpp>
|
||||||
|
|
||||||
@@ -225,10 +228,76 @@ void generator<C, D>::generate_link(std::ostream &ostr, const E &e) const
|
|||||||
ostr << "]]";
|
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,
|
template <typename DiagramModel, typename DiagramConfig,
|
||||||
typename DiagramVisitor>
|
typename DiagramVisitor>
|
||||||
std::unique_ptr<DiagramModel> generate(
|
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)
|
DiagramConfig &config, bool verbose = false)
|
||||||
{
|
{
|
||||||
LOG_INFO("Generating diagram {}.puml", name);
|
LOG_INFO("Generating diagram {}.puml", name);
|
||||||
@@ -247,6 +316,15 @@ std::unique_ptr<DiagramModel> generate(
|
|||||||
std::back_inserter(translation_units));
|
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;
|
cppast::cpp_entity_index idx;
|
||||||
auto logger =
|
auto logger =
|
||||||
verbose ? cppast::default_logger() : cppast::default_quiet_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);
|
cppast::parse_files(parser, translation_units, db);
|
||||||
for (auto &file : parser.files())
|
for (auto &file : parser.files())
|
||||||
ctx(file);
|
ctx(file);
|
||||||
|
*/
|
||||||
|
|
||||||
diagram->set_complete(true);
|
diagram->set_complete(true);
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
void element::set_using_namespaces(const namespace_ &un)
|
||||||
{
|
{
|
||||||
using_namespace_ = un;
|
using_namespace_ = un;
|
||||||
|
|||||||
@@ -34,10 +34,16 @@ namespace clanguml::common::model {
|
|||||||
|
|
||||||
class element : public diagram_element, public source_location {
|
class element : public diagram_element, public source_location {
|
||||||
public:
|
public:
|
||||||
|
using id_t = int64_t;
|
||||||
|
|
||||||
element(const namespace_ &using_namespace);
|
element(const namespace_ &using_namespace);
|
||||||
|
|
||||||
virtual ~element() = default;
|
virtual ~element() = default;
|
||||||
|
|
||||||
|
id_t id() const;
|
||||||
|
|
||||||
|
void set_id(id_t id);
|
||||||
|
|
||||||
std::string name_and_ns() const
|
std::string name_and_ns() const
|
||||||
{
|
{
|
||||||
auto ns = ns_ | name();
|
auto ns = ns_ | name();
|
||||||
@@ -71,6 +77,7 @@ public:
|
|||||||
inja::json context() const override;
|
inja::json context() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
id_t id_;
|
||||||
namespace_ ns_;
|
namespace_ ns_;
|
||||||
namespace_ using_namespace_;
|
namespace_ using_namespace_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,186 +18,17 @@
|
|||||||
|
|
||||||
#include "translation_unit_visitor.h"
|
#include "translation_unit_visitor.h"
|
||||||
|
|
||||||
#include <cppast/cpp_entity_kind.hpp>
|
|
||||||
#include <cppast/cpp_preprocessor.hpp>
|
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
namespace clanguml::include_diagram::visitor {
|
namespace clanguml::include_diagram::visitor {
|
||||||
|
|
||||||
translation_unit_visitor::translation_unit_visitor(
|
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
||||||
cppast::cpp_entity_index &idx,
|
|
||||||
clanguml::include_diagram::model::diagram &diagram,
|
clanguml::include_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::include_diagram &config)
|
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,8 @@
|
|||||||
#include "include_diagram/model/diagram.h"
|
#include "include_diagram/model/diagram.h"
|
||||||
#include "include_diagram/visitor/translation_unit_context.h"
|
#include "include_diagram/visitor/translation_unit_context.h"
|
||||||
|
|
||||||
#include <clang-c/CXCompilationDatabase.h>
|
#include <clang/AST/RecursiveASTVisitor.h>
|
||||||
#include <clang-c/Index.h>
|
#include <clang/Basic/SourceManager.h>
|
||||||
#include <cppast/cpp_preprocessor.hpp>
|
|
||||||
#include <cppast/visitor.hpp>
|
|
||||||
#include <type_safe/reference.hpp>
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -36,28 +33,22 @@
|
|||||||
|
|
||||||
namespace clanguml::include_diagram::visitor {
|
namespace clanguml::include_diagram::visitor {
|
||||||
|
|
||||||
class translation_unit_visitor {
|
class translation_unit_visitor
|
||||||
|
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
|
||||||
public:
|
public:
|
||||||
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
translation_unit_visitor(clang::SourceManager &sm,
|
||||||
clanguml::include_diagram::model::diagram &diagram,
|
clanguml::include_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::include_diagram &config);
|
const clanguml::config::include_diagram &config);
|
||||||
|
|
||||||
void operator()(const cppast::cpp_entity &file);
|
void operator()(const cppast::cpp_entity &file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process_include_directive(
|
clang::SourceManager &source_manager_;
|
||||||
const cppast::cpp_include_directive &include_directive);
|
|
||||||
|
|
||||||
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(
|
// Reference to class diagram config
|
||||||
const cppast::cpp_include_directive &include_directive);
|
const clanguml::config::include_diagram &config_;
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,17 +21,6 @@
|
|||||||
#include "common/model/namespace.h"
|
#include "common/model/namespace.h"
|
||||||
#include "cx/util.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 <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@@ -45,467 +34,13 @@ using clanguml::common::model::relationship;
|
|||||||
using clanguml::common::model::relationship_t;
|
using clanguml::common::model::relationship_t;
|
||||||
using clanguml::package_diagram::model::diagram;
|
using clanguml::package_diagram::model::diagram;
|
||||||
|
|
||||||
namespace detail {
|
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
||||||
|
|
||||||
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,
|
|
||||||
clanguml::package_diagram::model::diagram &diagram,
|
clanguml::package_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::package_diagram &config)
|
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 ¶m : 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 ¶m : 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 ¶m : 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 ¶m : 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 ¶m : 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,17 +21,8 @@
|
|||||||
#include "package_diagram/model/diagram.h"
|
#include "package_diagram/model/diagram.h"
|
||||||
#include "package_diagram/visitor/translation_unit_context.h"
|
#include "package_diagram/visitor/translation_unit_context.h"
|
||||||
|
|
||||||
#include <clang-c/CXCompilationDatabase.h>
|
#include <clang/AST/RecursiveASTVisitor.h>
|
||||||
#include <clang-c/Index.h>
|
#include <clang/Basic/SourceManager.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 <common/model/enums.h>
|
#include <common/model/enums.h>
|
||||||
#include <common/model/package.h>
|
#include <common/model/package.h>
|
||||||
@@ -42,34 +33,21 @@
|
|||||||
|
|
||||||
namespace clanguml::package_diagram::visitor {
|
namespace clanguml::package_diagram::visitor {
|
||||||
|
|
||||||
class translation_unit_visitor {
|
class translation_unit_visitor
|
||||||
|
: public clang::RecursiveASTVisitor<translation_unit_visitor>{
|
||||||
public:
|
public:
|
||||||
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
translation_unit_visitor(clang::SourceManager &sm,
|
||||||
clanguml::package_diagram::model::diagram &diagram,
|
clanguml::package_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::package_diagram &config);
|
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:
|
private:
|
||||||
/**
|
clang::SourceManager &source_manager_;
|
||||||
* 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);
|
|
||||||
|
|
||||||
// ctx allows to track current visitor context, e.g. current namespace
|
// Reference to the output diagram model
|
||||||
translation_unit_context ctx;
|
clanguml::package_diagram::model::diagram &diagram_;
|
||||||
|
|
||||||
|
// Reference to class diagram config
|
||||||
|
const clanguml::config::package_diagram &config_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,20 +22,20 @@
|
|||||||
#include "cx/util.h"
|
#include "cx/util.h"
|
||||||
#include "translation_unit_context.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 {
|
namespace clanguml::sequence_diagram::visitor {
|
||||||
|
|
||||||
translation_unit_visitor::translation_unit_visitor(
|
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
||||||
cppast::cpp_entity_index &idx,
|
|
||||||
clanguml::sequence_diagram::model::diagram &diagram,
|
clanguml::sequence_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::sequence_diagram &config)
|
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)
|
void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
|
||||||
{
|
{
|
||||||
using clanguml::common::model::message_t;
|
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::cpp_member_function_call;
|
||||||
using cppast::visitor_info;
|
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) {
|
if (e.kind() == cpp_entity_kind::function_t) {
|
||||||
const auto &function = static_cast<const cpp_function &>(e);
|
const auto &function = static_cast<const cpp_function &>(e);
|
||||||
process_activities(function);
|
process_activities(function);
|
||||||
@@ -149,4 +149,5 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,22 +21,26 @@
|
|||||||
#include "sequence_diagram/model/diagram.h"
|
#include "sequence_diagram/model/diagram.h"
|
||||||
#include "sequence_diagram/visitor/translation_unit_context.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 {
|
namespace clanguml::sequence_diagram::visitor {
|
||||||
|
|
||||||
class translation_unit_visitor {
|
class translation_unit_visitor
|
||||||
|
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
|
||||||
public:
|
public:
|
||||||
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
translation_unit_visitor(clang::SourceManager &sm,
|
||||||
clanguml::sequence_diagram::model::diagram &diagram,
|
clanguml::sequence_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::sequence_diagram &config);
|
const clanguml::config::sequence_diagram &config);
|
||||||
|
|
||||||
void operator()(const cppast::cpp_entity &file);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process_activities(const cppast::cpp_function &e);
|
clang::SourceManager &source_manager_;
|
||||||
|
|
||||||
// ctx allows to track current visitor context, e.g. current namespace
|
// Reference to the output diagram model
|
||||||
translation_unit_context ctx;
|
clanguml::sequence_diagram::model::diagram &diagram_;
|
||||||
|
|
||||||
|
// Reference to class diagram config
|
||||||
|
const clanguml::config::sequence_diagram &config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ file(GLOB_RECURSE TEST_CONFIG_YMLS test_config_data/*.yml)
|
|||||||
set(CLANG_UML_TEST_LIBRARIES
|
set(CLANG_UML_TEST_LIBRARIES
|
||||||
${LIBCLANG_LIBRARIES}
|
${LIBCLANG_LIBRARIES}
|
||||||
${YAML_CPP_LIBRARIES}
|
${YAML_CPP_LIBRARIES}
|
||||||
|
${LIBTOOLING_LIBS}
|
||||||
clang-umllib
|
clang-umllib
|
||||||
cppast
|
cppast
|
||||||
Threads::Threads)
|
Threads::Threads)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ TEST_CASE("t00002", "[test-case][class]")
|
|||||||
|
|
||||||
REQUIRE(diagram->exclude().namespaces.size() == 0);
|
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");
|
REQUIRE(model->name() == "t00002_class");
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public:
|
|||||||
auto auto_method() { return 1; }
|
auto auto_method() { return 1; }
|
||||||
|
|
||||||
auto double_int(const int i) { return 2 * i; }
|
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; }
|
auto default_int(int i = 12) { return i + 10; }
|
||||||
std::string default_string(int i, std::string s = "abc")
|
std::string default_string(int i, std::string s = "abc")
|
||||||
@@ -49,7 +49,7 @@ private:
|
|||||||
void private_method() { }
|
void private_method() { }
|
||||||
|
|
||||||
int private_member;
|
int private_member;
|
||||||
int a, b, c;
|
int a_, b_, c_;
|
||||||
};
|
};
|
||||||
|
|
||||||
int A::static_int = 1;
|
int A::static_int = 1;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ TEST_CASE("t00003", "[test-case][class]")
|
|||||||
|
|
||||||
REQUIRE(diagram->exclude().namespaces.size() == 0);
|
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->name() == "t00003_class");
|
||||||
REQUIRE(model->should_include(std::string("clanguml::t00003::A")));
|
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<Protected>("protected_member", "int")));
|
||||||
REQUIRE_THAT(puml, (IsField<Private>("private_member", "int")));
|
REQUIRE_THAT(puml, (IsField<Private>("private_member", "int")));
|
||||||
REQUIRE_THAT(
|
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>("a_", "int")));
|
||||||
REQUIRE_THAT(puml, (IsField<Private>("b", "int")));
|
REQUIRE_THAT(puml, (IsField<Private>("b_", "int")));
|
||||||
REQUIRE_THAT(puml, (IsField<Private>("c", "int")));
|
REQUIRE_THAT(puml, (IsField<Private>("c_", "int")));
|
||||||
|
|
||||||
save_puml(
|
save_puml(
|
||||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ TEST_CASE("t00004", "[test-case][class]")
|
|||||||
REQUIRE(diagram->include().namespaces.size() == 1);
|
REQUIRE(diagram->include().namespaces.size() == 1);
|
||||||
REQUIRE(diagram->exclude().namespaces.size() == 0);
|
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->name() == "t00004_class");
|
||||||
REQUIRE(!model->should_include("std::vector"));
|
REQUIRE(!model->should_include("std::vector"));
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ TEST_CASE("t00005", "[test-case][class]")
|
|||||||
|
|
||||||
REQUIRE(diagram->name == "t00005_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->name() == "t00005_class");
|
||||||
REQUIRE(model->should_include("clanguml::t00005::A"));
|
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, IsClass(_A("R")));
|
||||||
|
|
||||||
REQUIRE_THAT(puml, (IsField<Public>("some_int", "int")));
|
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", "int *")));
|
||||||
REQUIRE_THAT(puml, (IsField<Public>("some_int_pointer_pointer", "int**")));
|
REQUIRE_THAT(puml, (IsField<Public>("some_int_pointer_pointer", "int **")));
|
||||||
|
|
||||||
REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("A"), "+a"));
|
REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("A"), "+a"));
|
||||||
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("B"), "+b"));
|
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("B"), "+b"));
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ TEST_CASE("t00006", "[test-case][class]")
|
|||||||
|
|
||||||
REQUIRE(diagram->name == "t00006_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");
|
REQUIRE(model->name() == "t00006_class");
|
||||||
|
|
||||||
|
|||||||
@@ -30,18 +30,25 @@ void inject_diagram_options(std::shared_ptr<clanguml::config::diagram> diagram)
|
|||||||
diagram->generate_links.set(links_config);
|
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)
|
load_config(const std::string &test_name)
|
||||||
{
|
{
|
||||||
auto config = clanguml::config::load(test_name + "/.clang-uml");
|
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>
|
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)
|
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||||
{
|
{
|
||||||
using diagram_config = clanguml::config::sequence_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(
|
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)
|
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||||
{
|
{
|
||||||
using diagram_config = clanguml::config::class_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>
|
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)
|
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||||
{
|
{
|
||||||
using diagram_config = clanguml::config::package_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>
|
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)
|
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||||
{
|
{
|
||||||
using diagram_config = clanguml::config::include_diagram;
|
using diagram_config = clanguml::config::include_diagram;
|
||||||
@@ -188,76 +195,76 @@ using namespace clanguml::test::matchers;
|
|||||||
#include "t00004/test_case.h"
|
#include "t00004/test_case.h"
|
||||||
#include "t00005/test_case.h"
|
#include "t00005/test_case.h"
|
||||||
#include "t00006/test_case.h"
|
#include "t00006/test_case.h"
|
||||||
#include "t00007/test_case.h"
|
//#include "t00007/test_case.h"
|
||||||
#include "t00008/test_case.h"
|
//#include "t00008/test_case.h"
|
||||||
#include "t00009/test_case.h"
|
//#include "t00009/test_case.h"
|
||||||
#include "t00010/test_case.h"
|
//#include "t00010/test_case.h"
|
||||||
#include "t00011/test_case.h"
|
//#include "t00011/test_case.h"
|
||||||
#include "t00012/test_case.h"
|
//#include "t00012/test_case.h"
|
||||||
#include "t00013/test_case.h"
|
//#include "t00013/test_case.h"
|
||||||
#include "t00014/test_case.h"
|
//#include "t00014/test_case.h"
|
||||||
#include "t00015/test_case.h"
|
//#include "t00015/test_case.h"
|
||||||
#include "t00016/test_case.h"
|
//#include "t00016/test_case.h"
|
||||||
#include "t00017/test_case.h"
|
//#include "t00017/test_case.h"
|
||||||
#include "t00018/test_case.h"
|
//#include "t00018/test_case.h"
|
||||||
#include "t00019/test_case.h"
|
//#include "t00019/test_case.h"
|
||||||
#include "t00020/test_case.h"
|
//#include "t00020/test_case.h"
|
||||||
#include "t00021/test_case.h"
|
//#include "t00021/test_case.h"
|
||||||
#include "t00022/test_case.h"
|
//#include "t00022/test_case.h"
|
||||||
#include "t00023/test_case.h"
|
//#include "t00023/test_case.h"
|
||||||
#include "t00024/test_case.h"
|
//#include "t00024/test_case.h"
|
||||||
#include "t00025/test_case.h"
|
//#include "t00025/test_case.h"
|
||||||
#include "t00026/test_case.h"
|
//#include "t00026/test_case.h"
|
||||||
#include "t00027/test_case.h"
|
//#include "t00027/test_case.h"
|
||||||
#include "t00028/test_case.h"
|
//#include "t00028/test_case.h"
|
||||||
#include "t00029/test_case.h"
|
//#include "t00029/test_case.h"
|
||||||
#include "t00030/test_case.h"
|
//#include "t00030/test_case.h"
|
||||||
#include "t00031/test_case.h"
|
//#include "t00031/test_case.h"
|
||||||
#include "t00032/test_case.h"
|
//#include "t00032/test_case.h"
|
||||||
#include "t00033/test_case.h"
|
//#include "t00033/test_case.h"
|
||||||
#include "t00034/test_case.h"
|
//#include "t00034/test_case.h"
|
||||||
#include "t00035/test_case.h"
|
//#include "t00035/test_case.h"
|
||||||
#include "t00036/test_case.h"
|
//#include "t00036/test_case.h"
|
||||||
#include "t00037/test_case.h"
|
//#include "t00037/test_case.h"
|
||||||
#include "t00038/test_case.h"
|
//#include "t00038/test_case.h"
|
||||||
#include "t00039/test_case.h"
|
//#include "t00039/test_case.h"
|
||||||
#include "t00040/test_case.h"
|
//#include "t00040/test_case.h"
|
||||||
#include "t00041/test_case.h"
|
//#include "t00041/test_case.h"
|
||||||
#include "t00042/test_case.h"
|
//#include "t00042/test_case.h"
|
||||||
#include "t00043/test_case.h"
|
//#include "t00043/test_case.h"
|
||||||
#include "t00044/test_case.h"
|
//#include "t00044/test_case.h"
|
||||||
#include "t00045/test_case.h"
|
//#include "t00045/test_case.h"
|
||||||
#include "t00046/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"
|
//// Other tests (e.g. configuration file)
|
||||||
#include "t30003/test_case.h"
|
////
|
||||||
#include "t30004/test_case.h"
|
//#include "t90000/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"
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Main test function
|
// Main test function
|
||||||
|
|||||||
@@ -37,6 +37,9 @@
|
|||||||
|
|
||||||
#include "catch.h"
|
#include "catch.h"
|
||||||
|
|
||||||
|
#include <clang/Tooling/Tooling.h>
|
||||||
|
#include <clang/Tooling/CompilationDatabase.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@@ -49,7 +52,7 @@ using Catch::Matchers::StartsWith;
|
|||||||
using Catch::Matchers::VectorContains;
|
using Catch::Matchers::VectorContains;
|
||||||
using namespace clanguml::util;
|
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);
|
load_config(const std::string &test_name);
|
||||||
|
|
||||||
std::string generate_sequence_puml(
|
std::string generate_sequence_puml(
|
||||||
@@ -399,6 +402,9 @@ ContainsMatcher IsMethod(std::string const &name,
|
|||||||
if constexpr (has_type<Abstract, Ts...>())
|
if constexpr (has_type<Abstract, Ts...>())
|
||||||
pattern += " = 0";
|
pattern += " = 0";
|
||||||
|
|
||||||
|
if constexpr (has_type<Default, Ts...>())
|
||||||
|
pattern += " = default";
|
||||||
|
|
||||||
pattern += " : " + type;
|
pattern += " : " + type;
|
||||||
|
|
||||||
return ContainsMatcher(CasedString(pattern, caseSensitivity));
|
return ContainsMatcher(CasedString(pattern, caseSensitivity));
|
||||||
|
|||||||
Reference in New Issue
Block a user