[ASTImporter] Fix redecl chain of classes and class templates

Summary:
The crux of the issue that is being fixed is that lookup could not find
previous decls of a friend class. The solution involves making the
friend declarations visible in their decl context (i.e. adding them to
the lookup table).
Also, we simplify `VisitRecordDecl` greatly.

This fix involves two other repairs (without these the unittests fail):
(1) We could not handle the addition of injected class types properly
when a redecl chain was involved, now this is fixed.
(2) DeclContext::removeDecl failed if the lookup table in Vector form
did not contain the to be removed element. This caused troubles in
ASTImporter::ImportDeclContext. This is also fixed.

Reviewers: a_sidorin, balazske, a.sidorin

Subscribers: rnkovacs, dkrupp, Szelethus, cfe-commits

Differential Revision: https://reviews.llvm.org/D53655

llvm-svn: 349349
This commit is contained in:
Gabor Marton
2018-12-17 12:42:12 +00:00
parent e913b956aa
commit 7df342a4d1
8 changed files with 898 additions and 260 deletions

View File

@@ -300,6 +300,16 @@ Example matches f
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('indirectFieldDecl0')"><a name="indirectFieldDecl0Anchor">indirectFieldDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1IndirectFieldDecl.html">IndirectFieldDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="indirectFieldDecl0"><pre>Matches indirect field declarations.
Given
struct X { struct { int a; }; };
indirectFieldDecl()
matches 'a'.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('labelDecl0')"><a name="labelDecl0Anchor">labelDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1LabelDecl.html">LabelDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="labelDecl0"><pre>Matches a declaration of label.

View File

@@ -1158,6 +1158,17 @@ extern const internal::VariadicDynCastAllOfMatcher<Decl, VarDecl> varDecl;
/// matches 'm'.
extern const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> fieldDecl;
/// Matches indirect field declarations.
///
/// Given
/// \code
/// struct X { struct { int a; }; };
/// \endcode
/// indirectFieldDecl()
/// matches 'a'.
extern const internal::VariadicDynCastAllOfMatcher<Decl, IndirectFieldDecl>
indirectFieldDecl;
/// Matches function declarations.
///
/// Example matches f

View File

