mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 06:46:05 +00:00

`ASTImporterLookupTable` did use the `getPrimaryContext` function to get the declaration context of the inserted items. This is problematic because the primary context can change during import of AST items, most likely if a definition of a previously not defined class is imported. (For any record the primary context is the definition if there is one.) The use of primary context is really not important, only for namespaces because these can be re-opened and lookup in one namespace block is not enough. This special search is now moved into ASTImporter instead of relying on the lookup table.
206 lines
6.7 KiB
C++
206 lines
6.7 KiB
C++
//===- ASTImporterLookupTable.cpp - ASTImporter specific lookup -----------===//
|
|
//
|
|
// 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 ASTImporterLookupTable class which implements a
|
|
// lookup procedure for the import mechanism.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/ASTImporterLookupTable.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
namespace clang {
|
|
|
|
namespace {
|
|
|
|
struct Builder : RecursiveASTVisitor<Builder> {
|
|
ASTImporterLookupTable <
|
|
Builder(ASTImporterLookupTable <) : LT(LT) {}
|
|
|
|
bool VisitTypedefNameDecl(TypedefNameDecl *D) {
|
|
QualType Ty = D->getUnderlyingType();
|
|
Ty = Ty.getCanonicalType();
|
|
if (const auto *RTy = dyn_cast<RecordType>(Ty)) {
|
|
LT.add(RTy->getAsRecordDecl());
|
|
// iterate over the field decls, adding them
|
|
for (auto *it : RTy->getAsRecordDecl()->fields()) {
|
|
LT.add(it);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitNamedDecl(NamedDecl *D) {
|
|
LT.add(D);
|
|
return true;
|
|
}
|
|
// In most cases the FriendDecl contains the declaration of the befriended
|
|
// class as a child node, so it is discovered during the recursive
|
|
// visitation. However, there are cases when the befriended class is not a
|
|
// child, thus it must be fetched explicitly from the FriendDecl, and only
|
|
// then can we add it to the lookup table.
|
|
bool VisitFriendDecl(FriendDecl *D) {
|
|
if (D->getFriendType()) {
|
|
QualType Ty = D->getFriendType()->getType();
|
|
if (isa<ElaboratedType>(Ty))
|
|
Ty = cast<ElaboratedType>(Ty)->getNamedType();
|
|
// A FriendDecl with a dependent type (e.g. ClassTemplateSpecialization)
|
|
// always has that decl as child node.
|
|
// However, there are non-dependent cases which does not have the
|
|
// type as a child node. We have to dig up that type now.
|
|
if (!Ty->isDependentType()) {
|
|
if (const auto *RTy = dyn_cast<RecordType>(Ty))
|
|
LT.add(RTy->getAsCXXRecordDecl());
|
|
else if (const auto *SpecTy = dyn_cast<TemplateSpecializationType>(Ty))
|
|
LT.add(SpecTy->getAsCXXRecordDecl());
|
|
else if (const auto *SubstTy =
|
|
dyn_cast<SubstTemplateTypeParmType>(Ty)) {
|
|
if (SubstTy->getAsCXXRecordDecl())
|
|
LT.add(SubstTy->getAsCXXRecordDecl());
|
|
} else if (isa<TypedefType>(Ty)) {
|
|
// We do not put friend typedefs to the lookup table because
|
|
// ASTImporter does not organize typedefs into redecl chains.
|
|
} else if (isa<UsingType>(Ty)) {
|
|
// Similar to TypedefType, not putting into lookup table.
|
|
} else {
|
|
llvm_unreachable("Unhandled type of friend class");
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Override default settings of base.
|
|
bool shouldVisitTemplateInstantiations() const { return true; }
|
|
bool shouldVisitImplicitCode() const { return true; }
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
ASTImporterLookupTable::ASTImporterLookupTable(TranslationUnitDecl &TU) {
|
|
Builder B(*this);
|
|
B.TraverseDecl(&TU);
|
|
// The VaList declaration may be created on demand only or not traversed.
|
|
// To ensure it is present and found during import, add it to the table now.
|
|
if (auto *D =
|
|
dyn_cast_or_null<NamedDecl>(TU.getASTContext().getVaListTagDecl())) {
|
|
// On some platforms (AArch64) the VaList declaration can be inside a 'std'
|
|
// namespace. This is handled specially and not visible by AST traversal.
|
|
// ASTImporter must be able to find this namespace to import the VaList
|
|
// declaration (and the namespace) correctly.
|
|
if (auto *Ns = dyn_cast<NamespaceDecl>(D->getDeclContext()))
|
|
add(&TU, Ns);
|
|
add(D->getDeclContext(), D);
|
|
}
|
|
}
|
|
|
|
void ASTImporterLookupTable::add(DeclContext *DC, NamedDecl *ND) {
|
|
DeclList &Decls = LookupTable[DC][ND->getDeclName()];
|
|
// Inserts if and only if there is no element in the container equal to it.
|
|
Decls.insert(ND);
|
|
}
|
|
|
|
void ASTImporterLookupTable::remove(DeclContext *DC, NamedDecl *ND) {
|
|
const DeclarationName Name = ND->getDeclName();
|
|
DeclList &Decls = LookupTable[DC][Name];
|
|
bool EraseResult = Decls.remove(ND);
|
|
(void)EraseResult;
|
|
#ifndef NDEBUG
|
|
if (!EraseResult) {
|
|
std::string Message =
|
|
llvm::formatv(
|
|
"Trying to remove not contained Decl '{0}' of type {1} from a {2}",
|
|
Name.getAsString(), ND->getDeclKindName(), DC->getDeclKindName())
|
|
.str();
|
|
llvm_unreachable(Message.c_str());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ASTImporterLookupTable::add(NamedDecl *ND) {
|
|
assert(ND);
|
|
DeclContext *DC = ND->getDeclContext();
|
|
add(DC, ND);
|
|
DeclContext *ReDC = DC->getRedeclContext();
|
|
if (DC != ReDC)
|
|
add(ReDC, ND);
|
|
}
|
|
|
|
void ASTImporterLookupTable::remove(NamedDecl *ND) {
|
|
assert(ND);
|
|
DeclContext *DC = ND->getDeclContext();
|
|
remove(DC, ND);
|
|
DeclContext *ReDC = DC->getRedeclContext();
|
|
if (DC != ReDC)
|
|
remove(ReDC, ND);
|
|
}
|
|
|
|
void ASTImporterLookupTable::update(NamedDecl *ND, DeclContext *OldDC) {
|
|
assert(OldDC != ND->getDeclContext() &&
|
|
"DeclContext should be changed before update");
|
|
if (contains(ND->getDeclContext(), ND)) {
|
|
assert(!contains(OldDC, ND) &&
|
|
"Decl should not be found in the old context if already in the new");
|
|
return;
|
|
}
|
|
|
|
remove(OldDC, ND);
|
|
add(ND);
|
|
}
|
|
|
|
void ASTImporterLookupTable::updateForced(NamedDecl *ND, DeclContext *OldDC) {
|
|
LookupTable[OldDC][ND->getDeclName()].remove(ND);
|
|
add(ND);
|
|
}
|
|
|
|
ASTImporterLookupTable::LookupResult
|
|
ASTImporterLookupTable::lookup(DeclContext *DC, DeclarationName Name) const {
|
|
auto DCI = LookupTable.find(DC);
|
|
if (DCI == LookupTable.end())
|
|
return {};
|
|
|
|
const auto &FoundNameMap = DCI->second;
|
|
auto NamesI = FoundNameMap.find(Name);
|
|
if (NamesI == FoundNameMap.end())
|
|
return {};
|
|
|
|
return NamesI->second;
|
|
}
|
|
|
|
bool ASTImporterLookupTable::contains(DeclContext *DC, NamedDecl *ND) const {
|
|
return lookup(DC, ND->getDeclName()).contains(ND);
|
|
}
|
|
|
|
void ASTImporterLookupTable::dump(DeclContext *DC) const {
|
|
auto DCI = LookupTable.find(DC);
|
|
if (DCI == LookupTable.end())
|
|
llvm::errs() << "empty\n";
|
|
const auto &FoundNameMap = DCI->second;
|
|
for (const auto &Entry : FoundNameMap) {
|
|
DeclarationName Name = Entry.first;
|
|
llvm::errs() << "==== Name: ";
|
|
Name.dump();
|
|
const DeclList& List = Entry.second;
|
|
for (NamedDecl *ND : List) {
|
|
ND->dump();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ASTImporterLookupTable::dump() const {
|
|
for (const auto &Entry : LookupTable) {
|
|
DeclContext *DC = Entry.first;
|
|
llvm::errs() << "== DC:" << cast<Decl>(DC) << "\n";
|
|
dump(DC);
|
|
}
|
|
}
|
|
|
|
} // namespace clang
|