0.6.2
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
Namespaces | Classes | Typedefs | Enumerations | Functions
clanguml::common Namespace Reference

Detailed Description

This namespace provides common interfaces for all kinds of diagrams.

The core diagram functionality is divided into 3 groups: visitor, model and generators.

Namespaces

namespace  generators
 
namespace  jinja
 
namespace  model
 
namespace  visitor
 

Classes

class  compilation_database
 Custom compilation database class. More...
 
class  eid_t
 Universal class for representing all kinds of Id's in the diagram model. More...
 
class  optional_ref
 Simple optional reference type. More...
 
struct  or_regex
 Convenience class for configuration options with regex support. More...
 
struct  path_or_regex
 
struct  regex
 Wrapper around std::regex, which contains original pattern. More...
 

Typedefs

using compilation_database_ptr = std::unique_ptr< compilation_database >
 
template<typename T >
using opt_ref = optional_ref< T >
 
template<typename T >
using reference_vector = std::vector< std::reference_wrapper< T > >
 
template<typename T >
using reference_set = std::unordered_set< std::reference_wrapper< T > >
 
using string_or_regex = or_regex< std::string >
 
using namespace_or_regex = common::or_regex< common::model::namespace_ >
 

Enumerations

enum class  generator_type_t { plantuml , json , mermaid , graphml }
 

Functions

model::access_t access_specifier_to_access_t (clang::AccessSpecifier access_specifier)
 Convert clang::AccessSpecifier to.
 
model::access_t access_specifier_to_access_t (clang::ObjCIvarDecl::AccessControl access_specifier)
 
model::namespace_ get_tag_namespace (const clang::TagDecl &declaration)
 
model::namespace_ get_template_namespace (const clang::TemplateDecl &declaration)
 
std::string get_tag_name (const clang::TagDecl &declaration)
 Generate full qualified name for clang::TagDecl instance.
 
std::string to_string (const clang::ArrayType &array_type, const clang::ASTContext &ctx, bool try_canonical, std::vector< std::string > &dimensions)
 
std::string to_string (const clang::TemplateArgumentLoc &argLoc, const clang::ASTContext &context)
 
std::string to_string (const clang::QualType &type, const clang::ASTContext &ctx, bool try_canonical)
 
std::string to_string (const clang::RecordType &type, const clang::ASTContext &ctx, bool try_canonical)
 
std::string to_string (const clang::TemplateArgument &arg, const clang::ASTContext *ctx)
 
std::string to_string (const clang::TemplateName &templ)
 
std::string to_string (const clang::Expr *expr)
 
std::string to_string (const clang::ValueDecl *val)
 
std::string to_string (const clang::Stmt *stmt)
 
std::string to_string (const clang::FunctionTemplateDecl *decl)
 
std::string to_string (const clang::TypeConstraint *tc)
 
std::string get_source_text_raw (clang::SourceRange range, const clang::SourceManager &sm)
 Get raw text of specific source range.
 
std::string get_source_text (clang::SourceRange range, const clang::SourceManager &sm)
 Get printable range of text of specific source range.
 
std::tuple< unsigned int, unsigned int, std::string > extract_template_parameter_index (const std::string &type_parameter)
 Extract template depth and index.
 
void ensure_lambda_type_is_relative (const config::diagram &config, std::string &parameter_type)
 
bool is_subexpr_of (const clang::Stmt *parent_stmt, const clang::Stmt *sub_stmt)
 Check if an expression is contained in another expression.
 
template<>
eid_t to_id (const std::string &full_name)
 
eid_t to_id (const clang::QualType &type, const clang::ASTContext &ctx)
 
template<>
eid_t to_id (const clang::NamespaceDecl &declaration)
 
template<>
eid_t to_id (const clang::RecordDecl &declaration)
 
template<>
eid_t to_id (const clang::ObjCCategoryDecl &type)
 
template<>
eid_t to_id (const clang::ObjCInterfaceDecl &type)
 
template<>
eid_t to_id (const clang::ObjCProtocolDecl &type)
 
template<>
eid_t to_id (const clang::EnumDecl &declaration)
 
template<>
eid_t to_id (const clang::TagDecl &declaration)
 
template<>
eid_t to_id (const clang::CXXRecordDecl &declaration)
 
template<>
eid_t to_id (const clang::EnumType &t)
 
template<>
eid_t to_id (const std::filesystem::path &file)
 
template<>
eid_t to_id (const clang::TemplateArgument &template_argument)
 
std::pair< common::model::namespace_, std::string > split_ns (const std::string &full_name)
 Split qualified name to namespace and name.
 
std::vector< common::model::template_parameterparse_unexposed_template_params (const std::string &params, const std::function< std::string(const std::string &)> &ns_resolve, int depth=0)
 Parse unexposed (available as string) template params.
 
bool is_type_parameter (const std::string &t)
 
bool is_qualifier (const std::string &q)
 
bool is_bracket (const std::string &b)
 
bool is_identifier_character (char c)
 
bool is_identifier (const std::string &t)
 
bool is_keyword (const std::string &t)
 
bool is_qualified_identifier (const std::string &t)
 
bool is_type_token (const std::string &t)
 
std::string format_condition_text (const std::string &condition_text)
 
std::string get_condition_text (clang::SourceManager &sm, clang::IfStmt *stmt)
 
std::string get_condition_text (clang::SourceManager &sm, clang::WhileStmt *stmt)
 
std::string get_condition_text (clang::SourceManager &sm, clang::CXXForRangeStmt *stmt)
 
std::string get_condition_text (clang::SourceManager &sm, clang::ForStmt *stmt)
 
std::string get_condition_text (clang::SourceManager &sm, clang::DoStmt *stmt)
 
std::string get_condition_text (clang::SourceManager &sm, clang::ConditionalOperator *stmt)
 
clang::QualType dereference (clang::QualType type)
 
std::pair< clang::QualType, std::deque< common::model::context > > consume_type_context (clang::QualType type)
 Extract type context and return raw type.
 
std::vector< std::string > tokenize_unexposed_template_parameter (const std::string &t)
 
bool parse_source_location (const std::string &location_str, std::string &file, unsigned &line, unsigned &column)
 
clang::RawComment * get_expression_raw_comment (const clang::SourceManager &sm, const clang::ASTContext &context, const clang::Stmt *stmt)
 Extract a comment before or next to a statement.
 
clang::RawComment * get_declaration_raw_comment (const clang::SourceManager &sm, const clang::ASTContext &context, const clang::Decl *decl)
 
clang::RawComment * get_raw_comment (const clang::SourceManager &sm, const clang::ASTContext &context, const clang::SourceRange &source_range)
 
bool is_coroutine (const clang::FunctionDecl &decl)
 
bool is_struct (const clang::NamedDecl *decl)
 
bool has_attr (const clang::FunctionDecl *decl, clang::attr::Kind function_attr)
 
std::optional< size_t > get_array_size (const clang::ArrayType &type)
 
void set_source_location (clang::SourceManager &source_manager, const clang::SourceLocation &location, clanguml::common::model::source_location &element, std::filesystem::path tu_path, std::filesystem::path relative_to_path_)
 
const clang::Type * get_unqualified_type (const clang::TypedefDecl *decl)
 
const clang::EnumDecl * get_typedef_enum_decl (const clang::TypedefDecl *decl)
 
bool is_lambda_call (const clang::Expr *expr)
 