@@ -122,6 +122,8 @@ namespace clang {
return getCanonicalForwardRedeclChain<FunctionDecl>(FD);
if (auto *VD = dyn_cast<VarDecl>(D))
return getCanonicalForwardRedeclChain<VarDecl>(VD);
if (auto *TD = dyn_cast<TagDecl>(D))
return getCanonicalForwardRedeclChain<TagDecl>(TD);
llvm_unreachable("Bad declaration kind");
}
@@ -2607,10 +2609,9 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
return std::move(Err);
IDNS = Decl::IDNS_Ordinary;
} else if (Importer.getToContext().getLangOpts().CPlusPlus)
IDNS |= Decl::IDNS_Ordinary;
IDNS |= Decl::IDNS_Ordinary | Decl::IDNS_TagFriend;
// We may already have a record of the same name; try to find and match it.
RecordDecl *AdoptDecl = nullptr;
RecordDecl *PrevDecl = nullptr;
if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
@@ -2643,26 +2644,22 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
}
if (auto *FoundRecord = dyn_cast<RecordDecl>(Found)) {
if (!SearchName) {
// Do not emit false positive diagnostic in case of unnamed
// struct/union and in case of anonymous structs. Would be false
// because there may be several anonymous/unnamed structs in a class.
// E.g. these are both valid:
// struct A { // unnamed structs
// struct { struct A *next; } entry0;
// struct { struct A *next; } entry1;
// };
// struct X { struct { int a; }; struct { int b; }; }; // anon structs
if (!SearchName)
if (!IsStructuralMatch(D, FoundRecord, false))
continue;
} else {
if (!IsStructuralMatch(D, FoundRecord)) {
ConflictingDecls.push_back(FoundDecl);
continue;
}
}
PrevDecl = FoundRecord;
if (RecordDecl *FoundDef = FoundRecord->getDefinition()) {
if ((SearchName && !D->isCompleteDefinition() && !IsFriendTemplate)
|| (D->isCompleteDefinition() &&
D->isAnonymousStructOrUnion()
== FoundDef->isAnonymousStructOrUnion())) {
// The record types structurally match, or the "from" translation
// unit only had a forward declaration anyway; call it the same
// function.
if (IsStructuralMatch(D, FoundRecord)) {
RecordDecl *FoundDef = FoundRecord->getDefinition();
if (D->isThisDeclarationADefinition() && FoundDef) {
// FIXME: Structural equivalence check should check for same
// user-defined methods.
Importer.MapImported(D, FoundDef);
@@ -2670,46 +2667,20 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
auto *FoundCXX = dyn_cast<CXXRecordDecl>(FoundDef);
assert(FoundCXX && "Record type mismatch");
if (D->isCompleteDefinition() && !Importer.isMinimalImport())
if (!Importer.isMinimalImport())
// FoundDef may not have every implicit method that D has
// because implicit methods are created only if they are used.
if (Error Err = ImportImplicitMethods(DCXX, FoundCXX))
return std::move(Err);
}
return FoundDef;
}
if (IsFriendTemplate)
continue;
} else if (!D->isCompleteDefinition()) {
// We have a forward declaration of this type, so adopt that forward
// declaration rather than building a new one.
// If one or both can be completed from external storage then try one
// last time to complete and compare them before doing this.
if (FoundRecord->hasExternalLexicalStorage() &&
!FoundRecord->isCompleteDefinition())
FoundRecord->getASTContext().getExternalSource()->CompleteType(FoundRecord);
if (D->hasExternalLexicalStorage())
D->getASTContext().getExternalSource()->CompleteType(D);
if (FoundRecord->isCompleteDefinition() &&
D->isCompleteDefinition() &&
!IsStructuralMatch(D, FoundRecord)) {
ConflictingDecls.push_back(FoundDecl);
continue;
}
AdoptDecl = FoundRecord;
continue;
PrevDecl = FoundRecord->getMostRecentDecl();
break;
}
continue;
} else if (isa<ValueDecl>(Found))
continue;
}
ConflictingDecls.push_back(FoundDecl);
}
} // for
if (!ConflictingDecls.empty() && SearchName) {
Name = Importer.HandleNameConflict(Name, DC, IDNS,
@@ -2725,79 +2696,90 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
return BeginLocOrErr.takeError();
// Create the record declaration.
RecordDecl *D2 = AdoptDecl;
if (!D2) {
CXXRecordDecl *D2CXX = nullptr;
if (auto *DCXX = dyn_cast<CXXRecordDecl>(D)) {
if (DCXX->isLambda()) {
auto TInfoOrErr = import(DCXX->getLambdaTypeInfo());
if (!TInfoOrErr)
return TInfoOrErr.takeError();
if (GetImportedOrCreateSpecialDecl(
D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(),
DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(),
DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
return D2CXX;
ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
if (!CDeclOrErr)
return CDeclOrErr.takeError();
D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr);
} else if (DCXX->isInjectedClassName()) {
// We have to be careful to do a similar dance to the one in
// Sema::ActOnStartCXXMemberDeclarations
CXXRecordDecl *const PrevDecl = nullptr;
const bool DelayTypeCreation = true;
if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
D->getTagKind(), DC, *BeginLocOrErr, Loc,
Name.getAsIdentifierInfo(), PrevDecl,
DelayTypeCreation))
return D2CXX;
Importer.getToContext().getTypeDeclType(
D2CXX, dyn_cast<CXXRecordDecl>(DC));
} else {
if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
D->getTagKind(), DC, *BeginLocOrErr, Loc,
Name.getAsIdentifierInfo(),
cast_or_null<CXXRecordDecl>(PrevDecl)))
return D2CXX;
}
RecordDecl *D2 = nullptr;
CXXRecordDecl *D2CXX = nullptr;
if (auto *DCXX = dyn_cast<CXXRecordDecl>(D)) {
if (DCXX->isLambda()) {
auto TInfoOrErr = import(DCXX->getLambdaTypeInfo());
if (!TInfoOrErr)
return TInfoOrErr.takeError();
if (GetImportedOrCreateSpecialDecl(
D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(),
DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(),
DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
return D2CXX;
ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
if (!CDeclOrErr)
return CDeclOrErr.takeError();
D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr);
} else if (DCXX->isInjectedClassName()) {
// We have to be careful to do a similar dance to the one in
// Sema::ActOnStartCXXMemberDeclarations
const bool DelayTypeCreation = true;
if (GetImportedOrCreateDecl(
D2CXX, D, Importer.getToContext(), D->getTagKind(), DC,
*BeginLocOrErr, Loc, Name.getAsIdentifierInfo(),
cast_or_null<CXXRecordDecl>(PrevDecl), DelayTypeCreation))
return D2CXX;
Importer.getToContext().getTypeDeclType(
D2CXX, dyn_cast<CXXRecordDecl>(DC));
} else {
if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
D->getTagKind(), DC, *BeginLocOrErr, Loc,
Name.getAsIdentifierInfo(),
cast_or_null<CXXRecordDecl>(PrevDecl)))
return D2CXX;
}
D2 = D2CXX;
D2->setAccess(D->getAccess());
D2->setLexicalDeclContext(LexicalDC);
if (!DCXX->getDescribedClassTemplate() || DCXX->isImplicit())
LexicalDC->addDeclInternal(D2);
D2 = D2CXX;
D2->setAccess(D->getAccess());
D2->setLexicalDeclContext(LexicalDC);
if (!DCXX->getDescribedClassTemplate() || DCXX->isImplicit())
LexicalDC->addDeclInternal(D2);
if (ClassTemplateDecl *FromDescribed =
DCXX->getDescribedClassTemplate()) {
ClassTemplateDecl *ToDescribed;
if (Error Err = importInto(ToDescribed, FromDescribed))
return std::move(Err);
D2CXX->setDescribedClassTemplate(ToDescribed);
if (!DCXX->isInjectedClassName() && !IsFriendTemplate) {
// In a record describing a template the type should be an
// InjectedClassNameType (see Sema::CheckClassTemplate). Update the
// previously set type to the correct value here (ToDescribed is not
// available at record create).
// FIXME: The previous type is cleared but not removed from
// ASTContext's internal storage.
CXXRecordDecl *Injected = nullptr;
for (NamedDecl *Found : D2CXX->noload_lookup(Name)) {
auto *Record = dyn_cast<CXXRecordDecl>(Found);
if (Record && Record->isInjectedClassName()) {
Injected = Record;
break;
}
}
D2CXX->setTypeForDecl(nullptr);
Importer.getToContext().getInjectedClassNameType(D2CXX,
ToDescribed->getInjectedClassNameSpecialization());
if (Injected) {
Injected->setTypeForDecl(nullptr);
Importer.getToContext().getTypeDeclType(Injected, D2CXX);
if (LexicalDC != DC && D->isInIdentifierNamespace(Decl::IDNS_TagFriend))
DC->makeDeclVisibleInContext(D2);
if (ClassTemplateDecl *FromDescribed =
DCXX->getDescribedClassTemplate()) {
ClassTemplateDecl *ToDescribed;
if (Error Err = importInto(ToDescribed, FromDescribed))
return std::move(Err);
D2CXX->setDescribedClassTemplate(ToDescribed);
if (!DCXX->isInjectedClassName() && !IsFriendTemplate) {
// In a record describing a template the type should be an
// InjectedClassNameType (see Sema::CheckClassTemplate). Update the
// previously set type to the correct value here (ToDescribed is not
// available at record create).
// FIXME: The previous type is cleared but not removed from
// ASTContext's internal storage.
CXXRecordDecl *Injected = nullptr;
for (NamedDecl *Found : D2CXX->noload_lookup(Name)) {
auto *Record = dyn_cast<CXXRecordDecl>(Found);
if (Record && Record->isInjectedClassName()) {
Injected = Record;
break;
}
}
} else if (MemberSpecializationInfo *MemberInfo =
// Create an injected type for the whole redecl chain.
SmallVector<Decl *, 2> Redecls =
getCanonicalForwardRedeclChain(D2CXX);
for (auto *R : Redecls) {
auto *RI = cast<CXXRecordDecl>(R);
RI->setTypeForDecl(nullptr);
// Below we create a new injected type and assign that to the
// canonical decl, subsequent declarations in the chain will reuse
// that type.
Importer.getToContext().getInjectedClassNameType(
RI, ToDescribed->getInjectedClassNameSpecialization());
}
// Set the new type for the previous injected decl too.
if (Injected) {
Injected->setTypeForDecl(nullptr);
Importer.getToContext().getTypeDeclType(Injected, D2CXX);
}
}
} else if (MemberSpecializationInfo *MemberInfo =
DCXX->getMemberSpecializationInfo()) {
TemplateSpecializationKind SK =
MemberInfo->getTemplateSpecializationKind();
@@ -2814,27 +2796,24 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
*POIOrErr);
else
return POIOrErr.takeError();
}
} else {
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(),
D->getTagKind(), DC, *BeginLocOrErr, Loc,
Name.getAsIdentifierInfo(), PrevDecl))
return D2;
D2->setLexicalDeclContext(LexicalDC);
LexicalDC->addDeclInternal(D2);
}
if (auto QualifierLocOrErr = import(D->getQualifierLoc()))
D2->setQualifierInfo(*QualifierLocOrErr);
else
return QualifierLocOrErr.takeError();
if (D->isAnonymousStructOrUnion())
D2->setAnonymousStructOrUnion(true);
} else {
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(),
D->getTagKind(), DC, *BeginLocOrErr, Loc,
Name.getAsIdentifierInfo(), PrevDecl))
return D2;
D2->setLexicalDeclContext(LexicalDC);
LexicalDC->addDeclInternal(D2);
}
Importer.MapImported(D, D2);
if (auto QualifierLocOrErr = import(D->getQualifierLoc()))
D2->setQualifierInfo(*QualifierLocOrErr);
else
return QualifierLocOrErr.takeError();
if (D->isAnonymousStructOrUnion())
D2->setAnonymousStructOrUnion(true);
if (D->isCompleteDefinition())
if (Error Err = ImportDefinition(D, D2, IDK_Default))
@@ -4990,14 +4969,12 @@ static ClassTemplateDecl *getDefinition(ClassTemplateDecl *D) {
ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
bool IsFriend = D->getFriendObjectKind() != Decl::FOK_None;
// If this record has a definition in the translation unit we're coming from,
// but this particular declaration is not that definition, import the
// If this template has a definition in the translation unit we're coming
// from, but this particular declaration is not that definition, import the
// definition and map to that.
auto *Definition =
cast_or_null<CXXRecordDecl>(D->getTemplatedDecl()->getDefinition());
if (Definition && Definition != D->getTemplatedDecl() && !IsFriend) {
if (ExpectedDecl ImportedDefOrErr = import(
Definition->getDescribedClassTemplate()))
ClassTemplateDecl *Definition = getDefinition(D);
if (Definition && Definition != D && !IsFriend) {
if (ExpectedDecl ImportedDefOrErr = import(Definition))
return Importer.MapImported(D, *ImportedDefOrErr);
else
return ImportedDefOrErr.takeError();
@@ -5013,38 +4990,29 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
if (ToD)
return ToD;
ClassTemplateDecl *FoundByLookup = nullptr;
// 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);
for (auto *FoundDecl : FoundDecls) {
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary |
Decl::IDNS_TagFriend))
continue;
Decl *Found = FoundDecl;
if (auto *FoundTemplate = dyn_cast<ClassTemplateDecl>(Found)) {
// The class to be imported is a definition.
if (D->isThisDeclarationADefinition()) {
// Lookup will find the fwd decl only if that is more recent than the
// definition. So, try to get the definition if that is available in
// the redecl chain.
ClassTemplateDecl *TemplateWithDef = getDefinition(FoundTemplate);
if (TemplateWithDef)
FoundTemplate = TemplateWithDef;
else
continue;
}
auto *FoundTemplate = dyn_cast<ClassTemplateDecl>(Found);
if (FoundTemplate) {
if (IsStructuralMatch(D, FoundTemplate)) {
if (!IsFriend) {
Importer.MapImported(D->getTemplatedDecl(),
FoundTemplate->getTemplatedDecl());
return Importer.MapImported(D, FoundTemplate);
ClassTemplateDecl *TemplateWithDef = getDefinition(FoundTemplate);
if (D->isThisDeclarationADefinition() && TemplateWithDef) {
return Importer.MapImported(D, TemplateWithDef);
}
continue;
FoundByLookup = FoundTemplate;
break;
}
}
@@ -5081,18 +5049,39 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
ToTemplated->setDescribedClassTemplate(D2);
if (ToTemplated->getPreviousDecl()) {
assert(
ToTemplated->getPreviousDecl()->getDescribedClassTemplate() &&
"Missing described template");
D2->setPreviousDecl(
ToTemplated->getPreviousDecl()->getDescribedClassTemplate());
}
D2->setAccess(D->getAccess());
D2->setLexicalDeclContext(LexicalDC);
if (!IsFriend)
if (D->getDeclContext()->containsDeclAndLoad(D))
DC->addDeclInternal(D2);
if (DC != LexicalDC && D->getLexicalDeclContext()->containsDeclAndLoad(D))
LexicalDC->addDeclInternal(D2);
if (FoundByLookup) {
auto *Recent =
const_cast<ClassTemplateDecl *>(FoundByLookup->getMostRecentDecl());
// It is possible that during the import of the class template definition
// we start the import of a fwd friend decl of the very same class template
// and we add the fwd friend decl to the lookup table. But the ToTemplated
// had been created earlier and by that time the lookup could not find
// anything existing, so it has no previous decl. Later, (still during the
// import of the fwd friend decl) we start to import the definition again
// and this time the lookup finds the previous fwd friend class template.
// In this case we must set up the previous decl for the templated decl.
if (!ToTemplated->getPreviousDecl()) {
CXXRecordDecl *PrevTemplated =
FoundByLookup->getTemplatedDecl()->getMostRecentDecl();
if (ToTemplated != PrevTemplated)
ToTemplated->setPreviousDecl(PrevTemplated);
}
D2->setPreviousDecl(Recent);
}
if (LexicalDC != DC && IsFriend)
DC->makeDeclVisibleInContext(D2);
if (FromTemplated->isCompleteDefinition() &&
!ToTemplated->isCompleteDefinition()) {
// FIXME: Import definition!

View File

@@ -1463,7 +1463,9 @@ void DeclContext::removeDecl(Decl *D) {
if (Map) {
StoredDeclsMap::iterator Pos = Map->find(ND->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
if (Pos->second.getAsVector() || Pos->second.getAsDecl() == ND)
// Remove the decl only if it is contained.
StoredDeclsList::DeclsTy *Vec = Pos->second.getAsVector();
if ((Vec && is_contained(*Vec, ND)) || Pos->second.getAsDecl() == ND)
Pos->second.remove(ND);
}
} while (DC->isTransparentContext() && (DC = DC->getParent()));

View File

@@ -606,6 +606,8 @@ const internal::VariadicDynCastAllOfMatcher<Decl, CXXConversionDecl>
cxxConversionDecl;
const internal::VariadicDynCastAllOfMatcher<Decl, VarDecl> varDecl;
const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> fieldDecl;
const internal::VariadicDynCastAllOfMatcher<Decl, IndirectFieldDecl>
indirectFieldDecl;
const internal::VariadicDynCastAllOfMatcher<Decl, FunctionDecl> functionDecl;
const internal::VariadicDynCastAllOfMatcher<Decl, FunctionTemplateDecl>
functionTemplateDecl;

View File

@@ -212,6 +212,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(expr);
REGISTER_MATCHER(exprWithCleanups);
REGISTER_MATCHER(fieldDecl);
REGISTER_MATCHER(indirectFieldDecl);
REGISTER_MATCHER(floatLiteral);
REGISTER_MATCHER(forEach);
REGISTER_MATCHER(forEachArgumentWithParam);

View File

@@ -11,9 +11,10 @@
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTImporter.h"
#include "MatchVerifier.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTImporter.h"
#include "clang/AST/DeclContextInternals.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
@@ -1808,6 +1809,65 @@ TEST_P(ASTImporterTestBase, ObjectsWithUnnamedStructType) {
EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
}
TEST_P(ASTImporterTestBase, AnonymousRecords) {
auto *Code =
R"(
struct X {
struct { int a; };
struct { int b; };
};
)";
Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c");
Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c");
auto *X0 =
FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
auto *X1 =
FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
Import(X0, Lang_C);
Import(X1, Lang_C);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// We expect no (ODR) warning during the import.
EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
EXPECT_EQ(1u,
DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
}
TEST_P(ASTImporterTestBase, AnonymousRecordsReversed) {
Decl *FromTU0 = getTuDecl(
R"(
struct X {
struct { int a; };
struct { int b; };
};
)",
Lang_C, "input0.c");
Decl *FromTU1 = getTuDecl(
R"(
struct X { // reversed order
struct { int b; };
struct { int a; };
};
)",
Lang_C, "input1.c");
auto *X0 =
FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
auto *X1 =
FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
Import(X0, Lang_C);
Import(X1, Lang_C);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// We expect one (ODR) warning during the import.
EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
EXPECT_EQ(2u,
DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
}
TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) {
auto Pattern = varDecl(hasName("x"));
VarDecl *Imported1;
@@ -2930,93 +2990,6 @@ TEST_P(ASTImporterTestBase, ImportUnnamedFieldsInCorrectOrder) {
EXPECT_EQ(FromIndex, 3u);
}
TEST_P(
ASTImporterTestBase,
ImportOfFriendRecordDoesNotMergeDefinition) {
Decl *FromTU = getTuDecl(
R"(
class A {
template <int I> class F {};
class X {
template <int I> friend class F;
};
};
)",
Lang_CXX, "input0.cc");
auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("F")));
ASSERT_TRUE(FromClass);
ASSERT_TRUE(FromFriendClass);
ASSERT_NE(FromClass, FromFriendClass);
ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
ASSERT_EQ(
FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
FromClass->getDescribedClassTemplate());
auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
EXPECT_TRUE(ToClass);
EXPECT_TRUE(ToFriendClass);
EXPECT_NE(ToClass, ToFriendClass);
EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
EXPECT_EQ(
ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
ToClass->getDescribedClassTemplate());
}
TEST_P(
ASTImporterTestBase,
ImportOfRecursiveFriendClass) {
Decl *FromTu = getTuDecl(
R"(
class declToImport {
friend class declToImport;
};
)",
Lang_CXX, "input.cc");
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTu, cxxRecordDecl(hasName("declToImport")));
auto *ToD = Import(FromD, Lang_CXX);
auto Pattern = cxxRecordDecl(hasName("declToImport"), has(friendDecl()));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
}
TEST_P(
ASTImporterTestBase,
ImportOfRecursiveFriendClassTemplate) {
Decl *FromTu = getTuDecl(
R"(
template <class A> class declToImport {
template <class A1> friend class declToImport;
};
)",
Lang_CXX, "input.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTu, classTemplateDecl(hasName("declToImport")));
auto *ToD = Import(FromD, Lang_CXX);
auto Pattern = classTemplateDecl(
has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
auto *Class =
FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
EXPECT_NE(Friend->getFriendDecl(), Class);
EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
}
TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) {
std::string ClassTemplate =
R"(
@@ -3381,6 +3354,542 @@ TEST_P(ImportVariables, InitAndDefinitionAreInTheFromContext) {
EXPECT_TRUE(ImportedD->getDefinition());
}
struct ImportClasses : ASTImporterTestBase {};
TEST_P(ImportClasses,
PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
Decl *FromTU = getTuDecl("class X;", Lang_CXX);
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
auto ToD = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedD == ToD);
EXPECT_FALSE(ToD->isThisDeclarationADefinition());
}
TEST_P(ImportClasses, ImportPrototypeAfterImportedPrototype) {
Decl *FromTU = getTuDecl("class X; class X;", Lang_CXX);
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
auto From1 = LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportClasses, DefinitionShouldBeImportedAsADefinition) {
Decl *FromTU = getTuDecl("class X {};", Lang_CXX);
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
EXPECT_TRUE(cast<CXXRecordDecl>(ImportedD)->isThisDeclarationADefinition());
}
TEST_P(ImportClasses, ImportPrototypeFromDifferentTUAfterImportedPrototype) {
Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportClasses, ImportDefinitions) {
Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(Imported0, Imported1);
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(To0->isThisDeclarationADefinition());
}
TEST_P(ImportClasses, ImportDefinitionThenPrototype) {
Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto ToDef = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto ToProto = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
}
TEST_P(ImportClasses, ImportPrototypeThenDefinition) {
Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto ToProto = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto ToDef = LastDeclMatcher<CXXRecordDecl>().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,
PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
Decl *FromTU = getTuDecl("template <class T> class X;", Lang_CXX);
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedD == ToD);
ASSERT_TRUE(ToD->getTemplatedDecl());
EXPECT_FALSE(ToD->isThisDeclarationADefinition());
}
TEST_P(ImportClassTemplates, ImportPrototypeAfterImportedPrototype) {
Decl *FromTU = getTuDecl(
"template <class T> class X; template <class T> class X;", Lang_CXX);
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
auto From1 = LastDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
ASSERT_TRUE(To0->getTemplatedDecl());
ASSERT_TRUE(To1->getTemplatedDecl());
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
To0->getTemplatedDecl());
}
TEST_P(ImportClassTemplates, DefinitionShouldBeImportedAsADefinition) {
Decl *FromTU = getTuDecl("template <class T> class X {};", Lang_CXX);
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
ASSERT_TRUE(ToD->getTemplatedDecl());
EXPECT_TRUE(ToD->isThisDeclarationADefinition());
}
TEST_P(ImportClassTemplates,
ImportPrototypeFromDifferentTUAfterImportedPrototype) {
Decl *FromTU0 =
getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
ASSERT_TRUE(To0->getTemplatedDecl());
ASSERT_TRUE(To1->getTemplatedDecl());
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
To0->getTemplatedDecl());
}
TEST_P(ImportClassTemplates, ImportDefinitions) {
Decl *FromTU0 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(Imported0, Imported1);
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
ASSERT_TRUE(To0->getTemplatedDecl());
EXPECT_TRUE(To0->isThisDeclarationADefinition());
}
TEST_P(ImportClassTemplates, ImportDefinitionThenPrototype) {
Decl *FromTU0 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto FromProto =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto ToDef = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto ToProto = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
ASSERT_TRUE(ToDef->getTemplatedDecl());
ASSERT_TRUE(ToProto->getTemplatedDecl());
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
EXPECT_EQ(ToProto->getTemplatedDecl()->getPreviousDecl(),
ToDef->getTemplatedDecl());
}
TEST_P(ImportClassTemplates, ImportPrototypeThenDefinition) {
Decl *FromTU0 =
getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto FromProto =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto ToProto = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto ToDef = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
ASSERT_TRUE(ToProto->getTemplatedDecl());
ASSERT_TRUE(ToDef->getTemplatedDecl());
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
EXPECT_EQ(ToDef->getTemplatedDecl()->getPreviousDecl(),
ToProto->getTemplatedDecl());
}
struct ImportFriendClasses : ASTImporterTestBase {};
TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
Decl *FromTU = getTuDecl(
R"(
class A {
template <int I> class F {};
class X {
template <int I> friend class F;
};
};
)",
Lang_CXX, "input0.cc");
auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("F")));
ASSERT_TRUE(FromClass);
ASSERT_TRUE(FromFriendClass);
ASSERT_NE(FromClass, FromFriendClass);
ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
FromClass->getDescribedClassTemplate());
auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
EXPECT_TRUE(ToClass);
EXPECT_TRUE(ToFriendClass);
EXPECT_NE(ToClass, ToFriendClass);
EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
ToClass->getDescribedClassTemplate());
}
TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) {
Decl *FromTu = getTuDecl(
R"(
class declToImport {
friend class declToImport;
};
)",
Lang_CXX, "input.cc");
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTu, cxxRecordDecl(hasName("declToImport")));
auto *ToD = Import(FromD, Lang_CXX);
auto Pattern = cxxRecordDecl(has(friendDecl()));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
}
TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
Decl *FromTu = getTuDecl(
R"(
template<class A> class declToImport {
template<class A1> friend class declToImport;
};
)",
Lang_CXX, "input.cc");
auto *FromD =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
auto *ToD = Import(FromD, Lang_CXX);
auto Pattern = classTemplateDecl(
has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
auto *Class =
FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
EXPECT_NE(Friend->getFriendDecl(), Class);
EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
}
TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {
auto Pattern = classTemplateSpecializationDecl(hasName("X"));
ClassTemplateSpecializationDecl *Imported1;
{
Decl *FromTU = getTuDecl("template<class T> class X;"
"struct Y { friend class X<int>; };",
Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, Pattern);
Imported1 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
}
ClassTemplateSpecializationDecl *Imported2;
{
Decl *FromTU = getTuDecl("template<class T> class X;"
"template<> class X<int>{};"
"struct Z { friend class X<int>; };",
Lang_CXX, "input1.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, Pattern);
Imported2 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern),
2u);
ASSERT_TRUE(Imported2->getPreviousDecl());
EXPECT_EQ(Imported2->getPreviousDecl(), Imported1);
}
TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) {
Decl *FromTU0 = getTuDecl(
R"(
class X {
class Y;
};
class X::Y {
template <typename T>
friend class F; // The decl context of F is the global namespace.
};
)",
Lang_CXX, "input0.cc");
auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU0, classTemplateDecl(hasName("F")));
auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
Decl *FromTU1 = getTuDecl(
R"(
template <typename T>
class F {};
)",
Lang_CXX, "input1.cc");
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU1, classTemplateDecl(hasName("F")));
auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
Imported1->getTemplatedDecl()->getTypeForDecl());
}
TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("class declToImport {};", Lang_CXX,
"class Y { friend class declToImport; };", Lang_CXX);
auto *Imported = cast<CXXRecordDecl>(To);
EXPECT_TRUE(Imported->getPreviousDecl());
}
TEST_P(ImportFriendClasses,
ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
Decl *ToTU = getToTuDecl(
R"(
class X {
class Y;
};
class X::Y {
template <typename T>
friend class F; // The decl context of F is the global namespace.
};
)",
Lang_CXX);
auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("F")));
Decl *FromTU = getTuDecl(
R"(
template <typename T>
class F {};
)",
Lang_CXX, "input0.cc");
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("F")));
auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
EXPECT_TRUE(ImportedDef->getPreviousDecl());
EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl());
EXPECT_EQ(ToDecl->getTemplatedDecl(),
ImportedDef->getTemplatedDecl()->getPreviousDecl());
}
TEST_P(ImportFriendClasses,
ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) {
Decl *FromTU0 = getTuDecl(
R"(
class X {
class Y;
};
class X::Y {
template <typename T>
friend class F; // The decl context of F is the global namespace.
};
)",
Lang_CXX, "input0.cc");
auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU0, classTemplateDecl(hasName("F")));
auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
Decl *FromTU1 = getTuDecl(
R"(
template <typename T>
class F {};
)",
Lang_CXX, "input1.cc");
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU1, classTemplateDecl(hasName("F")));
auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
EXPECT_TRUE(ImportedDef->getPreviousDecl());
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
EXPECT_EQ(ImportedFwd->getTemplatedDecl(),
ImportedDef->getTemplatedDecl()->getPreviousDecl());
}
TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
Decl *FromTU0 = getTuDecl(
R"(
class X {
class Y;
};
class X::Y {
friend class F; // The decl context of F is the global namespace.
};
)",
Lang_CXX, "input0.cc");
auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl());
QualType FT = Friend->getFriendType()->getType();
FT = FromTU0->getASTContext().getCanonicalType(FT);
auto *Fwd = cast<TagType>(FT)->getDecl();
auto *ImportedFwd = Import(Fwd, Lang_CXX);
Decl *FromTU1 = getTuDecl(
R"(
class F {};
)",
Lang_CXX, "input1.cc");
auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU1, cxxRecordDecl(hasName("F")));
auto *ImportedDef = Import(Definition, Lang_CXX);
EXPECT_TRUE(ImportedDef->getPreviousDecl());
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
}
struct DeclContextTest : ASTImporterTestBase {};
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
@@ -3410,6 +3919,40 @@ TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
EXPECT_FALSE(NS->containsDecl(Spec));
}
TEST_P(DeclContextTest,
removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
// Investigate the list.
auto *DC = A0->getDeclContext();
ASSERT_TRUE(DC->containsDecl(A0));
ASSERT_TRUE(DC->containsDecl(A1));
// Investigate the lookup table.
auto *Map = DC->getLookupPtr();
ASSERT_TRUE(Map);
auto I = Map->find(A0->getDeclName());
ASSERT_NE(I, Map->end());
StoredDeclsList &L = I->second;
// The lookup table contains the most recent decl of A.
ASSERT_NE(L.getAsDecl(), A0);
ASSERT_EQ(L.getAsDecl(), A1);
ASSERT_TRUE(L.getAsDecl());
// Simulate the private function DeclContext::reconcileExternalVisibleStorage.
// The point here is to have a Vec with only one element, which is not the
// one we are going to delete from the DC later.
L.setHasExternalDecls();
ASSERT_TRUE(L.getAsVector());
ASSERT_EQ(1u, L.getAsVector()->size());
// This asserts in the old implementation.
DC->removeDecl(A0);
EXPECT_FALSE(DC->containsDecl(A0));
}
struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {};
TEST_P(ImportFunctionTemplateSpecializations,
@@ -3827,6 +4370,15 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests,
ImportFunctionTemplateSpecializations,
DefaultTestValuesForRunOptions, );

