forked from OSchip/llvm-project
Re-apply: [ASTImporter] Import the whole redecl chain of functions
Summary: With this patch when any `FunctionDecl` of a redeclaration chain is imported then we bring in the whole declaration chain. This involves functions and function template specializations. Also friend functions are affected. The chain is imported as it is in the "from" tu, the order of the redeclarations are kept. I also changed the lookup logic in order to find friends, but first making them visible in their declaration context. We may have long redeclaration chains if all TU contains the same prototype, but our measurements shows no degradation in time of CTU analysis (Tmux, Xerces, Bitcoin, Protobuf). Also, as further work we could squash redundant prototypes, but first ensure that functionality is working properly; then should we optimize. This may seem like a huge patch, sorry about that. But, most of the changes are new tests, changes in the production code is not that much. I also tried to create a smaller patch which does not affect specializations, but that patch failed to pass some of the `clang-import-test`s because there we import function specializations. Also very importantly, we can't just change the import of `FunctionDecl`s without changing the import of function template specializations because they are handled as `FunctionDecl`s. Reviewers: a.sidorin, r.stahl, xazax.hun, balazske, a_sidorin Reviewed By: a_sidorin Subscribers: labath, aprantl, a_sidorin, rnkovacs, dkrupp, cfe-commits Differential Revision: https://reviews.llvm.org/D47532 Re-apply commit rC335480 llvm-svn: 335731
This commit is contained in:
parent
1cdd816c12
commit
5254e64a8e
|
@ -43,6 +43,15 @@ class TagDecl;
|
||||||
class TypeSourceInfo;
|
class TypeSourceInfo;
|
||||||
class Attr;
|
class Attr;
|
||||||
|
|
||||||
|
// \brief Returns with a list of declarations started from the canonical decl
|
||||||
|
// then followed by subsequent decls in the translation unit.
|
||||||
|
// This gives a canonical list for each entry in the redecl chain.
|
||||||
|
// `Decl::redecls()` gives a list of decls which always start from the
|
||||||
|
// previous decl and the next item is actually the previous item in the order
|
||||||
|
// of source locations. Thus, `Decl::redecls()` gives different lists for
|
||||||
|
// the different entries in a given redecl chain.
|
||||||
|
llvm::SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D);
|
||||||
|
|
||||||
/// Imports selected nodes from one AST context into another context,
|
/// Imports selected nodes from one AST context into another context,
|
||||||
/// merging AST nodes where appropriate.
|
/// merging AST nodes where appropriate.
|
||||||
class ASTImporter {
|
class ASTImporter {
|
||||||
|
|
|
@ -1779,10 +1779,14 @@ public:
|
||||||
|
|
||||||
/// Removes a declaration from this context.
|
/// Removes a declaration from this context.
|
||||||
void removeDecl(Decl *D);
|
void removeDecl(Decl *D);
|
||||||
|
|
||||||
/// Checks whether a declaration is in this context.
|
/// Checks whether a declaration is in this context.
|
||||||
bool containsDecl(Decl *D) const;
|
bool containsDecl(Decl *D) const;
|
||||||
|
|
||||||
|
/// Checks whether a declaration is in this context.
|
||||||
|
/// This also loads the Decls from the external source before the check.
|
||||||
|
bool containsDeclAndLoad(Decl *D) const;
|
||||||
|
|
||||||
using lookup_result = DeclContextLookupResult;
|
using lookup_result = DeclContextLookupResult;
|
||||||
using lookup_iterator = lookup_result::iterator;
|
using lookup_iterator = lookup_result::iterator;
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,25 @@
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
SmallVector<Decl*, 2>
|
||||||
|
getCanonicalForwardRedeclChain(Redeclarable<T>* D) {
|
||||||
|
SmallVector<Decl*, 2> Redecls;
|
||||||
|
for (auto *R : D->getFirstDecl()->redecls()) {
|
||||||
|
if (R != D->getFirstDecl())
|
||||||
|
Redecls.push_back(R);
|
||||||
|
}
|
||||||
|
Redecls.push_back(D->getFirstDecl());
|
||||||
|
std::reverse(Redecls.begin(), Redecls.end());
|
||||||
|
return Redecls;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D) {
|
||||||
|
// Currently only FunctionDecl is supported
|
||||||
|
auto FD = cast<FunctionDecl>(D);
|
||||||
|
return getCanonicalForwardRedeclChain<FunctionDecl>(FD);
|
||||||
|
}
|
||||||
|
|
||||||
class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, QualType>,
|
class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, QualType>,
|
||||||
public DeclVisitor<ASTNodeImporter, Decl *>,
|
public DeclVisitor<ASTNodeImporter, Decl *>,
|
||||||
public StmtVisitor<ASTNodeImporter, Stmt *> {
|
public StmtVisitor<ASTNodeImporter, Stmt *> {
|
||||||
|
@ -195,6 +214,12 @@ namespace clang {
|
||||||
const InContainerTy &Container,
|
const InContainerTy &Container,
|
||||||
TemplateArgumentListInfo &Result);
|
TemplateArgumentListInfo &Result);
|
||||||
|
|
||||||
|
using TemplateArgsTy = SmallVector<TemplateArgument, 8>;
|
||||||
|
using OptionalTemplateArgsTy = Optional<TemplateArgsTy>;
|
||||||
|
std::tuple<FunctionTemplateDecl *, OptionalTemplateArgsTy>
|
||||||
|
ImportFunctionTemplateWithTemplateArgsFromSpecialization(
|
||||||
|
FunctionDecl *FromFD);
|
||||||
|
|
||||||
bool ImportTemplateInformation(FunctionDecl *FromFD, FunctionDecl *ToFD);
|
bool ImportTemplateInformation(FunctionDecl *FromFD, FunctionDecl *ToFD);
|
||||||
|
|
||||||
bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord,
|
bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord,
|
||||||
|
@ -408,6 +433,8 @@ namespace clang {
|
||||||
|
|
||||||
// Importing overrides.
|
// Importing overrides.
|
||||||
void ImportOverrides(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod);
|
void ImportOverrides(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod);
|
||||||
|
|
||||||
|
FunctionDecl *FindFunctionTemplateSpecialization(FunctionDecl *FromFD);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename InContainerTy>
|
template <typename InContainerTy>
|
||||||
|
@ -437,6 +464,25 @@ bool ASTNodeImporter::ImportTemplateArgumentListInfo<
|
||||||
From.arguments(), Result);
|
From.arguments(), Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<FunctionTemplateDecl *, ASTNodeImporter::OptionalTemplateArgsTy>
|
||||||
|
ASTNodeImporter::ImportFunctionTemplateWithTemplateArgsFromSpecialization(
|
||||||
|
FunctionDecl *FromFD) {
|
||||||
|
assert(FromFD->getTemplatedKind() ==
|
||||||
|
FunctionDecl::TK_FunctionTemplateSpecialization);
|
||||||
|
auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
|
||||||
|
auto *Template = cast_or_null<FunctionTemplateDecl>(
|
||||||
|
Importer.Import(FTSInfo->getTemplate()));
|
||||||
|
|
||||||
|
// Import template arguments.
|
||||||
|
auto TemplArgs = FTSInfo->TemplateArguments->asArray();
|
||||||
|
TemplateArgsTy ToTemplArgs;
|
||||||
|
if (ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(),
|
||||||
|
ToTemplArgs)) // Error during import.
|
||||||
|
return std::make_tuple(Template, OptionalTemplateArgsTy());
|
||||||
|
|
||||||
|
return std::make_tuple(Template, ToTemplArgs);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
@ -2275,23 +2321,17 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,
|
||||||
}
|
}
|
||||||
|
|
||||||
case FunctionDecl::TK_FunctionTemplateSpecialization: {
|
case FunctionDecl::TK_FunctionTemplateSpecialization: {
|
||||||
auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
|
FunctionTemplateDecl* Template;
|
||||||
auto *Template = cast_or_null<FunctionTemplateDecl>(
|
OptionalTemplateArgsTy ToTemplArgs;
|
||||||
Importer.Import(FTSInfo->getTemplate()));
|
std::tie(Template, ToTemplArgs) =
|
||||||
if (!Template)
|
ImportFunctionTemplateWithTemplateArgsFromSpecialization(FromFD);
|
||||||
return true;
|
if (!Template || !ToTemplArgs)
|
||||||
TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();
|
|
||||||
|
|
||||||
// Import template arguments.
|
|
||||||
auto TemplArgs = FTSInfo->TemplateArguments->asArray();
|
|
||||||
SmallVector<TemplateArgument, 8> ToTemplArgs;
|
|
||||||
if (ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(),
|
|
||||||
ToTemplArgs))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
TemplateArgumentList *ToTAList = TemplateArgumentList::CreateCopy(
|
TemplateArgumentList *ToTAList = TemplateArgumentList::CreateCopy(
|
||||||
Importer.getToContext(), ToTemplArgs);
|
Importer.getToContext(), *ToTemplArgs);
|
||||||
|
|
||||||
|
auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
|
||||||
TemplateArgumentListInfo ToTAInfo;
|
TemplateArgumentListInfo ToTAInfo;
|
||||||
const auto *FromTAArgsAsWritten = FTSInfo->TemplateArgumentsAsWritten;
|
const auto *FromTAArgsAsWritten = FTSInfo->TemplateArgumentsAsWritten;
|
||||||
if (FromTAArgsAsWritten)
|
if (FromTAArgsAsWritten)
|
||||||
|
@ -2300,6 +2340,7 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,
|
||||||
|
|
||||||
SourceLocation POI = Importer.Import(FTSInfo->getPointOfInstantiation());
|
SourceLocation POI = Importer.Import(FTSInfo->getPointOfInstantiation());
|
||||||
|
|
||||||
|
TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();
|
||||||
ToFD->setFunctionTemplateSpecialization(
|
ToFD->setFunctionTemplateSpecialization(
|
||||||
Template, ToTAList, /* InsertPos= */ nullptr,
|
Template, ToTAList, /* InsertPos= */ nullptr,
|
||||||
TSK, FromTAArgsAsWritten ? &ToTAInfo : nullptr, POI);
|
TSK, FromTAArgsAsWritten ? &ToTAInfo : nullptr, POI);
|
||||||
|
@ -2335,7 +2376,31 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,
|
||||||
llvm_unreachable("All cases should be covered!");
|
llvm_unreachable("All cases should be covered!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionDecl *
|
||||||
|
ASTNodeImporter::FindFunctionTemplateSpecialization(FunctionDecl *FromFD) {
|
||||||
|
FunctionTemplateDecl* Template;
|
||||||
|
OptionalTemplateArgsTy ToTemplArgs;
|
||||||
|
std::tie(Template, ToTemplArgs) =
|
||||||
|
ImportFunctionTemplateWithTemplateArgsFromSpecialization(FromFD);
|
||||||
|
if (!Template || !ToTemplArgs)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
void *InsertPos = nullptr;
|
||||||
|
auto *FoundSpec = Template->findSpecialization(*ToTemplArgs, InsertPos);
|
||||||
|
return FoundSpec;
|
||||||
|
}
|
||||||
|
|
||||||
Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||||
|
|
||||||
|
SmallVector<Decl*, 2> Redecls = getCanonicalForwardRedeclChain(D);
|
||||||
|
auto RedeclIt = Redecls.begin();
|
||||||
|
// Import the first part of the decl chain. I.e. import all previous
|
||||||
|
// declarations starting from the canonical decl.
|
||||||
|
for (; RedeclIt != Redecls.end() && *RedeclIt != D; ++RedeclIt)
|
||||||
|
if (!Importer.Import(*RedeclIt))
|
||||||
|
return nullptr;
|
||||||
|
assert(*RedeclIt == D);
|
||||||
|
|
||||||
// Import the major distinguishing characteristics of this function.
|
// Import the major distinguishing characteristics of this function.
|
||||||
DeclContext *DC, *LexicalDC;
|
DeclContext *DC, *LexicalDC;
|
||||||
DeclarationName Name;
|
DeclarationName Name;
|
||||||
|
@ -2346,13 +2411,27 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||||
if (ToD)
|
if (ToD)
|
||||||
return ToD;
|
return ToD;
|
||||||
|
|
||||||
const FunctionDecl *FoundWithoutBody = nullptr;
|
const FunctionDecl *FoundByLookup = nullptr;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// FIXME handle member function templates (TK_MemberSpecialization) similarly?
|
||||||
|
if (D->getTemplatedKind() ==
|
||||||
|
FunctionDecl::TK_FunctionTemplateSpecialization) {
|
||||||
|
if (FunctionDecl *FoundFunction = FindFunctionTemplateSpecialization(D)) {
|
||||||
|
if (D->doesThisDeclarationHaveABody() &&
|
||||||
|
FoundFunction->hasBody())
|
||||||
|
return Importer.Imported(D, FoundFunction);
|
||||||
|
FoundByLookup = FoundFunction;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Try to find a function in our own ("to") context with the same name, same
|
// Try to find a function in our own ("to") context with the same name, same
|
||||||
// type, and in the same context as the function we're importing.
|
// type, and in the same context as the function we're importing.
|
||||||
if (!LexicalDC->isFunctionOrMethod()) {
|
else if (!LexicalDC->isFunctionOrMethod()) {
|
||||||
SmallVector<NamedDecl *, 4> ConflictingDecls;
|
SmallVector<NamedDecl *, 4> ConflictingDecls;
|
||||||
unsigned IDNS = Decl::IDNS_Ordinary;
|
unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_OrdinaryFriend;
|
||||||
SmallVector<NamedDecl *, 2> FoundDecls;
|
SmallVector<NamedDecl *, 2> FoundDecls;
|
||||||
DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
|
DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
|
||||||
for (auto *FoundDecl : FoundDecls) {
|
for (auto *FoundDecl : FoundDecls) {
|
||||||
|
@ -2364,15 +2443,11 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||||
D->hasExternalFormalLinkage()) {
|
D->hasExternalFormalLinkage()) {
|
||||||
if (Importer.IsStructurallyEquivalent(D->getType(),
|
if (Importer.IsStructurallyEquivalent(D->getType(),
|
||||||
FoundFunction->getType())) {
|
FoundFunction->getType())) {
|
||||||
// FIXME: Actually try to merge the body and other attributes.
|
if (D->doesThisDeclarationHaveABody() &&
|
||||||
const FunctionDecl *FromBodyDecl = nullptr;
|
FoundFunction->hasBody())
|
||||||
D->hasBody(FromBodyDecl);
|
return Importer.Imported(D, FoundFunction);
|
||||||
if (D == FromBodyDecl && !FoundFunction->hasBody()) {
|
FoundByLookup = FoundFunction;
|
||||||
// This function is needed to merge completely.
|
|
||||||
FoundWithoutBody = FoundFunction;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
return Importer.Imported(D, FoundFunction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Check for overloading more carefully, e.g., by boosting
|
// FIXME: Check for overloading more carefully, e.g., by boosting
|
||||||
|
@ -2522,9 +2597,9 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||||
}
|
}
|
||||||
ToFunction->setParams(Parameters);
|
ToFunction->setParams(Parameters);
|
||||||
|
|
||||||
if (FoundWithoutBody) {
|
if (FoundByLookup) {
|
||||||
auto *Recent = const_cast<FunctionDecl *>(
|
auto *Recent = const_cast<FunctionDecl *>(
|
||||||
FoundWithoutBody->getMostRecentDecl());
|
FoundByLookup->getMostRecentDecl());
|
||||||
ToFunction->setPreviousDecl(Recent);
|
ToFunction->setPreviousDecl(Recent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2546,10 +2621,11 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||||
ToFunction->setType(T);
|
ToFunction->setType(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import the body, if any.
|
if (D->doesThisDeclarationHaveABody()) {
|
||||||
if (Stmt *FromBody = D->getBody()) {
|
if (Stmt *FromBody = D->getBody()) {
|
||||||
if (Stmt *ToBody = Importer.Import(FromBody)) {
|
if (Stmt *ToBody = Importer.Import(FromBody)) {
|
||||||
ToFunction->setBody(ToBody);
|
ToFunction->setBody(ToBody);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2559,14 +2635,29 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||||
if (ImportTemplateInformation(D, ToFunction))
|
if (ImportTemplateInformation(D, ToFunction))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Add this function to the lexical context.
|
bool IsFriend = D->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend);
|
||||||
// NOTE: If the function is templated declaration, it should be not added into
|
|
||||||
// LexicalDC. But described template is imported during import of
|
// TODO Can we generalize this approach to other AST nodes as well?
|
||||||
// FunctionTemplateDecl (it happens later). So, we use source declaration
|
if (D->getDeclContext()->containsDeclAndLoad(D))
|
||||||
// to determine if we should add the result function.
|
DC->addDeclInternal(ToFunction);
|
||||||
if (!D->getDescribedFunctionTemplate())
|
if (DC != LexicalDC && D->getLexicalDeclContext()->containsDeclAndLoad(D))
|
||||||
LexicalDC->addDeclInternal(ToFunction);
|
LexicalDC->addDeclInternal(ToFunction);
|
||||||
|
|
||||||
|
// Friend declaration's lexical context is the befriending class, but the
|
||||||
|
// semantic context is the enclosing scope of the befriending class.
|
||||||
|
// We want the friend functions to be found in the semantic context by lookup.
|
||||||
|
// FIXME should we handle this generically in VisitFriendDecl?
|
||||||
|
// In Other cases when LexicalDC != DC we don't want it to be added,
|
||||||
|
// e.g out-of-class definitions like void B::f() {} .
|
||||||
|
if (LexicalDC != DC && IsFriend) {
|
||||||
|
DC->makeDeclVisibleInContext(ToFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import the rest of the chain. I.e. import all subsequent declarations.
|
||||||
|
for (++RedeclIt; RedeclIt != Redecls.end(); ++RedeclIt)
|
||||||
|
if (!Importer.Import(*RedeclIt))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
if (auto *FromCXXMethod = dyn_cast<CXXMethodDecl>(D))
|
if (auto *FromCXXMethod = dyn_cast<CXXMethodDecl>(D))
|
||||||
ImportOverrides(cast<CXXMethodDecl>(ToFunction), FromCXXMethod);
|
ImportOverrides(cast<CXXMethodDecl>(ToFunction), FromCXXMethod);
|
||||||
|
|
||||||
|
|
|
@ -1347,6 +1347,12 @@ bool DeclContext::containsDecl(Decl *D) const {
|
||||||
(D->NextInContextAndBits.getPointer() || D == LastDecl));
|
(D->NextInContextAndBits.getPointer() || D == LastDecl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeclContext::containsDeclAndLoad(Decl *D) const {
|
||||||
|
if (hasExternalLexicalStorage())
|
||||||
|
LoadLexicalDeclsFromExternalStorage();
|
||||||
|
return containsDecl(D);
|
||||||
|
}
|
||||||
|
|
||||||
/// shouldBeHidden - Determine whether a declaration which was declared
|
/// shouldBeHidden - Determine whether a declaration which was declared
|
||||||
/// within its semantic context should be invisible to qualified name lookup.
|
/// within its semantic context should be invisible to qualified name lookup.
|
||||||
static bool shouldBeHidden(NamedDecl *D) {
|
static bool shouldBeHidden(NamedDecl *D) {
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
// CHECK: class1.cpp:19:3: note: enumerator 'b' with value 1 here
|
// CHECK: class1.cpp:19:3: note: enumerator 'b' with value 1 here
|
||||||
// CHECK: class2.cpp:12:3: note: enumerator 'a' with value 0 here
|
// CHECK: class2.cpp:12:3: note: enumerator 'a' with value 0 here
|
||||||
|
|
||||||
// CHECK: class1.cpp:36:8: warning: type 'F2' has incompatible definitions in different translation units
|
|
||||||
// CHECK: class1.cpp:39:3: note: friend declared here
|
|
||||||
// CHECK: class2.cpp:30:8: note: no corresponding friend here
|
|
||||||
|
|
||||||
// CHECK: class1.cpp:43:8: warning: type 'F3' has incompatible definitions in different translation units
|
// CHECK: class1.cpp:43:8: warning: type 'F3' has incompatible definitions in different translation units
|
||||||
// CHECK: class1.cpp:46:3: note: friend declared here
|
// CHECK: class1.cpp:46:3: note: friend declared here
|
||||||
// CHECK: class2.cpp:36:8: note: no corresponding friend here
|
// CHECK: class2.cpp:36:8: note: no corresponding friend here
|
||||||
|
|
||||||
|
// CHECK: class1.cpp:36:8: warning: type 'F2' has incompatible definitions in different translation units
|
||||||
|
// CHECK: class1.cpp:39:3: note: friend declared here
|
||||||
|
// CHECK: class2.cpp:30:8: note: no corresponding friend here
|
||||||
|
|
||||||
// CHECK: 4 warnings generated.
|
// CHECK: 4 warnings generated.
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "DeclMatcher.h"
|
#include "DeclMatcher.h"
|
||||||
#include "Language.h"
|
#include "Language.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gmock/gmock.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
#include "llvm/ADT/StringMap.h"
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
@ -428,6 +428,48 @@ struct ImportExpr : TestImportBase {};
|
||||||
struct ImportType : TestImportBase {};
|
struct ImportType : TestImportBase {};
|
||||||
struct ImportDecl : TestImportBase {};
|
struct ImportDecl : TestImportBase {};
|
||||||
|
|
||||||
|
struct CanonicalRedeclChain : ASTImporterTestBase {};
|
||||||
|
|
||||||
|
TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) {
|
||||||
|
Decl *FromTU = getTuDecl("void f();", Lang_CXX);
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
auto Redecls = getCanonicalForwardRedeclChain(D0);
|
||||||
|
ASSERT_EQ(Redecls.size(), 1u);
|
||||||
|
EXPECT_EQ(D0, Redecls[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers2) {
|
||||||
|
Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
auto *D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
FunctionDecl *D1 = D2->getPreviousDecl();
|
||||||
|
|
||||||
|
auto Redecls = getCanonicalForwardRedeclChain(D0);
|
||||||
|
ASSERT_EQ(Redecls.size(), 3u);
|
||||||
|
EXPECT_EQ(D0, Redecls[0]);
|
||||||
|
EXPECT_EQ(D1, Redecls[1]);
|
||||||
|
EXPECT_EQ(D2, Redecls[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) {
|
||||||
|
Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
auto *D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
FunctionDecl *D1 = D2->getPreviousDecl();
|
||||||
|
|
||||||
|
auto RedeclsD0 = getCanonicalForwardRedeclChain(D0);
|
||||||
|
auto RedeclsD1 = getCanonicalForwardRedeclChain(D1);
|
||||||
|
auto RedeclsD2 = getCanonicalForwardRedeclChain(D2);
|
||||||
|
|
||||||
|
EXPECT_THAT(RedeclsD0, ::testing::ContainerEq(RedeclsD1));
|
||||||
|
EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_P(ImportExpr, ImportStringLiteral) {
|
TEST_P(ImportExpr, ImportStringLiteral) {
|
||||||
MatchVerifier<Decl> Verifier;
|
MatchVerifier<Decl> Verifier;
|
||||||
testImport("void declToImport() { \"foo\"; }",
|
testImport("void declToImport() { \"foo\"; }",
|
||||||
|
@ -1672,34 +1714,6 @@ TEST_P(
|
||||||
|
|
||||||
struct ImportFunctions : ASTImporterTestBase {};
|
struct ImportFunctions : ASTImporterTestBase {};
|
||||||
|
|
||||||
TEST_P(ImportFunctions,
|
|
||||||
PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
|
|
||||||
Decl *FromTU = getTuDecl("void f();", Lang_CXX);
|
|
||||||
auto Pattern = functionDecl(hasName("f"));
|
|
||||||
FunctionDecl *FromD =
|
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
|
||||||
|
|
||||||
Decl *ImportedD = Import(FromD, Lang_CXX);
|
|
||||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
|
||||||
|
|
||||||
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
|
||||||
EXPECT_TRUE(!cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(ImportFunctions,
|
|
||||||
PrototypeShouldBeImportedAsDefintionWhenThereIsADefinition) {
|
|
||||||
Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX);
|
|
||||||
auto Pattern = functionDecl(hasName("f"));
|
|
||||||
FunctionDecl *FromD = // Prototype
|
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
|
||||||
|
|
||||||
Decl *ImportedD = Import(FromD, Lang_CXX);
|
|
||||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
|
||||||
|
|
||||||
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
|
||||||
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(ImportFunctions,
|
TEST_P(ImportFunctions,
|
||||||
DefinitionShouldBeImportedAsDefintionWhenThereIsAPrototype) {
|
DefinitionShouldBeImportedAsDefintionWhenThereIsAPrototype) {
|
||||||
Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX);
|
Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX);
|
||||||
|
@ -1710,7 +1724,7 @@ TEST_P(ImportFunctions,
|
||||||
Decl *ImportedD = Import(FromD, Lang_CXX);
|
Decl *ImportedD = Import(FromD, Lang_CXX);
|
||||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1727,30 +1741,40 @@ TEST_P(ImportFunctions, DefinitionShouldBeImportedAsADefinition) {
|
||||||
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImportFunctions, DISABLED_ImportPrototypeOfRecursiveFunction) {
|
TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) {
|
||||||
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
|
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
|
||||||
auto Pattern = functionDecl(hasName("f"));
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
FunctionDecl *PrototypeFD =
|
auto *From =
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Proto
|
||||||
|
|
||||||
Decl *ImportedD = Import(PrototypeFD, Lang_CXX);
|
Decl *ImportedD = Import(From, Lang_CXX);
|
||||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) {
|
TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) {
|
||||||
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
|
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
|
||||||
auto Pattern = functionDecl(hasName("f"));
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
FunctionDecl *DefinitionFD =
|
auto *From =
|
||||||
LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Def
|
||||||
|
|
||||||
Decl *ImportedD = Import(DefinitionFD, Lang_CXX);
|
Decl *ImportedD = Import(From, Lang_CXX);
|
||||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To1);
|
||||||
|
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImportFunctions, ImportPrototypes) {
|
TEST_P(ImportFunctions, ImportPrototypes) {
|
||||||
|
@ -1759,23 +1783,48 @@ TEST_P(ImportFunctions, ImportPrototypes) {
|
||||||
Decl *ImportedD;
|
Decl *ImportedD;
|
||||||
{
|
{
|
||||||
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
|
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
|
||||||
FunctionDecl *FromD =
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
|
||||||
|
|
||||||
ImportedD = Import(FromD, Lang_CXX);
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
}
|
}
|
||||||
Decl *ImportedD1;
|
|
||||||
{
|
{
|
||||||
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
|
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
|
||||||
FunctionDecl *FromD =
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
Import(FromD, Lang_CXX);
|
||||||
ImportedD1 = Import(FromD, Lang_CXX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctions, ImportDefinitions) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
Decl *ImportedD;
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl("void f(){};", Lang_CXX, "input1.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
||||||
EXPECT_EQ(ImportedD, ImportedD1);
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
EXPECT_TRUE(!cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
|
TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
|
||||||
|
@ -1784,23 +1833,24 @@ TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
|
||||||
Decl *ImportedD;
|
Decl *ImportedD;
|
||||||
{
|
{
|
||||||
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
|
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
|
||||||
FunctionDecl *FromD =
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
|
||||||
|
|
||||||
ImportedD = Import(FromD, Lang_CXX);
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
}
|
}
|
||||||
Decl *ImportedD1;
|
|
||||||
{
|
{
|
||||||
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
|
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
|
||||||
FunctionDecl *FromD =
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
Import(FromD, Lang_CXX);
|
||||||
ImportedD1 = Import(FromD, Lang_CXX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
|
||||||
EXPECT_EQ(ImportedD, ImportedD1);
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImportFunctions, ImportPrototypeThenDefinition) {
|
TEST_P(ImportFunctions, ImportPrototypeThenDefinition) {
|
||||||
|
@ -1823,38 +1873,40 @@ TEST_P(ImportFunctions, ImportPrototypeThenDefinition) {
|
||||||
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
EXPECT_TRUE(!ProtoD->doesThisDeclarationHaveABody());
|
EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
|
||||||
FunctionDecl *DefinitionD =
|
FunctionDecl *DefinitionD =
|
||||||
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
|
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
|
||||||
EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
|
EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImportFunctions, DISABLED_ImportPrototypeThenProtoAndDefinition) {
|
TEST_P(ImportFunctions, ImportPrototypeThenProtoAndDefinition) {
|
||||||
auto Pattern = functionDecl(hasName("f"));
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
{
|
{
|
||||||
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
|
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
|
||||||
FunctionDecl *FromD =
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
|
||||||
|
|
||||||
Import(FromD, Lang_CXX);
|
Import(FromD, Lang_CXX);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Decl *FromTU = getTuDecl("void f(); void f(){}", Lang_CXX, "input1.cc");
|
Decl *FromTU = getTuDecl("void f(); void f(){}", Lang_CXX, "input1.cc");
|
||||||
FunctionDecl *FromD =
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
|
||||||
Import(FromD, Lang_CXX);
|
Import(FromD, Lang_CXX);
|
||||||
}
|
}
|
||||||
|
|
||||||
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 3u);
|
||||||
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
EXPECT_TRUE(!ProtoD->doesThisDeclarationHaveABody());
|
EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
|
||||||
|
|
||||||
FunctionDecl *DefinitionD =
|
FunctionDecl *DefinitionD =
|
||||||
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
|
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
|
||||||
EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
|
|
||||||
|
EXPECT_TRUE(DefinitionD->getPreviousDecl());
|
||||||
|
EXPECT_FALSE(DefinitionD->getPreviousDecl()->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(DefinitionD->getPreviousDecl()->getPreviousDecl(), ProtoD);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) {
|
TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) {
|
||||||
|
@ -1894,6 +1946,202 @@ TEST_P(ImportFunctions, VirtualFlagShouldBePreservedWhenImportingPrototype) {
|
||||||
EXPECT_TRUE(To->isVirtual());
|
EXPECT_TRUE(To->isVirtual());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctions,
|
||||||
|
ImportDefinitionIfThereIsAnExistingDefinitionAndFwdDecl) {
|
||||||
|
Decl *ToTU = getToTuDecl(
|
||||||
|
R"(
|
||||||
|
void f() {}
|
||||||
|
void f();
|
||||||
|
)",
|
||||||
|
Lang_CXX);
|
||||||
|
ASSERT_EQ(1u,
|
||||||
|
DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
|
||||||
|
return FD->doesThisDeclarationHaveABody();
|
||||||
|
}).match(ToTU, functionDecl()));
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl("void f() {}", Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
|
||||||
|
|
||||||
|
Import(FromD, Lang_CXX);
|
||||||
|
|
||||||
|
EXPECT_EQ(1u,
|
||||||
|
DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
|
||||||
|
return FD->doesThisDeclarationHaveABody();
|
||||||
|
}).match(ToTU, functionDecl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImportFriendFunctions : ImportFunctions {};
|
||||||
|
|
||||||
|
TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl("struct X { friend void f(); };"
|
||||||
|
"void f();",
|
||||||
|
Lang_CXX,
|
||||||
|
"input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
|
||||||
|
auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFriendFunctions,
|
||||||
|
ImportFriendFunctionRedeclChainProto_OutOfClassProtoFirst) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl("void f();"
|
||||||
|
"struct X { friend void f(); };",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
|
||||||
|
auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDef) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl("struct X { friend void f(){} };"
|
||||||
|
"void f();",
|
||||||
|
Lang_CXX,
|
||||||
|
"input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
|
||||||
|
auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFriendFunctions,
|
||||||
|
ImportFriendFunctionRedeclChainDef_OutOfClassDef) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl("struct X { friend void f(); };"
|
||||||
|
"void f(){}",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
|
||||||
|
auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is disabled, because ATM we create a redundant FunctionDecl. We
|
||||||
|
// start the import with the definition of `f` then we continue with the import
|
||||||
|
// of the type of `f` which involves `X`. During the import of `X` we start
|
||||||
|
// again the import of the definition of `f` and then finally we create the
|
||||||
|
// node. But then in the first frame of `VisitFunctionDecl` we create a node
|
||||||
|
// again since we do not check if such a node exists yet or not. This is being
|
||||||
|
// fixed in a separate patch: https://reviews.llvm.org/D47632
|
||||||
|
// FIXME enable this test once the above patch is approved.
|
||||||
|
TEST_P(ImportFriendFunctions,
|
||||||
|
DISABLED_ImportFriendFunctionRedeclChainDefWithClass) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl(
|
||||||
|
R"(
|
||||||
|
class X;
|
||||||
|
void f(X *x){}
|
||||||
|
class X{
|
||||||
|
friend void f(X *x);
|
||||||
|
};
|
||||||
|
)",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
|
||||||
|
auto *InClassFD = cast<FunctionDecl>(FirstDeclMatcher<FriendDecl>()
|
||||||
|
.match(ToTU, friendDecl())
|
||||||
|
->getFriendDecl());
|
||||||
|
EXPECT_FALSE(InClassFD->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(InClassFD->getPreviousDecl(), ImportedD);
|
||||||
|
// The parameters must refer the same type
|
||||||
|
EXPECT_EQ((*InClassFD->param_begin())->getOriginalType(),
|
||||||
|
(*ImportedD->param_begin())->getOriginalType());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is disabled, because ATM we create a redundant FunctionDecl. We
|
||||||
|
// start the import with the definition of `f` then we continue with the import
|
||||||
|
// of the type of `f` which involves `X`. During the import of `X` we start
|
||||||
|
// again the import of the definition of `f` and then finally we create the
|
||||||
|
// node. But then in the first frame of `VisitFunctionDecl` we create a node
|
||||||
|
// again since we do not check if such a node exists yet or not. This is being
|
||||||
|
// fixed in a separate patch: https://reviews.llvm.org/D47632
|
||||||
|
// FIXME enable this test once the above patch is approved.
|
||||||
|
TEST_P(ImportFriendFunctions,
|
||||||
|
DISABLED_ImportFriendFunctionRedeclChainDefWithClass_ImportTheProto) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl(
|
||||||
|
R"(
|
||||||
|
class X;
|
||||||
|
void f(X *x){}
|
||||||
|
class X{
|
||||||
|
friend void f(X *x);
|
||||||
|
};
|
||||||
|
)",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
|
||||||
|
auto *OutOfClassFD = FirstDeclMatcher<FunctionDecl>().match(
|
||||||
|
ToTU, functionDecl(unless(hasParent(friendDecl()))));
|
||||||
|
|
||||||
|
EXPECT_TRUE(OutOfClassFD->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(ImportedD->getPreviousDecl(), OutOfClassFD);
|
||||||
|
// The parameters must refer the same type
|
||||||
|
EXPECT_EQ((*OutOfClassFD->param_begin())->getOriginalType(),
|
||||||
|
(*ImportedD->param_begin())->getOriginalType());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFriendFunctions, ImportFriendFunctionFromMultipleTU) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"));
|
||||||
|
|
||||||
|
FunctionDecl *ImportedD;
|
||||||
|
{
|
||||||
|
Decl *FromTU =
|
||||||
|
getTuDecl("struct X { friend void f(){} };", Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
}
|
||||||
|
FunctionDecl *ImportedD1;
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
ImportedD1 = cast<FunctionDecl>(Import(FromD, Lang_CXX));
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_FALSE(ImportedD1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD);
|
||||||
|
}
|
||||||
|
|
||||||
AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>,
|
AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>,
|
||||||
InnerMatcher) {
|
InnerMatcher) {
|
||||||
if (auto *Typedef = Node.getTypedefNameForAnonDecl())
|
if (auto *Typedef = Node.getTypedefNameForAnonDecl())
|
||||||
|
@ -2024,9 +2272,328 @@ TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
|
||||||
EXPECT_FALSE(NS->containsDecl(Spec));
|
EXPECT_FALSE(NS->containsDecl(Spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {};
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations,
|
||||||
|
TUshouldNotContainFunctionTemplateImplicitInstantiation) {
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl(
|
||||||
|
R"(
|
||||||
|
template<class T>
|
||||||
|
int f() { return 0; }
|
||||||
|
void foo() { f<int>(); }
|
||||||
|
)",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
|
||||||
|
// Check that the function template instantiation is NOT the child of the TU.
|
||||||
|
auto Pattern = translationUnitDecl(
|
||||||
|
unless(has(functionDecl(hasName("f"), isTemplateInstantiation()))));
|
||||||
|
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));
|
||||||
|
|
||||||
|
auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
|
||||||
|
FromTU, functionDecl(hasName("foo")));
|
||||||
|
ASSERT_TRUE(Import(Foo, Lang_CXX));
|
||||||
|
|
||||||
|
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations,
|
||||||
|
TUshouldNotContainFunctionTemplateExplicitInstantiation) {
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl(
|
||||||
|
R"(
|
||||||
|
template<class T>
|
||||||
|
int f() { return 0; }
|
||||||
|
template int f<int>();
|
||||||
|
)",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
|
||||||
|
// Check that the function template instantiation is NOT the child of the TU.
|
||||||
|
auto Instantiation = functionDecl(hasName("f"), isTemplateInstantiation());
|
||||||
|
auto Pattern = translationUnitDecl(unless(has(Instantiation)));
|
||||||
|
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
Import(FirstDeclMatcher<Decl>().match(FromTU, Instantiation), Lang_CXX));
|
||||||
|
|
||||||
|
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations,
|
||||||
|
TUshouldContainFunctionTemplateSpecialization) {
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl(
|
||||||
|
R"(
|
||||||
|
template<class T>
|
||||||
|
int f() { return 0; }
|
||||||
|
template <> int f<int>() { return 4; }
|
||||||
|
)",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
|
||||||
|
// Check that the function template specialization is the child of the TU.
|
||||||
|
auto Specialization =
|
||||||
|
functionDecl(hasName("f"), isExplicitTemplateSpecialization());
|
||||||
|
auto Pattern = translationUnitDecl(has(Specialization));
|
||||||
|
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
Import(FirstDeclMatcher<Decl>().match(FromTU, Specialization), Lang_CXX));
|
||||||
|
|
||||||
|
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations,
|
||||||
|
FunctionTemplateSpecializationRedeclChain) {
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl(
|
||||||
|
R"(
|
||||||
|
template<class T>
|
||||||
|
int f() { return 0; }
|
||||||
|
template <> int f<int>() { return 4; }
|
||||||
|
)",
|
||||||
|
Lang_CXX, "input0.cc");
|
||||||
|
|
||||||
|
auto Spec = functionDecl(hasName("f"), isExplicitTemplateSpecialization(),
|
||||||
|
hasParent(translationUnitDecl()));
|
||||||
|
auto *FromSpecD = FirstDeclMatcher<Decl>().match(FromTU, Spec);
|
||||||
|
{
|
||||||
|
auto *TU = FromTU;
|
||||||
|
auto *SpecD = FromSpecD;
|
||||||
|
auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
|
||||||
|
TU, functionTemplateDecl());
|
||||||
|
auto *FirstSpecD = *(TemplateD->spec_begin());
|
||||||
|
ASSERT_EQ(SpecD, FirstSpecD);
|
||||||
|
ASSERT_TRUE(SpecD->getPreviousDecl());
|
||||||
|
ASSERT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl())
|
||||||
|
->doesThisDeclarationHaveABody());
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_TRUE(Import(FromSpecD, Lang_CXX));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto *TU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
auto *SpecD = FirstDeclMatcher<Decl>().match(TU, Spec);
|
||||||
|
auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
|
||||||
|
TU, functionTemplateDecl());
|
||||||
|
auto *FirstSpecD = *(TemplateD->spec_begin());
|
||||||
|
EXPECT_EQ(SpecD, FirstSpecD);
|
||||||
|
ASSERT_TRUE(SpecD->getPreviousDecl());
|
||||||
|
EXPECT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl())
|
||||||
|
->doesThisDeclarationHaveABody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations,
|
||||||
|
MatchNumberOfFunctionTemplateSpecializations) {
|
||||||
|
|
||||||
|
Decl *FromTU = getTuDecl(
|
||||||
|
R"(
|
||||||
|
template <typename T> constexpr int f() { return 0; }
|
||||||
|
template <> constexpr int f<int>() { return 4; }
|
||||||
|
void foo() {
|
||||||
|
static_assert(f<char>() == 0, "");
|
||||||
|
static_assert(f<int>() == 4, "");
|
||||||
|
}
|
||||||
|
)",
|
||||||
|
Lang_CXX11, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
|
||||||
|
FromTU, functionDecl(hasName("foo")));
|
||||||
|
|
||||||
|
Import(FromD, Lang_CXX11);
|
||||||
|
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||||
|
EXPECT_EQ(
|
||||||
|
DeclCounter<FunctionDecl>().match(FromTU, functionDecl(hasName("f"))),
|
||||||
|
DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations,
|
||||||
|
ImportPrototypes) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
|
||||||
|
auto Code =
|
||||||
|
R"(
|
||||||
|
// Proto of the primary template.
|
||||||
|
template <class T>
|
||||||
|
void f();
|
||||||
|
// Proto of the specialization.
|
||||||
|
template <>
|
||||||
|
void f<int>();
|
||||||
|
)";
|
||||||
|
|
||||||
|
Decl *ImportedD;
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
|
||||||
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc");
|
||||||
|
auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_TRUE(ImportedD != To1);
|
||||||
|
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
|
||||||
|
// Check that they are part of the same redecl chain.
|
||||||
|
EXPECT_EQ(To1->getCanonicalDecl(), To0->getCanonicalDecl());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations, ImportDefinitions) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
|
||||||
|
auto Code =
|
||||||
|
R"(
|
||||||
|
// Proto of the primary template.
|
||||||
|
template <class T>
|
||||||
|
void f();
|
||||||
|
// Specialization and definition.
|
||||||
|
template <>
|
||||||
|
void f<int>() {}
|
||||||
|
)";
|
||||||
|
|
||||||
|
Decl *ImportedD;
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
|
||||||
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
|
||||||
|
|
||||||
|
auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
|
||||||
|
ToTU, functionTemplateDecl());
|
||||||
|
auto *FirstSpecD = *(TemplateD->spec_begin());
|
||||||
|
EXPECT_EQ(FirstSpecD->getCanonicalDecl(), To0->getCanonicalDecl());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenPrototype) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
|
||||||
|
auto Code =
|
||||||
|
R"(
|
||||||
|
// Proto of the primary template.
|
||||||
|
template <class T>
|
||||||
|
void f();
|
||||||
|
// Specialization proto.
|
||||||
|
template <>
|
||||||
|
void f<int>();
|
||||||
|
// Specialization proto.
|
||||||
|
template <>
|
||||||
|
void f<int>();
|
||||||
|
)";
|
||||||
|
|
||||||
|
Decl *ImportedD;
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_TRUE(ImportedD != To1);
|
||||||
|
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenDefinition) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
|
||||||
|
auto Code =
|
||||||
|
R"(
|
||||||
|
// Proto of the primary template.
|
||||||
|
template <class T>
|
||||||
|
void f();
|
||||||
|
// Specialization proto.
|
||||||
|
template <>
|
||||||
|
void f<int>();
|
||||||
|
// Specialization definition.
|
||||||
|
template <>
|
||||||
|
void f<int>() {}
|
||||||
|
)";
|
||||||
|
|
||||||
|
Decl *ImportedD;
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_TRUE(ImportedD != To1);
|
||||||
|
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(ImportFunctionTemplateSpecializations, DefinitionThenPrototype) {
|
||||||
|
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
|
||||||
|
auto Code =
|
||||||
|
R"(
|
||||||
|
// Proto of the primary template.
|
||||||
|
template <class T>
|
||||||
|
void f();
|
||||||
|
// Specialization definition.
|
||||||
|
template <>
|
||||||
|
void f<int>() {}
|
||||||
|
// Specialization proto.
|
||||||
|
template <>
|
||||||
|
void f<int>();
|
||||||
|
)";
|
||||||
|
|
||||||
|
Decl *ImportedD;
|
||||||
|
{
|
||||||
|
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
|
||||||
|
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
|
||||||
|
ImportedD = Import(FromD, Lang_CXX);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||||
|
|
||||||
|
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
|
||||||
|
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
|
||||||
|
EXPECT_TRUE(ImportedD == To0);
|
||||||
|
EXPECT_TRUE(ImportedD != To1);
|
||||||
|
EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
|
||||||
|
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||||
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
|
INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
|
||||||
::testing::Values(ArgVector()), );
|
::testing::Values(ArgVector()), );
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
ParameterizedTests, CanonicalRedeclChain,
|
||||||
|
::testing::Values(ArgVector()),);
|
||||||
|
|
||||||
auto DefaultTestValuesForRunOptions = ::testing::Values(
|
auto DefaultTestValuesForRunOptions = ::testing::Values(
|
||||||
ArgVector(),
|
ArgVector(),
|
||||||
ArgVector{"-fdelayed-template-parsing"},
|
ArgVector{"-fdelayed-template-parsing"},
|
||||||
|
@ -2048,5 +2615,12 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterTestBase,
|
||||||
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
|
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
|
||||||
DefaultTestValuesForRunOptions, );
|
DefaultTestValuesForRunOptions, );
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
|
||||||
|
DefaultTestValuesForRunOptions, );
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(ParameterizedTests,
|
||||||
|
ImportFunctionTemplateSpecializations,
|
||||||
|
DefaultTestValuesForRunOptions, );
|
||||||
|
|
||||||
} // end namespace ast_matchers
|
} // end namespace ast_matchers
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
Loading…
Reference in New Issue