bool is_lambda_method (const clang::FunctionDecl *decl)
 
template<typename T >
std::string get_qualified_name (const T &declaration)
 Get qualified name of some Clang declaration.
 
template<typename T >
eid_t to_id (const T &declaration)
 
template<>
eid_t to_id (const std::string &full_name)
 
template<>
eid_t to_id (const clang::NamespaceDecl &declaration)
 
template<>
eid_t to_id (const clang::CXXRecordDecl &declaration)
 
template<>
eid_t to_id (const clang::RecordDecl &declaration)
 
template<>
eid_t to_id (const clang::ObjCCategoryDecl &type)
 
template<>
eid_t to_id (const clang::ObjCInterfaceDecl &type)
 
template<>
eid_t to_id (const clang::ObjCProtocolDecl &type)
 
template<>
eid_t to_id (const clang::EnumDecl &declaration)
 
template<>
eid_t to_id (const clang::TagDecl &declaration)
 
template<>
eid_t to_id (const clang::EnumType &type)
 
template<>
eid_t to_id (const clang::TemplateSpecializationType &type)
 
template<>
eid_t to_id (const std::filesystem::path &type)
 
template<typename T , typename P , typename F >
void if_dyn_cast (P pointer, F &&func)
 
bool operator== (const eid_t &lhs, const eid_t &rhs)
 
bool operator== (const eid_t &lhs, const uint64_t &v)
 
bool operator!= (const eid_t &lhs, const uint64_t &v)
 
bool operator!= (const eid_t &lhs, const eid_t &rhs)
 
bool operator< (const eid_t &lhs, const eid_t &rhs)
 
std::string to_string (const bool v)
 
std::string to_string (const std::string &s)
 
std::string to_string (const string_or_regex &sr)
 
std::string to_string (const std::filesystem::path &p)
 
std::string to_string (const generator_type_t type)
 
YAML::Emitter & operator<< (YAML::Emitter &out, const string_or_regex &m)
 

Typedef Documentation

◆ compilation_database_ptr

Definition at line 143 of file compilation_database.h.

◆ namespace_or_regex

Definition at line 347 of file types.h.

◆ opt_ref

template<typename T >
using clanguml::common::opt_ref = typedef optional_ref<T>

Definition at line 229 of file types.h.

◆ reference_set

template<typename T >
using clanguml::common::reference_set = typedef std::unordered_set<std::reference_wrapper<T> >

Definition at line 235 of file types.h.

◆ reference_vector

template<typename T >
using clanguml::common::reference_vector = typedef std::vector<std::reference_wrapper<T> >

Definition at line 232 of file types.h.

◆ string_or_regex

using clanguml::common::string_or_regex = typedef or_regex<std::string>

Definition at line 343 of file types.h.

Enumeration Type Documentation

◆ generator_type_t

Type of output diagram format generator.

Enumerator
plantuml 

Diagrams will be generated in PlantUML format

json 

Diagrams will be generated in JSON format

mermaid 

Diagrams will be generated in MermaidJS format

graphml 

Diagrams will be generated in GraphML format

Definition at line 86 of file types.h.

86 {
87 plantuml, /*!< Diagrams will be generated in PlantUML format */
88 json, /*!< Diagrams will be generated in JSON format */
89 mermaid, /*!< Diagrams will be generated in MermaidJS format */
90 graphml /*!< Diagrams will be generated in GraphML format */
91};

Function Documentation

◆ access_specifier_to_access_t() [1/2]

model::access_t clanguml::common::access_specifier_to_access_t ( clang::AccessSpecifier  access_specifier)

Convert clang::AccessSpecifier to.

See also
clanguml::model::access_t
Parameters
access_specifierClang member access specifier
Returns
Enum value of
See also
clanguml::model::access_t

Definition at line 25 of file clang_utils.cc.

27{
28 auto access = model::access_t::kPublic;
29 switch (access_specifier) {
30 case clang::AccessSpecifier::AS_public:
31 access = model::access_t::kPublic;
32 break;
33 case clang::AccessSpecifier::AS_private:
34 access = model::access_t::kPrivate;
35 break;
36 case clang::AccessSpecifier::AS_protected:
37 access = model::access_t::kProtected;
38 break;
39 default:
40 break;
41 }
42
43 return access;
44}

◆ access_specifier_to_access_t() [2/2]

model::access_t clanguml::common::access_specifier_to_access_t ( clang::ObjCIvarDecl::AccessControl  access_specifier)

Definition at line 46 of file clang_utils.cc.

48{
49 auto access = model::access_t::kPublic;
50 switch (access_specifier) {
51 case clang::ObjCIvarDecl::AccessControl::Public:
52 access = model::access_t::kPublic;
53 break;
54 case clang::ObjCIvarDecl::AccessControl::Private:
55 access = model::access_t::kPrivate;
56 break;
57 case clang::ObjCIvarDecl::AccessControl::Protected:
58 access = model::access_t::kProtected;
59 break;
60 default:
61 break;
62 }
63
64 return access;
65}

◆ consume_type_context()

std::pair< clang::QualType, std::deque< common::model::context > > clanguml::common::consume_type_context ( clang::QualType  type)

Extract type context and return raw type.

This function removes the context for a type, for example for: std::string const& it will return (std::string, [const&])

Parameters
typeType to process
Returns
(type, [qualifiers])

Definition at line 792 of file clang_utils.cc.

793{
794 std::deque<common::model::context> res;
795
796 while (true) {
797 bool try_again{false};
799
800 if (type.isConstQualified()) {
801 ctx.is_const = true;
802 try_again = true;
803 }
804
805 if (type.isVolatileQualified()) {
806 ctx.is_volatile = true;
807 try_again = true;
808 }
809
810 if (type->isPointerType() || type->isReferenceType()) {
811 if (type.isConstQualified() || type.isVolatileQualified()) {
812 ctx.is_ref_const = type.isConstQualified();
813 ctx.is_ref_volatile = type.isVolatileQualified();
814
815 try_again = true;
816 }
817 }
818
819 if (type->isLValueReferenceType()) {
820 ctx.pr = common::model::rpqualifier::kLValueReference;
821 try_again = true;
822 }
823 else if (type->isRValueReferenceType()) {
824 ctx.pr = common::model::rpqualifier::kRValueReference;
825 try_again = true;
826 }
827 else if (type->isMemberFunctionPointerType() &&
828 type->getPointeeType()->getAs<clang::FunctionProtoType>() !=
829 nullptr) {
830 const auto ref_qualifier =
831 type->getPointeeType() // NOLINT
832 ->getAs<clang::FunctionProtoType>() // NOLINT
833 ->getRefQualifier();
834
835 if (ref_qualifier == clang::RefQualifierKind::RQ_RValue) {
836 ctx.pr = common::model::rpqualifier::kRValueReference;
837 try_again = true;
838 }
839 else if (ref_qualifier == clang::RefQualifierKind::RQ_LValue) {
840 ctx.pr = common::model::rpqualifier::kLValueReference;
841 try_again = true;
842 }
843 }
844 else if (type->isPointerType()) {
845 ctx.pr = common::model::rpqualifier::kPointer;
846 try_again = true;
847 }
848
849 if (try_again) {
850 if (type->isPointerType()) {
851 if (type->getPointeeType().isConstQualified())
852 ctx.is_const = true;
853 if (type->getPointeeType().isVolatileQualified())
854 ctx.is_volatile = true;
855
856 type = type->getPointeeType().getUnqualifiedType();
857 }
858 else if (type->isReferenceType()) {
859 if (type.getNonReferenceType().isConstQualified())
860 ctx.is_const = true;
861 if (type.getNonReferenceType().isVolatileQualified())
862 ctx.is_volatile = true;
863
864 type = type.getNonReferenceType().getUnqualifiedType();
865 }
866 else if (type.isConstQualified() || type.isVolatileQualified()) {
867 ctx.is_const = type.isConstQualified();
868 ctx.is_volatile = type.isVolatileQualified();
869 type = type.getUnqualifiedType();
870 }
871
872 res.push_front(ctx);
873
874 if (type->isMemberFunctionPointerType())
875 return std::make_pair(type, res);
876 }
877 else
878 return std::make_pair(type, res);
879 }
880}

