Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
QualTypeNames.h
Go to the documentation of this file.
1//===------- QualTypeNames.cpp - Generate Complete QualType Names ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#pragma once
10
11// Those directives indirectly includes "clang/AST/Attrs.h" which
12// includes "clang/AST/Attrs.inc".
13// "clang/AST/Attrs.inc", produces some "C4267" warnings specifically
14// on MSVC 2019.
15// This in turn blocks CI integrations for configuration with that
16// compiler that treats warnings as errors.
17// As that header is not under our control, we disable the warning
18// completely when going through those includes.
19#include <QtCore/qcompilerdetection.h>
20
21QT_WARNING_PUSH
22QT_WARNING_DISABLE_MSVC(4267)
23
24#include "clang/AST/DeclTemplate.h"
25#include "clang/AST/DeclarationName.h"
26#include "clang/AST/GlobalDecl.h"
27#include "clang/AST/Mangle.h"
28#include "clang/Basic/Version.h"
29
30QT_WARNING_POP
31
32#include <stdio.h>
33#include <memory>
34
35namespace clang {
36
37namespace TypeName {
38
39#if CLANG_VERSION_MAJOR >= 22
40
41// =========================================================================
42// LLVM 22+ implementation
43//
44// Adapted from upstream clang/lib/AST/QualTypeNames.cpp (release/22.x).
45// LLVM 22 changed NestedNameSpecifier from pointer to value type,
46// merged RecordType into TagType, and merged ElaboratedType into
47// TypedefType/TagType via TypeWithKeyword.
48//
49// QDoc divergences from upstream are marked with "QDoc divergence" comments.
50// =========================================================================
51
54
55static inline NestedNameSpecifier
58
59static inline NestedNameSpecifier
62
63static inline NestedNameSpecifier
65 bool FullyQualified,
67
68static inline NestedNameSpecifier
72
73static inline bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
75 bool WithGlobalNsPrefix) {
76 bool Changed = false;
78
80 if (!ArgTDecl) // ArgTDecl can be null in dependent contexts.
81 return false;
82
84
85 if (QTName &&
87 (NNS = QTName->getQualifier())) {
90 if (QNNS != NNS) {
91 Changed = true;
92 NNS = QNNS;
93 } else {
94 NNS = std::nullopt;
95 }
96 } else {
99 }
100 if (NNS) {
104 TName =
106 /*TemplateKeyword=*/false, UnderlyingTN);
107 Changed = true;
108 }
109 return Changed;
110}
111
112static inline bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
114 bool WithGlobalNsPrefix) {
115 bool Changed = false;
116
117 // Note: we do not handle TemplateArgument::Expression, to replace it
118 // we need the information for the template instance decl.
119
123 if (Changed) {
125 }
126 } else if (Arg.getKind() == TemplateArgument::Type) {
128 // Check if the type needs more desugaring and recurse.
130 if (QTFQ != SubTy) {
132 Changed = true;
133 }
134 }
135 return Changed;
136}
137
138static inline const Type *getFullyQualifiedTemplateType(
139 const ASTContext &Ctx,
140 const TagType *TSTRecord,
143 bool WithGlobalNsPrefix) {
144 // We are asked to fully qualify and we have a Record Type,
145 // which can point to a template instantiation with no sugar in any of
146 // its template argument, however we still need to fully qualify them.
147
148 const auto *TD = TSTRecord->getDecl();
150 if (!TSTDecl)
151 return Ctx.getTagType(Keyword, Qualifier, TD, /*OwnsTag=*/false)
152 .getTypePtr();
153
155
156 bool MightHaveChanged = false;
158 for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
159 // cheap to copy and potentially modified by
160 // getFullyQualifedTemplateArgument
165 }
166
167 if (!MightHaveChanged)
168 return Ctx.getTagType(Keyword, Qualifier, TD, /*OwnsTag=*/false)
169 .getTypePtr();
170 // If a fully qualified arg is different from the unqualified arg,
171 // allocate new type in the AST.
173 Qualifier, /*TemplateKeyword=*/false,
176 Keyword, TN, FQArgs,
177 /*CanonicalArgs=*/{}, TSTRecord->getCanonicalTypeInternal());
178 // getTemplateSpecializationType returns a fully qualified
179 // version of the specialization itself, so no need to qualify
180 // it.
181 return QT.getTypePtr();
182}
183
184static inline const Type *
187 bool WithGlobalNsPrefix) {
189 bool MightHaveChanged =
192 // Cheap to copy and potentially modified by
193 // getFullyQualifedTemplateArgument.
198 }
199
200 if (!MightHaveChanged)
201 return TST;
202
205 /*CanonicalArgs=*/{}, TST->desugar());
206 // getTemplateSpecializationType returns a fully qualified
207 // version of the specialization itself, so no need to qualify
208 // it.
209 return NewQT.getTypePtr();
210}
211
213 const Decl *D,
214 bool FullyQualify,
215 bool WithGlobalNsPrefix) {
216 const DeclContext *DC = D->getDeclContext();
217 if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
218 while (NS && NS->isInline()) {
219 // Ignore inline namespace;
221 }
222 if (NS && NS->getDeclName()) {
224 }
225 return std::nullopt; // no starting '::', no anonymous
226 }
227 if (const auto *TD = dyn_cast<TagDecl>(DC))
229 if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC))
234 return std::nullopt; // no starting '::' if |WithGlobalNsPrefix| is false
235}
236
237/// Return a fully qualified version of this name specifier.
240 bool WithGlobalNsPrefix) {
241 switch (Scope.getKind()) {
243 llvm_unreachable("can't fully qualify the empty nested name specifier");
246 // Already fully qualified
247 return Scope;
253 const Type *Type = Scope.getAsType();
254 // Find decl context.
255 const TypeDecl *TD;
256 if (const TagType *TagDeclType = Type->getAs<TagType>())
258 else if (const auto *D = dyn_cast<TypedefType>(Type))
259 TD = D->getDecl();
260 else
261 return Scope;
262 return TypeName::createNestedNameSpecifier(Ctx, TD, /*FullyQualify=*/true,
264 }
265 }
266 llvm_unreachable("bad NNS kind");
267}
268
269/// Create a nested name specifier for the declaring context of
270/// the type.
271static inline NestedNameSpecifier
273 bool FullyQualified,
274 bool WithGlobalNsPrefix) {
275 assert(Decl);
276
277 // Some declaration cannot be qualified.
279 return std::nullopt;
281 const auto *Outer = dyn_cast<NamedDecl>(DC);
282 const auto *OuterNS = dyn_cast<NamespaceDecl>(DC);
285 if (Outer) {
286#if 0
287 // QDoc divergence: upstream picks an arbitrary template specialization
288 // as the declaring context when a type is declared inside a class
289 // template but is not type-dependent. This produces unstable output
290 // (depends on specialization order) and is incorrect for QDoc's use
291 // case where we want the unspecialized template name.
292 // See QTBUG-144620.
293 if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) {
296 if (!ClassTempl->specializations().empty()) {
297 Decl = *(ClassTempl->spec_begin());
300 }
301 }
302 }
303#endif
304
305 if (OuterNS) {
307 } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
310 } else if (isa<TranslationUnitDecl>(Outer)) {
311 // Context is the TU. Nothing needs to be done.
312 return std::nullopt;
313 } else {
314 // Decl's context was neither the TU, a namespace, nor a
315 // TagDecl, which means it is a type local to a scope, and not
316 // accessible at the end of the TU.
317 return std::nullopt;
318 }
319 } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
321 }
322 return std::nullopt;
323}
324
325/// Create a nested name specifier for the declaring context of
326/// the type.
327static inline NestedNameSpecifier
329 bool FullyQualified,
330 bool WithGlobalNsPrefix) {
331 if (!TypePtr)
332 return std::nullopt;
333
334 Decl *Decl = nullptr;
335 // There are probably other cases ...
336 if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
337 Decl = TDT->getDecl();
338 } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {
340 } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) {
342 } else {
344 }
345
346 if (!Decl)
347 return std::nullopt;
348
351}
352
355 bool WithGlobalNsPrefix) {
356 while (Namespace && Namespace->isInline()) {
357 // Ignore inline namespace;
359 }
360 if (!Namespace)
361 return std::nullopt;
362
363 bool FullyQualify = true; // doesn't matter, DeclContexts are namespaces
364 return NestedNameSpecifier(
365 Ctx, Namespace,
367}
368
371 bool FullyQualify, bool WithGlobalNsPrefix) {
373 if (auto *RD = dyn_cast<TagType>(TypePtr)) {
374 // We are asked to fully qualify and we have a Record Type (which
375 // may point to a template specialization) or Template
376 // Specialization Type. We need to fully qualify their arguments.
381 } else if (auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) {
383 }
385}
386
387/// Return the fully qualified type, including fully-qualified
388/// versions of any template parameters.
390 bool WithGlobalNsPrefix = false) {
391 // In case of myType* we need to strip the pointer first, fully
392 // qualify and attach the pointer once again.
393 if (isa<PointerType>(QT.getTypePtr())) {
394 // Get the qualifiers.
398 // Add back the qualifiers.
400 return QT;
401 }
402
403 if (auto *MPT = dyn_cast<MemberPointerType>(QT.getTypePtr())) {
404 // Get the qualifiers.
406 // Fully qualify the pointee and class types.
412 // Add back the qualifiers.
414 return QT;
415 }
416
417 // In case of myType& we need to strip the reference first, fully
418 // qualify and attach the reference once again.
419 if (isa<ReferenceType>(QT.getTypePtr())) {
420 // Get the qualifiers.
424 // Add the r- or l-value reference type back to the fully
425 // qualified one.
426 if (IsLValueRefTy)
428 else
430 // Add back the qualifiers.
432 return QT;
433 }
434
435 // Handle types with attributes such as `unique_ptr<int> _Nonnull`.
436 if (auto *AT = dyn_cast<AttributedType>(QT.getTypePtr())) {
442 return Ctx.getQualifiedType(
444 Qualifiers);
445 }
446
447 // Remove the part of the type related to the type being a template
448 // parameter (we won't report it as part of the 'type name' and it
449 // is actually make the code below to be more complex (to handle
450 // those)
452 // Get the qualifiers.
454
456
457 // Add back the qualifiers.
459 }
460
461 if (const auto *TST =
463
465 if (T == TST)
466 return QT;
468 }
469
470 // Local qualifiers are attached to the QualType outside of the
471 // elaborated type. Retrieve them before descending into the
472 // elaborated type.
474 QT = QualType(QT.getTypePtr(), 0);
475
476 // We don't consider the alias introduced by `using a::X` as a new type.
477 // The qualified name is still a::X.
478 if (const auto *UT = QT->getAs<UsingType>()) {
481 }
482
483 // Create a nested name specifier if needed.
485 Ctx, QT.getTypePtr(), true /*FullyQualified*/, WithGlobalNsPrefix);
486
487 // In case of template specializations iterate over the arguments and
488 // fully qualify them as well.
489 if (const auto *TT = dyn_cast<TagType>(QT.getTypePtr())) {
490 // We are asked to fully qualify and we have a Record Type (which
491 // may point to a template specialization) or Template
492 // Specialization Type. We need to fully qualify their arguments.
493
496 QT = QualType(TypePtr, 0);
497 } else if (const auto *TT = dyn_cast<TypedefType>(QT.getTypePtr())) {
498 // QDoc divergence: prefer the existing qualifier from the TypedefType
499 // when available, falling back to the computed Prefix. This preserves
500 // member type alias qualifiers (e.g., QList<QVariant>::parameter_type)
501 // that would otherwise be lost when the Prefix is recomputed from the
502 // declaring context. See QTBUG-144620.
506 TT->getDecl(),
508 } else {
509 // QDoc divergence: upstream asserts here (!Prefix && "Unhandled type node").
510 // QDoc encounters types (such as AutoType and BuiltinType) that may have
511 // a non-null Prefix but are not TagType or TypedefType. Silently dropping
512 // the prefix is safe — it only affects qualification of the printed name.
513 }
515 return QT;
516}
517
518#else // CLANG_VERSION_MAJOR < 22
519
520// =========================================================================
521// Pre-LLVM 22 implementation
522//
523// This block is the existing fork, unchanged. It supports LLVM 17–21
524// with version-specific guards for API differences between those releases.
525// =========================================================================
526
527inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
528 bool WithGlobalNsPrefix);
529
530/// Create a NestedNameSpecifier for Namesp and its enclosing
531/// scopes.
532///
533/// \param[in] Ctx - the AST Context to be used.
534/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier
535/// is requested.
536/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
537/// specifier "::" should be prepended or not.
538static inline NestedNameSpecifier *createNestedNameSpecifier(
539 const ASTContext &Ctx,
540 const NamespaceDecl *Namesp,
541 bool WithGlobalNsPrefix);
542
543/// Create a NestedNameSpecifier for TagDecl and its enclosing
544/// scopes.
545///
546/// \param[in] Ctx - the AST Context to be used.
547/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is
548/// requested.
549/// \param[in] FullyQualify - Convert all template arguments into fully
550/// qualified names.
551/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
552/// specifier "::" should be prepended or not.
553static inline NestedNameSpecifier *createNestedNameSpecifier(
554 const ASTContext &Ctx, const TypeDecl *TD,
555 bool FullyQualify, bool WithGlobalNsPrefix);
556
557static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
558 const ASTContext &Ctx, const Decl *decl,
559 bool FullyQualified, bool WithGlobalNsPrefix);
560
561static inline NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
562 const ASTContext &Ctx, NestedNameSpecifier *scope, bool WithGlobalNsPrefix);
563
564static inline bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
565 TemplateName &TName,
566 bool WithGlobalNsPrefix) {
567 bool Changed = false;
568 NestedNameSpecifier *NNS = nullptr;
569
570 TemplateDecl *ArgTDecl = TName.getAsTemplateDecl();
571 // ArgTDecl won't be NULL because we asserted that this isn't a
572 // dependent context very early in the call chain.
573 assert(ArgTDecl != nullptr);
574 QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName();
575
576 if (QTName &&
577 !QTName->hasTemplateKeyword() &&
578 (NNS = QTName->getQualifier())) {
579 NestedNameSpecifier *QNNS = getFullyQualifiedNestedNameSpecifier(
580 Ctx, NNS, WithGlobalNsPrefix);
581 if (QNNS != NNS) {
582 Changed = true;
583 NNS = QNNS;
584 } else {
585 NNS = nullptr;
586 }
587 } else {
588 NNS = createNestedNameSpecifierForScopeOf(
589 Ctx, ArgTDecl, true, WithGlobalNsPrefix);
590 }
591 if (NNS) {
592 TemplateName UnderlyingTN(ArgTDecl);
593 if (UsingShadowDecl *USD = TName.getAsUsingShadowDecl())
594 UnderlyingTN = TemplateName(USD);
595 TName =
596 Ctx.getQualifiedTemplateName(NNS,
597 /*TemplateKeyword=*/false, UnderlyingTN);
598 Changed = true;
599 }
600 return Changed;
601}
602
603static inline bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
604 TemplateArgument &Arg,
605 bool WithGlobalNsPrefix) {
606 bool Changed = false;
607
608 // Note: we do not handle TemplateArgument::Expression, to replace it
609 // we need the information for the template instance decl.
610
611 if (Arg.getKind() == TemplateArgument::Template) {
612 TemplateName TName = Arg.getAsTemplate();
613 Changed = getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix);
614 if (Changed) {
615 Arg = TemplateArgument(TName);
616 }
617 } else if (Arg.getKind() == TemplateArgument::Type) {
618 QualType SubTy = Arg.getAsType();
619 // Check if the type needs more desugaring and recurse.
620 QualType QTFQ = getFullyQualifiedType(SubTy, Ctx, WithGlobalNsPrefix);
621 if (QTFQ != SubTy) {
622 Arg = TemplateArgument(QTFQ);
623 Changed = true;
624 }
625 }
626 return Changed;
627}
628
629static inline const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx,
630 const Type *TypePtr,
631 bool WithGlobalNsPrefix) {
632 // DependentTemplateTypes exist within template declarations and
633 // definitions. Therefore we shouldn't encounter them at the end of
634 // a translation unit. If we do, the caller has made an error.
635 assert(!isa<DependentTemplateSpecializationType>(TypePtr));
636 // In case of template specializations, iterate over the arguments
637 // and fully qualify them as well.
638 if (const auto *TST = dyn_cast<const TemplateSpecializationType>(TypePtr)) {
639 bool MightHaveChanged = false;
640 SmallVector<TemplateArgument, 4> FQArgs;
641 // Cheap to copy and potentially modified by
642 // getFullyQualifedTemplateArgument.
643 for (TemplateArgument Arg : TST->template_arguments()) {
644 MightHaveChanged |= getFullyQualifiedTemplateArgument(
645 Ctx, Arg, WithGlobalNsPrefix);
646 FQArgs.push_back(Arg);
647 }
648
649 // If a fully qualified arg is different from the unqualified arg,
650 // allocate new type in the AST.
651 if (MightHaveChanged) {
652#if CLANG_VERSION_MAJOR >= 21
653 QualType QT = Ctx.getTemplateSpecializationType(
654 TST->getTemplateName(), FQArgs, /*CanonicalArgs=*/{},
655 TST->getCanonicalTypeInternal());
656#else
657 QualType QT = Ctx.getTemplateSpecializationType(
658 TST->getTemplateName(), FQArgs,
659 TST->getCanonicalTypeInternal());
660#endif
661 // getTemplateSpecializationType returns a fully qualified
662 // version of the specialization itself, so no need to qualify
663 // it.
664 return QT.getTypePtr();
665 }
666 } else if (const auto *TSTRecord = dyn_cast<const RecordType>(TypePtr)) {
667 // We are asked to fully qualify and we have a Record Type,
668 // which can point to a template instantiation with no sugar in any of
669 // its template argument, however we still need to fully qualify them.
670
671 if (const auto *TSTDecl =
672 dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) {
673 const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs();
674
675 bool MightHaveChanged = false;
676 SmallVector<TemplateArgument, 4> FQArgs;
677 for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
678 // cheap to copy and potentially modified by
679 // getFullyQualifedTemplateArgument
680 TemplateArgument Arg(TemplateArgs[I]);
681 MightHaveChanged |= getFullyQualifiedTemplateArgument(
682 Ctx, Arg, WithGlobalNsPrefix);
683 FQArgs.push_back(Arg);
684 }
685
686 // If a fully qualified arg is different from the unqualified arg,
687 // allocate new type in the AST.
688 if (MightHaveChanged) {
689 TemplateName TN(TSTDecl->getSpecializedTemplate());
690#if CLANG_VERSION_MAJOR >= 21
691 QualType QT = Ctx.getTemplateSpecializationType(
692 TN, FQArgs, /*CanonicalArgs=*/{},
693 TSTRecord->getCanonicalTypeInternal());
694#else
695 QualType QT = Ctx.getTemplateSpecializationType(
696 TN, FQArgs,
697 TSTRecord->getCanonicalTypeInternal());
698#endif
699 // getTemplateSpecializationType returns a fully qualified
700 // version of the specialization itself, so no need to qualify
701 // it.
702 return QT.getTypePtr();
703 }
704 }
705 }
706 return TypePtr;
707}
708
709static inline NestedNameSpecifier *createOuterNNS(const ASTContext &Ctx, const Decl *D,
710 bool FullyQualify,
711 bool WithGlobalNsPrefix) {
712 const DeclContext *DC = D->getDeclContext();
713 if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
714 while (NS && NS->isInline()) {
715 // Ignore inline namespace;
716 NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
717 }
718 if (NS && NS->getDeclName()) {
719 return createNestedNameSpecifier(Ctx, NS, WithGlobalNsPrefix);
720 }
721 return nullptr; // no starting '::', no anonymous
722 } else if (const auto *TD = dyn_cast<TagDecl>(DC)) {
723 return createNestedNameSpecifier(Ctx, TD, FullyQualify, WithGlobalNsPrefix);
724 } else if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC)) {
725 return createNestedNameSpecifier(
726 Ctx, TDD, FullyQualify, WithGlobalNsPrefix);
727 } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
728 return NestedNameSpecifier::GlobalSpecifier(Ctx);
729 }
730 return nullptr; // no starting '::' if |WithGlobalNsPrefix| is false
731}
732
733/// Return a fully qualified version of this name specifier.
734static inline NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
735 const ASTContext &Ctx, NestedNameSpecifier *Scope,
736 bool WithGlobalNsPrefix) {
737 switch (Scope->getKind()) {
738 case NestedNameSpecifier::Global:
739 // Already fully qualified
740 return Scope;
741 case NestedNameSpecifier::Namespace:
743 Ctx, Scope->getAsNamespace(), WithGlobalNsPrefix);
744 case NestedNameSpecifier::NamespaceAlias:
745 // Namespace aliases are only valid for the duration of the
746 // scope where they were introduced, and therefore are often
747 // invalid at the end of the TU. So use the namespace name more
748 // likely to be valid at the end of the TU.
750 Ctx,
751 Scope->getAsNamespaceAlias()->getNamespace()->getCanonicalDecl(),
752 WithGlobalNsPrefix);
753 case NestedNameSpecifier::Identifier:
754 // A function or some other construct that makes it un-namable
755 // at the end of the TU. Skip the current component of the name,
756 // but use the name of it's prefix.
758 Ctx, Scope->getPrefix(), WithGlobalNsPrefix);
759 case NestedNameSpecifier::Super:
760 case NestedNameSpecifier::TypeSpec:
761#if CLANG_VERSION_MAJOR < 21
762 case NestedNameSpecifier::TypeSpecWithTemplate:
763#endif
764 {
765 const Type *Type = Scope->getAsType();
766 // Find decl context.
767 const TagDecl *TD = nullptr;
768 if (const TagType *TagDeclType = Type->getAs<TagType>()) {
769 TD = TagDeclType->getDecl();
770 } else {
771 TD = Type->getAsCXXRecordDecl();
772 }
773 if (TD) {
774 return TypeName::createNestedNameSpecifier(Ctx, TD,
775 true /*FullyQualified*/,
776 WithGlobalNsPrefix);
777 } else if (const auto *TDD = dyn_cast<TypedefType>(Type)) {
778 return TypeName::createNestedNameSpecifier(Ctx, TDD->getDecl(),
779 true /*FullyQualified*/,
780 WithGlobalNsPrefix);
781 }
782 return Scope;
783 }
784 }
785 llvm_unreachable("bad NNS kind");
786}
787
788/// Create a nested name specifier for the declaring context of
789/// the type.
790static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
791 const ASTContext &Ctx, const Decl *Decl,
792 bool FullyQualified, bool WithGlobalNsPrefix) {
793 assert(Decl);
794
795 const DeclContext *DC = Decl->getDeclContext()->getRedeclContext();
796 const auto *Outer = dyn_cast<NamedDecl>(DC);
797 const auto *OuterNS = dyn_cast<NamespaceDecl>(DC);
798 if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) {
799 if (OuterNS) {
800 return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
801 } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
802 return createNestedNameSpecifier(
803 Ctx, TD, FullyQualified, WithGlobalNsPrefix);
804 } else if (isa<TranslationUnitDecl>(Outer)) {
805 // Context is the TU. Nothing needs to be done.
806 return nullptr;
807 } else {
808 // Decl's context was neither the TU, a namespace, nor a
809 // TagDecl, which means it is a type local to a scope, and not
810 // accessible at the end of the TU.
811 return nullptr;
812 }
813 } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
814 return NestedNameSpecifier::GlobalSpecifier(Ctx);
815 }
816 return nullptr;
817}
818
819/// Create a nested name specifier for the declaring context of
820/// the type.
821static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
822 const ASTContext &Ctx, const Type *TypePtr,
823 bool FullyQualified, bool WithGlobalNsPrefix) {
824 if (!TypePtr) return nullptr;
825
826 Decl *Decl = nullptr;
827 // There are probably other cases ...
828 if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
829 Decl = TDT->getDecl();
830 } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {
831 Decl = TagDeclType->getDecl();
832 } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) {
833 Decl = TST->getTemplateName().getAsTemplateDecl();
834 } else {
835 Decl = TypePtr->getAsCXXRecordDecl();
836 }
837
838 if (!Decl) return nullptr;
839
841 Ctx, Decl, FullyQualified, WithGlobalNsPrefix);
842}
843
844inline NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
845 const NamespaceDecl *Namespace,
846 bool WithGlobalNsPrefix) {
847 while (Namespace && Namespace->isInline()) {
848 // Ignore inline namespace;
849 Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext());
850 }
851 if (!Namespace) return nullptr;
852
853 bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces
854 return NestedNameSpecifier::Create(
855 Ctx,
856 createOuterNNS(Ctx, Namespace, FullyQualified, WithGlobalNsPrefix),
857 Namespace);
858}
859
860inline NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
861 const TypeDecl *TD,
862 bool FullyQualify,
863 bool WithGlobalNsPrefix) {
864 const Type *TypePtr = TD->getTypeForDecl();
865 if (isa<const TemplateSpecializationType>(TypePtr) ||
866 isa<const RecordType>(TypePtr)) {
867 // We are asked to fully qualify and we have a Record Type (which
868 // may point to a template specialization) or Template
869 // Specialization Type. We need to fully qualify their arguments.
870
871 TypePtr = getFullyQualifiedTemplateType(Ctx, TypePtr, WithGlobalNsPrefix);
872 }
873
874 return NestedNameSpecifier::Create(
875 Ctx, createOuterNNS(Ctx, TD, FullyQualify, WithGlobalNsPrefix),
876#if CLANG_VERSION_MAJOR < 21
877 false /*No TemplateKeyword*/,
878#endif
879 TypePtr);
880}
881
882/// Return the fully qualified type, including fully-qualified
883/// versions of any template parameters.
884inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
885 bool WithGlobalNsPrefix = false) {
886 // In case of myType* we need to strip the pointer first, fully
887 // qualify and attach the pointer once again.
888 if (isa<PointerType>(QT.getTypePtr())) {
889 // Get the qualifiers.
890 Qualifiers Quals = QT.getQualifiers();
891 QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
892 QT = Ctx.getPointerType(QT);
893 // Add back the qualifiers.
894 QT = Ctx.getQualifiedType(QT, Quals);
895 return QT;
896 }
897
898 if (auto *MPT = dyn_cast<MemberPointerType>(QT.getTypePtr())) {
899 // Get the qualifiers.
900 Qualifiers Quals = QT.getQualifiers();
901 // Fully qualify the pointee and class types.
902 QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
903#if CLANG_VERSION_MAJOR >= 21
904 QT = Ctx.getMemberPointerType(QT, MPT->getQualifier(), MPT->getMostRecentCXXRecordDecl());
905#else
906 QualType Class = getFullyQualifiedType(QualType(MPT->getClass(), 0), Ctx,
907 WithGlobalNsPrefix);
908 QT = Ctx.getMemberPointerType(QT, Class.getTypePtr());
909#endif
910 // Add back the qualifiers.
911 QT = Ctx.getQualifiedType(QT, Quals);
912 return QT;
913 }
914
915 // In case of myType& we need to strip the reference first, fully
916 // qualify and attach the reference once again.
917 if (isa<ReferenceType>(QT.getTypePtr())) {
918 // Get the qualifiers.
919 bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
920 Qualifiers Quals = QT.getQualifiers();
921 QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
922 // Add the r- or l-value reference type back to the fully
923 // qualified one.
924 if (IsLValueRefTy)
925 QT = Ctx.getLValueReferenceType(QT);
926 else
927 QT = Ctx.getRValueReferenceType(QT);
928 // Add back the qualifiers.
929 QT = Ctx.getQualifiedType(QT, Quals);
930 return QT;
931 }
932
933 // Remove the part of the type related to the type being a template
934 // parameter (we won't report it as part of the 'type name' and it
935 // is actually make the code below to be more complex (to handle
936 // those)
937 while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {
938 // Get the qualifiers.
939 Qualifiers Quals = QT.getQualifiers();
940
941 QT = cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();
942
943 // Add back the qualifiers.
944 QT = Ctx.getQualifiedType(QT, Quals);
945 }
946
947 NestedNameSpecifier *Prefix = nullptr;
948 // Local qualifiers are attached to the QualType outside of the
949 // elaborated type. Retrieve them before descending into the
950 // elaborated type.
951 Qualifiers PrefixQualifiers = QT.getLocalQualifiers();
952 QT = QualType(QT.getTypePtr(), 0);
953#if LIBCLANG_VERSION_MAJOR >= 18
954 constexpr ElaboratedTypeKeyword ETK_None = ElaboratedTypeKeyword::None;
955#endif
956 ElaboratedTypeKeyword Keyword = ETK_None;
957 if (const auto *ETypeInput = dyn_cast<ElaboratedType>(QT.getTypePtr())) {
958 QT = ETypeInput->getNamedType();
959 assert(!QT.hasLocalQualifiers());
960 Keyword = ETypeInput->getKeyword();
961 }
962
963 // We don't consider the alias introduced by `using a::X` as a new type.
964 // The qualified name is still a::X.
965 if (const auto *UT = QT->getAs<UsingType>()) {
966 QT = Ctx.getQualifiedType(UT->getUnderlyingType(), PrefixQualifiers);
967 return getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
968 }
969
970 // Create a nested name specifier if needed.
971 Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(),
972 true /*FullyQualified*/,
973 WithGlobalNsPrefix);
974
975 // In case of template specializations iterate over the arguments and
976 // fully qualify them as well.
977 if (isa<const TemplateSpecializationType>(QT.getTypePtr()) ||
978 isa<const RecordType>(QT.getTypePtr())) {
979 // We are asked to fully qualify and we have a Record Type (which
980 // may point to a template specialization) or Template
981 // Specialization Type. We need to fully qualify their arguments.
982
983 const Type *TypePtr = getFullyQualifiedTemplateType(
984 Ctx, QT.getTypePtr(), WithGlobalNsPrefix);
985 QT = QualType(TypePtr, 0);
986 }
987 if (Prefix || Keyword != ETK_None) {
988 QT = Ctx.getElaboratedType(Keyword, Prefix, QT);
989 }
990 QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
991 return QT;
992}
993
994#endif // CLANG_VERSION_MAJOR >= 22
995
996inline std::string getFullyQualifiedName(QualType QT,
997 const ASTContext &Ctx,
998 const PrintingPolicy &Policy,
999 bool WithGlobalNsPrefix = false) {
1000 QualType FQQT = getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
1001 return FQQT.getAsString(Policy);
1002}
1003
1004} // end namespace TypeName
1005} // end namespace clang
Access
Definition access.h:11
static const clang::Decl * get_cursor_declaration(CXCursor cursor)
Returns the underlying Decl that cursor represents.
static QString reconstructQualifiedPathForCursor(CXCursor cur)
Reconstruct the qualified path name of a function that is being overridden.
static std::optional< QString > classNameFromParameterType(clang::QualType param_type)
QString functionName(CXCursor cursor)
Returns the function name from a given cursor representing a function declaration.
static std::string get_default_value_initializer_as_string(const clang::TemplateTemplateParmDecl *parameter)
static QString fromCXString(CXString &&string)
convert a CXString to a QString, and dispose the CXString
static QDebug operator<<(QDebug debug, const std::vector< T > &v)
static QString getSpelling(CXSourceRange range)
static void setOverridesForFunction(FunctionNode *fn, CXCursor cursor)
static QList< QByteArray > includePathsFromHeaders(const std::set< Config::HeaderFilePath > &allHeaders)
static const auto kClangDontDisplayDiagnostics
static const clang::TemplateSpecializationType * find_template_specialization_through_sugar(const clang::Type *type)
void getMoreArgs(const std::vector< QByteArray > &include_paths, const std::set< Config::HeaderFilePath > &all_headers, std::vector< const char * > &args)
Load the include paths into moreArgs.
static std::string get_default_value_initializer_as_string(const clang::ParmVarDecl *parameter)
void getDefaultArgs(const QList< QByteArray > &defines, std::vector< const char * > &args)
Load the default arguments and the defines into args.
static std::optional< SfinaeConstraint > detect_sfinae_constraint(const clang::NonTypeTemplateParmDecl *param)
static std::string get_expression_as_string(const clang::Expr *expression, const clang::ASTContext &declaration_context)
bool visitChildrenLambda(CXCursor cursor, T &&lambda)
Call clang_visitChildren on the given cursor with the lambda as a callback T can be any functor that ...
static std::string get_default_value_initializer_as_string(const clang::NamedDecl *declaration)
static RelaxedTemplateDeclaration get_template_declaration(const clang::TemplateDecl *template_declaration)
static std::string get_default_value_initializer_as_string(const clang::NonTypeTemplateParmDecl *parameter)
static QString fromCache(const QByteArray &cache, unsigned int offset1, unsigned int offset2)
static bool is_enable_if_name(const std::string &qualified_name)
static float getUnpatchedVersion(QString t)
static Location fromCXSourceLocation(CXSourceLocation location)
convert a CXSourceLocation to a qdoc Location
static QString cleanAnonymousTypeName(const QString &typeName)
static Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec)
convert a CX_CXXAccessSpecifier to Node::Access
static std::string get_default_value_initializer_as_string(const clang::TemplateTypeParmDecl *parameter)
constexpr const char fnDummyFileName[]
static CXTranslationUnit_Flags flags_
static Node * findNodeForCursor(QDocDatabase *qdb, CXCursor cur)
Find the node from the QDocDatabase qdb that corresponds to the declaration represented by the cursor...
static void autoGenerateSmfDoc(FunctionNode *fn, const QString &className)
static std::string get_fully_qualified_type_name(clang::QualType type, const clang::ASTContext &declaration_context)
static void printDiagnostics(const CXTranslationUnit &translationUnit)
static const char * defaultArgs_[]
std::optional< PCHFile > buildPCH(QDocDatabase *qdb, QString module_header, const std::set< Config::HeaderFilePath > &all_headers, const std::vector< QByteArray > &include_paths, const QList< QByteArray > &defines, const InclusionPolicy &policy)
Building the PCH must be possible when there are no .cpp files, so it is moved here to its own member...
static QString readFile(CXFile cxFile, unsigned int offset1, unsigned int offset2)
static std::string ensureAnonymousTagKeyword(std::string typeName, clang::QualType type)
Returns a string representing the name of type as if it was referred to at the end of the translation...
void addChild(Node *child)
Adds the child to this node's child list and sets the child's parent pointer to this Aggregate.
ParsedCppFileIR parse_cpp_file(const QString &filePath)
Get ready to parse the C++ cpp file identified by filePath and add its parsed contents to the databas...
ClangCodeParser(QDocDatabase *qdb, Config &, const std::vector< QByteArray > &include_paths, const QList< QByteArray > &defines, std::optional< std::reference_wrapper< const PCHFile > > pch)
Node * nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc)
Given a comment at location loc, return a Node for this comment nextCommentLoc is the location of the...
CXChildVisitResult visitChildren(CXCursor cursor)
QDocDatabase * qdocDB()
ClangVisitor(QDocDatabase *qdb, const std::set< Config::HeaderFilePath > &allHeaders, const Config::InternalFilePatterns &internalFilePatterns)
CXChildVisitResult visitFnArg(CXCursor cursor, Node **fnNode, bool &ignoreSignature)
The ClassNode represents a C++ class.
Definition classnode.h:23
static bool isWorthWarningAbout(const Doc &doc)
Test for whether a doc comment warrants warnings.
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:85
Definition doc.h:32
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:89
void markAutoGenerated()
Marks this documentation as auto-generated by QDoc.
Definition doc.cpp:250
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
Definition doc.cpp:270
This node is used to represent any kind of function being documented.
void setConst(bool b)
void markDeletedAsWritten()
void setStatic(bool b)
void setVirtualness(Virtualness virtualness)
bool isNonvirtual() const
bool isMAssign() const
void setInvokable(bool b)
bool isCAssign() const
void setRef(bool b)
bool isDtor() const
void setOverride(bool b)
void setRefRef(bool b)
bool isSpecialMemberFunction() const
void markConstexpr()
bool isDeletedAsWritten() const
void markExplicit()
void markExplicitlyDefaulted()
bool isCCtor() const
bool isMCtor() const
bool isCtor() const
void setMetaness(Metaness metaness)
bool isExplicitlyDefaulted() const
Parameters & parameters()
void setHiddenFriend(bool b)
The Location class provides a way to mark a location in a file.
Definition location.h:20
void setColumnNo(int no)
Definition location.h:43
void setLineNo(int no)
Definition location.h:42
This class represents a C++ namespace.
This class describes one instance of using the Q_PROPERTY macro.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
Status
Specifies the status of the QQmlIncubator.
void setStatic(bool b)
const QString & leftType() const
Node * clone(Aggregate *parent) override
Clone this node on the heap and make the clone a child of parent.
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
QString dataType() const
const QString & rightType() const
#define COMMAND_SINCE
Definition codeparser.h:75
#define COMMAND_FN
Definition codeparser.h:26
#define COMMAND_PAGE
Definition codeparser.h:44
#define CONFIG_VERSION
Definition config.h:449
#define CONFIG_DOCUMENTATIONINHEADERS
Definition config.h:379
bool hasTooManyTopics(const Doc &doc)
Checks if there are too many topic commands in doc.
NodeType
Definition genustypes.h:150
Combined button and popup list for selecting options.
This namespace holds QDoc-internal utility methods.
Definition utilities.h:21
static NestedNameSpecifier * createOuterNNS(const ASTContext &Ctx, const Decl *D, bool FullyQualify, bool WithGlobalNsPrefix)
QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, bool WithGlobalNsPrefix)
static bool getFullyQualifiedTemplateName(const ASTContext &Ctx, TemplateName &TName, bool WithGlobalNsPrefix)
static const Type * getFullyQualifiedTemplateType(const ASTContext &Ctx, const Type *TypePtr, bool WithGlobalNsPrefix)
static NestedNameSpecifier * createNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Type *TypePtr, bool FullyQualified, bool WithGlobalNsPrefix)
static NestedNameSpecifier * createNestedNameSpecifier(const ASTContext &Ctx, const TypeDecl *TD, bool FullyQualify, bool WithGlobalNsPrefix)
static NestedNameSpecifier * createNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Decl *decl, bool FullyQualified, bool WithGlobalNsPrefix)
static NestedNameSpecifier * getFullyQualifiedNestedNameSpecifier(const ASTContext &Ctx, NestedNameSpecifier *scope, bool WithGlobalNsPrefix)
Return a fully qualified version of this name specifier.
static bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx, TemplateArgument &Arg, bool WithGlobalNsPrefix)
std::string getFullyQualifiedName(QualType QT, const ASTContext &Ctx, const PrintingPolicy &Policy, bool WithGlobalNsPrefix=false)
static NestedNameSpecifier * createNestedNameSpecifier(const ASTContext &Ctx, const NamespaceDecl *Namesp, bool WithGlobalNsPrefix)
QList< Node * > NodeVector
Definition node.h:47
#define assert
@ Public
Definition access.h:11
@ Private
Definition access.h:11
@ Protected
Definition access.h:11
@ Internal
Definition status.h:15
Returns the spelling in the file for a source range.
std::variant< Node *, FnMatchError > operator()(const Location &location, const QString &fnSignature, const QString &idTag, QStringList context)
Use clang to parse the function signature from a function command.
Encapsulates information about.
Definition parsererror.h:13
The Node class is the base class for all the nodes in QDoc's parse tree.
void setGenus(Genus t)
Definition node.h:86
void setAccess(Access t)
Sets the node's access type to t.
Definition node.h:170
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:108
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:126
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:131
void setLocation(const Location &t)
Sets the node's declaration location, its definition location, or both, depending on the suffix of th...
Definition node.cpp:900
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:136
virtual void setRelatedNonmember(bool b)
Sets a flag in the node indicating whether this node is a related nonmember of something.
Definition node.h:185
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:231
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:100
void setDoc(const Doc &doc, bool replace=false)
Sets this Node's Doc to doc.
Definition node.cpp:557
bool isClass() const
Returns true if the node type is Class.
Definition node.h:90
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:933
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:143
A class for parsing and managing a function parameter list.
Definition main.cpp:28
Parameter & operator[](int index)
Definition parameters.h:39
void pop_back()
Definition parameters.h:43
void reserve(int count)
Definition parameters.h:35
void clear()
Definition parameters.h:24
Parameter & last()
Definition parameters.h:37
int count() const
Definition parameters.h:34
void setPrivateSignal()
Definition parameters.h:44
Holds the source-level alias with its template arguments for a SFINAE constraint detected in a non-ty...
CXTranslationUnit tu