0.5.4
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  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 }
 

Functions

model::access_t access_specifier_to_access_t (clang::AccessSpecifier access_specifier)
 Convert clang::AccessSpecifier to.
 
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::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::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)
 
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::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 std::string &s)
 
std::string to_string (const string_or_regex &sr)
 
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 126 of file compilation_database.h.

◆ namespace_or_regex

Definition at line 342 of file types.h.

◆ opt_ref

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

Definition at line 224 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 230 of file types.h.

◆ reference_vector

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

Definition at line 227 of file types.h.

◆ string_or_regex

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

Definition at line 338 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

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};

Function Documentation

◆ access_specifier_to_access_t()

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}

◆ 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 731 of file clang_utils.cc.

732{
733 std::deque<common::model::context> res;
734
735 while (true) {
736 bool try_again{false};
738
739 if (type.isConstQualified()) {
740 ctx.is_const = true;
741 try_again = true;
742 }
743
744 if (type.isVolatileQualified()) {
745 ctx.is_volatile = true;
746 try_again = true;
747 }
748
749 if (type->isPointerType() || type->isReferenceType()) {
750 if (type.isConstQualified() || type.isVolatileQualified()) {
751 ctx.is_ref_const = type.isConstQualified();
752 ctx.is_ref_volatile = type.isVolatileQualified();
753
754 try_again = true;
755 }
756 }
757
758 if (type->isLValueReferenceType()) {
759 ctx.pr = common::model::rpqualifier::kLValueReference;
760 try_again = true;
761 }
762 else if (type->isRValueReferenceType()) {
763 ctx.pr = common::model::rpqualifier::kRValueReference;
764 try_again = true;
765 }
766 else if (type->isMemberFunctionPointerType() &&
767 type->getPointeeType()->getAs<clang::FunctionProtoType>() !=
768 nullptr) {
769 const auto ref_qualifier =
770 type->getPointeeType() // NOLINT
771 ->getAs<clang::FunctionProtoType>() // NOLINT
772 ->getRefQualifier();
773
774 if (ref_qualifier == clang::RefQualifierKind::RQ_RValue) {
775 ctx.pr = common::model::rpqualifier::kRValueReference;
776 try_again = true;
777 }
778 else if (ref_qualifier == clang::RefQualifierKind::RQ_LValue) {
779 ctx.pr = common::model::rpqualifier::kLValueReference;
780 try_again = true;
781 }
782 }
783 else if (type->isPointerType()) {
784 ctx.pr = common::model::rpqualifier::kPointer;
785 try_again = true;
786 }
787
788 if (try_again) {
789 if (type->isPointerType()) {
790 if (type->getPointeeType().isConstQualified())
791 ctx.is_const = true;
792 if (type->getPointeeType().isVolatileQualified())
793 ctx.is_volatile = true;
794
795 type = type->getPointeeType().getUnqualifiedType();
796 }
797 else if (type->isReferenceType()) {
798 if (type.getNonReferenceType().isConstQualified())
799 ctx.is_const = true;
800 if (type.getNonReferenceType().isVolatileQualified())
801 ctx.is_volatile = true;
802
803 type = type.getNonReferenceType().getUnqualifiedType();
804 }
805 else if (type.isConstQualified() || type.isVolatileQualified()) {
806 ctx.is_const = type.isConstQualified();
807 ctx.is_volatile = type.isVolatileQualified();
808 type = type.getUnqualifiedType();
809 }
810
811 res.push_front(ctx);
812
813 if (type->isMemberFunctionPointerType())
814 return std::make_pair(type, res);
815 }
816 else
817 return std::make_pair(type, res);
818 }
819}

◆ dereference()

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

Definition at line 714 of file clang_utils.cc.

715{
716 auto res = type;
717
718 while (true) {
719 if (res->isReferenceType())
720 res = res.getNonReferenceType();
721 else if (res->isPointerType())
722 res = res->getPointeeType();
723 else
724 break;
725 }
726
727 return res;
728}

◆ ensure_lambda_type_is_relative()

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

Definition at line 372 of file clang_utils.cc.

