Continued refactor of template building code to template_builder
This commit is contained in:
@@ -29,32 +29,19 @@ namespace clanguml::class_diagram::visitor {
|
||||
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
||||
clanguml::class_diagram::model::diagram &diagram,
|
||||
const clanguml::config::class_diagram &config)
|
||||
: common::visitor::translation_unit_visitor{sm, config}
|
||||
, diagram_{diagram}
|
||||
, config_{config}
|
||||
, template_builder_{diagram_, config_, *this,
|
||||
[uns = config_.using_namespace()](const clang::NamedDecl *decl) {
|
||||
auto cls = std::make_unique<class_>(uns);
|
||||
cls->is_struct(common::is_struct(decl));
|
||||
return cls;
|
||||
},
|
||||
[this](common::model::template_element &template_instantiation_base,
|
||||
const std::string &full_name, common::id_t templated_decl_id) {
|
||||
find_instantiation_relationships(
|
||||
template_instantiation_base, full_name, templated_decl_id);
|
||||
},
|
||||
[](clanguml::common::model::template_element &tinst,
|
||||
clanguml::common::id_t id, const std::string &full_name) {
|
||||
model::class_parent cp;
|
||||
cp.set_access(common::model::access_t::kPublic);
|
||||
cp.set_name(full_name);
|
||||
cp.set_id(id);
|
||||
|
||||
dynamic_cast<class_ &>(tinst).add_parent(std::move(cp));
|
||||
}}
|
||||
: visitor_specialization_t{sm, diagram, config}
|
||||
, template_builder_{diagram, config, *this}
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<class_> translation_unit_visitor::create_element(
|
||||
const clang::NamedDecl *decl) const
|
||||
{
|
||||
auto cls = std::make_unique<class_>(config().using_namespace());
|
||||
cls->is_struct(common::is_struct(decl));
|
||||
return cls;
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
|
||||
{
|
||||
assert(ns != nullptr);
|
||||
@@ -126,7 +113,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm)
|
||||
enm->getQualifiedNameAsString(),
|
||||
enm->getLocation().printToString(source_manager()));
|
||||
|
||||
auto e_ptr = std::make_unique<enum_>(config_.using_namespace());
|
||||
auto e_ptr = std::make_unique<enum_>(config().using_namespace());
|
||||
auto &e = *e_ptr;
|
||||
|
||||
std::string qualified_name = common::get_qualified_name(*enm);
|
||||
@@ -274,7 +261,7 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl(
|
||||
|
||||
auto template_specialization_ptr =
|
||||
std::make_unique<class_>(config().using_namespace());
|
||||
tbuilder().build(
|
||||
tbuilder().build_from_template_specialization_type(
|
||||
*template_specialization_ptr, cls, *template_type_specialization_ptr);
|
||||
|
||||
if (diagram().should_include(*template_specialization_ptr)) {
|
||||
@@ -309,11 +296,10 @@ bool translation_unit_visitor::VisitClassTemplateDecl(
|
||||
|
||||
add_processed_template_class(cls->getQualifiedNameAsString());
|
||||
|
||||
tbuilder().build_from_template_declaration(*c_ptr, *cls, *c_ptr);
|
||||
|
||||
// Override the id with the template id, for now we don't care about the
|
||||
// underlying templated class id
|
||||
|
||||
process_template_parameters(*cls, *c_ptr, *c_ptr);
|
||||
|
||||
const auto cls_full_name = c_ptr->full_name(false);
|
||||
const auto id = common::to_id(cls_full_name);
|
||||
|
||||
@@ -420,7 +406,7 @@ bool translation_unit_visitor::TraverseConceptDecl(clang::ConceptDecl *cpt)
|
||||
|
||||
id_mapper().add(cpt->getID(), concept_id);
|
||||
|
||||
process_template_parameters(*cpt, *concept_model);
|
||||
tbuilder().build_from_template_declaration(*concept_model, *cpt);
|
||||
|
||||
constexpr auto kMaxConstraintCount = 24U;
|
||||
llvm::SmallVector<const clang::Expr *, kMaxConstraintCount> constraints{};
|
||||
@@ -774,7 +760,7 @@ translation_unit_visitor::create_concept_declaration(clang::ConceptDecl *cpt)
|
||||
return {};
|
||||
|
||||
auto concept_ptr{
|
||||
std::make_unique<model::concept_>(config_.using_namespace())};
|
||||
std::make_unique<model::concept_>(config().using_namespace())};
|
||||
auto &concept_model = *concept_ptr;
|
||||
|
||||
auto ns = common::get_template_namespace(*cpt);
|
||||
@@ -803,7 +789,7 @@ std::unique_ptr<class_> translation_unit_visitor::create_record_declaration(
|
||||
if (!should_include(rec))
|
||||
return {};
|
||||
|
||||
auto record_ptr{std::make_unique<class_>(config_.using_namespace())};
|
||||
auto record_ptr{std::make_unique<class_>(config().using_namespace())};
|
||||
auto &record = *record_ptr;
|
||||
|
||||
process_record_parent(rec, record, namespace_{});
|
||||
@@ -849,7 +835,7 @@ std::unique_ptr<class_> translation_unit_visitor::create_class_declaration(
|
||||
if (!should_include(cls))
|
||||
return {};
|
||||
|
||||
auto c_ptr{std::make_unique<class_>(config_.using_namespace())};
|
||||
auto c_ptr{std::make_unique<class_>(config().using_namespace())};
|
||||
auto &c = *c_ptr;
|
||||
|
||||
auto ns{common::get_tag_namespace(*cls)};
|
||||
@@ -909,11 +895,11 @@ void translation_unit_visitor::process_record_parent(
|
||||
}
|
||||
}
|
||||
|
||||
if (id_opt && diagram_.find<class_>(*id_opt)) {
|
||||
if (id_opt && diagram().find<class_>(*id_opt)) {
|
||||
// Here we have 2 options, either:
|
||||
// - the parent is a regular C++ class/struct
|
||||
// - the parent is a class template declaration/specialization
|
||||
auto parent_class = diagram_.find<class_>(*id_opt);
|
||||
auto parent_class = diagram().find<class_>(*id_opt);
|
||||
|
||||
c.set_namespace(parent_ns);
|
||||
const auto cls_name = cls->getNameAsString();
|
||||
@@ -963,102 +949,6 @@ void translation_unit_visitor::process_class_declaration(
|
||||
c.complete(true);
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::process_template_parameters(
|
||||
const clang::TemplateDecl &template_declaration,
|
||||
common::model::template_trait &c,
|
||||
common::optional_ref<common::model::element> templated_element)
|
||||
{
|
||||
LOG_DBG("Processing {} template parameters...",
|
||||
common::get_qualified_name(template_declaration));
|
||||
|
||||
if (template_declaration.getTemplateParameters() == nullptr)
|
||||
return false;
|
||||
|
||||
for (const auto *parameter :
|
||||
*template_declaration.getTemplateParameters()) {
|
||||
if (clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter) !=
|
||||
nullptr) {
|
||||
const auto *template_type_parameter =
|
||||
clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter);
|
||||
|
||||
std::optional<std::string> default_arg;
|
||||
if (template_type_parameter->hasDefaultArgument()) {
|
||||
default_arg =
|
||||
template_type_parameter->getDefaultArgument().getAsString();
|
||||
}
|
||||
|
||||
auto parameter_name = template_type_parameter->getNameAsString();
|
||||
if (parameter_name.empty())
|
||||
parameter_name = "typename";
|
||||
|
||||
auto ct = template_parameter::make_template_type(parameter_name,
|
||||
default_arg, template_type_parameter->isParameterPack());
|
||||
|
||||
if (template_type_parameter->getTypeConstraint() != nullptr) {
|
||||
util::if_not_null(template_type_parameter->getTypeConstraint()
|
||||
->getNamedConcept(),
|
||||
[this, &ct, &templated_element](
|
||||
const clang::ConceptDecl *named_concept) mutable {
|
||||
ct.set_concept_constraint(
|
||||
named_concept->getQualifiedNameAsString());
|
||||
if (templated_element &&
|
||||
should_include(named_concept)) {
|
||||
templated_element.value().add_relationship(
|
||||
{relationship_t::kConstraint,
|
||||
id_mapper()
|
||||
.get_global_id(named_concept->getID())
|
||||
.value(),
|
||||
access_t::kNone, ct.name().value()});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
c.add_template(std::move(ct));
|
||||
}
|
||||
else if (clang::dyn_cast_or_null<clang::NonTypeTemplateParmDecl>(
|
||||
parameter) != nullptr) {
|
||||
const auto *template_nontype_parameter =
|
||||
clang::dyn_cast_or_null<clang::NonTypeTemplateParmDecl>(
|
||||
parameter);
|
||||
|
||||
std::optional<std::string> default_arg;
|
||||
|
||||
if (template_nontype_parameter->hasDefaultArgument())
|
||||
default_arg = common::to_string(
|
||||
template_nontype_parameter->getDefaultArgument());
|
||||
|
||||
auto ct = template_parameter::make_non_type_template(
|
||||
template_nontype_parameter->getType().getAsString(),
|
||||
template_nontype_parameter->getNameAsString(), default_arg,
|
||||
template_nontype_parameter->isParameterPack());
|
||||
|
||||
c.add_template(std::move(ct));
|
||||
}
|
||||
else if (clang::dyn_cast_or_null<clang::TemplateTemplateParmDecl>(
|
||||
parameter) != nullptr) {
|
||||
const auto *template_template_parameter =
|
||||
clang::dyn_cast_or_null<clang::TemplateTemplateParmDecl>(
|
||||
parameter);
|
||||
std::optional<std::string> default_arg;
|
||||
if (template_template_parameter->hasDefaultArgument()) {
|
||||
default_arg = common::to_string(
|
||||
template_template_parameter->getDefaultArgument()
|
||||
.getArgument());
|
||||
}
|
||||
auto ct = template_parameter::make_template_template_type(
|
||||
template_template_parameter->getNameAsString(), default_arg,
|
||||
template_template_parameter->isParameterPack());
|
||||
|
||||
c.add_template(std::move(ct));
|
||||
}
|
||||
else {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_class_bases(
|
||||
const clang::CXXRecordDecl *cls, class_ &c)
|
||||
{
|
||||
@@ -1074,7 +964,8 @@ void translation_unit_visitor::process_class_bases(
|
||||
tsp != nullptr) {
|
||||
auto template_specialization_ptr =
|
||||
std::make_unique<class_>(config().using_namespace());
|
||||
tbuilder().build(*template_specialization_ptr, cls, *tsp, {});
|
||||
tbuilder().build_from_template_specialization_type(
|
||||
*template_specialization_ptr, cls, *tsp, {});
|
||||
|
||||
cp.set_id(template_specialization_ptr->id());
|
||||
cp.set_name(template_specialization_ptr->full_name(false));
|
||||
@@ -1330,7 +1221,8 @@ void translation_unit_visitor::process_method(
|
||||
if (unaliased_type != nullptr) {
|
||||
auto template_specialization_ptr =
|
||||
std::make_unique<class_>(config().using_namespace());
|
||||
tbuilder().build(*template_specialization_ptr,
|
||||
tbuilder().build_from_template_specialization_type(
|
||||
*template_specialization_ptr,
|
||||
unaliased_type->getTemplateName().getAsTemplateDecl(),
|
||||
*unaliased_type, &c);
|
||||
|
||||
@@ -1476,14 +1368,13 @@ void translation_unit_visitor::process_template_method(
|
||||
// Is there a better way to do this?
|
||||
method_name = method_name.substr(0, method_name.find('<'));
|
||||
}
|
||||
|
||||
util::if_not_null(
|
||||
clang::dyn_cast<clang::CXXMethodDecl>(mf.getTemplatedDecl()),
|
||||
[&](const auto *decl) {
|
||||
process_method_properties(*decl, c, method_name, method);
|
||||
});
|
||||
|
||||
process_template_parameters(mf, method);
|
||||
tbuilder().build_from_template_declaration(method, mf);
|
||||
|
||||
process_comment(mf, method);
|
||||
|
||||
@@ -1705,7 +1596,8 @@ void translation_unit_visitor::process_function_parameter(
|
||||
templ != nullptr) {
|
||||
auto template_specialization_ptr =
|
||||
std::make_unique<class_>(config().using_namespace());
|
||||
tbuilder().build(*template_specialization_ptr,
|
||||
tbuilder().build_from_template_specialization_type(
|
||||
*template_specialization_ptr,
|
||||
templ->getTemplateName().getAsTemplateDecl(), *templ, &c);
|
||||
|
||||
if (diagram().should_include(*template_specialization_ptr)) {
|
||||
@@ -1933,7 +1825,8 @@ void translation_unit_visitor::process_field(
|
||||
// Build the template instantiation for the field type
|
||||
auto template_specialization_ptr =
|
||||
std::make_unique<class_>(config().using_namespace());
|
||||
tbuilder().build(*template_specialization_ptr,
|
||||
tbuilder().build_from_template_specialization_type(
|
||||
*template_specialization_ptr,
|
||||
field_type->getAs<clang::TemplateSpecializationType>()
|
||||
->getTemplateName()
|
||||
.getAsTemplateDecl(),
|
||||
@@ -2100,25 +1993,6 @@ void translation_unit_visitor::extract_constrained_template_param_name(
|
||||
}
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::should_include(const clang::NamedDecl *decl)
|
||||
{
|
||||
if (decl == nullptr)
|
||||
return false;
|
||||
|
||||
if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
|
||||
return false;
|
||||
|
||||
auto should_include_namespace =
|
||||
diagram().should_include(namespace_{decl->getQualifiedNameAsString()});
|
||||
|
||||
const auto decl_file = decl->getLocation().printToString(source_manager());
|
||||
|
||||
const auto should_include_decl_file =
|
||||
diagram().should_include(common::model::source_file{decl_file});
|
||||
|
||||
return should_include_namespace && should_include_decl_file;
|
||||
}
|
||||
|
||||
void translation_unit_visitor::add_processed_template_class(
|
||||
std::string qualified_name)
|
||||
{
|
||||
|
||||
@@ -54,6 +54,10 @@ using clanguml::common::model::template_trait;
|
||||
using clanguml::common::visitor::found_relationships_t;
|
||||
using clanguml::common::visitor::template_builder;
|
||||
|
||||
using visitor_specialization_t =
|
||||
common::visitor::translation_unit_visitor<clanguml::config::class_diagram,
|
||||
clanguml::class_diagram::model::diagram>;
|
||||
|
||||
/**
|
||||
* @brief Class diagram translation unit visitor
|
||||
*
|
||||
@@ -62,8 +66,13 @@ using clanguml::common::visitor::template_builder;
|
||||
*/
|
||||
class translation_unit_visitor
|
||||
: public clang::RecursiveASTVisitor<translation_unit_visitor>,
|
||||
public common::visitor::translation_unit_visitor {
|
||||
public visitor_specialization_t {
|
||||
public:
|
||||
using visitor_specialization_t::config_t;
|
||||
using visitor_specialization_t::diagram_t;
|
||||
|
||||
using template_builder_t = template_builder<translation_unit_visitor>;
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*
|
||||
@@ -102,30 +111,6 @@ public:
|
||||
virtual bool TraverseConceptDecl(clang::ConceptDecl *cpt);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
clanguml::class_diagram::model::diagram &diagram() { return diagram_; }
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
const clanguml::class_diagram::model::diagram &diagram() const
|
||||
{
|
||||
return diagram_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get diagram config instance
|
||||
*
|
||||
* @return Reference to config instance
|
||||
*/
|
||||
const clanguml::config::class_diagram &config() const { return config_; }
|
||||
|
||||
/**
|
||||
* @brief Finalize diagram model
|
||||
*
|
||||
@@ -157,15 +142,16 @@ public:
|
||||
*/
|
||||
void add_concept(std::unique_ptr<concept_> &&c);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Check if the diagram should include a declaration.
|
||||
*
|
||||
* @param decl Clang declaration.
|
||||
* @return True, if the entity should be included in the diagram.
|
||||
*/
|
||||
bool should_include(const clang::NamedDecl *decl);
|
||||
void add_diagram_element(
|
||||
std::unique_ptr<common::model::template_element> element) override;
|
||||
|
||||
std::unique_ptr<class_> create_element(const clang::NamedDecl *decl) const;
|
||||
|
||||
void find_instantiation_relationships(
|
||||
common::model::template_element &template_instantiation_base,
|
||||
const std::string &full_name, common::id_t templated_decl_id);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Create class element model from class declaration
|
||||
*
|
||||
@@ -244,20 +230,6 @@ private:
|
||||
void process_template_specialization_children(
|
||||
const clang::ClassTemplateSpecializationDecl *cls, class_ &c);
|
||||
|
||||
/**
|
||||
* @brief Process template parameters
|
||||
*
|
||||
* @param template_declaration Template declaration
|
||||
* @param t `template_trait` instance to which the parameters should be
|
||||
* added
|
||||
* @param templated_element Optional templated diagram element (e.g. class_)
|
||||
* @return Ignored
|
||||
*/
|
||||
bool process_template_parameters(
|
||||
const clang::TemplateDecl &template_declaration,
|
||||
clanguml::common::model::template_trait &t,
|
||||
common::optional_ref<common::model::element> templated_element = {});
|
||||
|
||||
/**
|
||||
* @brief Process class method
|
||||
*
|
||||
@@ -451,27 +423,14 @@ private:
|
||||
*/
|
||||
bool has_processed_template_class(const std::string &qualified_name) const;
|
||||
|
||||
void add_diagram_element(
|
||||
std::unique_ptr<common::model::template_element> element) override;
|
||||
|
||||
void find_instantiation_relationships(
|
||||
common::model::template_element &template_instantiation_base,
|
||||
const std::string &full_name, common::id_t templated_decl_id);
|
||||
|
||||
/**
|
||||
* @brief Get template builder reference
|
||||
*
|
||||
* @return Reference to 'template_builder' instance
|
||||
*/
|
||||
template_builder &tbuilder() { return template_builder_; }
|
||||
template_builder_t &tbuilder() { return template_builder_; }
|
||||
|
||||
// Reference to the output diagram model
|
||||
clanguml::class_diagram::model::diagram &diagram_;
|
||||
|
||||
// Reference to class diagram config
|
||||
const clanguml::config::class_diagram &config_;
|
||||
|
||||
template_builder template_builder_;
|
||||
template_builder_t template_builder_;
|
||||
|
||||
std::map<common::id_t,
|
||||
std::unique_ptr<clanguml::class_diagram::model::class_>>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -16,174 +16,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "translation_unit_visitor.h"
|
||||
|
||||
#include "comment/clang_visitor.h"
|
||||
#include "comment/plain_visitor.h"
|
||||
#include "common/clang_utils.h"
|
||||
|
||||
#include <clang/AST/Expr.h>
|
||||
#include <clang/Basic/Module.h>
|
||||
|
||||
namespace clanguml::common::visitor {
|
||||
|
||||
translation_unit_visitor::translation_unit_visitor(
|
||||
clang::SourceManager &sm, const clanguml::config::diagram &config)
|
||||
: source_manager_{sm}
|
||||
, relative_to_path_{config.root_directory()}
|
||||
{
|
||||
if (config.comment_parser() == config::comment_parser_t::plain) {
|
||||
comment_visitor_ =
|
||||
std::make_unique<comment::plain_visitor>(source_manager_);
|
||||
}
|
||||
else if (config.comment_parser() == config::comment_parser_t::clang) {
|
||||
comment_visitor_ =
|
||||
std::make_unique<comment::clang_visitor>(source_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::set_tu_path(
|
||||
const std::string &translation_unit_path)
|
||||
{
|
||||
translation_unit_path_ = relative(
|
||||
std::filesystem::path{translation_unit_path}, relative_to_path_);
|
||||
translation_unit_path_.make_preferred();
|
||||
}
|
||||
|
||||
const std::filesystem::path &translation_unit_visitor::tu_path() const
|
||||
{
|
||||
return translation_unit_path_;
|
||||
}
|
||||
|
||||
clang::SourceManager &translation_unit_visitor::source_manager() const
|
||||
{
|
||||
return source_manager_;
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_comment(
|
||||
const clang::NamedDecl &decl, clanguml::common::model::decorated_element &e)
|
||||
{
|
||||
assert(comment_visitor_.get() != nullptr);
|
||||
|
||||
comment_visitor_->visit(decl, e);
|
||||
|
||||
const auto *comment =
|
||||
decl.getASTContext().getRawCommentForDeclNoCache(&decl);
|
||||
|
||||
process_comment(comment, decl.getASTContext().getDiagnostics(), e);
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_comment(const clang::RawComment *comment,
|
||||
clang::DiagnosticsEngine &de, clanguml::common::model::decorated_element &e)
|
||||
{
|
||||
if (comment != nullptr) {
|
||||
auto [it, inserted] = processed_comments_.emplace(comment);
|
||||
|
||||
if (!inserted)
|
||||
return;
|
||||
|
||||
// Process clang-uml decorators in the comments
|
||||
// TODO: Refactor to use standard block comments processable by clang
|
||||
// comments
|
||||
e.add_decorators(
|
||||
decorators::parse(comment->getFormattedText(source_manager_, de)));
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::set_source_location(
|
||||
const clang::Decl &decl, clanguml::common::model::source_location &element)
|
||||
{
|
||||
set_source_location(decl.getLocation(), element);
|
||||
}
|
||||
|
||||
void translation_unit_visitor::set_source_location(
|
||||
const clang::Expr &expr, clanguml::common::model::source_location &element)
|
||||
{
|
||||
set_source_location(expr.getBeginLoc(), element);
|
||||
}
|
||||
|
||||
void translation_unit_visitor::set_source_location(
|
||||
const clang::Stmt &stmt, clanguml::common::model::source_location &element)
|
||||
{
|
||||
set_source_location(stmt.getBeginLoc(), element);
|
||||
}
|
||||
|
||||
void translation_unit_visitor::set_source_location(
|
||||
const clang::SourceLocation &location,
|
||||
clanguml::common::model::source_location &element)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::string file;
|
||||
unsigned line{};
|
||||
unsigned column{};
|
||||
|
||||
if (location.isValid()) {
|
||||
file = source_manager_.getFilename(location).str();
|
||||
line = source_manager_.getSpellingLineNumber(location);
|
||||
column = source_manager_.getSpellingColumnNumber(location);
|
||||
|
||||
if (file.empty()) {
|
||||
// Why do I have to do this?
|
||||
parse_source_location(
|
||||
location.printToString(source_manager()), file, line, column);
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto success = parse_source_location(
|
||||
location.printToString(source_manager()), file, line, column);
|
||||
if (!success) {
|
||||
LOG_DBG("Failed to extract source location for element from {}",
|
||||
location.printToString(source_manager_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the path is absolute
|
||||
fs::path file_path{file};
|
||||
if (!file_path.is_absolute()) {
|
||||
file_path = fs::absolute(file_path);
|
||||
}
|
||||
|
||||
file_path = fs::weakly_canonical(file_path);
|
||||
|
||||
file = file_path.string();
|
||||
|
||||
element.set_file(file);
|
||||
|
||||
if (util::is_relative_to(file_path, relative_to_path_)) {
|
||||
element.set_file_relative(util::path_to_url(
|
||||
fs::relative(element.file(), relative_to_path_).string()));
|
||||
}
|
||||
else {
|
||||
element.set_file_relative("");
|
||||
}
|
||||
|
||||
element.set_translation_unit(tu_path().string());
|
||||
element.set_line(line);
|
||||
element.set_column(column);
|
||||
element.set_location_id(location.getHashValue());
|
||||
}
|
||||
|
||||
void translation_unit_visitor::set_owning_module(
|
||||
const clang::Decl &decl, clanguml::common::model::element &element)
|
||||
{
|
||||
if (const clang::Module *module = decl.getOwningModule();
|
||||
module != nullptr) {
|
||||
std::string module_name = module->Name;
|
||||
bool is_private{false};
|
||||
#if LLVM_VERSION_MAJOR < 15
|
||||
is_private =
|
||||
module->Kind == clang::Module::ModuleKind::PrivateModuleFragment;
|
||||
#else
|
||||
is_private = module->isPrivateModule();
|
||||
#endif
|
||||
if (is_private) {
|
||||
// Clang just maps private modules names to "<private>"
|
||||
module_name = module->getTopLevelModule()->Name;
|
||||
}
|
||||
element.set_module(module_name);
|
||||
element.set_module_private(is_private);
|
||||
}
|
||||
}
|
||||
} // namespace clanguml::common::visitor
|
||||
@@ -17,14 +17,21 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "comment/clang_visitor.h"
|
||||
#include "comment/comment_visitor.h"
|
||||
#include "comment/plain_visitor.h"
|
||||
#include "common/clang_utils.h"
|
||||
#include "common/model/namespace.h"
|
||||
#include "common/model/source_file.h"
|
||||
#include "common/model/source_location.h"
|
||||
#include "common/model/template_element.h"
|
||||
#include "common/visitor/ast_id_mapper.h"
|
||||
#include "config/config.h"
|
||||
|
||||
#include <clang/AST/Comment.h>
|
||||
#include <clang/AST/Expr.h>
|
||||
#include <clang/AST/RawCommentList.h>
|
||||
#include <clang/Basic/Module.h>
|
||||
#include <clang/Basic/SourceManager.h>
|
||||
|
||||
#include <deque>
|
||||
@@ -44,8 +51,11 @@ using found_relationships_t = std::vector<
|
||||
* This class provides common interface for diagram translation unit
|
||||
* visitors.
|
||||
*/
|
||||
class translation_unit_visitor {
|
||||
template <typename ConfigT, typename DiagramT> class translation_unit_visitor {
|
||||
public:
|
||||
using config_t = ConfigT;
|
||||
using diagram_t = DiagramT;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
@@ -54,17 +64,39 @@ public:
|
||||
* instance
|
||||
*/
|
||||
explicit translation_unit_visitor(
|
||||
clang::SourceManager &sm, const clanguml::config::diagram &config);
|
||||
clang::SourceManager &sm, DiagramT &diagram, const ConfigT &config)
|
||||
: diagram_{diagram}
|
||||
, config_{config}
|
||||
, source_manager_{sm}
|
||||
, relative_to_path_{config.root_directory()}
|
||||
{
|
||||
if (config.comment_parser() == config::comment_parser_t::plain) {
|
||||
comment_visitor_ =
|
||||
std::make_unique<comment::plain_visitor>(source_manager_);
|
||||
}
|
||||
else if (config.comment_parser() == config::comment_parser_t::clang) {
|
||||
comment_visitor_ =
|
||||
std::make_unique<comment::clang_visitor>(source_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~translation_unit_visitor() = default;
|
||||
|
||||
void set_tu_path(const std::string &translation_unit_path);
|
||||
void set_tu_path(const std::string &translation_unit_path)
|
||||
{
|
||||
translation_unit_path_ = relative(
|
||||
std::filesystem::path{translation_unit_path}, relative_to_path_);
|
||||
translation_unit_path_.make_preferred();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return relative path to current translation unit
|
||||
* @return Current translation unit path
|
||||
*/
|
||||
const std::filesystem::path &tu_path() const;
|
||||
const std::filesystem::path &tu_path() const
|
||||
{
|
||||
return translation_unit_path_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get reference to Clang AST to clang-uml id mapper
|
||||
@@ -78,7 +110,7 @@ public:
|
||||
* @return Reference to @ref clang::SourceManager used by this translation
|
||||
* unit visitor
|
||||
*/
|
||||
clang::SourceManager &source_manager() const;
|
||||
clang::SourceManager &source_manager() const { return source_manager_; }
|
||||
|
||||
/**
|
||||
* @brief Set source location in diagram element
|
||||
@@ -87,7 +119,10 @@ public:
|
||||
* @param element Reference to element to be updated
|
||||
*/
|
||||
void set_source_location(const clang::Decl &decl,
|
||||
clanguml::common::model::source_location &element);
|
||||
clanguml::common::model::source_location &element)
|
||||
{
|
||||
set_source_location(decl.getLocation(), element);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set source location in diagram element
|
||||
@@ -96,10 +131,25 @@ public:
|
||||
* @param element Reference to element to be updated
|
||||
*/
|
||||
void set_source_location(const clang::Expr &expr,
|
||||
clanguml::common::model::source_location &element);
|
||||
clanguml::common::model::source_location &element)
|
||||
{
|
||||
set_source_location(expr.getBeginLoc(), element);
|
||||
}
|
||||
|
||||
void set_source_location(const clang::Stmt &stmt,
|
||||
clanguml::common::model::source_location &element);
|
||||
clanguml::common::model::source_location &element)
|
||||
{
|
||||
set_source_location(stmt.getBeginLoc(), element);
|
||||
}
|
||||
|
||||
void set_qualified_name(
|
||||
const clang::NamedDecl &decl, clanguml::common::model::element &element)
|
||||
{
|
||||
common::model::namespace_ ns{decl.getQualifiedNameAsString()};
|
||||
element.set_name(ns.name());
|
||||
ns.pop_back();
|
||||
element.set_namespace(ns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set source location in diagram element
|
||||
@@ -108,17 +158,88 @@ public:
|
||||
* @param element Reference to element to be updated
|
||||
*/
|
||||
void set_source_location(const clang::SourceLocation &location,
|
||||
clanguml::common::model::source_location &element);
|
||||
clanguml::common::model::source_location &element)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::string file;
|
||||
unsigned line{};
|
||||
unsigned column{};
|
||||
|
||||
if (location.isValid()) {
|
||||
file = source_manager_.getFilename(location).str();
|
||||
line = source_manager_.getSpellingLineNumber(location);
|
||||
column = source_manager_.getSpellingColumnNumber(location);
|
||||
|
||||
if (file.empty()) {
|
||||
// Why do I have to do this?
|
||||
parse_source_location(location.printToString(source_manager()),
|
||||
file, line, column);
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto success = parse_source_location(
|
||||
location.printToString(source_manager()), file, line, column);
|
||||
if (!success) {
|
||||
LOG_DBG("Failed to extract source location for element from {}",
|
||||
location.printToString(source_manager_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the path is absolute
|
||||
fs::path file_path{file};
|
||||
if (!file_path.is_absolute()) {
|
||||
file_path = fs::absolute(file_path);
|
||||
}
|
||||
|
||||
file_path = fs::weakly_canonical(file_path);
|
||||
|
||||
file = file_path.string();
|
||||
|
||||
element.set_file(file);
|
||||
|
||||
if (util::is_relative_to(file_path, relative_to_path_)) {
|
||||
element.set_file_relative(util::path_to_url(
|
||||
fs::relative(element.file(), relative_to_path_).string()));
|
||||
}
|
||||
else {
|
||||
element.set_file_relative("");
|
||||
}
|
||||
|
||||
element.set_translation_unit(tu_path().string());
|
||||
element.set_line(line);
|
||||
element.set_column(column);
|
||||
element.set_location_id(location.getHashValue());
|
||||
}
|
||||
|
||||
void set_owning_module(
|
||||
const clang::Decl &decl, clanguml::common::model::element &element);
|
||||
const clang::Decl &decl, clanguml::common::model::element &element)
|
||||
{
|
||||
if (const clang::Module *module = decl.getOwningModule();
|
||||
module != nullptr) {
|
||||
std::string module_name = module->Name;
|
||||
bool is_private{false};
|
||||
#if LLVM_VERSION_MAJOR < 15
|
||||
is_private = module->Kind ==
|
||||
clang::Module::ModuleKind::PrivateModuleFragment;
|
||||
#else
|
||||
is_private = module->isPrivateModule();
|
||||
#endif
|
||||
if (is_private) {
|
||||
// Clang just maps private modules names to "<private>"
|
||||
module_name = module->getTopLevelModule()->Name;
|
||||
}
|
||||
element.set_module(module_name);
|
||||
element.set_module_private(is_private);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void add_diagram_element(
|
||||
std::unique_ptr<common::model::template_element> element)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Process comment directives in comment attached to a declaration
|
||||
*
|
||||
@@ -126,7 +247,17 @@ protected:
|
||||
* @param element Reference to element to be updated
|
||||
*/
|
||||
void process_comment(const clang::NamedDecl &decl,
|
||||
clanguml::common::model::decorated_element &e);
|
||||
clanguml::common::model::decorated_element &e)
|
||||
{
|
||||
assert(comment_visitor_.get() != nullptr);
|
||||
|
||||
comment_visitor_->visit(decl, e);
|
||||
|
||||
const auto *comment =
|
||||
decl.getASTContext().getRawCommentForDeclNoCache(&decl);
|
||||
|
||||
process_comment(comment, decl.getASTContext().getDiagnostics(), e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process comment directives in raw comment
|
||||
@@ -137,9 +268,77 @@ protected:
|
||||
*/
|
||||
void process_comment(const clang::RawComment *comment,
|
||||
clang::DiagnosticsEngine &de,
|
||||
clanguml::common::model::decorated_element &e);
|
||||
clanguml::common::model::decorated_element &e)
|
||||
{
|
||||
if (comment != nullptr) {
|
||||
auto [it, inserted] = processed_comments_.emplace(comment);
|
||||
|
||||
if (!inserted)
|
||||
return;
|
||||
|
||||
// Process clang-uml decorators in the comments
|
||||
// TODO: Refactor to use standard block comments processable by
|
||||
// clang comments
|
||||
e.add_decorators(decorators::parse(
|
||||
comment->getFormattedText(source_manager_, de)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the diagram should include a declaration.
|
||||
*
|
||||
* @param decl Clang declaration.
|
||||
* @return True, if the entity should be included in the diagram.
|
||||
*/
|
||||
bool should_include(const clang::NamedDecl *decl)
|
||||
{
|
||||
if (decl == nullptr)
|
||||
return false;
|
||||
|
||||
if (source_manager().isInSystemHeader(
|
||||
decl->getSourceRange().getBegin()))
|
||||
return false;
|
||||
|
||||
auto should_include_namespace = diagram().should_include(
|
||||
common::model::namespace_{decl->getQualifiedNameAsString()});
|
||||
|
||||
const auto decl_file =
|
||||
decl->getLocation().printToString(source_manager());
|
||||
|
||||
const auto should_include_decl_file =
|
||||
diagram().should_include(common::model::source_file{decl_file});
|
||||
|
||||
return should_include_namespace && should_include_decl_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
DiagramT &diagram() { return diagram_; }
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
const DiagramT &diagram() const { return diagram_; }
|
||||
|
||||
/**
|
||||
* @brief Get diagram config instance
|
||||
*
|
||||
* @return Reference to config instance
|
||||
*/
|
||||
const ConfigT &config() const { return config_; }
|
||||
|
||||
private:
|
||||
// Reference to the output diagram model
|
||||
DiagramT &diagram_;
|
||||
|
||||
// Reference to class diagram config
|
||||
const ConfigT &config_;
|
||||
|
||||
clang::SourceManager &source_manager_;
|
||||
|
||||
std::unique_ptr<comment::comment_visitor> comment_visitor_;
|
||||
|
||||
@@ -37,9 +37,7 @@ translation_unit_visitor::include_visitor::include_visitor(
|
||||
clang::SourceManager &sm,
|
||||
clanguml::include_diagram::model::diagram &diagram,
|
||||
const clanguml::config::include_diagram &config)
|
||||
: common::visitor::translation_unit_visitor{sm, config}
|
||||
, diagram_{diagram}
|
||||
, config_{config}
|
||||
: visitor_specialization_t{sm, diagram, config}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -112,18 +110,6 @@ void translation_unit_visitor::include_visitor::InclusionDirective(
|
||||
}
|
||||
}
|
||||
|
||||
clanguml::include_diagram::model::diagram &
|
||||
translation_unit_visitor::include_visitor::diagram()
|
||||
{
|
||||
return diagram_;
|
||||
}
|
||||
|
||||
const clanguml::config::include_diagram &
|
||||
translation_unit_visitor::include_visitor::config() const
|
||||
{
|
||||
return config_;
|
||||
}
|
||||
|
||||
void translation_unit_visitor::include_visitor::process_internal_header(
|
||||
const std::filesystem::path &include_path, bool is_system,
|
||||
const common::id_t current_file_id)
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
|
||||
namespace clanguml::include_diagram::visitor {
|
||||
|
||||
using visitor_specialization_t =
|
||||
common::visitor::translation_unit_visitor<clanguml::config::include_diagram,
|
||||
clanguml::include_diagram::model::diagram>;
|
||||
|
||||
/**
|
||||
* @brief Include diagram translation unit visitor wrapper
|
||||
*
|
||||
@@ -53,7 +57,7 @@ public:
|
||||
* include_visitor type from translation_unit_visitor type
|
||||
*/
|
||||
class include_visitor : public clang::PPCallbacks,
|
||||
public common::visitor::translation_unit_visitor {
|
||||
public visitor_specialization_t {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
@@ -123,27 +127,6 @@ public:
|
||||
*/
|
||||
std::optional<common::id_t> process_source_file(
|
||||
const std::filesystem::path &file);
|
||||
|
||||
/**
|
||||
* @brief Get reference to the include diagram model
|
||||
*
|
||||
* @return Reference to the include diagram model
|
||||
*/
|
||||
clanguml::include_diagram::model::diagram &diagram();
|
||||
|
||||
/**
|
||||
* @brief Get reference to the diagram configuration
|
||||
*
|
||||
* @return Reference to the diagram configuration
|
||||
*/
|
||||
const clanguml::config::include_diagram &config() const;
|
||||
|
||||
private:
|
||||
// Reference to the output diagram model
|
||||
clanguml::include_diagram::model::diagram &diagram_;
|
||||
|
||||
// Reference to class diagram config
|
||||
const clanguml::config::include_diagram &config_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,9 +37,7 @@ using clanguml::common::model::relationship_t;
|
||||
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
||||
clanguml::package_diagram::model::diagram &diagram,
|
||||
const clanguml::config::package_diagram &config)
|
||||
: common::visitor::translation_unit_visitor{sm, config}
|
||||
, diagram_{diagram}
|
||||
, config_{config}
|
||||
: visitor_specialization_t{sm, diagram, config}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -676,17 +674,6 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type,
|
||||
return result;
|
||||
}
|
||||
|
||||
clanguml::package_diagram::model::diagram &translation_unit_visitor::diagram()
|
||||
{
|
||||
return diagram_;
|
||||
}
|
||||
|
||||
const clanguml::config::package_diagram &
|
||||
translation_unit_visitor::config() const
|
||||
{
|
||||
return config_;
|
||||
}
|
||||
|
||||
void translation_unit_visitor::finalize() { }
|
||||
|
||||
std::vector<common::id_t> translation_unit_visitor::get_parent_package_ids(
|
||||
|
||||
@@ -36,6 +36,10 @@ namespace clanguml::package_diagram::visitor {
|
||||
using found_relationships_t = std::vector<
|
||||
std::pair<clanguml::common::id_t, common::model::relationship_t>>;
|
||||
|
||||
using visitor_specialization_t =
|
||||
common::visitor::translation_unit_visitor<clanguml::config::package_diagram,
|
||||
clanguml::package_diagram::model::diagram>;
|
||||
|
||||
/**
|
||||
* @brief Package diagram translation unit visitor
|
||||
*
|
||||
@@ -44,7 +48,7 @@ using found_relationships_t = std::vector<
|
||||
*/
|
||||
class translation_unit_visitor
|
||||
: public clang::RecursiveASTVisitor<translation_unit_visitor>,
|
||||
public common::visitor::translation_unit_visitor {
|
||||
public visitor_specialization_t {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
@@ -76,20 +80,6 @@ public:
|
||||
virtual bool VisitFunctionDecl(clang::FunctionDecl *function_declaration);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
clanguml::package_diagram::model::diagram &diagram();
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
const clanguml::config::package_diagram &config() const;
|
||||
|
||||
/**
|
||||
* @brief Finalize diagram model
|
||||
*/
|
||||
@@ -214,11 +204,5 @@ private:
|
||||
clang::Decl *cls, found_relationships_t &relationships);
|
||||
|
||||
std::vector<common::id_t> get_parent_package_ids(common::id_t id);
|
||||
|
||||
// Reference to the output diagram model
|
||||
clanguml::package_diagram::model::diagram &diagram_;
|
||||
|
||||
// Reference to class diagram config
|
||||
const clanguml::config::package_diagram &config_;
|
||||
};
|
||||
} // namespace clanguml::package_diagram::visitor
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,10 @@ using common::model::template_parameter;
|
||||
|
||||
std::string to_string(const clang::FunctionTemplateDecl *decl);
|
||||
|
||||
using visitor_specialization_t = common::visitor::translation_unit_visitor<
|
||||
clanguml::config::sequence_diagram,
|
||||
clanguml::sequence_diagram::model::diagram>;
|
||||
|
||||
/**
|
||||
* @brief Sequence diagram translation unit visitor
|
||||
*
|
||||
@@ -44,8 +48,14 @@ std::string to_string(const clang::FunctionTemplateDecl *decl);
|
||||
*/
|
||||
class translation_unit_visitor
|
||||
: public clang::RecursiveASTVisitor<translation_unit_visitor>,
|
||||
public common::visitor::translation_unit_visitor {
|
||||
public visitor_specialization_t {
|
||||
public:
|
||||
using visitor_specialization_t::config_t;
|
||||
using visitor_specialization_t::diagram_t;
|
||||
|
||||
using template_builder_t =
|
||||
common::visitor::template_builder<translation_unit_visitor>;
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*
|
||||
@@ -124,27 +134,6 @@ public:
|
||||
bool TraverseConditionalOperator(clang::ConditionalOperator *stmt);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
clanguml::sequence_diagram::model::diagram &diagram();
|
||||
|
||||
/**
|
||||
* @brief Get diagram model reference
|
||||
*
|
||||
* @return Reference to diagram model created by the visitor
|
||||
*/
|
||||
const clanguml::sequence_diagram::model::diagram &diagram() const;
|
||||
|
||||
/**
|
||||
* @brief Get diagram config instance
|
||||
*
|
||||
* @return Reference to config instance
|
||||
*/
|
||||
const clanguml::config::sequence_diagram &config() const;
|
||||
|
||||
/**
|
||||
* @brief Get current call expression context reference
|
||||
*
|
||||
@@ -255,6 +244,9 @@ public:
|
||||
*/
|
||||
void finalize();
|
||||
|
||||
std::unique_ptr<sequence_diagram::model::class_> create_element(
|
||||
const clang::NamedDecl *decl) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Check if the diagram should include a declaration.
|
||||
@@ -312,21 +304,12 @@ private:
|
||||
*/
|
||||
bool should_include(const clang::ClassTemplateDecl *decl) const;
|
||||
|
||||
/**
|
||||
* @todo #227 Refactor this group of methods to @ref template_builder
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
std::unique_ptr<clanguml::sequence_diagram::model::class_>
|
||||
create_class_model(clang::CXXRecordDecl *cls);
|
||||
|
||||
std::unique_ptr<clanguml::sequence_diagram::model::method>
|
||||
create_method_model(clang::CXXMethodDecl *cls);
|
||||
|
||||
bool process_template_parameters(
|
||||
const clang::TemplateDecl &template_declaration,
|
||||
common::model::template_trait &c);
|
||||
|
||||
std::unique_ptr<model::function_template>
|
||||
build_function_template_instantiation(const clang::FunctionDecl &pDecl);
|
||||
|
||||
@@ -336,63 +319,10 @@ private:
|
||||
std::unique_ptr<model::function_template> build_function_template(
|
||||
const clang::FunctionTemplateDecl &declaration);
|
||||
|
||||
void build_template_instantiation_process_template_arguments(
|
||||
model::template_trait *parent,
|
||||
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
|
||||
const clang::ArrayRef<clang::TemplateArgument> &template_args,
|
||||
model::template_trait &template_instantiation,
|
||||
const std::string &full_template_specialization_name,
|
||||
const clang::TemplateDecl *template_decl);
|
||||
|
||||
common::model::template_parameter
|
||||
build_template_instantiation_process_template_argument(
|
||||
const clang::TemplateArgument &arg) const;
|
||||
|
||||
common::model::template_parameter
|
||||
build_template_instantiation_process_integral_argument(
|
||||
const clang::TemplateArgument &arg) const;
|
||||
|
||||
common::model::template_parameter
|
||||
build_template_instantiation_process_expression_argument(
|
||||
const clang::TemplateArgument &arg) const;
|
||||
|
||||
void build_template_instantiation_process_tag_argument(
|
||||
model::template_trait &template_instantiation,
|
||||
const std::string &full_template_specialization_name,
|
||||
const clang::TemplateDecl *template_decl,
|
||||
const clang::TemplateArgument &arg,
|
||||
common::model::template_parameter &argument) const;
|
||||
|
||||
common::model::template_parameter
|
||||
build_template_instantiation_process_type_argument(
|
||||
model::template_trait *parent,
|
||||
const std::string &full_template_specialization_name,
|
||||
const clang::TemplateDecl *template_decl,
|
||||
const clang::TemplateArgument &arg,
|
||||
model::template_trait &template_instantiation);
|
||||
|
||||
std::unique_ptr<model::class_> process_template_specialization(
|
||||
std::unique_ptr<model::class_> process_class_template_specialization(
|
||||
clang::ClassTemplateSpecializationDecl *cls);
|
||||
|
||||
void process_template_specialization_argument(
|
||||
const clang::ClassTemplateSpecializationDecl *cls,
|
||||
model::class_ &template_instantiation,
|
||||
const clang::TemplateArgument &arg, size_t argument_index,
|
||||
bool in_parameter_pack = false);
|
||||
|
||||
void process_unexposed_template_specialization_parameters(
|
||||
const std::string &type_name, common::model::template_parameter &tp,
|
||||
model::class_ &c) const;
|
||||
|
||||
std::unique_ptr<model::class_> build_template_instantiation(
|
||||
const clang::TemplateSpecializationType &template_type_decl,
|
||||
model::class_ *parent);
|
||||
|
||||
bool simplify_system_template(common::model::template_parameter &ct,
|
||||
const std::string &full_name) const;
|
||||
|
||||
std::string simplify_system_template(const std::string &full_name) const;
|
||||
/** }@ */
|
||||
|
||||
/**
|
||||
* @brief Assuming `cls` is a lambda, create it's full name.
|
||||
@@ -516,13 +446,7 @@ private:
|
||||
*
|
||||
* @return Reference to 'template_builder' instance
|
||||
*/
|
||||
common::visitor::template_builder &tbuilder() { return template_builder_; }
|
||||
|
||||
// Reference to the output diagram model
|
||||
clanguml::sequence_diagram::model::diagram &diagram_;
|
||||
|
||||
// Reference to class diagram config
|
||||
const clanguml::config::sequence_diagram &config_;
|
||||
template_builder_t &tbuilder() { return template_builder_; }
|
||||
|
||||
call_expression_context call_expression_context_;
|
||||
|
||||
@@ -558,6 +482,6 @@ private:
|
||||
mutable std::set<std::pair<int64_t, const clang::RawComment *>>
|
||||
processed_comments_;
|
||||
|
||||
common::visitor::template_builder template_builder_;
|
||||
template_builder_t template_builder_;
|
||||
};
|
||||
} // namespace clanguml::sequence_diagram::visitor
|
||||
|
||||
11
tests/t20040/.clang-uml
Normal file
11
tests/t20040/.clang-uml
Normal file
@@ -0,0 +1,11 @@
|
||||
diagrams:
|
||||
t20040_sequence:
|
||||
type: sequence
|
||||
glob:
|
||||
- t20040.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20040
|
||||
using_namespace: clanguml::t20040
|
||||
from:
|
||||
- function: "clanguml::t20040::tmain()"
|
||||
32
tests/t20040/t20040.cc
Normal file
32
tests/t20040/t20040.cc
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20040 {
|
||||
void print() { }
|
||||
|
||||
template <typename T, typename... Ts> void print(T head, Ts... tail)
|
||||
{
|
||||
std::cout << head << std::endl;
|
||||
print(tail...);
|
||||
}
|
||||
|
||||
template <typename... Ts> void doublePrint(Ts... args)
|
||||
{
|
||||
print(args + args...);
|
||||
}
|
||||
|
||||
void tmain()
|
||||
{
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
print(1, 3.14, "test"s);
|
||||
|
||||
doublePrint("test"s, 2024 / 2);
|
||||
|
||||
// TODO: Add separate test case for variadic class template sequence diagram
|
||||
|
||||
// TODO: Add overload pattern test case
|
||||
}
|
||||
}
|
||||
}
|
||||
62
tests/t20040/test_case.h
Normal file
62
tests/t20040/test_case.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* tests/t20040/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
TEST_CASE("t20040", "[test-case][sequence]")
|
||||
{
|
||||
auto [config, db] = load_config("t20040");
|
||||
|
||||
auto diagram = config.diagrams["t20040_sequence"];
|
||||
|
||||
REQUIRE(diagram->name == "t20040_sequence");
|
||||
|
||||
auto model = generate_sequence_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t20040_sequence");
|
||||
|
||||
{
|
||||
auto src = generate_sequence_puml(diagram, *model);
|
||||
AliasMatcher _A(src);
|
||||
|
||||
REQUIRE_THAT(src, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(src, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
HasCall(_A("tmain()"),
|
||||
_A("print<int,double,std::string>(int,double,std::string)"),
|
||||
""));
|
||||
|
||||
save_puml(config.output_directory(), diagram->name + ".puml", src);
|
||||
}
|
||||
|
||||
{
|
||||
auto j = generate_sequence_json(diagram, *model);
|
||||
|
||||
using namespace json;
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto src = generate_sequence_mermaid(diagram, *model);
|
||||
|
||||
mermaid::AliasMatcher _A(src);
|
||||
using mermaid::IsClass;
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", src);
|
||||
}
|
||||
}
|
||||
@@ -461,6 +461,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20037/test_case.h"
|
||||
#include "t20038/test_case.h"
|
||||
#include "t20039/test_case.h"
|
||||
#include "t20040/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
Reference in New Issue
Block a user