//===----------------------------------------------------------------------===//
#include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterLookupTable.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/ASTStructuralEquivalence.h"
To->setIsUsed();
}
- Optional<unsigned> ASTImporter::getFieldIndex(Decl *F) {
- assert(F && (isa<FieldDecl>(*F) || isa<IndirectFieldDecl>(*F)) &&
- "Try to get field index for non-field.");
-
- auto *Owner = dyn_cast<RecordDecl>(F->getDeclContext());
- if (!Owner)
- return None;
-
- unsigned Index = 0;
- for (const auto *D : Owner->decls()) {
- if (D == F)
- return Index;
-
- if (isa<FieldDecl>(*D) || isa<IndirectFieldDecl>(*D))
- ++Index;
- }
-
- llvm_unreachable("Field was not found in its parent context.");
-
- return None;
- }
-
// FIXME: Temporary until every import returns Expected.
template <>
LLVM_NODISCARD Error
if (ToD)
return true; // Already imported.
ToD = CreateFun(std::forward<Args>(args)...);
+ // Keep track of imported Decls.
+ Importer.MapImported(FromD, ToD);
+ Importer.AddToLookupTable(ToD);
InitializeImportedDecl(FromD, ToD);
return false; // A new Decl is created.
}
void InitializeImportedDecl(Decl *FromD, Decl *ToD) {
- Importer.MapImported(FromD, ToD);
ToD->IdentifierNamespace = FromD->IdentifierNamespace;
if (FromD->hasAttrs())
for (const Attr *FromAttr : FromD->getAttrs())
MergeWithNamespace = cast<NamespaceDecl>(DC)->getAnonymousNamespace();
} else {
SmallVector<NamedDecl *, 4> ConflictingDecls;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Namespace))
continue;
if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
unsigned IDNS = Decl::IDNS_Ordinary;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
unsigned IDNS = Decl::IDNS_Ordinary;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
// We may already have an enum of the same name; try to find and match it.
if (!DC->isFunctionOrMethod() && SearchName) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(SearchName, FoundDecls);
+ auto FoundDecls =
+ Importer.findDeclsInToCtx(DC, SearchName);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
RecordDecl *PrevDecl = nullptr;
if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(SearchName, FoundDecls);
-
+ auto FoundDecls =
+ Importer.findDeclsInToCtx(DC, SearchName);
if (!FoundDecls.empty()) {
// We're going to have to compare D against potentially conflicting Decls, so complete it.
if (D->hasExternalLexicalStorage() && !D->isCompleteDefinition())
Found = Tag->getDecl();
}
- if (D->getDescribedTemplate()) {
- if (auto *Template = dyn_cast<ClassTemplateDecl>(Found)) {
- Found = Template->getTemplatedDecl();
- } else {
- ConflictingDecls.push_back(FoundDecl);
- continue;
- }
- }
-
if (auto *FoundRecord = dyn_cast<RecordDecl>(Found)) {
// Do not emit false positive diagnostic in case of unnamed
// struct/union and in case of anonymous structs. Would be false
if (!LexicalDC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
unsigned IDNS = Decl::IDNS_Ordinary;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
FunctionTemplateDecl *FromFT = D->getDescribedFunctionTemplate();
// If this is a function template specialization, then try to find the same
- // existing specialization in the "to" context. The localUncachedLookup
- // below will not find any specialization, but would find the primary
- // template; thus, we have to skip normal lookup in case of specializations.
+ // existing specialization in the "to" context. The lookup below will not
+ // find any specialization, but would find the primary template; thus, we
+ // have to skip normal lookup in case of specializations.
// FIXME handle member function templates (TK_MemberSpecialization) similarly?
if (D->getTemplatedKind() ==
FunctionDecl::TK_FunctionTemplateSpecialization) {
else if (!LexicalDC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_OrdinaryFriend;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
- // If template was found, look at the templated function.
- if (FromFT) {
- if (auto *Template = dyn_cast<FunctionTemplateDecl>(FoundDecl))
- FoundDecl = Template->getTemplatedDecl();
- else
- continue;
- }
-
if (auto *FoundFunction = dyn_cast<FunctionDecl>(FoundDecl)) {
if (FoundFunction->hasExternalFormalLinkage() &&
D->hasExternalFormalLinkage()) {
return ToD;
// Determine whether we've already imported this field.
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (FieldDecl *FoundField = dyn_cast<FieldDecl>(FoundDecl)) {
// For anonymous fields, match up by index.
return ToD;
// Determine whether we've already imported this field.
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (unsigned I = 0, N = FoundDecls.size(); I != N; ++I) {
if (auto *FoundField = dyn_cast<IndirectFieldDecl>(FoundDecls[I])) {
// For anonymous indirect fields, match up by index.
return std::move(Err);
// Determine whether we've already imported this decl.
- // FriendDecl is not a NamedDecl so we cannot use localUncachedLookup.
+ // FriendDecl is not a NamedDecl so we cannot use lookup.
auto *RD = cast<CXXRecordDecl>(DC);
FriendDecl *ImportedFriend = RD->getFirstFriend();
return ToD;
// Determine whether we've already imported this ivar
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (ObjCIvarDecl *FoundIvar = dyn_cast<ObjCIvarDecl>(FoundDecl)) {
if (Importer.IsStructurallyEquivalent(D->getType(),
if (D->isFileVarDecl()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
unsigned IDNS = Decl::IDNS_Ordinary;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
if (ToD)
return ToD;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (auto *FoundMethod = dyn_cast<ObjCMethodDecl>(FoundDecl)) {
if (FoundMethod->isInstanceMethod() != D->isInstanceMethod())
return ToD;
ObjCProtocolDecl *MergeWithProtocol = nullptr;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_ObjCProtocol))
continue;
// Look for an existing interface with the same name.
ObjCInterfaceDecl *MergeWithIface = nullptr;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
continue;
return ToD;
// Check whether we have already imported this property.
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (auto *FoundProp = dyn_cast<ObjCPropertyDecl>(FoundDecl)) {
// Check property types.
// We may already have a template of the same name; try to find and match it.
if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary |
Decl::IDNS_TagFriend))
assert(!DC->isFunctionOrMethod() &&
"Variable templates cannot be declared at function scope");
SmallVector<NamedDecl *, 4> ConflictingDecls;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
continue;
// type, and in the same context as the function we're importing.
if (!LexicalDC->isFunctionOrMethod()) {
unsigned IDNS = Decl::IDNS_Ordinary;
- SmallVector<NamedDecl *, 2> FoundDecls;
- DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
- bool MinimalImport)
- : ToContext(ToContext), FromContext(FromContext),
+ bool MinimalImport,
+ ASTImporterLookupTable *LookupTable)
+ : LookupTable(LookupTable), ToContext(ToContext), FromContext(FromContext),
ToFileManager(ToFileManager), FromFileManager(FromFileManager),
Minimal(MinimalImport) {
- ImportedDecls[FromContext.getTranslationUnitDecl()]
- = ToContext.getTranslationUnitDecl();
+
+ ImportedDecls[FromContext.getTranslationUnitDecl()] =
+ ToContext.getTranslationUnitDecl();
}
ASTImporter::~ASTImporter() = default;
return make_error<ImportError>();
return ToT;
}
+
+Optional<unsigned> ASTImporter::getFieldIndex(Decl *F) {
+ assert(F && (isa<FieldDecl>(*F) || isa<IndirectFieldDecl>(*F)) &&
+ "Try to get field index for non-field.");
+
+ auto *Owner = dyn_cast<RecordDecl>(F->getDeclContext());
+ if (!Owner)
+ return None;
+
+ unsigned Index = 0;
+ for (const auto *D : Owner->decls()) {
+ if (D == F)
+ return Index;
+
+ if (isa<FieldDecl>(*D) || isa<IndirectFieldDecl>(*D))
+ ++Index;
+ }
+
+ llvm_unreachable("Field was not found in its parent context.");
+
+ return None;
+}
+
+ASTImporter::FoundDeclsTy
+ASTImporter::findDeclsInToCtx(DeclContext *DC, DeclarationName Name) {
+ // We search in the redecl context because of transparent contexts.
+ // E.g. a simple C language enum is a transparent context:
+ // enum E { A, B };
+ // Now if we had a global variable in the TU
+ // int A;
+ // then the enum constant 'A' and the variable 'A' violates ODR.
+ // We can diagnose this only if we search in the redecl context.
+ DeclContext *ReDC = DC->getRedeclContext();
+ if (LookupTable) {
+ ASTImporterLookupTable::LookupResult LookupResult =
+ LookupTable->lookup(ReDC, Name);
+ return FoundDeclsTy(LookupResult.begin(), LookupResult.end());
+ } else {
+ // FIXME Can we remove this kind of lookup?
+ // Or lldb really needs this C/C++ lookup?
+ FoundDeclsTy Result;
+ ReDC->localUncachedLookup(Name, Result);
+ return Result;
+ }
+}
+
+void ASTImporter::AddToLookupTable(Decl *ToD) {
+ if (LookupTable)
+ if (auto *ToND = dyn_cast<NamedDecl>(ToD))
+ LookupTable->add(ToND);
+}
+
QualType ASTImporter::Import(QualType FromT) {
if (FromT.isNull())
return {};
}
ToD = *ToDOrErr;
+ // Once the decl is connected to the existing declarations, i.e. when the
+ // redecl chain is properly set then we populate the lookup again.
+ // This way the primary context will be able to find all decls.
+ AddToLookupTable(ToD);
+
// Notify subclasses.
Imported(FromD, ToD);
#include "MatchVerifier.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclContextInternals.h"
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterLookupTable.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
Unit->enableSourceFileDiagnostics();
}
- void lazyInitImporter(ASTUnit *ToAST) {
+ void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) {
assert(ToAST);
if (!Importer) {
- Importer.reset(new ASTImporter(
- ToAST->getASTContext(), ToAST->getFileManager(),
- Unit->getASTContext(), Unit->getFileManager(), false));
+ Importer.reset(
+ new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(),
+ Unit->getASTContext(), Unit->getFileManager(),
+ false, &LookupTable));
}
assert(&ToAST->getASTContext() == &Importer->getToContext());
createVirtualFileIfNeeded(ToAST, FileName, Code);
}
- Decl *import(ASTUnit *ToAST, Decl *FromDecl) {
- lazyInitImporter(ToAST);
+ Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
+ Decl *FromDecl) {
+ lazyInitImporter(LookupTable, ToAST);
return Importer->Import(FromDecl);
- }
+ }
- QualType import(ASTUnit *ToAST, QualType FromType) {
- lazyInitImporter(ToAST);
+ QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
+ QualType FromType) {
+ lazyInitImporter(LookupTable, ToAST);
return Importer->Import(FromType);
}
};
// vector is expanding, with the list we won't have these issues.
std::list<TU> FromTUs;
- void lazyInitToAST(Language ToLang) {
+ // Initialize the lookup table if not initialized already.
+ void lazyInitLookupTable(TranslationUnitDecl *ToTU) {
+ assert(ToTU);
+ if (!LookupTablePtr)
+ LookupTablePtr = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
+ }
+
+ void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName) {
if (ToAST)
return;
ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+ // Source code must be a valid live buffer through the tests lifetime.
+ ToCode = ToSrcCode;
// Build the AST from an empty file.
- ToAST = tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc");
+ ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);
ToAST->enableSourceFileDiagnostics();
+ lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl());
}
TU *findFromTU(Decl *From) {
return &*It;
}
+protected:
+
+ std::unique_ptr<ASTImporterLookupTable> LookupTablePtr;
+
public:
// We may have several From context but only one To context.
std::unique_ptr<ASTUnit> ToAST;
FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs);
TU &FromTU = FromTUs.back();
- ToCode = ToSrcCode;
assert(!ToAST);
- ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
- ToAST->enableSourceFileDiagnostics();
+ lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
ASTContext &FromCtx = FromTU.Unit->getASTContext();
- createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code);
-
IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
assert(ImportedII && "Declaration with the given identifier "
"should be specified in test!");
DeclarationName ImportDeclName(ImportedII);
- SmallVector<NamedDecl *, 4> FoundDecls;
+ SmallVector<NamedDecl *, 1> FoundDecls;
FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
FoundDecls);
assert(FoundDecls.size() == 1);
- Decl *Imported = FromTU.import(ToAST.get(), FoundDecls.front());
+ Decl *Imported =
+ FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front());
assert(Imported);
return std::make_tuple(*FoundDecls.begin(), Imported);
// Creates the To context with the given source code and returns the TU decl.
TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang) {
ArgVector ToArgs = getArgVectorForLanguage(ToLang);
- ToCode = ToSrcCode;
assert(!ToAST);
- ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
- ToAST->enableSourceFileDiagnostics();
-
+ lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
return ToAST->getASTContext().getTranslationUnitDecl();
}
// May be called several times in a given test.
// The different instances of the param From may have different ASTContext.
Decl *Import(Decl *From, Language ToLang) {
- lazyInitToAST(ToLang);
+ lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(From);
- return FromTU->import(ToAST.get(), From);
+ assert(LookupTablePtr);
+ return FromTU->import(*LookupTablePtr, ToAST.get(), From);
}
QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) {
- lazyInitToAST(ToLang);
+ lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(TUDecl);
- return FromTU->import(ToAST.get(), FromType);
+ assert(LookupTablePtr);
+ return FromTU->import(*LookupTablePtr, ToAST.get(), FromType);
}
~ASTImporterTestBase() {
CXXMethodDecl *Method =
FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher);
ToClass->removeDecl(Method);
+ LookupTablePtr->remove(Method);
}
ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u);
EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
}
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInToContext) {
+ Decl *ToTU = getToTuDecl("struct X;", Lang_C);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) {
+ Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContextCXX) {
+ Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_CXX);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) {
+ Decl *FromTU0 = getTuDecl("struct A { struct X *Xp; };", Lang_C, "input0.cc");
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto FromProto = FirstDeclMatcher<RecordDecl>().match(FromTU0, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedProto = Import(FromProto, Lang_C);
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
struct ImportClassTemplates : ASTImporterTestBase {};
TEST_P(ImportClassTemplates,
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
}
+TEST_P(ASTImporterTestBase, FriendFunInClassTemplate) {
+ auto *Code = R"(
+ template <class T>
+ struct X {
+ friend void foo(){}
+ };
+ )";
+ TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+ auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasName("foo")));
+
+ TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc");
+ auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+ auto *ImportedFoo = Import(FromFoo, Lang_CXX);
+ EXPECT_EQ(ImportedFoo, ToFoo);
+}
+
struct DeclContextTest : ASTImporterTestBase {};
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType());
}
+struct ASTImporterLookupTableTest : ASTImporterTestBase {};
+
+TEST_P(ASTImporterLookupTableTest, OneDecl) {
+ auto *ToTU = getToTuDecl("int a;", Lang_CXX);
+ auto *D = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("a")));
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(ToTU, D->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), D);
+}
+
+static Decl *findInDeclListOfDC(DeclContext *DC, DeclarationName Name) {
+ for (Decl *D : DC->decls()) {
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ if (ND->getDeclName() == Name)
+ return ND;
+ }
+ return nullptr;
+};
+
+TEST_P(ASTImporterLookupTableTest,
+ FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) {
+ auto *Code = R"(
+ template <class T>
+ struct X {
+ friend void foo(){}
+ };
+ )";
+ TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+ auto *X = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("X")));
+ auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasName("foo")));
+ DeclContext *FooDC = Foo->getDeclContext();
+ DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+ ASSERT_EQ(cast<Decl>(FooLexicalDC), X->getTemplatedDecl());
+ ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+ DeclarationName FooName = Foo->getDeclName();
+
+ // Cannot find in the LookupTable of its DC (TUDecl)
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Cannot find in the LookupTable of its LexicalDC (X)
+ FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Can't find in the list of Decls of the DC.
+ EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+ // Can't find in the list of Decls of the LexicalDC
+ EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), nullptr);
+
+ // ASTImporter specific lookup finds it.
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(FooDC, Foo->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ FwdDeclStructShouldBeFoundByImporterSpecificLookup) {
+ TranslationUnitDecl *ToTU =
+ getToTuDecl("struct A { struct Foo *p; };", Lang_C);
+ auto *Foo =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("Foo")));
+ auto *A =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+ DeclContext *FooDC = Foo->getDeclContext();
+ DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+ ASSERT_EQ(cast<Decl>(FooLexicalDC), A);
+ ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+ DeclarationName FooName = Foo->getDeclName();
+
+ // Cannot find in the LookupTable of its DC (TUDecl).
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Cannot find in the LookupTable of its LexicalDC (A).
+ FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Can't find in the list of Decls of the DC.
+ EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+ // Can find in the list of Decls of the LexicalDC.
+ EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), Foo);
+
+ // ASTImporter specific lookup finds it.
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(FooDC, Foo->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsNamesInDifferentDC) {
+ TranslationUnitDecl *ToTU =
+ getToTuDecl("int V; struct A { int V; }; struct B { int V; };", Lang_C);
+ DeclarationName VName = FirstDeclMatcher<VarDecl>()
+ .match(ToTU, varDecl(hasName("V")))
+ ->getDeclName();
+ auto *A =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+ auto *B =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("B")));
+
+ ASTImporterLookupTable LT(*ToTU);
+
+ auto Res = LT.lookup(cast<DeclContext>(A), VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+ ToTU, fieldDecl(hasName("V"),
+ hasParent(recordDecl(hasName("A"))))));
+ Res = LT.lookup(cast<DeclContext>(B), VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+ ToTU, fieldDecl(hasName("V"),
+ hasParent(recordDecl(hasName("B"))))));
+ Res = LT.lookup(ToTU, VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<VarDecl>().match(
+ ToTU, varDecl(hasName("V"),
+ hasParent(translationUnitDecl()))));
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ void foo();
+ void foo(int);
+ void foo(int, int);
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+ auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+ DeclarationName Name = F0->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 3u);
+ EXPECT_EQ(Res.count(F0), 1u);
+ EXPECT_EQ(Res.count(F2), 1u);
+}
+
+static const RecordDecl * getRecordDeclOfFriend(FriendDecl *FD) {
+ QualType Ty = FD->getFriendType()->getType();
+ QualType NamedTy = cast<ElaboratedType>(Ty)->getNamedType();
+ return cast<RecordType>(NamedTy)->getDecl();
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { friend class F; };
+ )",
+ Lang_CXX);
+
+ // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
+ // So we must dig up the underlying CXXRecordDecl.
+ ASTImporterLookupTable LT(*ToTU);
+ auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
+ auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("Y")));
+
+ DeclarationName Name = RD->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), RD);
+
+ Res = LT.lookup(Y, Name);
+ EXPECT_EQ(Res.size(), 0u);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { template <class T> friend class F; };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, DependentFriendClass) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ class F;
+
+ template <typename T>
+ class Y {
+ friend class F<T>;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, FriendClassTemplateSpecialization) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ class F;
+
+ class Y {
+ friend class F<int>;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ ASSERT_EQ(Res.size(), 3u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+ EXPECT_EQ(Res.count(*F->spec_begin()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { friend void F(); };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F =
+ FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), F);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ LookupFindsDeclsInClassTemplateSpecialization) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ struct X {
+ int F;
+ };
+ void foo() {
+ X<char> xc;
+ }
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+
+ auto *Template = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("X")));
+ auto *FieldInTemplate = FirstDeclMatcher<FieldDecl>().match(
+ ToTU,
+ fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl())))));
+
+ auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ FieldDecl *FieldInSpec = *Spec->field_begin();
+ ASSERT_TRUE(FieldInSpec);
+
+ DeclarationName Name = FieldInSpec->getDeclName();
+ auto TemplateDC = cast<DeclContext>(Template->getTemplatedDecl());
+
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ TemplateDC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+ EXPECT_EQ(FoundDecls[0], FieldInTemplate);
+
+ auto Res = LT.lookup(TemplateDC, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FieldInTemplate);
+
+ cast<DeclContext>(Spec)->getRedeclContext()->localUncachedLookup(Name,
+ FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+ EXPECT_EQ(FoundDecls[0], FieldInSpec);
+
+ Res = LT.lookup(cast<DeclContext>(Spec), Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FieldInSpec);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { template <class T> friend void F(); };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ ToTU, functionTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, MultipleBefriendingClasses) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ struct X;
+ struct A {
+ friend struct X;
+ };
+ struct B {
+ friend struct X;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *X = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X")));
+ auto *FriendD0 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ auto *FriendD1 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ const RecordDecl *RD0 = getRecordDeclOfFriend(FriendD0);
+ const RecordDecl *RD1 = getRecordDeclOfFriend(FriendD1);
+ ASSERT_EQ(RD0, RD1);
+ ASSERT_EQ(RD1, X);
+
+ DeclarationName Name = X->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), X);
+}
+
+TEST_P(ASTImporterLookupTableTest, EnumConstantDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ enum E {
+ A,
+ B
+ };
+ )",
+ Lang_C);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *E = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(hasName("E")));
+ auto *A = FirstDeclMatcher<EnumConstantDecl>().match(
+ ToTU, enumConstantDecl(hasName("A")));
+
+ DeclarationName Name = A->getDeclName();
+ // Redecl context is the TU.
+ ASSERT_EQ(E->getRedeclContext(), ToTU);
+
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ // Normal lookup finds in the DC.
+ E->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+
+ // Normal lookup finds in the Redecl context.
+ ToTU->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+
+ // Import specific lookup finds in the DC.
+ auto Res = LT.lookup(E, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+
+ // Import specific lookup finds in the Redecl context.
+ Res = LT.lookup(ToTU, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ namespace N {
+ int A;
+ }
+ namespace N {
+ }
+ )",
+ Lang_CXX);
+ auto *N1 =
+ LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N")));
+ auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A")));
+ DeclarationName Name = A->getDeclName();
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(N1, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+}
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
::testing::Values(ArgVector()), );
ArgVector{"-fms-compatibility"},
ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"});
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
+ DefaultTestValuesForRunOptions, );
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
DefaultTestValuesForRunOptions, );
-INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
DefaultTestValuesForRunOptions, );
-INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,