374{
375#ifdef _MSC_VER
376 auto root_name =
377 fmt::format("{}", std::filesystem::current_path().root_name().string());
378#else
379 auto root_name = std::string{"/"};
380#endif
381
382 std::string lambda_prefix{fmt::format("(lambda at {}", root_name)};
383
384 while (parameter_type.find(lambda_prefix) != std::string::npos) {
385 auto lambda_begin = parameter_type.find(lambda_prefix);
386 auto lambda_prefix_size = lambda_prefix.size();
387#ifdef _MSC_VER
388 // Skip the `\` or `/` after drive letter and semicolon
389 lambda_prefix_size++;
390#endif
391 auto absolute_lambda_path_end =
392 parameter_type.find(':', lambda_begin + lambda_prefix_size);
393 auto absolute_lambda_path = parameter_type.substr(
394 lambda_begin + lambda_prefix_size - 1,
395 absolute_lambda_path_end - (lambda_begin + lambda_prefix_size - 1));
396
397 auto relative_lambda_path = util::path_to_url(
398 config.make_path_relative(absolute_lambda_path).string());
399
400 parameter_type = fmt::format("{}(lambda at {}{}",
401 parameter_type.substr(0, lambda_begin), relative_lambda_path,
402 parameter_type.substr(absolute_lambda_path_end));
403 }
404}

◆ 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 353 of file clang_utils.cc.

354{
355 assert(type_parameter.find("type-parameter-") == 0);
356
357 auto type_parameter_and_suffix = util::split(type_parameter, " ");
358
359 auto toks = util::split(
360 type_parameter_and_suffix.front().substr(strlen("type-parameter-")),
361 "-");
362
363 std::string qualifier;
364
365 if (type_parameter_and_suffix.size() > 1) {
366 qualifier = type_parameter_and_suffix.at(1);
367 }
368
369 return {std::stoi(toks.at(0)), std::stoi(toks.at(1)), std::move(qualifier)};
370}

◆ format_condition_text()

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

Definition at line 645 of file clang_utils.cc.

646{
647 std::string result{condition_text};
648
649 if (result.size() < 2)
650 return {};
651
652 std::vector<std::string> text_lines = util::split(result, "\n", true);
653
654 // Trim each line
655 for (auto &line : text_lines) {
656 line = util::trim(line);
657 }
658
659 result = util::join(" ", text_lines);
660
661 if (result.at(0) == '(' && result.back() == ')')
662 return result.substr(1, result.size() - 2);
663
664 return result;
665}

◆ 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 1009 of file clang_utils.cc.

1010{
1011 if (const auto *constant_array =
1012 clang::dyn_cast<clang::ConstantArrayType>(&type);
1013 constant_array != nullptr) {
1014 return {constant_array->getSize().getZExtValue()};
1015 }
1016
1017 return {};
1018}

◆ get_condition_text() [1/6]

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

Definition at line 706 of file clang_utils.cc.

708{
709 auto condition_range = stmt->getCond()->getSourceRange();
710
711 return format_condition_text(get_source_text(condition_range, sm));
712}

◆ get_condition_text() [2/6]

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

Definition at line 683 of file clang_utils.cc.

685{
686 auto condition_range = stmt->getRangeStmt()->getSourceRange();
687
688 return format_condition_text(get_source_text(condition_range, sm));
689}

◆ get_condition_text() [3/6]

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

Definition at line 699 of file clang_utils.cc.

700{
701 auto condition_range = stmt->getCond()->getSourceRange();
702
703 return format_condition_text(get_source_text(condition_range, sm));
704}

◆ get_condition_text() [4/6]

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

Definition at line 691 of file clang_utils.cc.

692{
693 auto condition_range =
694 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
695
696 return format_condition_text(get_source_text(condition_range, sm));
697}

◆ get_condition_text() [5/6]

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

Definition at line 667 of file clang_utils.cc.

668{
669 auto condition_range =
670 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
671
672 return format_condition_text(get_source_text(condition_range, sm));
673}

◆ get_condition_text() [6/6]

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

Definition at line 675 of file clang_utils.cc.

676{
677 auto condition_range =
678 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
679
680 return format_condition_text(get_source_text(condition_range, sm));
681}

◆ 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 947 of file clang_utils.cc.

949{
950 return get_raw_comment(sm, context, decl->getSourceRange());
951}

◆ 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 941 of file clang_utils.cc.

943{
944 return get_raw_comment(sm, context, stmt->getSourceRange());
945}

◆ 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 69 of file clang_utils.h.

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

◆ 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 953 of file clang_utils.cc.