◆ dereference()

clang::QualType clanguml::common::dereference ( clang::QualType  type)

Definition at line 775 of file clang_utils.cc.

776{
777 auto res = type;
778
779 while (true) {
780 if (res->isReferenceType())
781 res = res.getNonReferenceType();
782 else if (res->isPointerType())
783 res = res->getPointeeType();
784 else
785 break;
786 }
787
788 return res;
789}

◆ ensure_lambda_type_is_relative()

void clanguml::common::ensure_lambda_type_is_relative ( const config::diagram config,
std::string &  parameter_type 
)

Definition at line 418 of file clang_utils.cc.

420{
421#ifdef _MSC_VER
422 auto root_name =
423 fmt::format("{}", std::filesystem::current_path().root_name().string());
424#else
425 auto root_name = std::string{"/"};
426#endif
427
428 std::string lambda_prefix{fmt::format("(lambda at {}", root_name)};
429
430 while (parameter_type.find(lambda_prefix) != std::string::npos) {
431 auto lambda_begin = parameter_type.find(lambda_prefix);
432 auto lambda_prefix_size = lambda_prefix.size();
433#ifdef _MSC_VER
434 // Skip the `\` or `/` after drive letter and semicolon
435 lambda_prefix_size++;
436#endif
437 auto absolute_lambda_path_end =
438 parameter_type.find(':', lambda_begin + lambda_prefix_size);
439 auto absolute_lambda_path = parameter_type.substr(
440 lambda_begin + lambda_prefix_size - 1,
441 absolute_lambda_path_end - (lambda_begin + lambda_prefix_size - 1));
442
443 auto relative_lambda_path = util::path_to_url(
444 config.make_path_relative(absolute_lambda_path).string());
445
446 parameter_type = fmt::format("{}(lambda at {}{}",
447 parameter_type.substr(0, lambda_begin), relative_lambda_path,
448 parameter_type.substr(absolute_lambda_path_end));
449 }
450}

◆ extract_template_parameter_index()

std::tuple< unsigned int, unsigned int, std::string > clanguml::common::extract_template_parameter_index ( const std::string &  type_parameter)

Extract template depth and index.

This function extracts template depth and index values from Clang's type-parameter- names.

Parameters
type_parameterClang's type parameter string
Returns
(depth, index, qualifier)

Definition at line 399 of file clang_utils.cc.

400{
401 assert(type_parameter.find("type-parameter-") == 0);
402
403 auto type_parameter_and_suffix = util::split(type_parameter, " ");
404
405 auto toks = util::split(
406 type_parameter_and_suffix.front().substr(strlen("type-parameter-")),
407 "-");
408
409 std::string qualifier;
410
411 if (type_parameter_and_suffix.size() > 1) {
412 qualifier = type_parameter_and_suffix.at(1);
413 }
414
415 return {std::stoi(toks.at(0)), std::stoi(toks.at(1)), std::move(qualifier)};
416}

◆ format_condition_text()

std::string clanguml::common::format_condition_text ( const std::string &  condition_text)

Definition at line 706 of file clang_utils.cc.

707{
708 std::string result{condition_text};
709
710 if (result.size() < 2)
711 return {};
712
713 std::vector<std::string> text_lines = util::split(result, "\n", true);
714
715 // Trim each line
716 for (auto &line : text_lines) {
717 line = util::trim(line);
718 }
719
720 result = util::join(" ", text_lines);
721
722 if (result.at(0) == '(' && result.back() == ')')
723 return result.substr(1, result.size() - 2);
724
725 return result;
726}

◆ get_array_size()

std::optional< size_t > clanguml::common::get_array_size ( const clang::ArrayType &  type)

If type is a constant array, return it's number of elements. Otherwise nothing.

Parameters
type
Returns
Number of elements in the array.

Definition at line 1070 of file clang_utils.cc.

1071{
1072 if (const auto *constant_array =
1073 clang::dyn_cast<clang::ConstantArrayType>(&type);
1074 constant_array != nullptr) {
1075 return {constant_array->getSize().getZExtValue()};
1076 }
1077
1078 return {};
1079}

◆ get_condition_text() [1/6]

std::string clanguml::common::get_condition_text ( clang::SourceManager &  sm,
clang::ConditionalOperator *  stmt 
)

Definition at line 767 of file clang_utils.cc.

769{
770 auto condition_range = stmt->getCond()->getSourceRange();
771
772 return format_condition_text(get_source_text(condition_range, sm));
773}

◆ get_condition_text() [2/6]

std::string clanguml::common::get_condition_text ( clang::SourceManager &  sm,
clang::CXXForRangeStmt *  stmt 
)

Definition at line 744 of file clang_utils.cc.

746{
747 auto condition_range = stmt->getRangeStmt()->getSourceRange();
748
749 return format_condition_text(get_source_text(condition_range, sm));
750}

◆ get_condition_text() [3/6]

std::string clanguml::common::get_condition_text ( clang::SourceManager &  sm,
clang::DoStmt *  stmt 
)

Definition at line 760 of file clang_utils.cc.

761{
762 auto condition_range = stmt->getCond()->getSourceRange();
763
764 return format_condition_text(get_source_text(condition_range, sm));
765}

◆ get_condition_text() [4/6]

std::string clanguml::common::get_condition_text ( clang::SourceManager &  sm,
clang::ForStmt *  stmt 
)

Definition at line 752 of file clang_utils.cc.

753{
754 auto condition_range =
755 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
756
757 return format_condition_text(get_source_text(condition_range, sm));
758}

◆ get_condition_text() [5/6]

std::string clanguml::common::get_condition_text ( clang::SourceManager &  sm,
clang::IfStmt *  stmt 
)

Definition at line 728 of file clang_utils.cc.

729{
730 auto condition_range =
731 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
732
733 return format_condition_text(get_source_text(condition_range, sm));
734}

◆ get_condition_text() [6/6]

std::string clanguml::common::get_condition_text ( clang::SourceManager &  sm,
clang::WhileStmt *  stmt 
)

Definition at line 736 of file clang_utils.cc.

737{
738 auto condition_range =
739 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
740
741 return format_condition_text(get_source_text(condition_range, sm));
742}

◆ get_declaration_raw_comment()

clang::RawComment * clanguml::common::get_declaration_raw_comment ( const clang::SourceManager &  sm,
const clang::ASTContext &  context,
const clang::Decl *  decl 
)

Definition at line 1008 of file clang_utils.cc.

1010{
1011 return get_raw_comment(sm, context, decl->getSourceRange());
1012}

