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
39inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
40 bool WithGlobalNsPrefix);
41
42/// Create a NestedNameSpecifier for Namesp and its enclosing
43/// scopes.
44///
45/// \param[in] Ctx - the AST Context to be used.
46/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier
47/// is requested.
48/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
49/// specifier "::" should be prepended or not.
50static inline NestedNameSpecifier *createNestedNameSpecifier(
51 const ASTContext &Ctx,
52 const NamespaceDecl *Namesp,
53 bool WithGlobalNsPrefix);
54
55/// Create a NestedNameSpecifier for TagDecl and its enclosing
56/// scopes.
57///
58/// \param[in] Ctx - the AST Context to be used.
59/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is
60/// requested.
61/// \param[in] FullyQualify - Convert all template arguments into fully
62/// qualified names.
63/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
64/// specifier "::" should be prepended or not.
65static inline NestedNameSpecifier *createNestedNameSpecifier(
66 const ASTContext &Ctx, const TypeDecl *TD,
67 bool FullyQualify, bool WithGlobalNsPrefix);
68
69static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
70 const ASTContext &Ctx, const Decl *decl,
71 bool FullyQualified, bool WithGlobalNsPrefix);
72
73static inline NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
74 const ASTContext &Ctx, NestedNameSpecifier *scope, bool WithGlobalNsPrefix);
75
76static inline bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
77 TemplateName &TName,
78 bool WithGlobalNsPrefix) {
79 bool Changed = false;
80 NestedNameSpecifier *NNS = nullptr;
81
82 TemplateDecl *ArgTDecl = TName.getAsTemplateDecl();
83 // ArgTDecl won't be NULL because we asserted that this isn't a
84 // dependent context very early in the call chain.
85 assert(ArgTDecl != nullptr);
86 QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName();
87
88 if (QTName &&
89 !QTName->hasTemplateKeyword() &&
90 (NNS = QTName->getQualifier())) {
91 NestedNameSpecifier *QNNS = getFullyQualifiedNestedNameSpecifier(
92 Ctx, scope: NNS, WithGlobalNsPrefix);
93 if (QNNS != NNS) {
94 Changed = true;
95 NNS = QNNS;
96 } else {
97 NNS = nullptr;
98 }
99 } else {
100 NNS = createNestedNameSpecifierForScopeOf(
101 Ctx, decl: ArgTDecl, FullyQualified: true, WithGlobalNsPrefix);
102 }
103 if (NNS) {
104 TemplateName UnderlyingTN(ArgTDecl);
105 if (UsingShadowDecl *USD = TName.getAsUsingShadowDecl())
106 UnderlyingTN = TemplateName(USD);
107 TName =
108 Ctx.getQualifiedTemplateName(NNS,
109 /*TemplateKeyword=*/TemplateKeyword: false, Template: UnderlyingTN);
110 Changed = true;
111 }
112 return Changed;
113}
114
115static inline bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
116 TemplateArgument &Arg,
117 bool WithGlobalNsPrefix) {
118 bool Changed = false;
119
120 // Note: we do not handle TemplateArgument::Expression, to replace it
121 // we need the information for the template instance decl.
122
123 if (Arg.getKind() == TemplateArgument::Template) {
124 TemplateName TName = Arg.getAsTemplate();
125 Changed = getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix);
126 if (Changed) {
127 Arg = TemplateArgument(TName);
128 }
129 } else if (Arg.getKind() == TemplateArgument::Type) {
130 QualType SubTy = Arg.getAsType();
131 // Check if the type needs more desugaring and recurse.
132 QualType QTFQ = getFullyQualifiedType(QT: SubTy, Ctx, WithGlobalNsPrefix);
133 if (QTFQ != SubTy) {
134 Arg = TemplateArgument(QTFQ);
135 Changed = true;
136 }
137 }
138 return Changed;
139}
140
141static inline const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx,
142 const Type *TypePtr,
143 bool WithGlobalNsPrefix) {
144 // DependentTemplateTypes exist within template declarations and
145 // definitions. Therefore we shouldn't encounter them at the end of
146 // a translation unit. If we do, the caller has made an error.
147 assert(!isa<DependentTemplateSpecializationType>(TypePtr));
148 // In case of template specializations, iterate over the arguments
149 // and fully qualify them as well.
150 if (const auto *TST = dyn_cast<const TemplateSpecializationType>(Val: TypePtr)) {
151 bool MightHaveChanged = false;
152 SmallVector<TemplateArgument, 4> FQArgs;
153 // Cheap to copy and potentially modified by
154 // getFullyQualifedTemplateArgument.
155 for (TemplateArgument Arg : TST->template_arguments()) {
156 MightHaveChanged |= getFullyQualifiedTemplateArgument(
157 Ctx, Arg, WithGlobalNsPrefix);
158 FQArgs.push_back(Elt: Arg);
159 }
160
161 // If a fully qualified arg is different from the unqualified arg,
162 // allocate new type in the AST.
163 if (MightHaveChanged) {
164#if CLANG_VERSION_MAJOR >= 21
165 QualType QT = Ctx.getTemplateSpecializationType(
166 TST->getTemplateName(), FQArgs, /*CanonicalArgs=*/{},
167 TST->getCanonicalTypeInternal());
168#else
169 QualType QT = Ctx.getTemplateSpecializationType(
170 T: TST->getTemplateName(), Args: FQArgs,
171 Canon: TST->getCanonicalTypeInternal());
172#endif
173 // getTemplateSpecializationType returns a fully qualified
174 // version of the specialization itself, so no need to qualify
175 // it.
176 return QT.getTypePtr();
177 }
178 } else if (const auto *TSTRecord = dyn_cast<const RecordType>(Val: TypePtr)) {
179 // We are asked to fully qualify and we have a Record Type,
180 // which can point to a template instantiation with no sugar in any of
181 // its template argument, however we still need to fully qualify them.
182
183 if (const auto *TSTDecl =
184 dyn_cast<ClassTemplateSpecializationDecl>(Val: TSTRecord->getDecl())) {
185 const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs();
186
187 bool MightHaveChanged = false;
188 SmallVector<TemplateArgument, 4> FQArgs;
189 for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
190 // cheap to copy and potentially modified by
191 // getFullyQualifedTemplateArgument
192 TemplateArgument Arg(TemplateArgs[I]);
193 MightHaveChanged |= getFullyQualifiedTemplateArgument(
194 Ctx, Arg, WithGlobalNsPrefix);
195 FQArgs.push_back(Elt: Arg);
196 }
197
198 // If a fully qualified arg is different from the unqualified arg,
199 // allocate new type in the AST.
200 if (MightHaveChanged) {
201 TemplateName TN(TSTDecl->getSpecializedTemplate());
202#if CLANG_VERSION_MAJOR >= 21
203 QualType QT = Ctx.getTemplateSpecializationType(
204 TN, FQArgs, /*CanonicalArgs=*/{},
205 TSTRecord->getCanonicalTypeInternal());
206#else
207 QualType QT = Ctx.getTemplateSpecializationType(
208 T: TN, Args: FQArgs,
209 Canon: TSTRecord->getCanonicalTypeInternal());
210#endif
211 // getTemplateSpecializationType returns a fully qualified
212 // version of the specialization itself, so no need to qualify
213 // it.
214 return QT.getTypePtr();
215 }
216 }
217 }
218 return TypePtr;
219}
220
221static inline NestedNameSpecifier *createOuterNNS(const ASTContext &Ctx, const Decl *D,
222 bool FullyQualify,
223 bool WithGlobalNsPrefix) {
224 const DeclContext *DC = D->getDeclContext();
225 if (const auto *NS = dyn_cast<NamespaceDecl>(Val: DC)) {
226 while (NS && NS->isInline()) {
227 // Ignore inline namespace;
228 NS = dyn_cast<NamespaceDecl>(Val: NS->getDeclContext());
229 }
230 if (NS && NS->getDeclName()) {
231 return createNestedNameSpecifier(Ctx, Namesp: NS, WithGlobalNsPrefix);
232 }
233 return nullptr; // no starting '::', no anonymous
234 } else if (const auto *TD = dyn_cast<TagDecl>(Val: DC)) {
235 return createNestedNameSpecifier(Ctx, TD, FullyQualify, WithGlobalNsPrefix);
236 } else if (const auto *TDD = dyn_cast<TypedefNameDecl>(Val: DC)) {
237 return createNestedNameSpecifier(
238 Ctx, TD: TDD, FullyQualify, WithGlobalNsPrefix);
239 } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
240 return NestedNameSpecifier::GlobalSpecifier(Context: Ctx);
241 }
242 return nullptr; // no starting '::' if |WithGlobalNsPrefix| is false
243}
244
245/// Return a fully qualified version of this name specifier.
246static inline NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
247 const ASTContext &Ctx, NestedNameSpecifier *Scope,
248 bool WithGlobalNsPrefix) {
249 switch (Scope->getKind()) {
250 case NestedNameSpecifier::Global:
251 // Already fully qualified
252 return Scope;
253 case NestedNameSpecifier::Namespace:
254 return TypeName::createNestedNameSpecifier(
255 Ctx, Namesp: Scope->getAsNamespace(), WithGlobalNsPrefix);
256 case NestedNameSpecifier::NamespaceAlias:
257 // Namespace aliases are only valid for the duration of the
258 // scope where they were introduced, and therefore are often
259 // invalid at the end of the TU. So use the namespace name more
260 // likely to be valid at the end of the TU.
261 return TypeName::createNestedNameSpecifier(
262 Ctx,
263 Namesp: Scope->getAsNamespaceAlias()->getNamespace()->getCanonicalDecl(),
264 WithGlobalNsPrefix);
265 case NestedNameSpecifier::Identifier:
266 // A function or some other construct that makes it un-namable
267 // at the end of the TU. Skip the current component of the name,
268 // but use the name of it's prefix.
269 return getFullyQualifiedNestedNameSpecifier(
270 Ctx, Scope: Scope->getPrefix(), WithGlobalNsPrefix);
271 case NestedNameSpecifier::Super:
272 case NestedNameSpecifier::TypeSpec:
273#if CLANG_VERSION_MAJOR < 21
274 case NestedNameSpecifier::TypeSpecWithTemplate:
275#endif
276 {
277 const Type *Type = Scope->getAsType();
278 // Find decl context.
279 const TagDecl *TD = nullptr;
280 if (const TagType *TagDeclType = Type->getAs<TagType>()) {
281 TD = TagDeclType->getDecl();
282 } else {
283 TD = Type->getAsCXXRecordDecl();
284 }
285 if (TD) {
286 return TypeName::createNestedNameSpecifier(Ctx, TD,
287 FullyQualify: true /*FullyQualified*/,
288 WithGlobalNsPrefix);
289 } else if (const auto *TDD = dyn_cast<TypedefType>(Val: Type)) {
290 return TypeName::createNestedNameSpecifier(Ctx, TD: TDD->getDecl(),
291 FullyQualify: true /*FullyQualified*/,
292 WithGlobalNsPrefix);
293 }
294 return Scope;
295 }
296 }
297 llvm_unreachable("bad NNS kind");
298}
299
300/// Create a nested name specifier for the declaring context of
301/// the type.
302static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
303 const ASTContext &Ctx, const Decl *Decl,
304 bool FullyQualified, bool WithGlobalNsPrefix) {
305 assert(Decl);
306
307 const DeclContext *DC = Decl->getDeclContext()->getRedeclContext();
308 const auto *Outer = dyn_cast<NamedDecl>(Val: DC);
309 const auto *OuterNS = dyn_cast<NamespaceDecl>(Val: DC);
310 if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) {
311 if (OuterNS) {
312 return createNestedNameSpecifier(Ctx, Namesp: OuterNS, WithGlobalNsPrefix);
313 } else if (const auto *TD = dyn_cast<TagDecl>(Val: Outer)) {
314 return createNestedNameSpecifier(
315 Ctx, TD, FullyQualify: FullyQualified, WithGlobalNsPrefix);
316 } else if (isa<TranslationUnitDecl>(Val: Outer)) {
317 // Context is the TU. Nothing needs to be done.
318 return nullptr;
319 } else {
320 // Decl's context was neither the TU, a namespace, nor a
321 // TagDecl, which means it is a type local to a scope, and not
322 // accessible at the end of the TU.
323 return nullptr;
324 }
325 } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
326 return NestedNameSpecifier::GlobalSpecifier(Context: Ctx);
327 }
328 return nullptr;
329}
330
331/// Create a nested name specifier for the declaring context of
332/// the type.
333static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
334 const ASTContext &Ctx, const Type *TypePtr,
335 bool FullyQualified, bool WithGlobalNsPrefix) {
336 if (!TypePtr) return nullptr;
337
338 Decl *Decl = nullptr;
339 // There are probably other cases ...
340 if (const auto *TDT = dyn_cast<TypedefType>(Val: TypePtr)) {
341 Decl = TDT->getDecl();
342 } else if (const auto *TagDeclType = dyn_cast<TagType>(Val: TypePtr)) {
343 Decl = TagDeclType->getDecl();
344 } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(Val: TypePtr)) {
345 Decl = TST->getTemplateName().getAsTemplateDecl();
346 } else {
347 Decl = TypePtr->getAsCXXRecordDecl();
348 }
349
350 if (!Decl) return nullptr;
351
352 return createNestedNameSpecifierForScopeOf(
353 Ctx, Decl, FullyQualified, WithGlobalNsPrefix);
354}
355
356inline NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
357 const NamespaceDecl *Namespace,
358 bool WithGlobalNsPrefix) {
359 while (Namespace && Namespace->isInline()) {
360 // Ignore inline namespace;
361 Namespace = dyn_cast<NamespaceDecl>(Val: Namespace->getDeclContext());
362 }
363 if (!Namespace) return nullptr;
364
365 bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces
366 return NestedNameSpecifier::Create(
367 Context: Ctx,
368 Prefix: createOuterNNS(Ctx, D: Namespace, FullyQualify: FullyQualified, WithGlobalNsPrefix),
369 NS: Namespace);
370}
371
372inline NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
373 const TypeDecl *TD,
374 bool FullyQualify,
375 bool WithGlobalNsPrefix) {
376 const Type *TypePtr = TD->getTypeForDecl();
377 if (isa<const TemplateSpecializationType>(Val: TypePtr) ||
378 isa<const RecordType>(Val: TypePtr)) {
379 // We are asked to fully qualify and we have a Record Type (which
380 // may point to a template specialization) or Template
381 // Specialization Type. We need to fully qualify their arguments.
382
383 TypePtr = getFullyQualifiedTemplateType(Ctx, TypePtr, WithGlobalNsPrefix);
384 }
385
386 return NestedNameSpecifier::Create(
387 Context: Ctx, Prefix: createOuterNNS(Ctx, D: TD, FullyQualify, WithGlobalNsPrefix),
388#if CLANG_VERSION_MAJOR < 21
389 Template: false /*No TemplateKeyword*/,
390#endif
391 T: TypePtr);
392}
393
394/// Return the fully qualified type, including fully-qualified
395/// versions of any template parameters.
396inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
397 bool WithGlobalNsPrefix = false) {
398 // In case of myType* we need to strip the pointer first, fully
399 // qualify and attach the pointer once again.
400 if (isa<PointerType>(Val: QT.getTypePtr())) {
401 // Get the qualifiers.
402 Qualifiers Quals = QT.getQualifiers();
403 QT = getFullyQualifiedType(QT: QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
404 QT = Ctx.getPointerType(T: QT);
405 // Add back the qualifiers.
406 QT = Ctx.getQualifiedType(T: QT, Qs: Quals);
407 return QT;
408 }
409
410 if (auto *MPT = dyn_cast<MemberPointerType>(Val: QT.getTypePtr())) {
411 // Get the qualifiers.
412 Qualifiers Quals = QT.getQualifiers();
413 // Fully qualify the pointee and class types.
414 QT = getFullyQualifiedType(QT: QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
415#if CLANG_VERSION_MAJOR >= 21
416 QT = Ctx.getMemberPointerType(QT, MPT->getQualifier(), MPT->getMostRecentCXXRecordDecl());
417#else
418 QualType Class = getFullyQualifiedType(QT: QualType(MPT->getClass(), 0), Ctx,
419 WithGlobalNsPrefix);
420 QT = Ctx.getMemberPointerType(T: QT, Cls: Class.getTypePtr());
421#endif
422 // Add back the qualifiers.
423 QT = Ctx.getQualifiedType(T: QT, Qs: Quals);
424 return QT;
425 }
426
427 // In case of myType& we need to strip the reference first, fully
428 // qualify and attach the reference once again.
429 if (isa<ReferenceType>(Val: QT.getTypePtr())) {
430 // Get the qualifiers.
431 bool IsLValueRefTy = isa<LValueReferenceType>(Val: QT.getTypePtr());
432 Qualifiers Quals = QT.getQualifiers();
433 QT = getFullyQualifiedType(QT: QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
434 // Add the r- or l-value reference type back to the fully
435 // qualified one.
436 if (IsLValueRefTy)
437 QT = Ctx.getLValueReferenceType(T: QT);
438 else
439 QT = Ctx.getRValueReferenceType(T: QT);
440 // Add back the qualifiers.
441 QT = Ctx.getQualifiedType(T: QT, Qs: Quals);
442 return QT;
443 }
444
445 // Remove the part of the type related to the type being a template
446 // parameter (we won't report it as part of the 'type name' and it
447 // is actually make the code below to be more complex (to handle
448 // those)
449 while (isa<SubstTemplateTypeParmType>(Val: QT.getTypePtr())) {
450 // Get the qualifiers.
451 Qualifiers Quals = QT.getQualifiers();
452
453 QT = cast<SubstTemplateTypeParmType>(Val: QT.getTypePtr())->desugar();
454
455 // Add back the qualifiers.
456 QT = Ctx.getQualifiedType(T: QT, Qs: Quals);
457 }
458
459 NestedNameSpecifier *Prefix = nullptr;
460 // Local qualifiers are attached to the QualType outside of the
461 // elaborated type. Retrieve them before descending into the
462 // elaborated type.
463 Qualifiers PrefixQualifiers = QT.getLocalQualifiers();
464 QT = QualType(QT.getTypePtr(), 0);
465#if LIBCLANG_VERSION_MAJOR >= 18
466 constexpr ElaboratedTypeKeyword ETK_None = ElaboratedTypeKeyword::None;
467#endif
468 ElaboratedTypeKeyword Keyword = ETK_None;
469 if (const auto *ETypeInput = dyn_cast<ElaboratedType>(Val: QT.getTypePtr())) {
470 QT = ETypeInput->getNamedType();
471 assert(!QT.hasLocalQualifiers());
472 Keyword = ETypeInput->getKeyword();
473 }
474
475 // We don't consider the alias introduced by `using a::X` as a new type.
476 // The qualified name is still a::X.
477 if (const auto *UT = QT->getAs<UsingType>()) {
478 QT = Ctx.getQualifiedType(T: UT->getUnderlyingType(), Qs: PrefixQualifiers);
479 return getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
480 }
481
482 // Create a nested name specifier if needed.
483 Prefix = createNestedNameSpecifierForScopeOf(Ctx, TypePtr: QT.getTypePtr(),
484 FullyQualified: true /*FullyQualified*/,
485 WithGlobalNsPrefix);
486
487 // In case of template specializations iterate over the arguments and
488 // fully qualify them as well.
489 if (isa<const TemplateSpecializationType>(Val: QT.getTypePtr()) ||
490 isa<const RecordType>(Val: QT.getTypePtr())) {
491 // We are asked to fully qualify and we have a Record Type (which
492 // may point to a template specialization) or Template
493 // Specialization Type. We need to fully qualify their arguments.
494
495 const Type *TypePtr = getFullyQualifiedTemplateType(
496 Ctx, TypePtr: QT.getTypePtr(), WithGlobalNsPrefix);
497 QT = QualType(TypePtr, 0);
498 }
499 if (Prefix || Keyword != ETK_None) {
500 QT = Ctx.getElaboratedType(Keyword, NNS: Prefix, NamedType: QT);
501 }
502 QT = Ctx.getQualifiedType(T: QT, Qs: PrefixQualifiers);
503 return QT;
504}
505
506inline std::string getFullyQualifiedName(QualType QT,
507 const ASTContext &Ctx,
508 const PrintingPolicy &Policy,
509 bool WithGlobalNsPrefix = false) {
510 QualType FQQT = getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
511 return FQQT.getAsString(Policy);
512}
513
514} // end namespace TypeName
515} // end namespace clang
516

source code of qttools/src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h