llvm-project/clang/lib/AST/ASTImporterLookupTable.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

192 lines
6.1 KiB
C++
Raw Normal View History

//===- 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 &LT;
Builder(ASTImporterLookupTable &LT) : 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());
[ASTImporter] Fix AST import crash for a friend decl Summary: Running CTU testing, we found that VisitFriendDecl in ASTImporterLookup.cpp was not handling a particular non-dependent case, so we reached the llvm_unreachable case. The FriendDecl and QualType not handled were: (gdb) p D->dump() FriendDecl 0x7ffff5cf1958 < <<srcfile>>, 'nlohmann::basic_json<std::map, std::vector, std::basic_string<char>, bool, long long, unsigned long long, double, std::allocator, adl_serializer, std::vector<unsigned char, std::allocator<unsigned char>>>':'nlohmann::basic_json<std::map, std::vector, std::basic_string<char>, bool, long long, unsigned long long, double, std::allocator, adl_serializer, std::vector<unsigned char, std::allocator<unsigned char>>>' (gdb) p Ty->dump() SubstTemplateTypeParmType 0x7ffff5cf0df0 'class nlohmann::basic_json<std::map, std::vector, class std::basic_string<char>, _Bool, long long, unsigned long long, double, std::allocator, adl_serializer, class std::vector<unsigned char, class std::allocator<unsigned char> > >' sugar |-TemplateTypeParmType 0x7ffff643ea40 'BasicJsonType' dependent depth 0 index 0 | `-TemplateTypeParm 0x7ffff643e9e8 'BasicJsonType' `-RecordType 0x1012ad20 'class nlohmann::basic_json<std::map, std::vector, class std::basic_string<char>, _Bool, long long, unsigned long long, double, std::allocator, adl_serializer, class std::vector<unsigned char, class std::allocator<unsigned char> > >' `-ClassTemplateSpecialization 0x1012ab68 'basic_json' Reviewers: martong, a.sidorin Reviewed By: martong Subscribers: kristof.beyls, rnkovacs, teemperor, cfe-commits, dkrupp Tags: #clang Differential Revision: https://reviews.llvm.org/D82882
2020-06-30 23:08:35 +08:00
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 {
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);
}
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}",
Name.getAsString(), DC->getDeclKindName())
.str();
llvm_unreachable(Message.c_str());
}
#endif
}
void ASTImporterLookupTable::add(NamedDecl *ND) {
assert(ND);
DeclContext *DC = ND->getDeclContext()->getPrimaryContext();
add(DC, ND);
DeclContext *ReDC = DC->getRedeclContext()->getPrimaryContext();
if (DC != ReDC)
add(ReDC, ND);
}
void ASTImporterLookupTable::remove(NamedDecl *ND) {
assert(ND);
DeclContext *DC = ND->getDeclContext()->getPrimaryContext();
remove(DC, ND);
DeclContext *ReDC = DC->getRedeclContext()->getPrimaryContext();
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->getPrimaryContext());
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->getPrimaryContext());
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;
StringRef Primary = DC->getPrimaryContext() ? " primary" : "";
llvm::errs() << "== DC:" << cast<Decl>(DC) << Primary << "\n";
dump(DC);
}
}
} // namespace clang