◆ get_expression_raw_comment()

clang::RawComment * clanguml::common::get_expression_raw_comment ( const clang::SourceManager &  sm,
const clang::ASTContext &  context,
const clang::Stmt *  stmt 
)

Extract a comment before or next to a statement.

Parameters
smclang::SourceManager reference
contextclang::ASTContext reference
stmtPointer to the current clang::Stmt
Returns
Pointer to a clang::RawComment* or nullptr

Definition at line 1002 of file clang_utils.cc.

1004{
1005 return get_raw_comment(sm, context, stmt->getSourceRange());
1006}

◆ get_qualified_name()

template<typename T >
std::string clanguml::common::get_qualified_name ( const T &  declaration)

Get qualified name of some Clang declaration.

This template is convenient for getting qualified name of various types of clang declarations.

Template Parameters
TType of Clang's declaration, e.g. clang::TagDecl
Parameters
declarationReference to a clang declaration
Returns
Fully qualified name

Definition at line 71 of file clang_utils.h.

72{
73 auto qualified_name = declaration.getQualifiedNameAsString();
74 util::replace_all(qualified_name, "(anonymous namespace)", "");
75 util::replace_all(qualified_name, "::::", "::");
76
77 if constexpr (std::is_base_of_v<clang::TagDecl, T>) {
78 auto base_name = get_tag_name(declaration);
79 model::namespace_ ns{qualified_name};
80 ns.pop_back();
81 ns = ns | base_name;
82
83 return ns.to_string();
84 }
85
86 return qualified_name;
87}

◆ get_raw_comment()

clang::RawComment * clanguml::common::get_raw_comment ( const clang::SourceManager &  sm,
const clang::ASTContext &  context,
const clang::SourceRange &  source_range 
)

Definition at line 1014 of file clang_utils.cc.

1016{
1017 auto expr_begin = sm.getExpansionLoc(source_range.getBegin());
1018 const auto expr_begin_line = sm.getExpansionLineNumber(expr_begin);
1019
1020 auto file_id = sm.getFileID(expr_begin);
1021
1022 const auto has_comments = !context.Comments.empty();
1023 const auto *comments = context.Comments.getCommentsInFile(file_id);
1024
1025 if (has_comments && comments != nullptr) {
1026 for (const auto [offset, raw_comment] :
1027 *context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
1028 const auto comment_end_line = sm.getExpansionLineNumber(
1029 raw_comment->getSourceRange().getEnd());
1030
1031 if (expr_begin_line == comment_end_line ||
1032 expr_begin_line == comment_end_line + 1)
1033 return raw_comment;
1034 }
1035 }
1036
1037 return {};
1038}

◆ get_source_text()

std::string clanguml::common::get_source_text ( clang::SourceRange  range,
const clang::SourceManager &  sm 
)

Get printable range of text of specific source range.

Parameters
rangeSource range
smSource manager reference
Returns
Printable source text

Definition at line 386 of file clang_utils.cc.

388{
389 const clang::LangOptions lo;
390
391 auto start_loc = sm.getSpellingLoc(range.getBegin());
392 auto last_token_loc = sm.getSpellingLoc(range.getEnd());
393 auto end_loc = clang::Lexer::getLocForEndOfToken(last_token_loc, 0, sm, lo);
394 auto printable_range = clang::SourceRange{start_loc, end_loc};
395 return get_source_text_raw(printable_range, sm);
396}

◆ get_source_text_raw()

std::string clanguml::common::get_source_text_raw ( clang::SourceRange  range,
const clang::SourceManager &  sm 
)

Get raw text of specific source range.

Parameters
rangeSource range
smSource manager reference
Returns
Raw source text

Definition at line 378 of file clang_utils.cc.

380{
381 return clang::Lexer::getSourceText(
382 clang::CharSourceRange::getCharRange(range), sm, clang::LangOptions())
383 .str();
384}

◆ get_tag_name()

std::string clanguml::common::get_tag_name ( const clang::TagDecl &  declaration)

Generate full qualified name for clang::TagDecl instance.

Parameters
declarationInput declaration
Returns
String representation including any templates, parameters and attribtues

Definition at line 105 of file clang_utils.cc.

106{
107 auto base_name = declaration.getNameAsString();
108
109 if (base_name.empty()) {
110 base_name =
111 fmt::format("(anonymous_{})", std::to_string(declaration.getID()));
112 }
113
114 if ((declaration.getParent() != nullptr) &&
115 declaration.getParent()->isRecord()) {
116 // If the record is nested within another record (e.g. class or struct)
117 // we have to maintain a containment namespace in order to ensure
118 // unique names within the diagram
119 std::deque<std::string> record_parent_names;
120 record_parent_names.push_front(base_name);
121
122 const auto *cls_parent{declaration.getParent()};
123 while (cls_parent->isRecord()) {
124 if (const auto *record_decl =
125 clang::dyn_cast<clang::RecordDecl>(cls_parent);
126 record_decl != nullptr) {
127 record_parent_names.push_front(record_decl->getNameAsString());
128 }
129 cls_parent = cls_parent->getParent();
130 }
131 return fmt::format("{}", fmt::join(record_parent_names, "##"));
132 }
133
134 return base_name;
135}

◆ get_tag_namespace()

model::namespace_ clanguml::common::get_tag_namespace ( const clang::TagDecl &  declaration)

Get namespace of a specific clang::TagDecl

Parameters
declarationReference to clang::TagDecl
Returns
Namespace instance

Definition at line 67 of file clang_utils.cc.

68{
70
71 const auto *parent{declaration.getParent()};
72
73 // First walk up to the nearest namespace, e.g. from nested class or enum
74 while ((parent != nullptr) && !parent->isNamespace()) {
75 parent = parent->getParent();
76 }
77
78 // Now build up the namespace
79 std::deque<std::string> namespace_tokens;
80 while ((parent != nullptr) && parent->isNamespace()) {
81 if (const auto *ns_decl = clang::dyn_cast<clang::NamespaceDecl>(parent);
82 ns_decl != nullptr) {
83 if (!ns_decl->isInline() && !ns_decl->isAnonymousNamespace())
84 namespace_tokens.push_front(ns_decl->getNameAsString());
85 }
86
87 parent = parent->getParent();
88 }
89
90 for (const auto &ns_token : namespace_tokens) {
91 ns |= ns_token;
92 }
93
94 return ns;
95}

◆ get_template_namespace()

model::namespace_ clanguml::common::get_template_namespace ( const clang::TemplateDecl &  declaration)

Get namespace of a specific clang::TemplateDecl

Parameters
declarationReference to clang::TemplateDecl
Returns
Namespace instance

Definition at line 97 of file clang_utils.cc.

98{
99 model::namespace_ ns{declaration.getQualifiedNameAsString()};
100 ns.pop_back();
101
102 return ns;
103}

◆ get_typedef_enum_decl()

const clang::EnumDecl * clanguml::common::get_typedef_enum_decl ( const clang::TypedefDecl *  decl)

Get pointer to enum decl in typedef decl

Parameters
decltypedef decl
Returns
Pointer to enum decl inside typedef

Definition at line 1151 of file clang_utils.cc.

