//===- NestedNameSpecifier.cpp - C++ nested name specifiers ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the NestedNameSpecifier class, which represents // a C++ nested-name-specifier. // //===----------------------------------------------------------------------===// #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DependenceFlags.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include using namespace clang; const NamespaceAndPrefixStorage * NestedNameSpecifier::MakeNamespaceAndPrefixStorage( const ASTContext &Ctx, const NamespaceBaseDecl *Namespace, NestedNameSpecifier Prefix) { llvm::FoldingSetNodeID ID; NamespaceAndPrefixStorage::Profile(ID, Namespace, Prefix); void *InsertPos = nullptr; NamespaceAndPrefixStorage *S = Ctx.NamespaceAndPrefixStorages.FindNodeOrInsertPos(ID, InsertPos); if (!S) { S = new (Ctx, alignof(NamespaceAndPrefixStorage)) NamespaceAndPrefixStorage(Namespace, Prefix); Ctx.NamespaceAndPrefixStorages.InsertNode(S, InsertPos); } return S; } bool NestedNameSpecifier::isFullyQualified() const { switch (getKind()) { case NestedNameSpecifier::Kind::Global: return true; case NestedNameSpecifier::Kind::Null: case NestedNameSpecifier::Kind::MicrosoftSuper: return false; case NestedNameSpecifier::Kind::Namespace: return getAsNamespaceAndPrefix().Prefix.isFullyQualified(); case NestedNameSpecifier::Kind::Type: return getAsType()->getPrefix().isFullyQualified(); } llvm_unreachable("Invalid NNS Kind!"); } NestedNameSpecifierDependence NestedNameSpecifier::getDependence() const { switch (getKind()) { case Kind::Null: case Kind::Global: case Kind::Namespace: return NestedNameSpecifierDependence::None; case Kind::MicrosoftSuper: { CXXRecordDecl *RD = getAsMicrosoftSuper(); return RD->isDependentContext() ? NestedNameSpecifierDependence::DependentInstantiation | NestedNameSpecifierDependence::Dependent : NestedNameSpecifierDependence::None; } case Kind::Type: return toNestedNameSpecifierDependence(getAsType()->getDependence()); } llvm_unreachable("Invalid NNS Kind!"); } /// Print this nested name specifier to the given output /// stream. void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy, bool ResolveTemplateArguments, bool PrintFinalScopeResOp) const { switch (getKind()) { case Kind::Namespace: { auto [Namespace, Prefix] = getAsNamespaceAndPrefix(); Prefix.print(OS, Policy); if (const auto *NS = dyn_cast(Namespace)) { assert(!NS->isAnonymousNamespace()); OS << NS->getName(); } else { OS << cast(Namespace)->getName(); } break; } case Kind::Global: OS << "::"; return; case Kind::MicrosoftSuper: OS << "__super"; break; case Kind::Type: { PrintingPolicy InnerPolicy(Policy); InnerPolicy.SuppressTagKeyword = true; QualType(getAsType(), 0).print(OS, InnerPolicy); break; } case Kind::Null: return; } if (PrintFinalScopeResOp) OS << "::"; } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream *OS, const LangOptions *LO) const { print(OS ? *OS : llvm::errs(), LO ? *LO : LangOptions()); } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(const LangOptions &LO) const { dump(/*OS=*/nullptr, &LO); } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS) const { dump(&OS); } LLVM_DUMP_METHOD void NestedNameSpecifier::dump(llvm::raw_ostream &OS, const LangOptions &LO) const { dump(&OS, &LO); } SourceLocation NestedNameSpecifierLoc::getBeginLoc() const { if (!Qualifier) return SourceLocation(); NestedNameSpecifierLoc First = *this; while (NestedNameSpecifierLoc Prefix = First.getAsNamespaceAndPrefix().Prefix) First = Prefix; return First.getLocalSourceRange().getBegin(); } static void Append(char *Start, char *End, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity) { if (Start == End) return; if (BufferSize + (End - Start) > BufferCapacity) { // Reallocate the buffer. unsigned NewCapacity = std::max( (unsigned)(BufferCapacity ? BufferCapacity * 2 : sizeof(void *) * 2), (unsigned)(BufferSize + (End - Start))); if (!BufferCapacity) { char *NewBuffer = static_cast(llvm::safe_malloc(NewCapacity)); if (Buffer) memcpy(NewBuffer, Buffer, BufferSize); Buffer = NewBuffer; } else { Buffer = static_cast(llvm::safe_realloc(Buffer, NewCapacity)); } BufferCapacity = NewCapacity; } assert(Buffer && Start && End && End > Start && "Illegal memory buffer copy"); memcpy(Buffer + BufferSize, Start, End - Start); BufferSize += End - Start; } /// Save a source location to the given buffer. static void SaveSourceLocation(SourceLocation Loc, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity) { SourceLocation::UIntTy Raw = Loc.getRawEncoding(); Append(reinterpret_cast(&Raw), reinterpret_cast(&Raw) + sizeof(Raw), Buffer, BufferSize, BufferCapacity); } /// Save a pointer to the given buffer. static void SavePointer(void *Ptr, char *&Buffer, unsigned &BufferSize, unsigned &BufferCapacity) { Append(reinterpret_cast(&Ptr), reinterpret_cast(&Ptr) + sizeof(void *), Buffer, BufferSize, BufferCapacity); } NestedNameSpecifierLocBuilder:: NestedNameSpecifierLocBuilder(const NestedNameSpecifierLocBuilder &Other) : Representation(Other.Representation) { if (!Other.Buffer) return; if (Other.BufferCapacity == 0) { // Shallow copy is okay. Buffer = Other.Buffer; BufferSize = Other.BufferSize; return; } // Deep copy Append(Other.Buffer, Other.Buffer + Other.BufferSize, Buffer, BufferSize, BufferCapacity); } NestedNameSpecifierLocBuilder & NestedNameSpecifierLocBuilder:: operator=(const NestedNameSpecifierLocBuilder &Other) { Representation = Other.Representation; if (Buffer && Other.Buffer && BufferCapacity >= Other.BufferSize) { // Re-use our storage. BufferSize = Other.BufferSize; memcpy(Buffer, Other.Buffer, BufferSize); return *this; } // Free our storage, if we have any. if (BufferCapacity) { free(Buffer); BufferCapacity = 0; } if (!Other.Buffer) { // Empty. Buffer = nullptr; BufferSize = 0; return *this; } if (Other.BufferCapacity == 0) { // Shallow copy is okay. Buffer = Other.Buffer; BufferSize = Other.BufferSize; return *this; } // Deep copy. BufferSize = 0; Append(Other.Buffer, Other.Buffer + Other.BufferSize, Buffer, BufferSize, BufferCapacity); return *this; } void NestedNameSpecifierLocBuilder::Make(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc) { assert(!Representation); Representation = NestedNameSpecifier(TL.getTypePtr()); // Push source-location info into the buffer. SavePointer(TL.getOpaqueData(), Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::Extend(ASTContext &Context, const NamespaceBaseDecl *Namespace, SourceLocation NamespaceLoc, SourceLocation ColonColonLoc) { Representation = NestedNameSpecifier(Context, Namespace, Representation); // Push source-location info into the buffer. SaveSourceLocation(NamespaceLoc, Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::MakeGlobal(ASTContext &Context, SourceLocation ColonColonLoc) { assert(!Representation && "Already have a nested-name-specifier!?"); Representation = NestedNameSpecifier::getGlobal(); // Push source-location info into the buffer. SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::MakeMicrosoftSuper( ASTContext &Context, CXXRecordDecl *RD, SourceLocation SuperLoc, SourceLocation ColonColonLoc) { Representation = NestedNameSpecifier(RD); // Push source-location info into the buffer. SaveSourceLocation(SuperLoc, Buffer, BufferSize, BufferCapacity); SaveSourceLocation(ColonColonLoc, Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::PushTrivial(ASTContext &Context, NestedNameSpecifier Qualifier, SourceRange R) { // Construct bogus (but well-formed) source information for the // nested-name-specifier. switch (Qualifier.getKind()) { case NestedNameSpecifier::Kind::Null: return; case NestedNameSpecifier::Kind::Namespace: { auto [_1, Prefix] = Qualifier.getAsNamespaceAndPrefix(); PushTrivial(Context, Prefix, R.getBegin()); SaveSourceLocation(R.getBegin(), Buffer, BufferSize, BufferCapacity); break; } case NestedNameSpecifier::Kind::Type: { TypeSourceInfo *TSInfo = Context.getTrivialTypeSourceInfo( QualType(Qualifier.getAsType(), 0), R.getBegin()); SavePointer(TSInfo->getTypeLoc().getOpaqueData(), Buffer, BufferSize, BufferCapacity); break; } case NestedNameSpecifier::Kind::Global: case NestedNameSpecifier::Kind::MicrosoftSuper: break; } SaveSourceLocation(R.getEnd(), Buffer, BufferSize, BufferCapacity); } void NestedNameSpecifierLocBuilder::Adopt(NestedNameSpecifierLoc Other) { if (BufferCapacity) free(Buffer); if (!Other) { Representation = std::nullopt; BufferSize = 0; return; } // Rather than copying the data (which is wasteful), "adopt" the // pointer (which points into the ASTContext) but set the capacity to zero to // indicate that we don't own it. Representation = Other.getNestedNameSpecifier(); Buffer = static_cast(Other.getOpaqueData()); BufferSize = Other.getDataLength(); BufferCapacity = 0; } NestedNameSpecifierLoc NestedNameSpecifierLocBuilder::getWithLocInContext(ASTContext &Context) const { if (!Representation) return NestedNameSpecifierLoc(); // If we adopted our data pointer from elsewhere in the AST context, there's // no need to copy the memory. if (BufferCapacity == 0) return NestedNameSpecifierLoc(Representation, Buffer); // FIXME: After copying the source-location information, should we free // our (temporary) buffer and adopt the ASTContext-allocated memory? // Doing so would optimize repeated calls to getWithLocInContext(). void *Mem = Context.Allocate(BufferSize, alignof(void *)); memcpy(Mem, Buffer, BufferSize); return NestedNameSpecifierLoc(Representation, Mem); }