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