1152{
1153 if (decl == nullptr)
1154 return nullptr;
1155
1156 const clang::Type *unqualified_type = get_unqualified_type(decl);
1157
1158 if (unqualified_type->getTypeClass() == clang::Type::Elaborated) {
1159 const auto *tag_decl =
1160 clang::cast<clang::ElaboratedType>(unqualified_type)
1161 ->getNamedType()
1162 ->getAsTagDecl();
1163
1164 if (tag_decl == nullptr)
1165 return nullptr;
1166
1167 const auto *enum_decl = clang::dyn_cast<clang::EnumDecl>(tag_decl);
1168 if (enum_decl != nullptr && enum_decl->getIdentifier() == nullptr)
1169 return enum_decl;
1170 }
1171
1172 return nullptr;
1173}

◆ get_unqualified_type()

const clang::Type * clanguml::common::get_unqualified_type ( const clang::TypedefDecl *  decl)

Get the unqualified type of typedef decl

Parameters
decltypedef decl
Returns
Pointer to underlying type or nullptr

Definition at line 1141 of file clang_utils.cc.

1142{
1143 const auto *type_source_info = decl->getTypeSourceInfo();
1144
1145 if (type_source_info == nullptr)
1146 return nullptr;
1147
1148 return type_source_info->getType().split().Ty;
1149}

◆ has_attr()

bool clanguml::common::has_attr ( const clang::FunctionDecl *  decl,
clang::attr::Kind  function_attr 
)

Check if function declaration contains specified attributed

Parameters
declFunction declaration
function_attrClang function attribute
Returns
True, if decl contains specified function attribute

Definition at line 1063 of file clang_utils.cc.

1064{
1065 return std::any_of(decl->attrs().begin(), decl->attrs().end(),
1066 [function_attr](
1067 auto &&attr) { return attr->getKind() == function_attr; });
1068}

◆ if_dyn_cast()

template<typename T , typename P , typename F >
void clanguml::common::if_dyn_cast ( pointer,
F &&  func 
)

Definition at line 241 of file clang_utils.h.

242{
243 if (pointer == nullptr)
244 return;
245
246 if (const auto *dyn_cast_value = clang::dyn_cast<T>(pointer);
247 dyn_cast_value) {
248 std::forward<F>(func)(dyn_cast_value);
249 }
250}

◆ is_bracket()

bool clanguml::common::is_bracket ( const std::string &  b)

Definition at line 657 of file clang_utils.cc.

658{
659 return b == "(" || b == ")" || b == "[" || b == "]";
660}

◆ is_coroutine()

bool clanguml::common::is_coroutine ( const clang::FunctionDecl &  decl)

Check if function or method declaration is a C++20 coroutine.

Parameters
declFunction declaration
Returns
True, if the function is a C++20 coroutine.

Definition at line 1040 of file clang_utils.cc.

1041{
1042 const auto *body = decl.getBody();
1043 return clang::isa_and_nonnull<clang::CoroutineBodyStmt>(body);
1044}

◆ is_identifier()

bool clanguml::common::is_identifier ( const std::string &  t)

Definition at line 667 of file clang_utils.cc.

668{
669 return std::all_of(t.begin(), t.end(),
670 [](const char c) { return is_identifier_character(c); });
671}

◆ is_identifier_character()

bool clanguml::common::is_identifier_character ( char  c)

Definition at line 662 of file clang_utils.cc.

663{
664 return std::isalnum(c) != 0 || c == '_';
665}

◆ is_keyword()

bool clanguml::common::is_keyword ( const std::string &  t)

Definition at line 673 of file clang_utils.cc.

674{
675 static std::vector<std::string> keywords{"alignas", "alignof", "asm",
676 "auto", "bool", "break", "case", "catch", "char", "char16_t",
677 "char32_t", "class", "concept", "const", "constexpr", "const_cast",
678 "continue", "decltype", "default", "delete", "do", "double",
679 "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false",
680 "float", "for", "friend", "goto", "if", "inline", "int", "long",
681 "mutable", "namespace", "new", "noexcept", "nullptr", "operator",
682 "private", "protected", "public", "register", "reinterpret_cast",
683 "return", "requires", "short", "signed", "sizeof", "static",
684 "static_assert", "static_cast", "struct", "switch", "template", "this",
685 "thread_local", "throw", "true", "try", "typedef", "typeid", "typename",
686 "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t",
687 "while"};
688
689 return util::contains(keywords, t);
690}

◆ is_lambda_call()

bool clanguml::common::is_lambda_call ( const clang::Expr *  callExpr)

Check if clang::Expr is a call on lambda operator()

Parameters
callExpr
Returns
True, if the expression points to lambda invocation

Definition at line 1175 of file clang_utils.cc.

1176{
1177 const auto *call_expr = clang::dyn_cast<clang::CallExpr>(expr);
1178
1179 if (call_expr == nullptr)
1180 return false;
1181
1182 if (clang::dyn_cast_or_null<clang::LambdaExpr>(call_expr->getCallee()) !=
1183 nullptr) {
1184 return true;
1185 }
1186
1187 const auto *function_callee = call_expr->getDirectCallee();
1188 if (function_callee != nullptr) {
1189
1190 const auto *method_decl =
1191 clang::dyn_cast<clang::CXXMethodDecl>(function_callee);
1192 if (method_decl == nullptr)
1193 return false;
1194
1195 if (method_decl->getParent()->isLambda() &&
1196 method_decl->getOverloadedOperator() == clang::OO_Call) {
1197 return true;
1198 }
1199 }
1200
1201 return false;
1202}

◆ is_lambda_method()

bool clanguml::common::is_lambda_method ( const clang::FunctionDecl *  decl)

Definition at line 1204 of file clang_utils.cc.

1205{
1206 if (decl == nullptr)
1207 return false;
1208
1209 if (const auto *method = clang::dyn_cast<clang::CXXMethodDecl>(decl);
1210 method != nullptr) {
1211 if (method->getParent() != nullptr && method->getParent()->isLambda())
1212 return true;
1213 }
1214
1215 return false;
1216}

◆ is_qualified_identifier()

bool clanguml::common::is_qualified_identifier ( const std::string &  t)

Definition at line 692 of file clang_utils.cc.

693{
694 return std::isalpha(t.at(0)) != 0 &&
695 std::all_of(t.begin(), t.end(), [](const char c) {
696 return is_identifier_character(c) || c == ':';
697 });
698}

◆ is_qualifier()

bool clanguml::common::is_qualifier ( const std::string &  q)

Definition at line 652 of file clang_utils.cc.

653{
654 return q == "&" || q == "&&" || q == "const&";
655}

◆ is_struct()

bool clanguml::common::is_struct ( const clang::NamedDecl *  decl)

Check if named declaration is a C++ struct.

Parameters
declDeclaration to check
Returns
True, if declaration represents a struct.

Definition at line 1046 of file clang_utils.cc.

1047{
1048 if (decl == nullptr)
1049 return false;
1050
1051 if (const auto *record = clang::dyn_cast<clang::CXXRecordDecl>(decl);
1052 record) {
1053 return record->isStruct();
1054 }
1055
1056 if (const auto *tag = clang::dyn_cast<clang::TagDecl>(decl); tag) {
1057 return tag->isStruct();
1058 }
1059
1060 return false;
1061}

◆ is_subexpr_of()

bool clanguml::common::is_subexpr_of ( const clang::Stmt *  parent_stmt,
const clang::Stmt *  sub_stmt 
)

Check if an expression is contained in another expression.