955{
956 auto expr_begin = source_range.getBegin();
957 const auto expr_begin_line = sm.getSpellingLineNumber(expr_begin);
958
959 std::string file_Path = sm.getFilename(expr_begin).str();
960
961 auto file_id = sm.getFileID(expr_begin);
962
963 if (!context.Comments.empty() &&
964 context.Comments.getCommentsInFile(file_id) != nullptr) {
965 for (const auto [offset, raw_comment] :
966 *context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
967 const auto comment_end_line = sm.getSpellingLineNumber(
968 raw_comment->getSourceRange().getEnd());
969
970 if (expr_begin_line == comment_end_line ||
971 expr_begin_line == comment_end_line + 1)
972 return raw_comment;
973 }
974 }
975
976 return {};
977}

◆ 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 340 of file clang_utils.cc.

342{
343 const clang::LangOptions lo;
344
345 auto start_loc = sm.getSpellingLoc(range.getBegin());
346 auto last_token_loc = sm.getSpellingLoc(range.getEnd());
347 auto end_loc = clang::Lexer::getLocForEndOfToken(last_token_loc, 0, sm, lo);
348 auto printable_range = clang::SourceRange{start_loc, end_loc};
349 return get_source_text_raw(printable_range, sm);
350}

◆ 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 332 of file clang_utils.cc.

334{
335 return clang::Lexer::getSourceText(
336 clang::CharSourceRange::getCharRange(range), sm, clang::LangOptions())
337 .str();
338}

◆ 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 84 of file clang_utils.cc.

85{
86 auto base_name = declaration.getNameAsString();
87
88 if (base_name.empty()) {
89 base_name =
90 fmt::format("(anonymous_{})", std::to_string(declaration.getID()));
91 }
92
93 if ((declaration.getParent() != nullptr) &&
94 declaration.getParent()->isRecord()) {
95 // If the record is nested within another record (e.g. class or struct)
96 // we have to maintain a containment namespace in order to ensure
97 // unique names within the diagram
98 std::deque<std::string> record_parent_names;
99 record_parent_names.push_front(base_name);
100
101 const auto *cls_parent{declaration.getParent()};
102 while (cls_parent->isRecord()) {
103 if (const auto *record_decl =
104 clang::dyn_cast<clang::RecordDecl>(cls_parent);
105 record_decl != nullptr) {
106 record_parent_names.push_front(record_decl->getNameAsString());
107 }
108 cls_parent = cls_parent->getParent();
109 }
110 return fmt::format("{}", fmt::join(record_parent_names, "##"));
111 }
112
113 return base_name;
114}

◆ 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 46 of file clang_utils.cc.

47{
49
50 const auto *parent{declaration.getParent()};
51
52 // First walk up to the nearest namespace, e.g. from nested class or enum
53 while ((parent != nullptr) && !parent->isNamespace()) {
54 parent = parent->getParent();
55 }
56
57 // Now build up the namespace
58 std::deque<std::string> namespace_tokens;
59 while ((parent != nullptr) && parent->isNamespace()) {
60 if (const auto *ns_decl = clang::dyn_cast<clang::NamespaceDecl>(parent);
61 ns_decl != nullptr) {
62 if (!ns_decl->isInline() && !ns_decl->isAnonymousNamespace())
63 namespace_tokens.push_front(ns_decl->getNameAsString());
64 }
65
66 parent = parent->getParent();
67 }
68
69 for (const auto &ns_token : namespace_tokens) {
70 ns |= ns_token;
71 }
72
73 return ns;
74}

◆ 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 76 of file clang_utils.cc.

77{
78 model::namespace_ ns{declaration.getQualifiedNameAsString()};
79 ns.pop_back();
80
81 return ns;
82}

◆ 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 1002 of file clang_utils.cc.

1003{
1004 return std::any_of(decl->attrs().begin(), decl->attrs().end(),
1005 [function_attr](
1006 auto &&attr) { return attr->getKind() == function_attr; });
1007}

◆ if_dyn_cast()

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

Definition at line 230 of file clang_utils.h.

231{
232 if (pointer == nullptr)
233 return;
234
235 if (const auto *dyn_cast_value = clang::dyn_cast<T>(pointer);
236 dyn_cast_value) {
237 std::forward<F>(func)(dyn_cast_value);
238 }
239}

◆ is_bracket()

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

Definition at line 596 of file clang_utils.cc.

597{
598 return b == "(" || b == ")" || b == "[" || b == "]";
599}

