506 lines · cpp
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-exception6//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 enclosing20/// scopes.21///22/// \param[in] Ctx - the AST Context to be used.23/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier24/// is requested.25/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace26/// specifier "::" should be prepended or not.27static NestedNameSpecifier28createNestedNameSpecifier(const ASTContext &Ctx, const NamespaceDecl *Namesp,29 bool WithGlobalNsPrefix);30 31/// Create a NestedNameSpecifier for TagDecl and its enclosing32/// scopes.33///34/// \param[in] Ctx - the AST Context to be used.35/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is36/// requested.37/// \param[in] FullyQualify - Convert all template arguments into fully38/// qualified names.39/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace40/// specifier "::" should be prepended or not.41static NestedNameSpecifier createNestedNameSpecifier(const ASTContext &Ctx,42 const TypeDecl *TD,43 bool FullyQualify,44 bool WithGlobalNsPrefix);45 46static NestedNameSpecifier47createNestedNameSpecifierForScopeOf(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, ArgTDecl, 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(NNS,87 /*TemplateKeyword=*/false, 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 it99 // 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(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 of126 // 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>(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 by140 // getFullyQualifedTemplateArgument141 TemplateArgument Arg(TemplateArgs[I]);142 MightHaveChanged |=143 getFullyQualifiedTemplateArgument(Ctx, Arg, WithGlobalNsPrefix);144 FQArgs.push_back(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 TemplateName(TSTDecl->getSpecializedTemplate()));155 QualType QT = Ctx.getTemplateSpecializationType(156 Keyword, TN, FQArgs,157 /*CanonicalArgs=*/{}, TSTRecord->getCanonicalTypeInternal());158 // getTemplateSpecializationType returns a fully qualified159 // version of the specialization itself, so no need to qualify160 // 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 by173 // getFullyQualifedTemplateArgument.174 for (TemplateArgument Arg : TST->template_arguments()) {175 MightHaveChanged |=176 getFullyQualifiedTemplateArgument(Ctx, Arg, WithGlobalNsPrefix);177 FQArgs.push_back(Arg);178 }179 180 if (!MightHaveChanged)181 return TST;182 183 QualType NewQT =184 Ctx.getTemplateSpecializationType(TST->getKeyword(), TName, FQArgs,185 /*CanonicalArgs=*/{}, TST->desugar());186 // getTemplateSpecializationType returns a fully qualified187 // version of the specialization itself, so no need to qualify188 // 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>(DC)) {197 while (NS && NS->isInline()) {198 // Ignore inline namespace;199 NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());200 }201 if (NS && NS->getDeclName()) {202 return createNestedNameSpecifier(Ctx, NS, WithGlobalNsPrefix);203 }204 return std::nullopt; // no starting '::', no anonymous205 }206 if (const auto *TD = dyn_cast<TagDecl>(DC))207 return createNestedNameSpecifier(Ctx, TD, FullyQualify, WithGlobalNsPrefix);208 if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC))209 return createNestedNameSpecifier(Ctx, TDD, FullyQualify,210 WithGlobalNsPrefix);211 if (WithGlobalNsPrefix && DC->isTranslationUnit())212 return NestedNameSpecifier::getGlobal();213 return std::nullopt; // no starting '::' if |WithGlobalNsPrefix| is false214}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 qualified225 return Scope;226 case NestedNameSpecifier::Kind::Namespace:227 return TypeName::createNestedNameSpecifier(228 Ctx, 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>(Type))237 TD = D->getDecl();238 else239 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 of248/// the type.249static NestedNameSpecifier250createNestedNameSpecifierForScopeOf(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>(DC);260 const auto *OuterNS = dyn_cast<NamespaceDecl>(DC);261 if (OuterNS && OuterNS->isAnonymousNamespace())262 OuterNS = dyn_cast<NamespaceDecl>(OuterNS->getParent());263 if (Outer) {264 if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) {265 if (ClassTemplateDecl *ClassTempl =266 CxxDecl->getDescribedClassTemplate()) {267 // We are in the case of a type(def) that was declared in a268 // class template but is *not* type dependent. In clang, it269 // gets attached to the class template declaration rather than270 // any specific class template instantiation. This result in271 // 'odd' fully qualified typename:272 //273 // vector<_Tp,_Alloc>::size_type274 //275 // Make the situation is 'useable' but looking a bit odd by276 // picking a random instance as the declaring context.277 if (!ClassTempl->specializations().empty()) {278 Decl = *(ClassTempl->spec_begin());279 Outer = dyn_cast<NamedDecl>(Decl);280 OuterNS = dyn_cast<NamespaceDecl>(Decl);281 }282 }283 }284 285 if (OuterNS) {286 return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);287 } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {288 return createNestedNameSpecifier(289 Ctx, TD, FullyQualified, WithGlobalNsPrefix);290 } else if (isa<TranslationUnitDecl>(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 a295 // TagDecl, which means it is a type local to a scope, and not296 // 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 of306/// the type.307static NestedNameSpecifier308createNestedNameSpecifierForScopeOf(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>(TypePtr)) {317 Decl = TDT->getDecl();318 } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {319 Decl = TagDeclType->getDecl();320 } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(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 NestedNameSpecifier334createNestedNameSpecifier(const ASTContext &Ctx, const NamespaceDecl *Namespace,335 bool WithGlobalNsPrefix) {336 while (Namespace && Namespace->isInline()) {337 // Ignore inline namespace;338 Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext());339 }340 if (!Namespace)341 return std::nullopt;342 343 bool FullyQualify = true; // doesn't matter, DeclContexts are namespaces344 return NestedNameSpecifier(345 Ctx, Namespace,346 createOuterNNS(Ctx, 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(TD).getTypePtr();354 if (auto *RD = dyn_cast<TagType>(TypePtr)) {355 // We are asked to fully qualify and we have a Record Type (which356 // may point to a template specialization) or Template357 // Specialization Type. We need to fully qualify their arguments.358 TypePtr = getFullyQualifiedTemplateType(359 Ctx, RD, ElaboratedTypeKeyword::None,360 createOuterNNS(Ctx, TD, FullyQualify, WithGlobalNsPrefix),361 WithGlobalNsPrefix);362 } else if (auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) {363 TypePtr = getFullyQualifiedTemplateType(Ctx, TST, WithGlobalNsPrefix);364 }365 return NestedNameSpecifier(TypePtr);366}367 368/// Return the fully qualified type, including fully-qualified369/// 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, fully373 // qualify and attach the pointer once again.374 if (isa<PointerType>(QT.getTypePtr())) {375 // Get the qualifiers.376 Qualifiers Quals = QT.getQualifiers();377 QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);378 QT = Ctx.getPointerType(QT);379 // Add back the qualifiers.380 QT = Ctx.getQualifiedType(QT, Quals);381 return QT;382 }383 384 if (auto *MPT = dyn_cast<MemberPointerType>(QT.getTypePtr())) {385 // Get the qualifiers.386 Qualifiers Quals = QT.getQualifiers();387 // Fully qualify the pointee and class types.388 QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);389 NestedNameSpecifier Qualifier = getFullyQualifiedNestedNameSpecifier(390 Ctx, MPT->getQualifier(), WithGlobalNsPrefix);391 QT = Ctx.getMemberPointerType(QT, Qualifier,392 MPT->getMostRecentCXXRecordDecl());393 // Add back the qualifiers.394 QT = Ctx.getQualifiedType(QT, Quals);395 return QT;396 }397 398 // In case of myType& we need to strip the reference first, fully399 // qualify and attach the reference once again.400 if (isa<ReferenceType>(QT.getTypePtr())) {401 // Get the qualifiers.402 bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());403 Qualifiers Quals = QT.getQualifiers();404 QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);405 // Add the r- or l-value reference type back to the fully406 // qualified one.407 if (IsLValueRefTy)408 QT = Ctx.getLValueReferenceType(QT);409 else410 QT = Ctx.getRValueReferenceType(QT);411 // Add back the qualifiers.412 QT = Ctx.getQualifiedType(QT, Quals);413 return QT;414 }415 416 // Handle types with attributes such as `unique_ptr<int> _Nonnull`.417 if (auto *AT = dyn_cast<AttributedType>(QT.getTypePtr())) {418 QualType NewModified =419 getFullyQualifiedType(AT->getModifiedType(), Ctx, WithGlobalNsPrefix);420 QualType NewEquivalent =421 getFullyQualifiedType(AT->getEquivalentType(), Ctx, WithGlobalNsPrefix);422 Qualifiers Qualifiers = QT.getLocalQualifiers();423 return Ctx.getQualifiedType(424 Ctx.getAttributedType(AT->getAttrKind(), NewModified, NewEquivalent),425 Qualifiers);426 }427 428 // Remove the part of the type related to the type being a template429 // parameter (we won't report it as part of the 'type name' and it430 // is actually make the code below to be more complex (to handle431 // those)432 while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {433 // Get the qualifiers.434 Qualifiers Quals = QT.getQualifiers();435 436 QT = cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();437 438 // Add back the qualifiers.439 QT = Ctx.getQualifiedType(QT, Quals);440 }441 442 if (const auto *TST =443 dyn_cast<const TemplateSpecializationType>(QT.getTypePtr())) {444 445 const Type *T = getFullyQualifiedTemplateType(Ctx, TST, WithGlobalNsPrefix);446 if (T == TST)447 return QT;448 return Ctx.getQualifiedType(T, QT.getQualifiers());449 }450 451 // Local qualifiers are attached to the QualType outside of the452 // elaborated type. Retrieve them before descending into the453 // 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(UT->desugar(), PrefixQualifiers);461 return getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);462 }463 464 // Create a nested name specifier if needed.465 NestedNameSpecifier Prefix = createNestedNameSpecifierForScopeOf(466 Ctx, QT.getTypePtr(), true /*FullyQualified*/, WithGlobalNsPrefix);467 468 // In case of template specializations iterate over the arguments and469 // fully qualify them as well.470 if (const auto *TT = dyn_cast<TagType>(QT.getTypePtr())) {471 // We are asked to fully qualify and we have a Record Type (which472 // may point to a template specialization) or Template473 // Specialization Type. We need to fully qualify their arguments.474 475 const Type *TypePtr = getFullyQualifiedTemplateType(476 Ctx, TT, TT->getKeyword(), Prefix, WithGlobalNsPrefix);477 QT = QualType(TypePtr, 0);478 } else if (const auto *TT = dyn_cast<TypedefType>(QT.getTypePtr())) {479 QT = Ctx.getTypedefType(480 TT->getKeyword(), Prefix, TT->getDecl(),481 getFullyQualifiedType(TT->desugar(), Ctx, WithGlobalNsPrefix));482 } else {483 assert(!Prefix && "Unhandled type node");484 }485 QT = Ctx.getQualifiedType(QT, 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 TypeName505} // end namespace clang506