This method returns true if sub_stmt is equal to or is contained in the AST subtree of parent_stmt

Parameters
parent_stmtParent statement
sub_stmtSub statement
Returns

Definition at line 452 of file clang_utils.cc.

453{
454 if (parent_stmt == nullptr || sub_stmt == nullptr)
455 return false;
456
457 if (parent_stmt == sub_stmt)
458 return true;
459
460 return std::any_of(parent_stmt->child_begin(), parent_stmt->child_end(),
461 [sub_stmt](const auto *e) { return is_subexpr_of(e, sub_stmt); });
462}

◆ is_type_parameter()

bool clanguml::common::is_type_parameter ( const std::string &  t)

Definition at line 647 of file clang_utils.cc.

648{
649 return t.find("type-parameter-") == 0;
650}

◆ is_type_token()

bool clanguml::common::is_type_token ( const std::string &  t)

Definition at line 700 of file clang_utils.cc.

701{
702 return is_type_parameter(t) ||
703 (is_identifier(t) && !is_qualifier(t) && !is_bracket(t));
704}

◆ operator!=() [1/2]

bool clanguml::common::operator!= ( const eid_t lhs,
const eid_t rhs 
)

Definition at line 66 of file types.cc.

66{ return !(lhs == rhs); }

◆ operator!=() [2/2]

bool clanguml::common::operator!= ( const eid_t lhs,
const uint64_t &  v 
)

Definition at line 59 of file types.cc.

60{
61 assert(v != 0);
62
63 return lhs.value_ != v;
64}

◆ operator<()

bool clanguml::common::operator< ( const eid_t lhs,
const eid_t rhs 
)

Definition at line 68 of file types.cc.

69{
70 if (lhs.is_global_ != rhs.is_global_) {
71 return lhs.value_ < rhs.value_ + 1;
72 }
73
74 return lhs.value_ < rhs.value_; // Compare values if is_global_ are the same
75}

◆ operator<<()

YAML::Emitter & clanguml::common::operator<< ( YAML::Emitter &  out,
const string_or_regex m 
)

Definition at line 23 of file yaml_emitters.cc.

24{
25 if (std::holds_alternative<std::string>(m.value())) {
26 out << std::get<std::string>(m.value());
27 }
28 else {
29 out << YAML::BeginMap;
30 out << YAML::Key << "r" << YAML::Value
31 << std::get<regex>(m.value()).pattern;
32 out << YAML::EndMap;
33 }
34
35 return out;
36}

◆ operator==() [1/2]

bool clanguml::common::operator== ( const eid_t lhs,
const eid_t rhs 
)

Definition at line 52 of file types.cc.

53{
54 return (lhs.is_global_ == rhs.is_global_) && (lhs.value_ == rhs.value_);
55}

◆ operator==() [2/2]

bool clanguml::common::operator== ( const eid_t lhs,
const uint64_t &  v 
)

Definition at line 57 of file types.cc.

57{ return lhs.value_ == v; }

◆ parse_source_location()

bool clanguml::common::parse_source_location ( const std::string &  location_str,
std::string &  file,
unsigned &  line,
unsigned &  column 
)

Definition at line 965 of file clang_utils.cc.

967{
968 auto tokens = util::split(location_str, ":");
969
970 if (tokens.size() < 3)
971 return false;
972
973 if (tokens.size() == 4) {
974 // Handle Windows paths
975 decltype(tokens) tmp_tokens{};
976 tmp_tokens.emplace_back(
977 fmt::format("{}:{}", tokens.at(0), tokens.at(1)));
978 tmp_tokens.emplace_back(tokens.at(2));
979 tmp_tokens.emplace_back(tokens.at(3));
980
981 tokens = std::move(tmp_tokens);
982 }
983
984 file = tokens.at(0);
985 try {
986 line = std::stoi(tokens.at(1));
987 }
988 catch (std::invalid_argument &e) {
989 return false;
990 }
991
992 try {
993 column = std::stoi(tokens.at(2));
994 }
995 catch (std::invalid_argument &e) {
996 column = 0;
997 }
998
999 return true;
1000}

◆ parse_unexposed_template_params()

std::vector< common::model::template_parameter > clanguml::common::parse_unexposed_template_params ( const std::string &  params,
const std::function< std::string(const std::string &)> &  ns_resolve,
int  depth = 0 
)

Parse unexposed (available as string) template params.

Parameters
paramsString parameters as provided by Clang
ns_resolveNamespace resolver function
depthCurrent depth in the template specification
Returns
Parsed template parameter

Definition at line 555 of file clang_utils.cc.

559{
561
562 std::vector<template_parameter> res;
563
564 auto it = params.begin();
565 while (std::isspace(*it) != 0)
566 ++it;
567
568 std::string type{};
569 std::vector<template_parameter> nested_params;
570 bool complete_class_template_argument{false};
571
572 while (it != params.end()) {
573 if (*it == '<') {
574 int nested_level{0};
575 auto bracket_match_begin = it + 1;
576 auto bracket_match_end = bracket_match_begin;
577 while (bracket_match_end != params.end()) {
578 if (*bracket_match_end == '<') {
579 nested_level++;
580 }
581 else if (*bracket_match_end == '>') {
582 if (nested_level > 0)
583 nested_level--;
584 else
585 break;
586 }
587 else {
588 }
589 bracket_match_end++;
590 }
591
592 std::string nested_params_str(
593 bracket_match_begin, bracket_match_end);
594
595 nested_params = parse_unexposed_template_params(
596 nested_params_str, ns_resolve, depth + 1);
597
598 if (nested_params.empty()) {
599 // We couldn't extract any nested template parameters from
600 // `nested_params_str` so just add it as type of template
601 // argument as is
602 nested_params.emplace_back(
603 template_parameter::make_unexposed_argument(
604 nested_params_str));
605 }
606
607 it = bracket_match_end - 1;
608 }
609 else if (*it == '>') {
610 complete_class_template_argument = true;
611 if (depth == 0) {
612 break;
613 }
614 }
615 else if (*it == ',') {
616 complete_class_template_argument = true;
617 }
618 else {
619 type += *it;
620 }
621 if (complete_class_template_argument) {
622 auto t = template_parameter::make_unexposed_argument(
623 ns_resolve(clanguml::util::trim_typename(type)));
624 type = "";
625 for (auto &&param : nested_params)
626 t.add_template_param(std::move(param));
627
628 res.emplace_back(std::move(t));
629 complete_class_template_argument = false;
630 }
631 it++;
632 }
633
634 if (!type.empty()) {
635 auto t = template_parameter::make_unexposed_argument(
636 ns_resolve(clanguml::util::trim_typename(type)));
637 type = "";
638 for (auto &&param : nested_params)
639 t.add_template_param(std::move(param));
640
641 res.emplace_back(std::move(t));
642 }
643
644 return res;
645}

◆ set_source_location()

void clanguml::common::set_source_location ( clang::SourceManager &  source_manager,
const clang::SourceLocation &  location,
clanguml::common::model::source_location element,
std::filesystem::path  tu_path,
std::filesystem::path  relative_to_path_ 
)

Definition at line 1081 of file clang_utils.cc.