◆ 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 979 of file clang_utils.cc.

980{
981 const auto *body = decl.getBody();
982 return clang::isa_and_nonnull<clang::CoroutineBodyStmt>(body);
983}

◆ is_identifier()

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

Definition at line 606 of file clang_utils.cc.

607{
608 return std::all_of(t.begin(), t.end(),
609 [](const char c) { return is_identifier_character(c); });
610}

◆ is_identifier_character()

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

Definition at line 601 of file clang_utils.cc.

602{
603 return std::isalnum(c) != 0 || c == '_';
604}

◆ is_keyword()

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

Definition at line 612 of file clang_utils.cc.

613{
614 static std::vector<std::string> keywords{"alignas", "alignof", "asm",
615 "auto", "bool", "break", "case", "catch", "char", "char16_t",
616 "char32_t", "class", "concept", "const", "constexpr", "const_cast",
617 "continue", "decltype", "default", "delete", "do", "double",
618 "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false",
619 "float", "for", "friend", "goto", "if", "inline", "int", "long",
620 "mutable", "namespace", "new", "noexcept", "nullptr", "operator",
621 "private", "protected", "public", "register", "reinterpret_cast",
622 "return", "requires", "short", "signed", "sizeof", "static",
623 "static_assert", "static_cast", "struct", "switch", "template", "this",
624 "thread_local", "throw", "true", "try", "typedef", "typeid", "typename",
625 "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t",
626 "while"};
627
628 return util::contains(keywords, t);
629}

◆ is_qualified_identifier()

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

Definition at line 631 of file clang_utils.cc.

632{
633 return std::isalpha(t.at(0)) != 0 &&
634 std::all_of(t.begin(), t.end(), [](const char c) {
635 return is_identifier_character(c) || c == ':';
636 });
637}

◆ is_qualifier()

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

Definition at line 591 of file clang_utils.cc.

592{
593 return q == "&" || q == "&&" || q == "const&";
594}

◆ 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 985 of file clang_utils.cc.

986{
987 if (decl == nullptr)
988 return false;
989
990 if (const auto *record = clang::dyn_cast<clang::CXXRecordDecl>(decl);
991 record) {
992 return record->isStruct();
993 }
994
995 if (const auto *tag = clang::dyn_cast<clang::TagDecl>(decl); tag) {
996 return tag->isStruct();
997 }
998
999 return false;
1000}

◆ 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 406 of file clang_utils.cc.

407{
408 if (parent_stmt == nullptr || sub_stmt == nullptr)
409 return false;
410
411 if (parent_stmt == sub_stmt)
412 return true;
413
414 return std::any_of(parent_stmt->child_begin(), parent_stmt->child_end(),
415 [sub_stmt](const auto *e) { return is_subexpr_of(e, sub_stmt); });
416}

◆ is_type_parameter()

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

Definition at line 586 of file clang_utils.cc.

587{
588 return t.find("type-parameter-") == 0;
589}

◆ is_type_token()

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

Definition at line 639 of file clang_utils.cc.

640{
641 return is_type_parameter(t) ||
642 (is_identifier(t) && !is_qualifier(t) && !is_bracket(t));
643}

◆ operator!=() [1/2]

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

Definition at line 72 of file types.cc.

72{ 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 // This is sadly necessary to catch accidental comparisons to empty
62 // std::optional<id_t>:
63 //
64 // std::optional<id_t> id{};
65 // if(id != 0) { /* id is nullopt, not 0 - so this executes... */ }
66 //
67 assert(v != 0);
68
69 return lhs.value_ != v;
70}

◆ operator<()

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

Definition at line 74 of file types.cc.

75{
76 if (lhs.is_global_ != rhs.is_global_) {
77 return lhs.value_ < rhs.value_ + 1;
78 }
79
80 return lhs.value_ < rhs.value_; // Compare values if is_global_ are the same
81}

◆ 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 904 of file clang_utils.cc.

906{
907 auto tokens = util::split(location_str, ":");
908
909 if (tokens.size() < 3)
910 return false;
911
912 if (tokens.size() == 4) {
913 // Handle Windows paths
914 decltype(tokens) tmp_tokens{};
915 tmp_tokens.emplace_back(
916 fmt::format("{}:{}", tokens.at(0), tokens.at(1)));
917 tmp_tokens.emplace_back(tokens.at(2));
918 tmp_tokens.emplace_back(tokens.at(3));
919
920 tokens = std::move(tmp_tokens);
921 }
922
923 file = tokens.at(0);
924 try {
925 line = std::stoi(tokens.at(1));
926 }
927 catch (std::invalid_argument &e) {
928 return false;
929 }
930
931 try {
932 column = std::stoi(tokens.at(2));
933 }
934 catch (std::invalid_argument &e) {
935 column = 0;
936 }
937
938 return true;
939}

