//===--- StandardLibrary.cpp ------------------------------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// #include "clang/Tooling/Inclusions/StandardLibrary.h" #include "clang/AST/Decl.h" #include "clang/Basic/LangOptions.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include namespace clang { namespace tooling { namespace stdlib { namespace { // Symbol name -> Symbol::ID, within a namespace. using NSSymbolMap = llvm::DenseMap; // A Mapping per language. struct SymbolHeaderMapping { llvm::StringRef *HeaderNames = nullptr; // Header name => Header::ID llvm::DenseMap *HeaderIDs; unsigned SymbolCount = 0; // Symbol::ID => symbol qualified_name/name/scope struct SymbolName { const char *Data; // std::vector unsigned ScopeLen; // ~~~~~ unsigned NameLen; // ~~~~~~ StringRef scope() const { return StringRef(Data, ScopeLen); } StringRef name() const { return StringRef(Data + ScopeLen, NameLen); } StringRef qualifiedName() const { return StringRef(Data, ScopeLen + NameLen); } } *SymbolNames = nullptr; // Symbol name -> Symbol::ID, within a namespace. llvm::DenseMap *NamespaceSymbols = nullptr; // Symbol::ID => Header::ID llvm::SmallVector *SymbolHeaderIDs = nullptr; }; } // namespace static SymbolHeaderMapping *LanguageMappings[static_cast(Lang::LastValue) + 1]; static const SymbolHeaderMapping *getMappingPerLang(Lang L) { return LanguageMappings[static_cast(L)]; } static int countSymbols(Lang Language) { ArrayRef Symbols; #define SYMBOL(Name, NS, Header) #NS #Name, switch (Language) { case Lang::C: { static constexpr const char *CSymbols[] = { #include "CSpecialSymbolMap.inc" #include "CSymbolMap.inc" }; Symbols = CSymbols; break; } case Lang::CXX: { static constexpr const char *CXXSymbols[] = { #include "StdSpecialSymbolMap.inc" #include "StdSymbolMap.inc" #include "StdTsSymbolMap.inc" }; Symbols = CXXSymbols; break; } } #undef SYMBOL return llvm::DenseSet(Symbols.begin(), Symbols.end()).size(); } static int initialize(Lang Language) { SymbolHeaderMapping *Mapping = new SymbolHeaderMapping(); LanguageMappings[static_cast(Language)] = Mapping; unsigned SymCount = countSymbols(Language); Mapping->SymbolCount = SymCount; Mapping->SymbolNames = new std::remove_reference_tSymbolNames)>[SymCount]; Mapping->SymbolHeaderIDs = new std::remove_reference_t< decltype(*Mapping->SymbolHeaderIDs)>[SymCount]; Mapping->NamespaceSymbols = new std::remove_reference_tNamespaceSymbols)>; Mapping->HeaderIDs = new std::remove_reference_tHeaderIDs)>; auto AddNS = [&](llvm::StringRef NS) -> NSSymbolMap & { auto R = Mapping->NamespaceSymbols->try_emplace(NS, nullptr); if (R.second) R.first->second = new NSSymbolMap(); return *R.first->second; }; auto AddHeader = [&](llvm::StringRef Header) -> unsigned { return Mapping->HeaderIDs->try_emplace(Header, Mapping->HeaderIDs->size()) .first->second; }; auto Add = [&, SymIndex(-1)](llvm::StringRef QName, unsigned NSLen, llvm::StringRef HeaderName) mutable { // Correct "Nonefoo" => foo. // FIXME: get rid of "None" from the generated mapping files. if (QName.take_front(NSLen) == "None") { QName = QName.drop_front(NSLen); NSLen = 0; } if (SymIndex >= 0 && Mapping->SymbolNames[SymIndex].qualifiedName() == QName) { // Not a new symbol, use the same index. assert(llvm::none_of(llvm::ArrayRef(Mapping->SymbolNames, SymIndex), [&QName](const SymbolHeaderMapping::SymbolName &S) { return S.qualifiedName() == QName; }) && "The symbol has been added before, make sure entries in the .inc " "file are grouped by symbol name!"); } else { // First symbol or new symbol, increment next available index. ++SymIndex; } Mapping->SymbolNames[SymIndex] = { QName.data(), NSLen, static_cast(QName.size() - NSLen)}; if (!HeaderName.empty()) Mapping->SymbolHeaderIDs[SymIndex].push_back(AddHeader(HeaderName)); NSSymbolMap &NSSymbols = AddNS(QName.take_front(NSLen)); NSSymbols.try_emplace(QName.drop_front(NSLen), SymIndex); }; struct Symbol { const char *QName; unsigned NSLen; const char *HeaderName; }; #define SYMBOL(Name, NS, Header) \ {#NS #Name, static_cast(StringRef(#NS).size()), \ #Header}, switch (Language) { case Lang::C: { static constexpr Symbol CSymbols[] = { #include "CSpecialSymbolMap.inc" #include "CSymbolMap.inc" }; for (const Symbol &S : CSymbols) Add(S.QName, S.NSLen, S.HeaderName); break; } case Lang::CXX: { static constexpr Symbol CXXSymbols[] = { #include "StdSpecialSymbolMap.inc" #include "StdSymbolMap.inc" #include "StdTsSymbolMap.inc" }; for (const Symbol &S : CXXSymbols) Add(S.QName, S.NSLen, S.HeaderName); break; } } #undef SYMBOL Mapping->HeaderNames = new llvm::StringRef[Mapping->HeaderIDs->size()]; for (const auto &E : *Mapping->HeaderIDs) Mapping->HeaderNames[E.second] = E.first; return 0; } static void ensureInitialized() { static int Dummy = []() { for (unsigned L = 0; L <= static_cast(Lang::LastValue); ++L) initialize(static_cast(L)); return 0; }(); (void)Dummy; } std::vector
Header::all(Lang L) { ensureInitialized(); std::vector
Result; const auto *Mapping = getMappingPerLang(L); Result.reserve(Mapping->HeaderIDs->size()); for (unsigned I = 0, E = Mapping->HeaderIDs->size(); I < E; ++I) Result.push_back(Header(I, L)); return Result; } std::optional
Header::named(llvm::StringRef Name, Lang L) { ensureInitialized(); const auto *Mapping = getMappingPerLang(L); auto It = Mapping->HeaderIDs->find(Name); if (It == Mapping->HeaderIDs->end()) return std::nullopt; return Header(It->second, L); } llvm::StringRef Header::name() const { return getMappingPerLang(Language)->HeaderNames[ID]; } std::vector Symbol::all(Lang L) { ensureInitialized(); std::vector Result; const auto *Mapping = getMappingPerLang(L); Result.reserve(Mapping->SymbolCount); for (unsigned I = 0, E = Mapping->SymbolCount; I < E; ++I) Result.push_back(Symbol(I, L)); return Result; } llvm::StringRef Symbol::scope() const { return getMappingPerLang(Language)->SymbolNames[ID].scope(); } llvm::StringRef Symbol::name() const { return getMappingPerLang(Language)->SymbolNames[ID].name(); } llvm::StringRef Symbol::qualifiedName() const { return getMappingPerLang(Language)->SymbolNames[ID].qualifiedName(); } std::optional Symbol::named(llvm::StringRef Scope, llvm::StringRef Name, Lang L) { ensureInitialized(); if (NSSymbolMap *NSSymbols = getMappingPerLang(L)->NamespaceSymbols->lookup(Scope)) { auto It = NSSymbols->find(Name); if (It != NSSymbols->end()) return Symbol(It->second, L); } return std::nullopt; } std::optional
Symbol::header() const { const auto& Headers = getMappingPerLang(Language)->SymbolHeaderIDs[ID]; if (Headers.empty()) return std::nullopt; return Header(Headers.front(), Language); } llvm::SmallVector
Symbol::headers() const { llvm::SmallVector
Results; for (auto HeaderID : getMappingPerLang(Language)->SymbolHeaderIDs[ID]) Results.emplace_back(Header(HeaderID, Language)); return Results; } Recognizer::Recognizer() { ensureInitialized(); } NSSymbolMap *Recognizer::namespaceSymbols(const DeclContext *DC, Lang L) { if (DC->isTranslationUnit()) // global scope. return getMappingPerLang(L)->NamespaceSymbols->lookup(""); auto It = NamespaceCache.find(DC); if (It != NamespaceCache.end()) return It->second; const NamespaceDecl *D = llvm::cast(DC); NSSymbolMap *Result = [&]() -> NSSymbolMap * { if (D->isAnonymousNamespace()) return nullptr; // Print the namespace and its parents ommitting inline scopes. std::string Scope; for (const auto *ND = D; ND; ND = llvm::dyn_cast_or_null(ND->getParent())) if (!ND->isInlineNamespace() && !ND->isAnonymousNamespace()) Scope = ND->getName().str() + "::" + Scope; return getMappingPerLang(L)->NamespaceSymbols->lookup(Scope); }(); NamespaceCache.try_emplace(D, Result); return Result; } std::optional Recognizer::operator()(const Decl *D) { Lang L; if (D->getLangOpts().CPlusPlus) L = Lang::CXX; else if (D->getLangOpts().C99) L = Lang::C; else return std::nullopt; // not a supported language. // If D is std::vector::iterator, `vector` is the outer symbol to look up. // We keep all the candidate DCs as some may turn out to be anon enums. // Do this resolution lazily as we may turn out not to have a std namespace. llvm::SmallVector IntermediateDecl; const DeclContext *DC = D->getDeclContext(); if (!DC) // The passed D is a TranslationUnitDecl! return std::nullopt; while (!DC->isNamespace() && !DC->isTranslationUnit()) { if (NamedDecl::classofKind(DC->getDeclKind())) IntermediateDecl.push_back(DC); DC = DC->getParent(); } NSSymbolMap *Symbols = namespaceSymbols(DC, L); if (!Symbols) return std::nullopt; llvm::StringRef Name = [&]() -> llvm::StringRef { for (const auto *SymDC : llvm::reverse(IntermediateDecl)) { DeclarationName N = cast(SymDC)->getDeclName(); if (const auto *II = N.getAsIdentifierInfo()) return II->getName(); if (!N.isEmpty()) return ""; // e.g. operator<: give up } if (const auto *ND = llvm::dyn_cast(D)) if (const auto *II = ND->getIdentifier()) return II->getName(); return ""; }(); if (Name.empty()) return std::nullopt; auto It = Symbols->find(Name); if (It == Symbols->end()) return std::nullopt; return Symbol(It->second, L); } } // namespace stdlib } // namespace tooling } // namespace clang