blob: 0832bcf66145faf83d5bb38c8d4eba593f9ac479 [file] [log] [blame]
//===--- 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 <optional>
namespace clang {
namespace tooling {
namespace stdlib {
namespace {
// Symbol name -> Symbol::ID, within a namespace.
using NSSymbolMap = llvm::DenseMap<llvm::StringRef, unsigned>;
// A Mapping per language.
struct SymbolHeaderMapping {
llvm::StringRef *HeaderNames = nullptr;
// Header name => Header::ID
llvm::DenseMap<llvm::StringRef, unsigned> *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<llvm::StringRef, NSSymbolMap *> *NamespaceSymbols = nullptr;
// Symbol::ID => Header::ID
llvm::SmallVector<unsigned> *SymbolHeaderIDs = nullptr;
};
} // namespace
static SymbolHeaderMapping
*LanguageMappings[static_cast<unsigned>(Lang::LastValue) + 1];
static const SymbolHeaderMapping *getMappingPerLang(Lang L) {
return LanguageMappings[static_cast<unsigned>(L)];
}
static int countSymbols(Lang Language) {
ArrayRef<const char *> 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<StringRef>(Symbols.begin(), Symbols.end()).size();
}
static int initialize(Lang Language) {
SymbolHeaderMapping *Mapping = new SymbolHeaderMapping();
LanguageMappings[static_cast<unsigned>(Language)] = Mapping;
unsigned SymCount = countSymbols(Language);
Mapping->SymbolCount = SymCount;
Mapping->SymbolNames =
new std::remove_reference_t<decltype(*Mapping->SymbolNames)>[SymCount];
Mapping->SymbolHeaderIDs = new std::remove_reference_t<
decltype(*Mapping->SymbolHeaderIDs)>[SymCount];
Mapping->NamespaceSymbols =
new std::remove_reference_t<decltype(*Mapping->NamespaceSymbols)>;
Mapping->HeaderIDs =
new std::remove_reference_t<decltype(*Mapping->HeaderIDs)>;
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<unsigned int>(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<decltype(Symbol::NSLen)>(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<unsigned>(Lang::LastValue); ++L)
initialize(static_cast<Lang>(L));
return 0;
}();
(void)Dummy;
}
std::vector<Header> Header::all(Lang L) {
ensureInitialized();
std::vector<Header> 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> 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> Symbol::all(Lang L) {
ensureInitialized();
std::vector<Symbol> 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> 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<Header> Symbol::header() const {
const auto& Headers = getMappingPerLang(Language)->SymbolHeaderIDs[ID];
if (Headers.empty())
return std::nullopt;
return Header(Headers.front(), Language);
}
llvm::SmallVector<Header> Symbol::headers() const {
llvm::SmallVector<Header> 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<NamespaceDecl>(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<NamespaceDecl>(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<Symbol> 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<const DeclContext *> 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<NamedDecl>(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<NamedDecl>(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