◆ 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 494 of file clang_utils.cc.

498{
500
501 std::vector<template_parameter> res;
502
503 auto it = params.begin();
504 while (std::isspace(*it) != 0)
505 ++it;
506
507 std::string type{};
508 std::vector<template_parameter> nested_params;
509 bool complete_class_template_argument{false};
510
511 while (it != params.end()) {
512 if (*it == '<') {
513 int nested_level{0};
514 auto bracket_match_begin = it + 1;
515 auto bracket_match_end = bracket_match_begin;
516 while (bracket_match_end != params.end()) {
517 if (*bracket_match_end == '<') {
518 nested_level++;
519 }
520 else if (*bracket_match_end == '>') {
521 if (nested_level > 0)
522 nested_level--;
523 else
524 break;
525 }
526 else {
527 }
528 bracket_match_end++;
529 }
530
531 std::string nested_params_str(
532 bracket_match_begin, bracket_match_end);
533
534 nested_params = parse_unexposed_template_params(
535 nested_params_str, ns_resolve, depth + 1);
536
537 if (nested_params.empty()) {
538 // We couldn't extract any nested template parameters from
539 // `nested_params_str` so just add it as type of template
540 // argument as is
541 nested_params.emplace_back(
542 template_parameter::make_unexposed_argument(
543 nested_params_str));
544 }
545
546 it = bracket_match_end - 1;
547 }
548 else if (*it == '>') {
549 complete_class_template_argument = true;
550 if (depth == 0) {
551 break;
552 }
553 }
554 else if (*it == ',') {
555 complete_class_template_argument = true;
556 }
557 else {
558 type += *it;
559 }
560 if (complete_class_template_argument) {
561 auto t = template_parameter::make_unexposed_argument(
562 ns_resolve(clanguml::util::trim_typename(type)));
563 type = "";
564 for (auto &&param : nested_params)
565 t.add_template_param(std::move(param));
566
567 res.emplace_back(std::move(t));
568 complete_class_template_argument = false;
569 }
570 it++;
571 }
572
573 if (!type.empty()) {
574 auto t = template_parameter::make_unexposed_argument(
575 ns_resolve(clanguml::util::trim_typename(type)));
576 type = "";
577 for (auto &&param : nested_params)
578 t.add_template_param(std::move(param));
579
580 res.emplace_back(std::move(t));
581 }
582
583 return res;
584}

◆ 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 481 of file clang_utils.cc.

483{
484 assert(!full_name.empty());
485
486 auto name_before_template = ::clanguml::util::split(full_name, "<")[0];
488 ::clanguml::util::split(name_before_template, "::")};
489 auto name = ns.name();
490 ns.pop_back();
491 return {ns, name};
492}

◆ to_id()

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

Definition at line 464 of file clang_utils.cc.

465{
466 if (template_argument.getKind() == clang::TemplateArgument::Type) {
467 if (const auto *enum_type =
468 template_argument.getAsType()->getAs<clang::EnumType>();
469 enum_type != nullptr)
470 return to_id(*enum_type->getAsTagDecl());
471
472 if (const auto *record_type =
473 template_argument.getAsType()->getAs<clang::RecordType>();
474 record_type != nullptr)
475 return to_id(*record_type->getAsRecordDecl());
476 }
477
478 throw std::runtime_error("Cannot generate id for template argument");
479}

◆ to_string() [1/13]

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 116 of file clang_utils.cc.

119{
120 auto maybe_size = get_array_size(array_type);
121 std::string array_size =
122 maybe_size.has_value() ? std::to_string(maybe_size.value()) : "";
123 dimensions.emplace_back(std::move(array_size));
124
125 const auto underlying_type = array_type.getElementType();
126
127 if (underlying_type->isArrayType())
128 return to_string(*underlying_type->getAsArrayTypeUnsafe(), ctx,
129 try_canonical, dimensions);
130
131 std::string dimensions_str;
132 for (const auto &d : dimensions) {
133 dimensions_str += fmt::format("[{}]", d);
134 }
135 return fmt::format(
136 "{}{}", to_string(underlying_type, ctx, try_canonical), dimensions_str);
137}