1085{
1086 namespace fs = std::filesystem;
1087
1088 std::string file;
1089 unsigned line{};
1090 unsigned column{};
1091
1092 if (location.isValid()) {
1093 file =
1094 source_manager.getFilename(source_manager.getSpellingLoc(location))
1095 .str();
1096 line = source_manager.getSpellingLineNumber(location);
1097 column = source_manager.getSpellingColumnNumber(location);
1098
1099 if (file.empty()) {
1100 // Why do I have to do this?
1101 parse_source_location(
1102 location.printToString(source_manager), file, line, column);
1103 }
1104 }
1105 else {
1106 auto success = parse_source_location(
1107 location.printToString(source_manager), file, line, column);
1108 if (!success) {
1109 LOG_DBG("Failed to extract source location for element from {}",
1110 location.printToString(source_manager));
1111 return;
1112 }
1113 }
1114
1115 // ensure the path is absolute
1116 fs::path file_path{file};
1117 if (!file_path.is_absolute()) {
1118 file_path = fs::absolute(file_path);
1119 }
1120
1121 file_path = weakly_canonical(file_path);
1122
1123 file = file_path.string();
1124
1125 element.set_file(file);
1126
1127 if (util::is_relative_to(file_path, relative_to_path_)) {
1128 element.set_file_relative(util::path_to_url(
1129 fs::path{element.file()}.lexically_relative(relative_to_path_)));
1130 }
1131 else {
1132 element.set_file_relative("");
1133 }
1134
1135 element.set_translation_unit(tu_path.string());
1136 element.set_line(line);
1137 element.set_column(column);
1138 element.set_location_id(location.getHashValue());
1139}

◆ split_ns()

std::pair< common::model::namespace_, std::string > clanguml::common::split_ns ( const std::string &  full_name)

Split qualified name to namespace and name.

Parameters
full_nameFully qualified element name
Returns
(namespace, name)

Definition at line 542 of file clang_utils.cc.

544{
545 assert(!full_name.empty());
546
547 auto name_before_template = ::clanguml::util::split(full_name, "<")[0];
549 ::clanguml::util::split(name_before_template, "::")};
550 auto name = ns.name();
551 ns.pop_back();
552 return {ns, name};
553}

◆ to_id()

template<>
eid_t clanguml::common::to_id ( const clang::TemplateArgument &  template_argument)

Definition at line 525 of file clang_utils.cc.

526{
527 if (template_argument.getKind() == clang::TemplateArgument::Type) {
528 if (const auto *enum_type =
529 template_argument.getAsType()->getAs<clang::EnumType>();
530 enum_type != nullptr)
531 return to_id(*enum_type->getAsTagDecl());
532
533 if (const auto *record_type =
534 template_argument.getAsType()->getAs<clang::RecordType>();
535 record_type != nullptr)
536 return to_id(*record_type->getAsRecordDecl());
537 }
538
539 throw std::runtime_error("Cannot generate id for template argument");
540}

◆ to_string() [1/16]

std::string clanguml::common::to_string ( const bool  v)

Definition at line 86 of file types.cc.

86{ return v ? "true" : "false"; }

◆ to_string() [2/16]

std::string clanguml::common::to_string ( const clang::ArrayType &  array_type,
const clang::ASTContext &  ctx,
bool  try_canonical,
std::vector< std::string > &  dimensions 
)

Definition at line 137 of file clang_utils.cc.

140{
141 auto maybe_size = get_array_size(array_type);
142 std::string array_size =
143 maybe_size.has_value() ? std::to_string(maybe_size.value()) : "";
144 dimensions.emplace_back(std::move(array_size));
145
146 const auto underlying_type = array_type.getElementType();
147
148 if (underlying_type->isArrayType())
149 return to_string(*underlying_type->getAsArrayTypeUnsafe(), ctx,
150 try_canonical, dimensions);
151
152 std::string dimensions_str;
153 for (const auto &d : dimensions) {
154 dimensions_str += fmt::format("[{}]", d);
155 }
156 return fmt::format(
157 "{}{}", to_string(underlying_type, ctx, try_canonical), dimensions_str);
158}

◆ to_string() [3/16]

std::string clanguml::common::to_string ( const clang::Expr *  expr)

Definition at line 312 of file clang_utils.cc.

313{
314 const clang::LangOptions lang_options;
315 std::string result;
316 llvm::raw_string_ostream ostream(result);
317 expr->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options));
318
319 return result;
320}

◆ to_string() [4/16]

std::string clanguml::common::to_string ( const clang::FunctionTemplateDecl *  decl)

Definition at line 337 of file clang_utils.cc.

338{
339 std::vector<std::string> template_parameters;
340 // Handle template function
341 for (const auto *parameter : *decl->getTemplateParameters()) {
342 if (clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter) !=
343 nullptr) {
344 const auto *template_type_parameter =
345 clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter);
346
347 std::string template_parameter{
348 template_type_parameter->getNameAsString()};
349
350 if (template_type_parameter->isParameterPack())
351 template_parameter += "...";
352
353 template_parameters.emplace_back(std::move(template_parameter));
354 }
355 else {
356 // TODO
357 }
358 }
359 return fmt::format("{}<{}>({})", decl->getQualifiedNameAsString(),
360 fmt::join(template_parameters, ","), "");
361}

◆ to_string() [5/16]

std::string clanguml::common::to_string ( const clang::QualType &  type,
const clang::ASTContext &  ctx,
bool  try_canonical 
)

Definition at line 181 of file clang_utils.cc.

183{
184 if (type->isArrayType()) {
185 std::vector<std::string> dimensions;
186 return to_string(
187 *type->getAsArrayTypeUnsafe(), ctx, try_canonical, dimensions);
188 }
189
190 clang::PrintingPolicy print_policy(ctx.getLangOpts());
191 print_policy.SuppressScope = 0;
192#if LLVM_VERSION_MAJOR < 21
193 print_policy.PrintCanonicalTypes = 0;
194#else
195 print_policy.PrintAsCanonical = 0;
196#endif
197
198 std::string result;
199
200 result = type.getAsString(print_policy);
201
202 if (try_canonical && result.find('<') != std::string::npos) {
203 auto canonical_type_name =
204 type.getCanonicalType().getAsString(print_policy);
205
206 auto result_qualified_template_name =
207 result.substr(0, result.find('<'));
208 auto result_template_arguments = result.substr(result.find('<'));
209
210 auto canonical_qualified_template_name =
211 canonical_type_name.substr(0, canonical_type_name.find('<'));
212
213 // Choose the longer name (why do I have to do this?)
214 if (result_qualified_template_name.size() <
215 canonical_qualified_template_name.size()) {
216
217 result =
218 canonical_qualified_template_name + result_template_arguments;
219 }
220 }
221
222 // If for any reason clang reports the type as empty string, make sure
223 // it has some default name
224 if (result.empty())
225 result = "(anonymous)";
226 else if (util::contains(result, "unnamed struct") ||
227 util::contains(result, "unnamed union")) {
228 const auto *declarationTag = type->getAsTagDecl();
229 if (declarationTag == nullptr) {
230 result = "(unnamed undeclared)";
231 }
232 else {
233 result = common::get_tag_name(*declarationTag);
234 }
235 }
236 else if (util::contains(result, "anonymous struct") ||
237 util::contains(result, "anonymous union")) {
238 result = common::get_tag_name(*type->getAsTagDecl());
239 }
240
241 // Remove trailing spaces after commas in template arguments
242 clanguml::util::replace_all(result, ", ", ",");
243 clanguml::util::replace_all(result, "> >", ">>");
244
245 // Try to get rid of 'type-parameter-X-Y' ugliness
246 if (result.find("type-parameter-") != std::string::npos) {
247 util::if_not_null(
248 common::dereference(type)->getAs<clang::TypedefType>(),
249 [&result, &type](auto *p) {
250 auto [unqualified_type, context] =
251 common::consume_type_context(type);
252 result = p->getDecl()->getNameAsString();
253 if (!context.empty()) {
254 std::vector<std::string> deduced_contexts;
255
256 for (const auto &c : context) {
257 deduced_contexts.push_back(c.to_string());
258 }
259
260 result = fmt::format(
261 "{} {}", result, fmt::join(deduced_contexts, " "));
262 }
263 });
264 }
265
266 return result;
267}