View File

@@ -597,6 +597,77 @@ TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) {
EXPECT_FALSE(testStructuralMatch(R0, R1));
}
TEST_F(StructuralEquivalenceRecordTest, AnonymousRecordsShouldBeInequivalent) {
auto t = makeTuDecls(
R"(
struct X {
struct {
int a;
};
struct {
int b;
};
};
)",
"", Lang_C);
auto *TU = get<0>(t);
auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
TU, indirectFieldDecl(hasName("a")));
auto *FA = cast<FieldDecl>(A->chain().front());
RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
auto *B = FirstDeclMatcher<IndirectFieldDecl>().match(
TU, indirectFieldDecl(hasName("b")));
auto *FB = cast<FieldDecl>(B->chain().front());
RecordDecl *RB = cast<RecordType>(FB->getType().getTypePtr())->getDecl();
ASSERT_NE(RA, RB);
EXPECT_TRUE(testStructuralMatch(RA, RA));
EXPECT_TRUE(testStructuralMatch(RB, RB));
EXPECT_FALSE(testStructuralMatch(RA, RB));
}
TEST_F(StructuralEquivalenceRecordTest,
RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) {
auto t = makeTuDecls(
R"(
struct X {
struct { int a; };
struct { int b; };
};
)",
R"(
struct X { // The order is reversed.
struct { int b; };
struct { int a; };
};
)",
Lang_C);
auto *TU = get<0>(t);
auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
TU, indirectFieldDecl(hasName("a")));
auto *FA = cast<FieldDecl>(A->chain().front());
RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
auto *TU1 = get<1>(t);
auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match(
TU1, indirectFieldDecl(hasName("a")));
auto *FA1 = cast<FieldDecl>(A1->chain().front());
RecordDecl *RA1 = cast<RecordType>(FA1->getType().getTypePtr())->getDecl();
RecordDecl *X =
FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X")));
RecordDecl *X1 =
FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X")));
ASSERT_NE(X, X1);
EXPECT_FALSE(testStructuralMatch(X, X1));
ASSERT_NE(RA, RA1);
EXPECT_TRUE(testStructuralMatch(RA, RA));
EXPECT_TRUE(testStructuralMatch(RA1, RA1));
EXPECT_FALSE(testStructuralMatch(RA1, RA));
}
TEST_F(StructuralEquivalenceRecordTest,
UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) {
auto Code =