◆ to_string() [2/13]

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

Definition at line 266 of file clang_utils.cc.

267{
268 const clang::LangOptions lang_options;
269 std::string result;
270 llvm::raw_string_ostream ostream(result);
271 expr->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options));
272
273 return result;
274}

◆ to_string() [3/13]

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

Definition at line 291 of file clang_utils.cc.

292{
293 std::vector<std::string> template_parameters;
294 // Handle template function
295 for (const auto *parameter : *decl->getTemplateParameters()) {
296 if (clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter) !=
297 nullptr) {
298 const auto *template_type_parameter =
299 clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter);
300
301 std::string template_parameter{
302 template_type_parameter->getNameAsString()};
303
304 if (template_type_parameter->isParameterPack())
305 template_parameter += "...";
306
307 template_parameters.emplace_back(std::move(template_parameter));
308 }
309 else {
310 // TODO
311 }
312 }
313 return fmt::format("{}<{}>({})", decl->getQualifiedNameAsString(),
314 fmt::join(template_parameters, ","), "");
315}

◆ to_string() [4/13]

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

Definition at line 139 of file clang_utils.cc.

141{
142 if (type->isArrayType()) {
143 std::vector<std::string> dimensions;
144 return to_string(
145 *type->getAsArrayTypeUnsafe(), ctx, try_canonical, dimensions);
146 }
147
148 clang::PrintingPolicy print_policy(ctx.getLangOpts());
149 print_policy.SuppressScope = 0;
150 print_policy.PrintCanonicalTypes = 0;
151
152 std::string result;
153
154 result = type.getAsString(print_policy);
155
156 if (try_canonical && result.find('<') != std::string::npos) {
157 auto canonical_type_name =
158 type.getCanonicalType().getAsString(print_policy);
159
160 auto result_qualified_template_name =
161 result.substr(0, result.find('<'));
162 auto result_template_arguments = result.substr(result.find('<'));
163
164 auto canonical_qualified_template_name =
165 canonical_type_name.substr(0, canonical_type_name.find('<'));
166
167 // Choose the longer name (why do I have to do this?)
168 if (result_qualified_template_name.size() <
169 canonical_qualified_template_name.size()) {
170
171 result =
172 canonical_qualified_template_name + result_template_arguments;
173 }
174 }
175
176 // If for any reason clang reports the type as empty string, make sure
177 // it has some default name
178 if (result.empty())
179 result = "(anonymous)";
180 else if (util::contains(result, "unnamed struct") ||
181 util::contains(result, "unnamed union")) {
182 const auto *declarationTag = type->getAsTagDecl();
183 if (declarationTag == nullptr) {
184 result = "(unnamed undeclared)";
185 }
186 else {
187 result = common::get_tag_name(*declarationTag);
188 }
189 }
190 else if (util::contains(result, "anonymous struct") ||
191 util::contains(result, "anonymous union")) {
192 result = common::get_tag_name(*type->getAsTagDecl());
193 }
194
195 // Remove trailing spaces after commas in template arguments
196 clanguml::util::replace_all(result, ", ", ",");
197 clanguml::util::replace_all(result, "> >", ">>");
198
199 // Try to get rid of 'type-parameter-X-Y' ugliness
200 if (result.find("type-parameter-") != std::string::npos) {
201 util::if_not_null(
202 common::dereference(type)->getAs<clang::TypedefType>(),
203 [&result, &type](auto *p) {
204 auto [unqualified_type, context] =
205 common::consume_type_context(type);
206 result = p->getDecl()->getNameAsString();
207 if (!context.empty()) {
208 std::vector<std::string> deduced_contexts;
209
210 for (const auto &c : context) {
211 deduced_contexts.push_back(c.to_string());
212 }
213
214 result = fmt::format(
215 "{} {}", result, fmt::join(deduced_contexts, " "));
216 }
217 });
218 }
219
220 return result;
221}

◆ to_string() [5/13]

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

Definition at line 223 of file clang_utils.cc.

225{
226 return to_string(type.desugar(), ctx, try_canonical);
227}

◆ to_string() [6/13]

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

Definition at line 281 of file clang_utils.cc.

