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