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