282{
283 const clang::LangOptions lang_options;
284 std::string result;
285 llvm::raw_string_ostream ostream(result);
286 stmt->printPretty(ostream, nullptr, clang::PrintingPolicy(lang_options));
287
288 return result;
289}

◆ to_string() [7/13]

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

Definition at line 229 of file clang_utils.cc.

231{
232 switch (arg.getKind()) {
233 case clang::TemplateArgument::Expression:
234 return to_string(arg.getAsExpr());
235 case clang::TemplateArgument::Type:
236 return to_string(arg.getAsType(), *ctx, false);
237 case clang::TemplateArgument::Null:
238 return "";
239 case clang::TemplateArgument::NullPtr:
240 return "nullptr";
241 case clang::TemplateArgument::Integral:
242 return std::to_string(arg.getAsIntegral().getExtValue());
243 case clang::TemplateArgument::Template:
244 return to_string(arg.getAsTemplate());
245 case clang::TemplateArgument::TemplateExpansion:
246 return to_string(arg.getAsTemplateOrTemplatePattern());
247 default:
248 return "";
249 }
250}

◆ to_string() [8/13]

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

Definition at line 252 of file clang_utils.cc.

253{
254 if (templ.getAsTemplateDecl() != nullptr) {
255 return templ.getAsTemplateDecl()->getQualifiedNameAsString();
256 }
257
258 std::string result;
259 const clang::LangOptions lang_options;
260 llvm::raw_string_ostream ostream(result);
261 templ.print(ostream, clang::PrintingPolicy(lang_options));
262
263 return result;
264}

◆ to_string() [9/13]

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

Definition at line 317 of file clang_utils.cc.

318{
319 if (tc == nullptr)
320 return {};
321
322 const clang::PrintingPolicy print_policy(
323 tc->getNamedConcept()->getASTContext().getLangOpts());
324
325 std::string ostream_buf;
326 llvm::raw_string_ostream ostream{ostream_buf};
327 tc->print(ostream, print_policy);
328
329 return ostream.str();
330}

◆ to_string() [10/13]

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

Definition at line 276 of file clang_utils.cc.

277{
278 return val->getQualifiedNameAsString();
279}

◆ to_string() [11/13]

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

Definition at line 96 of file types.cc.

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

◆ to_string() [12/13]

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

Definition at line 92 of file types.cc.

92{ return s; }

◆ to_string() [13/13]

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

Definition at line 94 of file types.cc.

94{ 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 821 of file clang_utils.cc.

823{
824 std::vector<std::string> result;
825
826 auto spaced_out = util::split(t, " ");
827
828 for (const auto &word : spaced_out) {
829 if (is_qualified_identifier(word)) {
830 if (word != "class" && word != "templated" && word != "struct")
831 result.emplace_back(word);
832 continue;
833 }
834
835 std::string tok;
836
837 for (const char c : word) {
838 if (c == '(' || c == ')' || c == '[' || c == ']' || c == '<' ||
839 c == '>') {
840 if (!tok.empty())
841 result.emplace_back(tok);
842 result.emplace_back(std::string{c});
843 tok.clear();
844 }
845 else if (c == ':') {
846 if (!tok.empty() && tok != ":") {
847 result.emplace_back(tok);
848 tok = ":";
849 }
850 else if (tok == ":") {
851 result.emplace_back("::");
852 tok = "";
853 }
854 else {
855 tok += ':';
856 }
857 }
858 else if (c == ',') {
859 if (!tok.empty()) {
860 result.emplace_back(tok);
861 }
862 result.emplace_back(",");
863 tok.clear();
864 }
865 else if (c == '*') {
866 if (!tok.empty()) {
867 result.emplace_back(tok);
868 }
869 result.emplace_back("*");
870 tok.clear();
871 }
872 else if (c == '.') {
873 // This can only be the case if we have a variadic template,
874 // right?
875 if (tok == "..") {
876 result.emplace_back("...");
877 tok.clear();
878 }
879 else if (tok == ".") {
880 tok = "..";
881 }
882 else if (!tok.empty()) {
883 result.emplace_back(tok);
884 tok = ".";
885 }
886 }
887 else {
888 tok += c;
889 }
890 }
891
892 tok = util::trim(tok);
893
894 if (!tok.empty()) {
895 if (tok != "class" && tok != "typename" && word != "struct")
896 result.emplace_back(tok);
897 tok.clear();
898 }
899 }
900
901 return result;
902}