◆ to_string() [6/16]

std::string clanguml::common::to_string ( const clang::RecordType &  type,
const clang::ASTContext &  ctx,
bool  try_canonical 
)

Definition at line 269 of file clang_utils.cc.

271{
272 return to_string(type.desugar(), ctx, try_canonical);
273}

◆ to_string() [7/16]

std::string clanguml::common::to_string ( const clang::Stmt *  stmt)

Definition at line 327 of file clang_utils.cc.

328{
329 const clang::LangOptions lang_options;
330 std::string result;
331 llvm::raw_string_ostream ostream(result);
332 stmt->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options));
333
334 return result;
335}

◆ to_string() [8/16]

std::string clanguml::common::to_string ( const clang::TemplateArgument &  arg,
const clang::ASTContext *  ctx 
)

Definition at line 275 of file clang_utils.cc.

277{
278 switch (arg.getKind()) {
279 case clang::TemplateArgument::Expression:
280 return to_string(arg.getAsExpr());
281 case clang::TemplateArgument::Type:
282 return to_string(arg.getAsType(), *ctx, false);
283 case clang::TemplateArgument::Null:
284 return "";
285 case clang::TemplateArgument::NullPtr:
286 return "nullptr";
287 case clang::TemplateArgument::Integral:
288 return std::to_string(arg.getAsIntegral().getExtValue());
289 case clang::TemplateArgument::Template:
290 return to_string(arg.getAsTemplate());
291 case clang::TemplateArgument::TemplateExpansion:
292 return to_string(arg.getAsTemplateOrTemplatePattern());
293 default:
294 return "";
295 }
296}

◆ to_string() [9/16]

std::string clanguml::common::to_string ( const clang::TemplateArgumentLoc &  argLoc,
const clang::ASTContext &  context 
)

Definition at line 160 of file clang_utils.cc.

162{
163 std::string result;
164 llvm::raw_string_ostream stream(result);
165
166 clang::PrintingPolicy policy(context.getLangOpts());
167
168 const clang::TemplateArgument &arg = argLoc.getArgument();
169
170#if LLVM_VERSION_MAJOR > 18
171 arg.print(policy, stream, false);
172#else
173 arg.dump(stream);
174#endif
175
176 stream.flush();
177
178 return result;
179}

◆ to_string() [10/16]

std::string clanguml::common::to_string ( const clang::TemplateName &  templ)

Definition at line 298 of file clang_utils.cc.

299{
300 if (templ.getAsTemplateDecl() != nullptr) {
301 return templ.getAsTemplateDecl()->getQualifiedNameAsString();
302 }
303
304 std::string result;
305 const clang::LangOptions lang_options;
306 llvm::raw_string_ostream ostream(result);
307 templ.print(ostream, clang::PrintingPolicy(lang_options));
308
309 return result;
310}

◆ to_string() [11/16]

std::string clanguml::common::to_string ( const clang::TypeConstraint *  tc)

Definition at line 363 of file clang_utils.cc.

364{
365 if (tc == nullptr)
366 return {};
367
368 const clang::PrintingPolicy print_policy(
369 tc->getNamedConcept()->getASTContext().getLangOpts());
370
371 std::string ostream_buf;
372 llvm::raw_string_ostream ostream{ostream_buf};
373 tc->print(ostream, print_policy);
374
375 return ostream.str();
376}

◆ to_string() [12/16]

std::string clanguml::common::to_string ( const clang::ValueDecl *  val)

Definition at line 322 of file clang_utils.cc.

323{
324 return val->getQualifiedNameAsString();
325}

◆ to_string() [13/16]

std::string clanguml::common::to_string ( const generator_type_t  type)

Definition at line 94 of file types.cc.

95{
96 switch (type) {
97 case generator_type_t::plantuml:
98 return "plantuml";
99 case generator_type_t::mermaid:
100 return "mermaid";
101 case generator_type_t::json:
102 return "json";
103 case generator_type_t::graphml:
104 return "graphml";
105 default:
106 return "<unknown>";
107 }
108}

◆ to_string() [14/16]

std::string clanguml::common::to_string ( const std::filesystem::path &  p)

Definition at line 92 of file types.cc.

92{ return p.string(); }

◆ to_string() [15/16]

std::string clanguml::common::to_string ( const std::string &  s)

Definition at line 88 of file types.cc.

88{ return s; }

◆ to_string() [16/16]

std::string clanguml::common::to_string ( const string_or_regex sr)

Definition at line 90 of file types.cc.

90{ return sr.to_string(); }

◆ tokenize_unexposed_template_parameter()

std::vector< std::string > clanguml::common::tokenize_unexposed_template_parameter ( const std::string &  t)

Definition at line 882 of file clang_utils.cc.

884{
885 std::vector<std::string> result;
886
887 auto spaced_out = util::split(t, " ");
888
889 for (const auto &word : spaced_out) {
890 if (is_qualified_identifier(word)) {
891 if (word != "class" && word != "templated" && word != "struct")
892 result.emplace_back(word);
893 continue;
894 }
895
896 std::string tok;
897
898 for (const char c : word) {
899 if (c == '(' || c == ')' || c == '[' || c == ']' || c == '<' ||
900 c == '>') {
901 if (!tok.empty())
902 result.emplace_back(tok);
903 result.emplace_back(std::string{c});
904 tok.clear();
905 }
906 else if (c == ':') {
907 if (!tok.empty() && tok != ":") {
908 result.emplace_back(tok);
909 tok = ":";
910 }
911 else if (tok == ":") {
912 result.emplace_back("::");
913 tok = "";
914 }
915 else {
916 tok += ':';
917 }
918 }
919 else if (c == ',') {
920 if (!tok.empty()) {
921 result.emplace_back(tok);
922 }
923 result.emplace_back(",");
924 tok.clear();
925 }
926 else if (c == '*') {
927 if (!tok.empty()) {
928 result.emplace_back(tok);
929 }
930 result.emplace_back("*");
931 tok.clear();
932 }
933 else if (c == '.') {
934 // This can only be the case if we have a variadic template,
935 // right?
936 if (tok == "..") {
937 result.emplace_back("...");
938 tok.clear();
939 }
940 else if (tok == ".") {
941 tok = "..";
942 }
943 else if (!tok.empty()) {
944 result.emplace_back(tok);
945 tok = ".";
946 }
947 }
948 else {
949 tok += c;
950 }
951 }
952
953 tok = util::trim(tok);
954
955 if (!tok.empty()) {
956 if (tok != "class" && tok != "typename" && word != "struct")
957 result.emplace_back(tok);
958 tok.clear();
959 }
960 }
961
962 return result;
963}