0.6.0
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
template_builder.h
Go to the documentation of this file.
1/**
2 * @file src/class_diagram/visitor/template_builder.h
3 *
4 * Copyright (c) 2021-2025 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18#pragma once
19
25#include "config/config.h"
26
28
31using common::model::template_parameter;
32
33namespace detail {
34
36 const clang::ClassTemplateSpecializationDecl *decl, const std::string &tp);
37
39 const clang::TypeAliasTemplateDecl *decl, const std::string &tp);
40
41} // namespace detail
42
44 const clang::Decl *decl, const std::string &type_parameter);
45
46/**
47 * @brief Class responsible for building all kinds of templates from Clang AST.
48 */
49template <typename VisitorT> class template_builder {
50public:
51 /**
52 * @brief Constructor.
53 *
54 * @param visitor Reference to class diagram translation_unit_visitor
55 */
57 const clanguml::config::diagram &config_, VisitorT &visitor);
58
59 /**
60 * @brief Get reference to the current diagram model
61 *
62 * @return Reference to the current diagram model
63 */
65
66 /**
67 * @brief Get reference to the current diagram configuration
68 *
69 * @return Reference to the current diagram configuration
70 */
71 const config::diagram &config() const;
72
73 /**
74 * @brief Get diagram relative namespace
75 *
76 * @return Diagram relative namespace
77 */
78 const namespace_ &using_namespace() const;
79
80 /**
81 * @brief Simplify system templates
82 *
83 * This method tries to simplify all fully qualified template names
84 * in the `full_name` using substitutions from the configuration file
85 * ().
86 *
87 * Typical example is replace every `std::basic_string<char>` with
88 * `std::string`.
89 *
90 * @param ct Template parameter
91 * @param full_name Full template name
92 * @return
93 */
95 template_parameter &ct, const std::string &full_name) const;
96
99 const clang::TemplateDecl &template_declaration,
100 common::optional_ref<common::model::element> templated_element = {});
101
102 /**
103 * @brief Basic template class build method
104 *
105 * @param cls Clang template declaration
106 * @param template_type_decl Template specialization type
107 * @param parent Optional class in which this template is contained
108 * @return Created template class model
109 */
111 clanguml::common::model::template_element &template_instantiation,
112 const clang::NamedDecl *cls,
113 const clang::TemplateSpecializationType &template_type_decl,
114 std::optional<clanguml::common::model::template_element *> parent = {});
115
116 void build(
117 clanguml::common::model::template_element &template_instantiation,
118 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
119 clang::ArrayRef<clang::TemplateArgument> template_arguments,
120 std::string full_template_specialization_name,
121 std::optional<clanguml::common::model::template_element *> parent = {});
122
123 /**
124 * @brief Build template class from class template specialization decl
125 *
126 * @param template_specialization Class template specialization declaration
127 * @param parent Optional class in which this template is contained
128 * @return Created template class model
129 */
131 clanguml::common::model::template_element &template_instantiation,
132 const clang::ClassTemplateSpecializationDecl &template_specialization,
133 std::optional<clanguml::common::model::template_element *> parent = {});
134
135 /**
136 * @brief Add base classes to the template class, if any.
137 *
138 * This method adds base classes to a template declaration or
139 * specialization, including inferring whether variadic template
140 * parameter bases.
141 *
142 * @param tinst Class template model
143 * @param template_base_params List of base class template parameters
144 * @param arg_index Index of the template argument used for base class
145 * @param variadic_params Whether the parameter is variadic
146 * @param ct Template parameter model
147 * @return True, if any base classes were added
148 */
150 std::deque<std::tuple<std::string, int, bool>> &template_base_params,
151 int arg_index, bool variadic_params,
153
154 /**
155 * @brief Process template class parameters and arguments
156 *
157 * @param parent Optional class in which this template is contained
158 * @param cls Template class specialization declaration
159 * @param template_base_params List of base class template parameters
160 * @param template_args List of template arguments
161 * @param template_instantiation Template class model to add template args
162 * @param template_decl Base template declaration
163 */
165 std::optional<clanguml::common::model::template_element *> &parent,
166 const clang::NamedDecl *cls,
167 std::deque<std::tuple<std::string, int, bool>> &template_base_params,
168 const clang::ArrayRef<clang::TemplateArgument> &template_args,
169 clanguml::common::model::template_element &template_instantiation,
170 const clang::TemplateDecl *template_decl);
171
172 /**
173 * @brief Process template arguments based on their type
174 *
175 * @param parent Optional class in which this template is contained
176 * @param cls Template class specialization declaration
177 * @param template_instantiation Template class model to add template args
178 * @param template_decl Base template declaration
179 * @param arg Template argument
180 * @param argument_index Argument index
181 * @param argument Output list of arguments (can be more than one for
182 * variadic parameters)
183 */
185 std::optional<clanguml::common::model::template_element *> &parent,
186 const clang::NamedDecl *cls,
187 clanguml::common::model::template_element &template_instantiation,
188 const clang::TemplateDecl *template_decl,
189 const clang::TemplateArgument &arg, size_t argument_index,
190 std::vector<template_parameter> &argument);
191
192 /**
193 * @brief Process `clang::TemplateArgument::Expression`
194 *
195 * @note The template argument is a pack expansion of a template name that
196 * was provided for a template template parameter.
197 *
198 * @param arg Template argument
199 * @return Return template argument model
200 */
202 const clang::TemplateArgument &arg);
203
204 /**
205 * @brief Process `clang::TemplateArgument::Integral`
206 *
207 * @note The template argument is an integral value stored in an
208 * llvm::APSInt that was provided for an integral non-type template
209 * parameter.
210 *
211 * @param arg Template argument
212 * @return Return template argument model
213 */
215 const clang::TemplateArgument &arg,
216 const clang::ASTContext &ast_context);
217
218#if LLVM_VERSION_MAJOR > 17
219 /**
220 * @brief Process `clang::TemplateArgument::StructuralValue`
221 *
222 * @note The template argument is a non-type template argument that can't be
223 * represented by the special-case Declaration, NullPtr, or Integral
224 * forms.
225 *
226 * @param arg Template argument
227 * @return Return template argument model
228 */
229 template_parameter process_structural_argument(
230 const clang::TemplateArgument &arg,
231 const clang::ASTContext &ast_context);
232#endif
233
234 /**
235 * @brief Process `clang::TemplateArgument::NullPtr`
236 *
237 * @note The template argument is a null pointer or null pointer to member
238 * that was provided for a non-type template parameter.
239 *
240 * @param arg Template argument
241 * @return Return template argument model
242 */
244 const clang::TemplateArgument &arg);
245
246 /**
247 * @brief Process `clang::TemplateArgument::Null`
248 *
249 * @note Represents an empty template argument, e.g., one that has not
250 * been deduced.
251 *
252 * @param arg Template argument
253 * @return Return template argument model
254 */
256 const clang::TemplateArgument &arg);
257
258 /**
259 * @brief Process `clang::TemplateArgument::Pack`
260 *
261 * @note The template argument is actually a parameter pack. Arguments are
262 * stored in the Args struct.
263 *
264 * @param arg Template argument
265 * @return Return template argument model
266 */
267 std::vector<template_parameter> process_pack_argument(
268 std::optional<clanguml::common::model::template_element *> &parent,
269 const clang::NamedDecl *cls,
270 clanguml::common::model::template_element &template_instantiation,
271 const clang::TemplateDecl *base_template_decl,
272 const clang::TemplateArgument &arg, size_t argument_index,
273 std::vector<template_parameter> &argument);
274
275 /**
276 * @brief Process `clang::TemplateArgument::Type`
277 *
278 * @note The template argument is a type.
279 *
280 * @param arg Template argument
281 * @return Return template argument model
282 */
284 std::optional<clanguml::common::model::template_element *> &parent,
285 const clang::NamedDecl *cls,
286 const clang::TemplateDecl *base_template_decl, clang::QualType type,
287 clanguml::common::model::template_element &template_instantiation,
288 size_t argument_index);
289
290 /**
291 * @brief Process `clang::TemplateArgument::Template`
292 *
293 * @note The template argument is a template name that was provided for a
294 * template template parameter.
295 *
296 * @param arg Template argument
297 * @return Return template argument model
298 */
300 const clang::TemplateArgument &arg);
301
302 /**
303 * @brief Process `clang::TemplateArgument::TemplateExpansion`
304 *
305 * @note The template argument is a pack expansion of a template name that
306 * was provided for a template template parameter.
307 *
308 * @param arg Template argument
309 * @return Return template argument model
310 */
312 const clang::TemplateArgument &arg);
313
314 /**
315 * @brief Try to process template type argument as function template
316 *
317 * @param parent Optional class in which this template is contained
318 * @param cls Template class specialization declaration
319 * @param template_decl Base template declaration
320 * @param type Template type
321 * @param template_instantiation Template class model
322 * @param argument_index Argument index
323 * @return Function template argument if succeeds, or std::nullopt
324 */
325 std::optional<template_parameter> try_as_function_prototype(
326 std::optional<clanguml::common::model::template_element *> &parent,
327 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
328 clang::QualType &type,
329 clanguml::common::model::template_element &template_instantiation,
330 size_t argument_index);
331
332 /**
333 * @brief Try to process template type argument as array
334 *
335 * @param parent Optional class in which this template is contained
336 * @param cls Template class specialization declaration
337 * @param template_decl Base template declaration
338 * @param type Template type
339 * @param template_instantiation Template class model
340 * @param argument_index Argument index
341 * @return Array template argument if succeeds, or std::nullopt
342 */
343 std::optional<template_parameter> try_as_array(
344 std::optional<clanguml::common::model::template_element *> &parent,
345 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
346 clang::QualType &type,
347 clanguml::common::model::template_element &template_instantiation,
348 size_t argument_index);
349
350 /**
351 * @brief Try to process template type argument as specialization type
352 *
353 * @param parent Optional class in which this template is contained
354 * @param cls Template class specialization declaration
355 * @param template_decl Base template declaration
356 * @param type Template type
357 * @param template_instantiation Template class model
358 * @param argument_index Argument index
359 * @return Template specialization template argument if succeeds,
360 * or std::nullopt
361 */
362 std::optional<template_parameter> try_as_template_specialization_type(
363 std::optional<clanguml::common::model::template_element *> &parent,
364 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
365 clang::QualType &type,
366 clanguml::common::model::template_element &template_instantiation,
367 size_t argument_index);
368
369 /**
370 * @brief Try to process template type argument as template parameter
371 *
372 * @param cls Template class specialization declaration
373 * @param template_decl Base template declaration
374 * @param type Template type
375 * @return Template parameter if succeeds, or std::nullopt
376 */
377 std::optional<template_parameter> try_as_template_parm_type(
378 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
379 clang::QualType &type);
380
381 /**
382 * @brief Try to process template type argument as lambda
383 *
384 * @param cls Template class specialization declaration
385 * @param template_decl Base template declaration
386 * @param type Template type
387 * @return Lambda template argument if succeeds, or std::nullopt
388 */
389 std::optional<template_parameter> try_as_lambda(const clang::NamedDecl *cls,
390 const clang::TemplateDecl *template_decl, clang::QualType &type);
391
392 /**
393 * @brief Try to process template type argument as record type
394 *
395 * @param parent Optional class in which this template is contained
396 * @param cls Template class specialization declaration
397 * @param template_decl Base template declaration
398 * @param type Template type
399 * @param template_instantiation Template class model
400 * @param argument_index Argument index
401 * @return Record type template argument if succeeds, or std::nullopt
402 */
403 std::optional<template_parameter> try_as_record_type(
404 std::optional<clanguml::common::model::template_element *> &parent,
405 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
406 clang::QualType &type,
407 clanguml::common::model::template_element &template_instantiation,
408 size_t argument_index);
409
410 /**
411 * @brief Try to process template type argument as enum type
412 *
413 * @param parent Optional class in which this template is contained
414 * @param cls Template class specialization declaration
415 * @param template_decl Base template declaration
416 * @param type Template type
417 * @param template_instantiation Template class model
418 * @return Enum type template argument if succeeds, or std::nullopt
419 */
420 std::optional<template_parameter> try_as_enum_type(
421 std::optional<clanguml::common::model::template_element *> &parent,
422 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
423 clang::QualType &type,
424 clanguml::common::model::template_element &template_instantiation);
425
426 /**
427 * @brief Try to process template type argument as builtin type
428 *
429 * @param parent Optional class in which this template is contained
430 * @param type Template type
431 * @param template_decl Base template declaration
432 * @return Builtin type template argument if succeeds, or std::nullopt
433 */
434 std::optional<template_parameter> try_as_builtin_type(
435 std::optional<clanguml::common::model::template_element *> &parent,
436 clang::QualType &type, const clang::TemplateDecl *template_decl);
437
438 /**
439 * @brief Try to process template type argument as member pointer type
440 *
441 * @param parent Optional class in which this template is contained
442 * @param cls Template class specialization declaration
443 * @param template_decl Base template declaration
444 * @param type Template type
445 * @param template_instantiation Template class model
446 * @param argument_index Argument index
447 * @return Member pointer type template argument if succeeds,
448 * or std::nullopt
449 */
450 std::optional<template_parameter> try_as_member_pointer(
451 std::optional<clanguml::common::model::template_element *> &parent,
452 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
453 clang::QualType &type,
454 clanguml::common::model::template_element &template_instantiation,
455 size_t argument_index);
456
457 /**
458 * @brief Try to process template type argument as `decltype()` type
459 *
460 * @param parent Optional class in which this template is contained
461 * @param cls Template class specialization declaration
462 * @param template_decl Base template declaration
463 * @param type Template type
464 * @param template_instantiation Template class model
465 * @param argument_index Argument index
466 * @return `decltype()` type template argument if succeeds, or std::nullopt
467 */
468 std::optional<template_parameter> try_as_decl_type(
469 std::optional<clanguml::common::model::template_element *> &parent,
470 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
471 clang::QualType &type,
472 clanguml::common::model::template_element &template_instantiation,
473 size_t argument_index);
474
475 /**
476 * @brief Try to process template type argument as typedef/using type
477 *
478 * @param parent Optional class in which this template is contained
479 * @param cls Template class specialization declaration
480 * @param template_decl Base template declaration
481 * @param type Template type
482 * @param template_instantiation Template class model
483 * @param argument_index Argument index
484 * @return Typedef type template argument if succeeds, or std::nullopt
485 */
486 std::optional<template_parameter> try_as_typedef_type(
487 std::optional<clanguml::common::model::template_element *> &parent,
488 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
489 clang::QualType &type,
490 clanguml::common::model::template_element &template_instantiation,
491 size_t argument_index);
492
493 /**
494 * @brief Remove types context (e.g. const or reference qualifiers)
495 *
496 * This method removes all const and reference/pointer qualifiers from
497 * `type`, adds them to the template parameter model `tp` and returns
498 * a type without context.
499 *
500 * @param type Type to remove context from
501 * @param tp Template model to add context to
502 * @return Type without context
503 */
504 clang::QualType consume_context(
505 clang::QualType type, template_parameter &tp) const;
506
507 /**
508 * @brief Try to find additional relationships in unexposed parameters
509 *
510 * Sometimes, Clang will report a template parameter as unexposed, which
511 * means all we get a is a string representation of the type, sometimes
512 * with template parameter names replaced with `type-parameter-X-Y`
513 * string.
514 *
515 * This method tries to find any type names, which might be relevant for
516 * the diagram relationships.
517 *
518 * @param ct Template argument model
519 * @param relationships List of discovered relationships
520 * @return True, if any relationships were found
521 */
523 const template_parameter &ct, found_relationships_t &relationships);
524
526 common::model::template_element &template_instantiation, eid_t id,
527 const std::string &qualified_name) const;
528
529 /**
530 * @brief Get reference to Clang AST to clang-uml id mapper
531 *
532 * @return Reference to Clang AST to clang-uml id mapper
533 */
535
536 /**
537 * @brief Get reference to the current source manager
538 *
539 * @return Reference to the current source manager
540 */
541 clang::SourceManager &source_manager() const;
542
543private:
544 // Reference to the output diagram model
546
547 // Reference to diagram config
549
551
552 clang::SourceManager &source_manager_;
553
554 VisitorT &visitor_;
555};
556
557template <typename VisitorT>
560 const clanguml::config::diagram &config_, VisitorT &visitor)
561 : diagram_{diagram_}
562 , config_{config_}
563 , id_mapper_{visitor.id_mapper()}
564 , source_manager_{visitor.source_manager()}
565 , visitor_{visitor}
566{
567}
568
569template <typename VisitorT>
571{
572 return diagram_;
573}
574
575template <typename VisitorT>
577{
578 return config_;
579}
580
581template <typename VisitorT>
583{
584 return config_.using_namespace();
585}
586
587template <typename VisitorT>
589{
590 return id_mapper_;
591}
592
593template <typename VisitorT>
595{
596 return source_manager_;
597}
598
599template <typename VisitorT>
601 template_parameter &ct, const std::string &full_name) const
602{
604 return false;
605
606 auto simplified = config().simplify_template_type(full_name);
607
608 if (simplified != full_name) {
609 ct.set_type(simplified);
610 ct.set_id(common::to_id(simplified));
611 ct.clear_params();
612 return true;
613 }
614
615 return false;
616}
617
618template <typename VisitorT>
621 const clang::TemplateDecl &template_declaration,
623{
624 LOG_DBG("Processing {} template parameters...",
625 common::get_qualified_name(template_declaration));
626
627 if (template_declaration.getTemplateParameters() == nullptr)
628 return;
629
630 for (const auto *parameter :
631 *template_declaration.getTemplateParameters()) {
632 if (clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter) !=
633 nullptr) {
634 const auto *template_type_parameter =
635 clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter);
636
637 std::optional<std::string> default_arg;
638 if (template_type_parameter->hasDefaultArgument()) {
639 default_arg =
640#if LLVM_VERSION_MAJOR > 18
642 template_type_parameter->getDefaultArgument(),
643 template_declaration.getASTContext());
644#else
645 template_type_parameter->getDefaultArgument().getAsString();
646#endif
647 }
648
649 auto parameter_name = template_type_parameter->getNameAsString();
650 if (parameter_name.empty())
651 parameter_name = "typename";
652
653 auto ct = template_parameter::make_template_type(parameter_name,
654 default_arg, template_type_parameter->isParameterPack());
655
656 if constexpr (std::is_same_v<typename VisitorT::diagram_t,
658 if (template_type_parameter->getTypeConstraint() != nullptr) {
660 template_type_parameter->getTypeConstraint()
661 ->getNamedConcept(),
662 [this, &ct, &templated_element](
663 const clang::ConceptDecl *named_concept) mutable {
664 ct.set_concept_constraint(
665 named_concept->getQualifiedNameAsString());
666 if (templated_element &&
667 visitor_.should_include(named_concept)) {
668 templated_element.value().add_relationship(
669 {relationship_t::kConstraint,
670 id_mapper()
671 .get_global_id(
672 eid_t{named_concept->getID()})
673 .value(),
674 model::access_t::kNone,
675 ct.name().value()});
676 }
677 });
678 }
679 }
680 else {
681 (void)templated_element; // NOLINT
682 }
683
684 template_model.add_template(std::move(ct));
685 }
686 else if (clang::dyn_cast_or_null<clang::NonTypeTemplateParmDecl>(
687 parameter) != nullptr) {
688 const auto *template_nontype_parameter =
689 clang::dyn_cast_or_null<clang::NonTypeTemplateParmDecl>(
690 parameter);
691
692 std::optional<std::string> default_arg;
693
694 if (template_nontype_parameter->hasDefaultArgument()) {
695 default_arg =
696#if LLVM_VERSION_MAJOR > 18
698 template_nontype_parameter->getDefaultArgument(),
699 template_declaration.getASTContext());
700#else
702 template_nontype_parameter->getDefaultArgument());
703#endif
704 }
706 template_nontype_parameter->getType().getAsString(),
707 template_nontype_parameter->getNameAsString(), default_arg,
708 template_nontype_parameter->isParameterPack());
709
710 template_model.add_template(std::move(ct));
711 }
712 else if (clang::dyn_cast_or_null<clang::TemplateTemplateParmDecl>(
713 parameter) != nullptr) {
714 const auto *template_template_parameter =
715 clang::dyn_cast_or_null<clang::TemplateTemplateParmDecl>(
716 parameter);
717 std::optional<std::string> default_arg;
718 if (template_template_parameter->hasDefaultArgument()) {
719 default_arg = common::to_string(
720 template_template_parameter->getDefaultArgument()
721 .getArgument());
722 }
724 template_template_parameter->getNameAsString(), default_arg,
725 template_template_parameter->isParameterPack());
726
727 template_model.add_template(std::move(ct));
728 }
729 else {
730 // pass
731 }
732 }
733}
734
735template <typename VisitorT>
737 clanguml::common::model::template_element &template_instantiation,
738 const clang::NamedDecl *cls,
739 const clang::TemplateSpecializationType &template_type_decl,
740 std::optional<clanguml::common::model::template_element *> parent)
741{
742 const auto *template_type_ptr = &template_type_decl;
743
744 if (template_type_decl.isTypeAlias()) {
745 if (const auto *tsp =
746 template_type_decl.getAliasedType()
747 ->template getAs<clang::TemplateSpecializationType>();
748 tsp != nullptr)
749 template_type_ptr = tsp;
750 }
751
752 const auto &template_type = *template_type_ptr;
753
754 template_instantiation.is_template(true);
755
756 std::string full_template_specialization_name = common::to_string(
757 template_type.desugar(),
758 template_type.getTemplateName().getAsTemplateDecl()->getASTContext());
759
760 auto *template_decl{template_type.getTemplateName().getAsTemplateDecl()};
761
762 build(template_instantiation, cls, template_decl,
763 template_type.template_arguments(), full_template_specialization_name,
764 parent);
765}
766
767template <typename VisitorT>
769 clanguml::common::model::template_element &template_instantiation,
770 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
771 const clang::ArrayRef<clang::TemplateArgument> template_arguments,
772 std::string full_template_specialization_name,
773 std::optional<clanguml::common::model::template_element *> parent)
774{
775 //
776 // Here we'll hold the template base class params to replace with the
777 // instantiated values
778 //
779 std::deque<std::tuple</*parameter name*/ std::string, /* position */ int,
780 /*is variadic */ bool>>
781 template_base_params{};
782
783 auto template_decl_qualified_name =
784 template_decl->getQualifiedNameAsString();
785
786 if (const auto *class_template_decl =
787 clang::dyn_cast<clang::ClassTemplateDecl>(template_decl);
788 (class_template_decl != nullptr) &&
789 (class_template_decl->getTemplatedDecl() != nullptr) &&
790 (class_template_decl->getTemplatedDecl()->getParent() != nullptr) &&
791 class_template_decl->getTemplatedDecl()->getParent()->isRecord()) {
792
793 namespace_ ns{
794 common::get_tag_namespace(*class_template_decl->getTemplatedDecl()
795 ->getParent()
796 ->getOuterLexicalRecordContext())};
797
798 std::string ns_str = ns.to_string();
799 std::string name = template_decl->getQualifiedNameAsString();
800 if (!ns_str.empty()) {
801 name = name.substr(ns_str.size() + 2);
802 }
803
804 util::replace_all(name, "::", "##");
805 template_instantiation.set_name(name);
806
807 template_instantiation.set_namespace(ns);
808 }
809 else {
810 namespace_ ns{template_decl_qualified_name};
811 ns.pop_back();
812 template_instantiation.set_name(template_decl->getNameAsString());
813 template_instantiation.set_namespace(ns);
814 }
815
816 // TODO: Refactor handling of base parameters to a separate method
817
818 // We need this to match any possible base classes coming from template
819 // arguments
820 std::vector<
821 std::pair</* parameter name */ std::string, /* is variadic */ bool>>
822 template_parameter_names{};
823
824 for (const auto *parameter : *template_decl->getTemplateParameters()) {
825 if (parameter->isTemplateParameter() &&
826 (parameter->isTemplateParameterPack() ||
827 parameter->isParameterPack())) {
828 template_parameter_names.emplace_back(
829 parameter->getNameAsString(), true);
830 }
831 else
832 template_parameter_names.emplace_back(
833 parameter->getNameAsString(), false);
834 }
835
836 // Check if the primary template has any base classes
837 int base_index = 0;
838
839 const auto *templated_class_decl =
840 clang::dyn_cast_or_null<clang::CXXRecordDecl>(
841 template_decl->getTemplatedDecl());
842
843 if ((templated_class_decl != nullptr) &&
844 templated_class_decl->hasDefinition())
845 for (const auto &base : templated_class_decl->bases()) {
846 const auto base_class_name = common::to_string(
847 base.getType(), templated_class_decl->getASTContext(), false);
848
849 LOG_DBG("Found template instantiation base: {}, {}",
850 base_class_name, base_index);
851
852 // Check if any of the primary template arguments has a
853 // parameter equal to this type
854 auto it = std::find_if(template_parameter_names.begin(),
855 template_parameter_names.end(),
856 [&base_class_name](
857 const auto &p) { return p.first == base_class_name; });
858
859 if (it != template_parameter_names.end()) {
860 const auto &parameter_name = it->first;
861 const bool is_variadic = it->second;
862 // Found base class which is a template parameter
863 LOG_DBG("Found base class which is a template parameter "
864 "{}, {}, {}",
865 parameter_name, is_variadic,
866 std::distance(template_parameter_names.begin(), it));
867
868 template_base_params.emplace_back(parameter_name,
869 std::distance(template_parameter_names.begin(), it),
870 is_variadic);
871 }
872 else {
873 // This is a regular base class - it is handled by
874 // process_template
875 }
876 base_index++;
877 }
878
879 process_template_arguments(parent, cls, template_base_params,
880 template_arguments, template_instantiation, template_decl);
881
882 if constexpr (std::is_same_v<typename VisitorT::diagram_t,
884 find_instantiation_relationships(template_instantiation,
885 eid_t{template_decl->getID()}, full_template_specialization_name);
886 }
887
888 template_instantiation.set_id(
889 common::to_id(template_instantiation.full_name(false)));
890
891 visitor_.set_source_location(*cls, template_instantiation);
892}
893
894template <typename VisitorT>
896 clanguml::common::model::template_element &template_instantiation,
897 const clang::ClassTemplateSpecializationDecl &template_specialization,
898 std::optional<clanguml::common::model::template_element *> parent)
899{
900 //
901 // Here we'll hold the template base params to replace with the
902 // instantiated values
903 //
904 std::deque<std::tuple</*parameter name*/ std::string, /* position */ int,
905 /*is variadic */ bool>>
906 template_base_params{};
907
908 const clang::ClassTemplateDecl *template_decl =
909 template_specialization.getSpecializedTemplate();
910
911 auto qualified_name = template_decl->getQualifiedNameAsString();
912
913 namespace_ ns{qualified_name};
914 ns.pop_back();
915 template_instantiation.set_name(template_decl->getNameAsString());
916 template_instantiation.set_namespace(ns);
917
918 process_template_arguments(parent, &template_specialization,
919 template_base_params,
920 template_specialization.getTemplateArgs().asArray(),
921 template_instantiation, template_decl);
922
923 // Update the id after the template parameters are processed
924 template_instantiation.set_id(
925 common::to_id(template_instantiation.full_name(false)));
926
927 if constexpr (std::is_same_v<typename VisitorT::diagram_t,
929 find_instantiation_relationships(template_instantiation,
930 eid_t{template_specialization.getID()}, qualified_name);
931 }
932
933 visitor_.set_source_location(*template_decl, template_instantiation);
934}
935
936template <typename VisitorT>
938 common::model::template_element &template_instantiation, eid_t id,
939 const std::string &qualified_name) const
940{
942 template_instantiation, qualified_name, id);
943}
944
945template <typename VisitorT>
947 std::optional<clanguml::common::model::template_element *> &parent,
948 const clang::NamedDecl *cls,
949 std::deque<std::tuple<std::string, int, bool>> &template_base_params,
950 const clang::ArrayRef<clang::TemplateArgument> &template_args,
951 clanguml::common::model::template_element &template_instantiation,
952 const clang::TemplateDecl *template_decl)
953{
954 auto arg_index{0};
955
956 for (const auto &arg : template_args) {
957 // Argument can be a parameter pack in which case it gives multiple
958 // arguments
959 std::vector<template_parameter> arguments;
960
961 // Handle empty pack expansion
962 if (arg.getKind() == clang::TemplateArgument::ArgKind::Pack &&
963 arg.getPackAsArray().empty()) {
964 template_instantiation.add_template(
965 template_parameter::make_empty());
966 }
967
968 // For now ignore the default template arguments of templates which
969 // do not match the inclusion filters, to make the system
970 // templates 'nicer' - i.e. skipping the allocators and comparators
971 // TODO: Change this to ignore only when the arguments are set to
972 // default values, and add them when they are specifically
973 // overridden
974 if (!diagram().should_include(
975 namespace_{template_decl->getQualifiedNameAsString()})) {
976 const auto *maybe_type_parm_decl =
977 clang::dyn_cast<clang::TemplateTypeParmDecl>(
978 template_decl->getTemplateParameters()->getParam(
979 std::min<unsigned>(arg_index,
980 static_cast<unsigned>(
981 template_decl->getTemplateParameters()
982 ->size()) -
983 1)));
984 if (maybe_type_parm_decl != nullptr &&
985 maybe_type_parm_decl->hasDefaultArgument()) {
986 continue;
987 }
988 }
989
990 //
991 // Handle the template parameter/argument based on its kind
992 //
993 argument_process_dispatch(parent, cls, template_instantiation,
994 template_decl, arg, arg_index, arguments);
995
996 if (arguments.empty()) {
997 arg_index++;
998 continue;
999 }
1000
1001 // We can figure this only when we encounter variadic param in
1002 // the list of template params, from then this variable is true
1003 // and we can process following template parameters as belonging
1004 // to the variadic tuple
1005 [[maybe_unused]] auto variadic_params{false};
1006
1007 if constexpr (std::is_same_v<typename VisitorT::diagram_t,
1009 // In case any of the template arguments are base classes, add
1010 // them as parents of the current template instantiation class
1011 if (!template_base_params.empty()) {
1012 variadic_params = add_base_classes(template_instantiation,
1013 template_base_params, arg_index, variadic_params,
1014 arguments.front());
1015 }
1016 }
1017
1018 for (auto &argument : arguments) {
1019 simplify_system_template(
1020 argument, argument.to_string(using_namespace(), false, true));
1021
1022 LOG_DBG("Adding template argument {} to template "
1023 "specialization/instantiation {}",
1024 argument.to_string(using_namespace(), false),
1025 template_instantiation.name());
1026
1027 template_instantiation.add_template(std::move(argument));
1028 }
1029
1030 arg_index++;
1031 }
1032
1033 // Update id
1034 template_instantiation.set_id(
1035 common::to_id(template_instantiation.full_name(false)));
1036}
1037
1038template <typename VisitorT>
1040 std::optional<clanguml::common::model::template_element *> &parent,
1041 const clang::NamedDecl *cls,
1042 clanguml::common::model::template_element &template_instantiation,
1043 const clang::TemplateDecl *template_decl,
1044 const clang::TemplateArgument &arg, size_t argument_index,
1045 std::vector<template_parameter> &argument)
1046{
1047 LOG_DBG("Processing argument {} in template class: {}", argument_index,
1048 cls->getQualifiedNameAsString());
1049
1050 switch (arg.getKind()) {
1051 case clang::TemplateArgument::Null:
1052 argument.push_back(process_null_argument(arg));
1053 break;
1054 case clang::TemplateArgument::Template:
1055 argument.push_back(process_template_argument(arg));
1056 break;
1057 case clang::TemplateArgument::Type: {
1058 argument.push_back(process_type_argument(parent, cls, template_decl,
1059 arg.getAsType(), template_instantiation, argument_index));
1060 break;
1061 }
1062 case clang::TemplateArgument::Declaration:
1063 break;
1064 case clang::TemplateArgument::NullPtr:
1065 argument.push_back(process_nullptr_argument(arg));
1066 break;
1067 case clang::TemplateArgument::Integral:
1068 argument.push_back(
1069 process_integral_argument(arg, template_decl->getASTContext()));
1070 break;
1071 case clang::TemplateArgument::TemplateExpansion:
1072 argument.push_back(process_template_expansion(arg));
1073 break;
1074 case clang::TemplateArgument::Expression:
1075 argument.push_back(process_expression_argument(arg));
1076 break;
1077 case clang::TemplateArgument::Pack:
1078 for (auto &a :
1079 process_pack_argument(parent, cls, template_instantiation,
1080 template_decl, arg, argument_index, argument)) {
1081 argument.push_back(a);
1082 }
1083 break;
1084#if LLVM_VERSION_MAJOR > 17
1085 case clang::TemplateArgument::StructuralValue:
1086 break;
1087#endif
1088 }
1089}
1090
1091template <typename VisitorT>
1093 const clang::TemplateArgument &arg)
1094{
1095 auto arg_name = common::to_string(arg);
1096
1097 LOG_DBG("Processing template argument: {}", arg_name);
1098
1099 return template_parameter::make_template_type(arg_name);
1100}
1101
1102template <typename VisitorT>
1104 const clang::TemplateArgument &arg)
1105{
1106 auto arg_name = common::to_string(arg);
1107
1108 LOG_DBG("Processing template expansion argument: {}", arg_name);
1109
1111 arg.getAsTemplate().getAsTemplateDecl(), [&arg_name](const auto *decl) {
1112 arg_name = decl->getQualifiedNameAsString();
1113 });
1114
1115 auto param = template_parameter::make_template_type(arg_name);
1116 param.is_variadic(true);
1117
1118 return param;
1119}
1120
1121template <typename VisitorT>
1123 clang::QualType type, template_parameter &tp) const
1124{
1125 auto [unqualified_type, context] = common::consume_type_context(type);
1126
1127 tp.deduced_context(std::move(context));
1128
1129 return unqualified_type;
1130}
1131
1132template <typename VisitorT>
1134 std::optional<clanguml::common::model::template_element *> &parent,
1135 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
1136 clang::QualType type,
1137 clanguml::common::model::template_element &template_instantiation,
1138 size_t argument_index)
1139{
1140 std::optional<template_parameter> argument;
1141
1142 if (type->getAs<clang::ElaboratedType>() != nullptr) {
1143 type = type->getAs<clang::ElaboratedType>()->getNamedType(); // NOLINT
1144 }
1145
1146 auto type_name = common::to_string(type, &cls->getASTContext());
1147
1148 LOG_DBG("Processing template {} type argument {}: {}, {}, {}",
1149 template_decl->getQualifiedNameAsString(), argument_index, type_name,
1150 type->getTypeClassName(),
1151 common::to_string(type, cls->getASTContext()));
1152
1153 argument = try_as_function_prototype(parent, cls, template_decl, type,
1154 template_instantiation, argument_index);
1155 if (argument)
1156 return *argument;
1157
1158 argument = try_as_member_pointer(parent, cls, template_decl, type,
1159 template_instantiation, argument_index);
1160 if (argument)
1161 return *argument;
1162
1163 argument = try_as_array(parent, cls, template_decl, type,
1164 template_instantiation, argument_index);
1165 if (argument)
1166 return *argument;
1167
1168 argument = try_as_template_parm_type(cls, template_decl, type);
1169 if (argument)
1170 return *argument;
1171
1172 argument = try_as_template_specialization_type(parent, cls, template_decl,
1173 type, template_instantiation, argument_index);
1174 if (argument)
1175 return *argument;
1176
1177 argument = try_as_decl_type(parent, cls, template_decl, type,
1178 template_instantiation, argument_index);
1179 if (argument)
1180 return *argument;
1181
1182 argument = try_as_typedef_type(parent, cls, template_decl, type,
1183 template_instantiation, argument_index);
1184 if (argument)
1185 return *argument;
1186
1187 argument = try_as_lambda(cls, template_decl, type);
1188 if (argument)
1189 return *argument;
1190
1191 argument = try_as_record_type(parent, cls, template_decl, type,
1192 template_instantiation, argument_index);
1193 if (argument)
1194 return *argument;
1195
1196 argument = try_as_enum_type(
1197 parent, cls, template_decl, type, template_instantiation);
1198 if (argument)
1199 return *argument;
1200
1201 argument = try_as_builtin_type(parent, type, template_decl);
1202 if (argument)
1203 return *argument;
1204
1205 // fallback
1206 return template_parameter::make_argument(type_name);
1207}
1208
1209template <typename VisitorT>
1212 const template_parameter &ct, found_relationships_t &relationships)
1213{
1214 const auto &type = ct.type();
1215
1216 if (!type)
1217 return false;
1218
1219 bool found{false};
1220 LOG_DBG("Finding relationships in user defined type: {}",
1221 ct.to_string(config().using_namespace(), false));
1222
1223 auto type_with_namespace =
1224 std::make_optional<common::model::namespace_>(type.value());
1225
1226 if (!type_with_namespace.has_value()) {
1227 // Couldn't find declaration of this type
1228 type_with_namespace = common::model::namespace_{type.value()};
1229 }
1230
1231 auto element_opt = diagram().get(type_with_namespace.value().to_string());
1232 if (config_.generate_template_argument_dependencies() && element_opt) {
1233 relationships.emplace_back(
1234 element_opt.value().id(), relationship_t::kDependency);
1235 found = true;
1236 }
1237
1238 for (const auto &nested_template_params : ct.template_params()) {
1239 found = find_relationships_in_unexposed_template_params(
1240 nested_template_params, relationships) ||
1241 found;
1242 }
1243
1244 return found;
1245}
1246
1247template <typename VisitorT>
1249 const clang::TemplateArgument &arg, const clang::ASTContext &ast_context)
1250{
1251 assert(arg.getKind() == clang::TemplateArgument::Integral);
1252
1253 std::string result;
1254 llvm::raw_string_ostream ostream(result);
1255 clang::PrintingPolicy policy(ast_context.getLangOpts());
1256
1257#if LLVM_VERSION_MAJOR > 18
1258 arg.print(policy, ostream, false);
1259#else
1260 arg.dump(ostream);
1261#endif
1262
1263 return template_parameter::make_argument(result);
1264}
1265
1266#if LLVM_VERSION_MAJOR > 17
1267template <typename VisitorT>
1269 const clang::TemplateArgument &arg, const clang::ASTContext &ast_context)
1270{
1271 assert(arg.getKind() == clang::TemplateArgument::StructuralValue);
1272
1273 std::string result;
1274 llvm::raw_string_ostream ostream(result);
1275 clang::PrintingPolicy policy(ast_context.getLangOpts());
1276
1277#if LLVM_VERSION_MAJOR > 18
1278 arg.print(policy, ostream, false);
1279#else
1280 arg.dump(ostream);
1281#endif
1282
1283 return template_parameter::make_argument(result);
1284}
1285#endif
1286
1287template <typename VisitorT>
1289 const clang::TemplateArgument &arg)
1290{
1291 assert(arg.getKind() == clang::TemplateArgument::Null);
1292
1293 return template_parameter::make_argument("");
1294}
1295
1296template <typename VisitorT>
1298 const clang::TemplateArgument &arg)
1299{
1300 assert(arg.getKind() == clang::TemplateArgument::NullPtr);
1301
1302 LOG_DBG("Processing nullptr argument: {}", common::to_string(arg));
1303
1304 return template_parameter::make_argument("nullptr");
1305}
1306
1307template <typename VisitorT>
1309 const clang::TemplateArgument &arg)
1310{
1311 assert(arg.getKind() == clang::TemplateArgument::Expression);
1312 return template_parameter::make_argument(common::get_source_text(
1313 arg.getAsExpr()->getSourceRange(), source_manager()));
1314}
1315
1316template <typename VisitorT>
1317std::vector<template_parameter>
1319 std::optional<clanguml::common::model::template_element *> &parent,
1320 const clang::NamedDecl *cls,
1321 clanguml::common::model::template_element &template_instantiation,
1322 const clang::TemplateDecl *base_template_decl,
1323 const clang::TemplateArgument &arg, size_t argument_index,
1324 std::vector<template_parameter> & /*argument*/)
1325{
1326 assert(arg.getKind() == clang::TemplateArgument::Pack);
1327
1328 std::vector<template_parameter> res;
1329
1330 auto pack_argument_index = argument_index;
1331
1332 for (const auto &a : arg.getPackAsArray()) {
1333 argument_process_dispatch(parent, cls, template_instantiation,
1334 base_template_decl, a, pack_argument_index++, res);
1335 }
1336
1337 return res;
1338}
1339
1340template <typename VisitorT>
1341std::optional<template_parameter>
1343 std::optional<clanguml::common::model::template_element *> &parent,
1344 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
1345 clang::QualType &type,
1346 clanguml::common::model::template_element &template_instantiation,
1347 size_t argument_index)
1348{
1349 const auto *mp_type =
1350 common::dereference(type)->getAs<clang::MemberPointerType>();
1351 if (mp_type == nullptr)
1352 return {};
1353
1354 auto argument = template_parameter::make_template_type("");
1355 type = consume_context(type, argument);
1356
1357 // Handle a pointer to a data member of a class
1358 if (mp_type->isMemberDataPointer()) {
1359 argument.is_member_pointer(false);
1360 argument.is_data_pointer(true);
1361
1362 auto pointee_arg = process_type_argument(parent, cls, template_decl,
1363 mp_type->getPointeeType(), template_instantiation, argument_index);
1364
1365 argument.add_template_param(std::move(pointee_arg));
1366
1367 const auto *member_class_type = mp_type->getClass();
1368
1369 if (member_class_type == nullptr)
1370 return {};
1371
1372 auto class_type_arg = process_type_argument(parent, cls, template_decl,
1373 mp_type->getClass()->getCanonicalTypeUnqualified(),
1374 template_instantiation, argument_index);
1375
1376 argument.add_template_param(std::move(class_type_arg));
1377 }
1378 // Handle pointer to class method member
1379 else {
1380 argument.is_member_pointer(true);
1381 argument.is_data_pointer(false);
1382
1383 const auto *function_type =
1384 mp_type->getPointeeType()->getAs<clang::FunctionProtoType>();
1385
1386 assert(function_type != nullptr);
1387
1388 auto return_type_arg = process_type_argument(parent, cls, template_decl,
1389 function_type->getReturnType(), template_instantiation,
1390 argument_index);
1391
1392 // Add return type argument
1393 argument.add_template_param(std::move(return_type_arg));
1394
1395 const auto *member_class_type = mp_type->getClass();
1396
1397 if (member_class_type == nullptr)
1398 return {};
1399
1400 auto class_type_arg = process_type_argument(parent, cls, template_decl,
1401 mp_type->getClass()->getCanonicalTypeUnqualified(),
1402 template_instantiation, argument_index);
1403
1404 // Add class type argument
1405 argument.add_template_param(std::move(class_type_arg));
1406
1407 // Add argument types
1408 for (const auto &param_type : function_type->param_types()) {
1409 argument.add_template_param(
1410 process_type_argument(parent, cls, template_decl, param_type,
1411 template_instantiation, argument_index));
1412 }
1413 }
1414
1415 return argument;
1416}
1417
1418template <typename VisitorT>
1419std::optional<template_parameter> template_builder<VisitorT>::try_as_array(
1420 std::optional<clanguml::common::model::template_element *> &parent,
1421 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
1422 clang::QualType &type,
1423 clanguml::common::model::template_element &template_instantiation,
1424 size_t argument_index)
1425{
1426 const auto *array_type = common::dereference(type)->getAsArrayTypeUnsafe();
1427 if (array_type == nullptr)
1428 return {};
1429
1430 auto argument = template_parameter::make_template_type("");
1431
1432 type = consume_context(type, argument);
1433
1434 argument.is_array(true);
1435
1436 // Set function template return type
1437 auto element_type = process_type_argument(parent, cls, template_decl,
1438 array_type->getElementType(), template_instantiation, argument_index);
1439
1440 argument.add_template_param(element_type);
1441
1442 if (array_type->isDependentSizedArrayType() &&
1443 array_type->getDependence() ==
1444 clang::TypeDependence::DependentInstantiation) {
1445 argument.add_template_param(
1446 template_parameter::make_template_type(common::to_string(
1447 ((clang::DependentSizedArrayType *)array_type) // NOLINT
1448 ->getSizeExpr())));
1449 }
1450 else if (array_type->isConstantArrayType()) {
1451 argument.add_template_param(template_parameter::make_argument(
1452 std::to_string(((clang::ConstantArrayType *)array_type) // NOLINT
1453 ->getSize()
1454 .getLimitedValue())));
1455 }
1456
1457 // TODO: Handle variable sized arrays
1458
1459 return argument;
1460}
1461
1462template <typename VisitorT>
1463std::optional<template_parameter>
1465 std::optional<clanguml::common::model::template_element *> &parent,
1466 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
1467 clang::QualType &type,
1468 clanguml::common::model::template_element &template_instantiation,
1469 size_t argument_index)
1470{
1471 const auto *function_type = type->getAs<clang::FunctionProtoType>();
1472
1473 if (function_type == nullptr && type->isFunctionPointerType()) {
1474 function_type =
1475 type->getPointeeType()->getAs<clang::FunctionProtoType>();
1476 if (function_type == nullptr)
1477 return {};
1478 }
1479
1480 if (function_type == nullptr)
1481 return {};
1482
1483 LOG_DBG("Template argument is a function prototype");
1484
1485 auto argument = template_parameter::make_template_type("");
1486
1487 type = consume_context(type, argument);
1488
1489 argument.is_function_template(true);
1490
1491 // Set function template return type
1492 auto return_arg = process_type_argument(parent, cls, template_decl,
1493 function_type->getReturnType(), template_instantiation, argument_index);
1494
1495 argument.add_template_param(return_arg);
1496
1497 // Set function template argument types
1498 if (function_type->isVariadic() && function_type->param_types().empty()) {
1499 auto fallback_arg = template_parameter::make_argument({});
1500 fallback_arg.is_ellipsis(true);
1501 argument.add_template_param(std::move(fallback_arg));
1502 }
1503 else {
1504 for (const auto &param_type : function_type->param_types()) {
1505 argument.add_template_param(
1506 process_type_argument(parent, cls, template_decl, param_type,
1507 template_instantiation, argument_index));
1508 }
1509 }
1510
1511 return argument;
1512}
1513
1514template <typename VisitorT>
1515std::optional<template_parameter> template_builder<VisitorT>::try_as_decl_type(
1516 std::optional<clanguml::common::model::template_element *> & /*parent*/,
1517 const clang::NamedDecl * /*cls*/,
1518 const clang::TemplateDecl * /*template_decl*/, clang::QualType &type,
1519 clanguml::common::model::template_element & /*template_instantiation*/,
1520 size_t /*argument_index*/)
1521{
1522 const auto *decl_type =
1523 common::dereference(type)->getAs<clang::DecltypeType>();
1524 if (decl_type == nullptr) {
1525 return {};
1526 }
1527
1528 LOG_DBG("Template argument is a decltype()");
1529
1530 // TODO
1531 return {};
1532}
1533
1534template <typename VisitorT>
1535std::optional<template_parameter>
1537 std::optional<clanguml::common::model::template_element *> &parent,
1538 const clang::NamedDecl * /*cls*/,
1539 const clang::TemplateDecl * /*template_decl*/, clang::QualType &type,
1540 clanguml::common::model::template_element & /*template_instantiation*/,
1541 size_t /*argument_index*/)
1542{
1543 const auto *typedef_type =
1544 common::dereference(type)->getAs<clang::TypedefType>();
1545 if (typedef_type == nullptr) {
1546 return {};
1547 }
1548
1549 LOG_DBG("Template argument is a typedef/using");
1550
1551 // If this is a typedef/using alias to a decltype - we're not able
1552 // to figure out anything out of it probably
1553 if (typedef_type->getAs<clang::DecltypeType>() != nullptr) {
1554 // Here we need to figure out the parent context of this alias,
1555 // it can be a:
1556 // - class/struct
1557 if (typedef_type->getDecl()->isCXXClassMember() && parent) {
1558 return template_parameter::make_argument(
1559 fmt::format("{}::{}", parent.value()->full_name(false),
1560 typedef_type->getDecl()->getNameAsString()));
1561 }
1562 // - namespace
1563 return template_parameter::make_argument(
1564 typedef_type->getDecl()->getQualifiedNameAsString());
1565 }
1566
1567 return {};
1568}
1569
1570template <typename VisitorT>
1571std::optional<template_parameter>
1573 std::optional<clanguml::common::model::template_element *> &parent,
1574 const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
1575 clang::QualType &type,
1576 clanguml::common::model::template_element &template_instantiation,
1577 size_t argument_index)
1578{
1579 const auto *nested_template_type =
1580 common::dereference(type)->getAs<clang::TemplateSpecializationType>();
1581 if (nested_template_type == nullptr) {
1582 return {};
1583 }
1584
1585 LOG_DBG("Template argument is a template specialization type");
1586
1587 auto argument = template_parameter::make_argument("");
1588 type = consume_context(type, argument);
1589
1590 auto nested_type_name = nested_template_type->getTemplateName()
1591 .getAsTemplateDecl()
1592 ->getQualifiedNameAsString();
1593
1594 if (clang::dyn_cast<clang::TemplateTemplateParmDecl>(
1595 nested_template_type->getTemplateName().getAsTemplateDecl()) !=
1596 nullptr) {
1597 if (const auto *template_specialization_decl =
1598 clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(cls);
1599 template_specialization_decl != nullptr) {
1600 nested_type_name =
1601 template_specialization_decl->getDescribedTemplateParams()
1602 ->getParam(argument_index)
1603 ->getNameAsString();
1604 }
1605 else {
1606 // fallback
1607 nested_type_name = "template";
1608 }
1609
1610 argument.is_template_template_parameter(true);
1611 }
1612
1613 argument.set_type(nested_type_name);
1614
1615 auto nested_template_instantiation =
1616 visitor_.create_element(nested_template_type->getTemplateName()
1617 .getAsTemplateDecl()
1618 ->getTemplatedDecl());
1619 build_from_template_specialization_type(*nested_template_instantiation, cls,
1620 *nested_template_type,
1621 diagram().should_include(
1622 namespace_{template_decl->getQualifiedNameAsString()})
1623 ? std::make_optional(&template_instantiation)
1624 : parent);
1625
1626 argument.set_id(nested_template_instantiation->id());
1627
1628 for (const auto &t : nested_template_instantiation->template_params())
1629 argument.add_template_param(t);
1630
1631 // Check if this template should be simplified (e.g. system
1632 // template aliases such as 'std:basic_string<char>' should
1633 // be simply 'std::string')
1634 simplify_system_template(
1635 argument, argument.to_string(using_namespace(), false));
1636
1637 argument.set_id(
1638 common::to_id(argument.to_string(using_namespace(), false)));
1639
1640 const auto nested_template_instantiation_full_name =
1641 nested_template_instantiation->full_name(false);
1642
1643 if (nested_template_instantiation &&
1644 diagram().should_include(
1645 namespace_{nested_template_instantiation_full_name})) {
1646 if (config_.generate_template_argument_dependencies()) {
1647 if (diagram().should_include(
1648 namespace_{template_decl->getQualifiedNameAsString()})) {
1649 template_instantiation.add_relationship(
1650 {relationship_t::kDependency,
1651 nested_template_instantiation->id()});
1652 }
1653 else {
1654 if (parent.has_value())
1655 parent.value()->add_relationship(
1656 {relationship_t::kDependency,
1657 nested_template_instantiation->id()});
1658 }
1659 }
1660 }
1661
1662 if (diagram().should_include(
1663 namespace_{nested_template_instantiation_full_name})) {
1664 visitor_.set_source_location(*cls, *nested_template_instantiation);
1665 visitor_.add_diagram_element(std::move(nested_template_instantiation));
1666 }
1667
1668 return argument;
1669}
1670
1671template <typename VisitorT>
1672std::optional<template_parameter>
1674 const clang::NamedDecl *cls, const clang::TemplateDecl * /*template_decl*/,
1675 clang::QualType &type)
1676{
1677 auto is_variadic{false};
1678
1679 const auto *type_parameter =
1680 common::dereference(type)->getAs<clang::TemplateTypeParmType>();
1681
1682 auto type_name = common::to_string(type, &cls->getASTContext());
1683
1684 if (type_parameter == nullptr) {
1685 if (const auto *pet =
1686 common::dereference(type)->getAs<clang::PackExpansionType>();
1687 pet != nullptr) {
1688 is_variadic = true;
1689 type_parameter =
1690 pet->getPattern()->getAs<clang::TemplateTypeParmType>();
1691 }
1692 }
1693
1694 if (type_parameter == nullptr)
1695 return {};
1696
1697 LOG_DBG("Template argument is a template parameter type");
1698
1699 auto argument = template_parameter::make_template_type("");
1700 type = consume_context(type, argument);
1701
1702 auto type_parameter_name = common::to_string(type, cls->getASTContext());
1703 if (type_parameter_name.empty())
1704 type_parameter_name = "typename";
1705
1707 cls, type_parameter_name));
1708
1709 argument.is_variadic(is_variadic);
1710
1711 common::ensure_lambda_type_is_relative(config(), type_parameter_name);
1712
1713 return argument;
1714}
1715
1716template <typename VisitorT>
1717std::optional<template_parameter> template_builder<VisitorT>::try_as_lambda(
1718 const clang::NamedDecl *cls, const clang::TemplateDecl * /*template_decl*/,
1719 clang::QualType &type)
1720{
1721 auto type_name = common::to_string(type, &cls->getASTContext());
1722
1723 if (type_name.find("(lambda at ") != 0)
1724 return {};
1725
1726 LOG_DBG("Template argument is a lambda");
1727
1728 auto argument = template_parameter::make_argument("");
1729 type = consume_context(type, argument);
1730
1731 common::ensure_lambda_type_is_relative(config(), type_name);
1732 argument.set_type(type_name);
1733
1734 return argument;
1735}
1736
1737template <typename VisitorT>
1738std::optional<template_parameter>
1740 std::optional<clanguml::common::model::template_element *> &parent,
1741 const clang::NamedDecl * /*cls*/, const clang::TemplateDecl *template_decl,
1742 clang::QualType &type,
1743 clanguml::common::model::template_element &template_instantiation,
1744 size_t /*argument_index*/)
1745{
1746 const auto *record_type =
1747 common::dereference(type)->getAs<clang::RecordType>();
1748 if (record_type == nullptr)
1749 return {};
1750
1751 LOG_DBG("Template argument is a c++ record");
1752
1753 auto argument = template_parameter::make_argument({});
1754 type = consume_context(type, argument);
1755
1756 const auto type_name = config().simplify_template_type(
1757 common::to_string(type, template_decl->getASTContext()));
1758
1759 argument.set_type(type_name);
1760 const auto type_id = common::to_id(type_name);
1761
1762 argument.set_id(type_id);
1763
1764 const auto *class_template_specialization =
1765 clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(
1766 record_type->getAsRecordDecl());
1767
1768 if (class_template_specialization != nullptr) {
1769 auto tag_argument =
1770 visitor_.create_element(class_template_specialization);
1771
1772 build_from_class_template_specialization(
1773 *tag_argument, *class_template_specialization);
1774
1775 tag_argument->is_template(true);
1776
1777 if (tag_argument) {
1778 argument.set_type(tag_argument->name_and_ns());
1779 for (const auto &p : tag_argument->template_params())
1780 argument.add_template_param(p);
1781 for (auto &r : tag_argument->relationships()) {
1782 template_instantiation.add_relationship(std::move(r));
1783 }
1784
1785 if (config_.generate_template_argument_dependencies() &&
1786 diagram().should_include(tag_argument->get_namespace())) {
1787 if (parent.has_value())
1788 parent.value()->add_relationship(
1789 {relationship_t::kDependency, tag_argument->id()});
1790 visitor_.set_source_location(*template_decl, *tag_argument);
1791 visitor_.add_diagram_element(std::move(tag_argument));
1792 }
1793 }
1794 }
1795 else if (const auto *record_type_decl = record_type->getAsRecordDecl();
1796 record_type_decl != nullptr) {
1797 if (config_.generate_template_argument_dependencies() &&
1798 diagram().should_include(namespace_{type_name})) {
1799 // Add dependency relationship to the parent
1800 // template
1801 template_instantiation.add_relationship(
1802 {relationship_t::kDependency, type_id});
1803 }
1804 }
1805
1806 return argument;
1807}
1808
1809template <typename VisitorT>
1810std::optional<template_parameter> template_builder<VisitorT>::try_as_enum_type(
1811 std::optional<clanguml::common::model::template_element *> & /*parent*/,
1812 const clang::NamedDecl * /*cls*/, const clang::TemplateDecl *template_decl,
1813 clang::QualType &type,
1814 clanguml::common::model::template_element &template_instantiation)
1815{
1816 const auto *enum_type = type->getAs<clang::EnumType>();
1817 if (enum_type == nullptr)
1818 return {};
1819
1820 LOG_DBG("Template argument is a an enum");
1821
1822 auto argument = template_parameter::make_argument({});
1823 type = consume_context(type, argument);
1824
1825 auto type_name = common::to_string(type, template_decl->getASTContext());
1826 argument.set_type(type_name);
1827 const auto type_id = common::to_id(type_name);
1828 argument.set_id(type_id);
1829
1830 if (enum_type->getAsTagDecl() != nullptr &&
1831 config_.generate_template_argument_dependencies()) {
1832 template_instantiation.add_relationship(
1833 {relationship_t::kDependency, type_id});
1834 }
1835
1836 return argument;
1837}
1838
1839template <typename VisitorT>
1840std::optional<template_parameter>
1842 std::optional<clanguml::common::model::template_element *> & /*parent*/,
1843 clang::QualType &type, const clang::TemplateDecl *template_decl)
1844{
1845 const auto *builtin_type = type->getAs<clang::BuiltinType>();
1846 if (builtin_type == nullptr)
1847 return {};
1848
1849 LOG_DBG("Template argument is a builtin type");
1850
1851 auto type_name = common::to_string(type, template_decl->getASTContext());
1852 auto argument = template_parameter::make_argument(type_name);
1853
1854 type = consume_context(type, argument);
1855 argument.set_type(type_name);
1856
1857 return argument;
1858}
1859
1860template <typename VisitorT>
1863 std::deque<std::tuple<std::string, int, bool>> &template_base_params,
1864 int arg_index, bool variadic_params, const template_parameter &ct)
1865{
1866 bool add_template_argument_as_base_class = false;
1867
1868 if (variadic_params) {
1869 add_template_argument_as_base_class = true;
1870 }
1871 else {
1872 auto [arg_name, index, is_variadic] = template_base_params.front();
1873
1874 variadic_params = is_variadic;
1875 if ((arg_index == index) || (is_variadic && arg_index >= index)) {
1876 add_template_argument_as_base_class = true;
1877 if (!is_variadic) {
1878 // Don't remove the remaining variadic parameter
1879 template_base_params.pop_front();
1880 }
1881 }
1882 }
1883
1884 const auto maybe_id = ct.id();
1885 if (add_template_argument_as_base_class && maybe_id) {
1886 LOG_DBG("Adding template argument as base class '{}'",
1887 ct.to_string({}, false));
1888
1889 dynamic_cast<class_diagram::model::class_ &>(tinst).add_relationship(
1891 maybe_id.value(), common::model::access_t::kPublic, false});
1892 }
1893
1894 return variadic_params;
1895}
1896
1897} // namespace clanguml::common::visitor