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