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