blob: 9c0d6a3617c460b07ccceb2254f1843926a8bd72 [file] [log] [blame]
//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Tests for the correct import of AST nodes from one AST context to another.
//
//===----------------------------------------------------------------------===//
#include "MatchVerifier.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTImporter.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "DeclMatcher.h"
#include "gtest/gtest.h"
#include "llvm/ADT/StringMap.h"
namespace clang {
namespace ast_matchers {
using internal::Matcher;
using internal::BindableMatcher;
using llvm::StringMap;
typedef std::vector<std::string> ArgVector;
typedef std::vector<ArgVector> RunOptions;
static bool isCXX(Language Lang) {
return Lang == Lang_CXX || Lang == Lang_CXX11;
}
static ArgVector getBasicRunOptionsForLanguage(Language Lang) {
ArgVector BasicArgs;
// Test with basic arguments.
switch (Lang) {
case Lang_C:
BasicArgs = {"-x", "c", "-std=c99"};
break;
case Lang_C89:
BasicArgs = {"-x", "c", "-std=c89"};
break;
case Lang_CXX:
BasicArgs = {"-std=c++98", "-frtti"};
break;
case Lang_CXX11:
BasicArgs = {"-std=c++11", "-frtti"};
break;
case Lang_OpenCL:
case Lang_OBJCXX:
llvm_unreachable("Not implemented yet!");
}
return BasicArgs;
}
static RunOptions getRunOptionsForLanguage(Language Lang) {
ArgVector BasicArgs = getBasicRunOptionsForLanguage(Lang);
// For C++, test with "-fdelayed-template-parsing" enabled to handle MSVC
// default behaviour.
if (isCXX(Lang)) {
ArgVector ArgsForDelayedTemplateParse = BasicArgs;
ArgsForDelayedTemplateParse.emplace_back("-fdelayed-template-parsing");
return {BasicArgs, ArgsForDelayedTemplateParse};
}
return {BasicArgs};
}
// Creates a virtual file and assigns that to the context of given AST. If the
// file already exists then the file will not be created again as a duplicate.
static void
createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {
assert(ToAST);
ASTContext &ToCtx = ToAST->getASTContext();
auto *OFS = static_cast<vfs::OverlayFileSystem *>(
ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get());
auto *MFS =
static_cast<vfs::InMemoryFileSystem *>(OFS->overlays_begin()->get());
MFS->addFile(FileName, 0, std::move(Buffer));
}
static void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
StringRef Code) {
return createVirtualFileIfNeeded(ToAST, FileName,
llvm::MemoryBuffer::getMemBuffer(Code));
}
template <typename NodeType>
NodeType importNode(ASTUnit *From, ASTUnit *To, ASTImporter &Importer,
NodeType Node) {
ASTContext &ToCtx = To->getASTContext();
// Add 'From' file to virtual file system so importer can 'find' it
// while importing SourceLocations. It is safe to add same file multiple
// times - it just isn't replaced.
StringRef FromFileName = From->getMainFileName();
createVirtualFileIfNeeded(To, FromFileName,
From->getBufferForFile(FromFileName));
auto Imported = Importer.Import(Node);
// This should dump source locations and assert if some source locations
// were not imported.
SmallString<1024> ImportChecker;
llvm::raw_svector_ostream ToNothing(ImportChecker);
ToCtx.getTranslationUnitDecl()->print(ToNothing);
// This traverses the AST to catch certain bugs like poorly or not
// implemented subtrees.
Imported->dump(ToNothing);
return Imported;
}
const StringRef DeclToImportID = "declToImport";
const StringRef DeclToVerifyID = "declToVerify";
template <typename NodeType>
testing::AssertionResult
testImport(const std::string &FromCode, const ArgVector &FromArgs,
const std::string &ToCode, const ArgVector &ToArgs,
MatchVerifier<NodeType> &Verifier,
const BindableMatcher<NodeType> &SearchMatcher,
const BindableMatcher<NodeType> &VerificationMatcher) {
const char *const InputFileName = "input.cc";
const char *const OutputFileName = "output.cc";
std::unique_ptr<ASTUnit>
FromAST = tooling::buildASTFromCodeWithArgs(
FromCode, FromArgs, InputFileName),
ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
ASTContext &FromCtx = FromAST->getASTContext(),
&ToCtx = ToAST->getASTContext();
ASTImporter Importer(ToCtx, ToAST->getFileManager(),
FromCtx, FromAST->getFileManager(), false);
auto FoundNodes = match(SearchMatcher, FromCtx);
if (FoundNodes.size() != 1)
return testing::AssertionFailure()
<< "Multiple potential nodes were found!";
auto ToImport = selectFirst<NodeType>(DeclToImportID, FoundNodes);
if (!ToImport)
return testing::AssertionFailure() << "Node type mismatch!";
// Sanity check: the node being imported should match in the same way as
// the result node.
BindableMatcher<NodeType> WrapperMatcher(VerificationMatcher);
EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher));
auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport);
if (!Imported)
return testing::AssertionFailure() << "Import failed, nullptr returned!";
return Verifier.match(Imported, WrapperMatcher);
}
template <typename NodeType>
testing::AssertionResult
testImport(const std::string &FromCode, const ArgVector &FromArgs,
const std::string &ToCode, const ArgVector &ToArgs,
MatchVerifier<NodeType> &Verifier,
const BindableMatcher<NodeType> &VerificationMatcher) {
return testImport(
FromCode, FromArgs, ToCode, ToArgs, Verifier,
translationUnitDecl(
has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))),
VerificationMatcher);
}
/// Test how AST node named "declToImport" located in the translation unit
/// of "FromCode" virtual file is imported to "ToCode" virtual file.
/// The verification is done by running AMatcher over the imported node.
template <typename NodeType, typename MatcherType>
void testImport(const std::string &FromCode, Language FromLang,
const std::string &ToCode, Language ToLang,
MatchVerifier<NodeType> &Verifier,
const MatcherType &AMatcher) {
auto RunOptsFrom = getRunOptionsForLanguage(FromLang);
auto RunOptsTo = getRunOptionsForLanguage(ToLang);
for (const auto &FromArgs : RunOptsFrom)
for (const auto &ToArgs : RunOptsTo)
EXPECT_TRUE(testImport(FromCode, FromArgs, ToCode, ToArgs,
Verifier, AMatcher));
}
// This class provides generic methods to write tests which can check internal
// attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,
// this fixture makes it possible to import from several "From" contexts.
class ASTImporterTestBase : public ::testing::TestWithParam<ArgVector> {
const char *const InputFileName = "input.cc";
const char *const OutputFileName = "output.cc";
// Buffer for the To context, must live in the test scope.
std::string ToCode;
struct TU {
// Buffer for the context, must live in the test scope.
StringRef Code;
StringRef FileName;
std::unique_ptr<ASTUnit> Unit;
TranslationUnitDecl *TUDecl = nullptr;
TU(StringRef Code, StringRef FileName, ArgVector Args)
: Code(Code), FileName(FileName),
Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args,
this->FileName)),
TUDecl(Unit->getASTContext().getTranslationUnitDecl()) {}
};
// We may have several From contexts and related translation units. In each
// AST, the buffers for the source are handled via references and are set
// during the creation of the AST. These references must point to a valid
// buffer until the AST is alive. Thus, we must use a list in order to avoid
// moving of the stored objects because that would mean breaking the
// references in the AST. By using a vector a move could happen when the
// vector is expanding, with the list we won't have these issues.
std::list<TU> FromTUs;
public:
// We may have several From context but only one To context.
std::unique_ptr<ASTUnit> ToAST;
// Returns the argument vector used for a specific language, this set
// can be tweaked by the test parameters.
ArgVector getArgVectorForLanguage(Language Lang) {
ArgVector Args = getBasicRunOptionsForLanguage(Lang);
ArgVector ExtraArgs = GetParam();
for (const auto& Arg : ExtraArgs) {
Args.push_back(Arg);
}
return Args;
}
// Creates an AST both for the From and To source code and imports the Decl
// of the identifier into the To context.
// Must not be called more than once within the same test.
std::tuple<Decl *, Decl *>
getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode,
Language ToLang, StringRef Identifier = DeclToImportID) {
ArgVector FromArgs = getArgVectorForLanguage(FromLang),
ToArgs = getArgVectorForLanguage(ToLang);
FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs);
TU &FromTU = FromTUs.back();
ToCode = ToSrcCode;
assert(!ToAST);
ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
ASTContext &FromCtx = FromTU.Unit->getASTContext(),
&ToCtx = ToAST->getASTContext();
createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code);
ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx,
FromTU.Unit->getFileManager(), false);
IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
assert(ImportedII && "Declaration with the given identifier "
"should be specified in test!");
DeclarationName ImportDeclName(ImportedII);
SmallVector<NamedDecl *, 4> FoundDecls;
FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
FoundDecls);
assert(FoundDecls.size() == 1);
Decl *Imported = Importer.Import(FoundDecls.front());
assert(Imported);
return std::make_tuple(*FoundDecls.begin(), Imported);
}
// Creates a TU decl for the given source code.
// May be called several times in a given test.
TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang,
StringRef FileName = "input.cc") {
assert(
std::find_if(FromTUs.begin(), FromTUs.end(), [FileName](const TU &E) {
return E.FileName == FileName;
}) == FromTUs.end());
ArgVector Args = getArgVectorForLanguage(Lang);
FromTUs.emplace_back(SrcCode, FileName, Args);
TU &Tu = FromTUs.back();
return Tu.TUDecl;
}
// Import the given Decl into the ToCtx.
// May be called several times in a given test.
// The different instances of the param From may have different ASTContext.
Decl *Import(Decl *From, Language ToLang) {
if (!ToAST) {
ArgVector ToArgs = getArgVectorForLanguage(ToLang);
// Build the AST from an empty file.
ToAST =
tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc");
}
// Create a virtual file in the To Ctx which corresponds to the file from
// which we want to import the `From` Decl. Without this source locations
// will be invalid in the ToCtx.
auto It = std::find_if(FromTUs.begin(), FromTUs.end(), [From](const TU &E) {
return E.TUDecl == From->getTranslationUnitDecl();
});
assert(It != FromTUs.end());
createVirtualFileIfNeeded(ToAST.get(), It->FileName, It->Code);
ASTContext &FromCtx = From->getASTContext(),
&ToCtx = ToAST->getASTContext();
ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx,
FromCtx.getSourceManager().getFileManager(), false);
return Importer.Import(From);
}
~ASTImporterTestBase() {
if (!::testing::Test::HasFailure()) return;
for (auto &Tu : FromTUs) {
assert(Tu.Unit);
llvm::errs() << "FromAST:\n";
Tu.Unit->getASTContext().getTranslationUnitDecl()->dump();
llvm::errs() << "\n";
}
if (ToAST) {
llvm::errs() << "ToAST:\n";
ToAST->getASTContext().getTranslationUnitDecl()->dump();
}
}
};
struct ImportAction {
StringRef FromFilename;
StringRef ToFilename;
// FIXME: Generalize this to support other node kinds.
BindableMatcher<Decl> ImportPredicate;
ImportAction(StringRef FromFilename, StringRef ToFilename,
DeclarationMatcher ImportPredicate)
: FromFilename(FromFilename), ToFilename(ToFilename),
ImportPredicate(ImportPredicate) {}
ImportAction(StringRef FromFilename, StringRef ToFilename,
const std::string &DeclName)
: FromFilename(FromFilename), ToFilename(ToFilename),
ImportPredicate(namedDecl(hasName(DeclName))) {}
};
using SingleASTUnitForAllOpts = std::vector<std::unique_ptr<ASTUnit>>;
using AllASTUnitsForAllOpts = StringMap<SingleASTUnitForAllOpts>;
struct CodeEntry {
std::string CodeSample;
Language Lang;
/// Builds N copies of ASTUnits for each potential compile options set
/// for further import actions. N is equal to size of this option set.
SingleASTUnitForAllOpts createASTUnits(StringRef FileName) const {
auto RunOpts = getRunOptionsForLanguage(Lang);
size_t NumOpts = RunOpts.size();
SingleASTUnitForAllOpts ResultASTs(NumOpts);
for (size_t CompileOpt = 0; CompileOpt < NumOpts; ++CompileOpt) {
auto AST = tooling::buildASTFromCodeWithArgs(
CodeSample, RunOpts[CompileOpt], FileName);
EXPECT_TRUE(AST.get());
ResultASTs[CompileOpt] = std::move(AST);
}
return ResultASTs;
}
};
using CodeFiles = StringMap<CodeEntry>;
/// Test an arbitrary sequence of imports for a set of given in-memory files.
/// The verification is done by running VerificationMatcher against a specified
/// AST node inside of one of given files.
/// \param CodeSamples Map whose key is the file name and the value is the file
/// content.
/// \param ImportActions Sequence of imports. Each import in sequence
/// specifies "from file" and "to file" and a matcher that is used for
/// searching a declaration for import in "from file".
/// \param FileForFinalCheck Name of virtual file for which the final check is
/// applied.
/// \param FinalSelectPredicate Matcher that specifies the AST node in the
/// FileForFinalCheck for which the verification will be done.
/// \param VerificationMatcher Matcher that will be used for verification after
/// all imports in sequence are done.
void testImportSequence(const CodeFiles &CodeSamples,
const std::vector<ImportAction> &ImportActions,
StringRef FileForFinalCheck,
BindableMatcher<Decl> FinalSelectPredicate,
BindableMatcher<Decl> VerificationMatcher) {
AllASTUnitsForAllOpts AllASTUnits;
using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>;
llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> Importers;
auto GenASTsIfNeeded = [&AllASTUnits, &CodeSamples](StringRef Filename) {
if (!AllASTUnits.count(Filename)) {
auto Found = CodeSamples.find(Filename);
assert(Found != CodeSamples.end() && "Wrong file for import!");
AllASTUnits[Filename] = Found->getValue().createASTUnits(Filename);
}
};
size_t NumCompileOpts = 0;
for (const ImportAction &Action : ImportActions) {
StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename;
GenASTsIfNeeded(FromFile);
GenASTsIfNeeded(ToFile);
NumCompileOpts = AllASTUnits[FromFile].size();
for (size_t CompileOpt = 0; CompileOpt < NumCompileOpts; ++CompileOpt) {
ASTUnit *From = AllASTUnits[FromFile][CompileOpt].get();
ASTUnit *To = AllASTUnits[ToFile][CompileOpt].get();
// Create a new importer if needed.
std::unique_ptr<ASTImporter> &ImporterRef = Importers[{From, To}];
if (!ImporterRef)
ImporterRef.reset(new ASTImporter(
To->getASTContext(), To->getFileManager(), From->getASTContext(),
From->getFileManager(), false));
// Find the declaration and import it.
auto FoundDecl = match(Action.ImportPredicate.bind(DeclToImportID),
From->getASTContext());
EXPECT_TRUE(FoundDecl.size() == 1);
const Decl *ToImport = selectFirst<Decl>(DeclToImportID, FoundDecl);
auto Imported = importNode(From, To, *ImporterRef, ToImport);
EXPECT_TRUE(Imported);
}
}
// NOTE: We don't do cross-option import check here due to fast growth of
// potential option sets.
for (size_t CompileOpt = 0; CompileOpt < NumCompileOpts; ++CompileOpt) {
// Find the declaration and import it.
auto FoundDecl =
match(FinalSelectPredicate.bind(DeclToVerifyID),
AllASTUnits[FileForFinalCheck][CompileOpt]->getASTContext());
EXPECT_TRUE(FoundDecl.size() == 1);
const Decl *ToVerify = selectFirst<Decl>(DeclToVerifyID, FoundDecl);
MatchVerifier<Decl> Verifier;
EXPECT_TRUE(Verifier.match(ToVerify,
BindableMatcher<Decl>(VerificationMatcher)));
}
}
TEST(ImportExpr, ImportStringLiteral) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { \"foo\"; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
stringLiteral(
hasType(
asString("const char [4]"))))))));
testImport("void declToImport() { L\"foo\"; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
stringLiteral(
hasType(
asString("const wchar_t [4]"))))))));
testImport("void declToImport() { \"foo\" \"bar\"; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
stringLiteral(
hasType(
asString("const char [7]"))))))));
}
TEST(ImportExpr, ImportGNUNullExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { __null; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
gnuNullExpr(
hasType(isInteger())))))));
}
TEST(ImportExpr, ImportCXXNullPtrLiteralExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { nullptr; }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
cxxNullPtrLiteralExpr())))));
}
TEST(ImportExpr, ImportFloatinglLiteralExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { 1.0; }",
Lang_C, "", Lang_C, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
floatLiteral(
equals(1.0),
hasType(asString("double"))))))));
testImport("void declToImport() { 1.0e-5f; }",
Lang_C, "", Lang_C, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
floatLiteral(
equals(1.0e-5f),
hasType(asString("float"))))))));
}
TEST(ImportExpr, ImportCompoundLiteralExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() {"
" struct s { int x; long y; unsigned z; }; "
" (struct s){ 42, 0L, 1U }; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
compoundLiteralExpr(
hasType(asString("struct s")),
has(initListExpr(
hasType(asString("struct s")),
has(integerLiteral(
equals(42), hasType(asString("int")))),
has(integerLiteral(
equals(0), hasType(asString("long")))),
has(integerLiteral(
equals(1),
hasType(asString("unsigned int"))))
))))))));
}
TEST(ImportExpr, ImportCXXThisExpr) {
MatchVerifier<Decl> Verifier;
testImport("class declToImport { void f() { this; } };",
Lang_CXX, "", Lang_CXX, Verifier,
cxxRecordDecl(
hasMethod(
hasBody(
compoundStmt(
has(
cxxThisExpr(
hasType(
asString("class declToImport *")))))))));
}
TEST(ImportExpr, ImportAtomicExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { int *ptr; __atomic_load_n(ptr, 1); }",
Lang_C, "", Lang_C, Verifier,
functionDecl(hasBody(compoundStmt(has(atomicExpr(
has(ignoringParenImpCasts(
declRefExpr(hasDeclaration(varDecl(hasName("ptr"))),
hasType(asString("int *"))))),
has(integerLiteral(equals(1), hasType(asString("int"))))))))));
}
TEST(ImportExpr, ImportLabelDeclAndAddrLabelExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"void declToImport() { loop: goto loop; &&loop; }", Lang_C, "", Lang_C,
Verifier,
functionDecl(hasBody(compoundStmt(
has(labelStmt(hasDeclaration(labelDecl(hasName("loop"))))),
has(addrLabelExpr(hasDeclaration(labelDecl(hasName("loop")))))))));
}
AST_MATCHER_P(TemplateDecl, hasTemplateDecl,
internal::Matcher<NamedDecl>, InnerMatcher) {
const NamedDecl *Template = Node.getTemplatedDecl();
return Template && InnerMatcher.matches(*Template, Finder, Builder);
}
TEST(ImportExpr, ImportParenListExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"template<typename T> class dummy { void f() { dummy X(*this); } };"
"typedef dummy<int> declToImport;"
"template class dummy<int>;",
Lang_CXX, "", Lang_CXX, Verifier,
typedefDecl(hasType(templateSpecializationType(
hasDeclaration(classTemplateSpecializationDecl(hasSpecializedTemplate(
classTemplateDecl(hasTemplateDecl(cxxRecordDecl(hasMethod(allOf(
hasName("f"),
hasBody(compoundStmt(has(declStmt(hasSingleDecl(
varDecl(hasInitializer(parenListExpr(has(unaryOperator(
hasOperatorName("*"),
hasUnaryOperand(cxxThisExpr())))))))))))))))))))))));
}
TEST(ImportExpr, ImportSwitch) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { int b; switch (b) { case 1: break; } }",
Lang_C, "", Lang_C, Verifier,
functionDecl(hasBody(compoundStmt(
has(switchStmt(has(compoundStmt(has(caseStmt())))))))));
}
TEST(ImportExpr, ImportStmtExpr) {
MatchVerifier<Decl> Verifier;
// NOTE: has() ignores implicit casts, using hasDescendant() to match it
testImport(
"void declToImport() { int b; int a = b ?: 1; int C = ({int X=4; X;}); }",
Lang_C, "", Lang_C, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
declStmt(
hasSingleDecl(
varDecl(
hasName("C"),
hasType(asString("int")),
hasInitializer(
stmtExpr(
hasAnySubstatement(
declStmt(
hasSingleDecl(
varDecl(
hasName("X"),
hasType(asString("int")),
hasInitializer(
integerLiteral(equals(4))))))),
hasDescendant(
implicitCastExpr()
)))))))))));
}
TEST(ImportExpr, ImportConditionalOperator) {
MatchVerifier<Decl> Verifier;
testImport(
"void declToImport() { true ? 1 : -5; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
conditionalOperator(
hasCondition(cxxBoolLiteral(equals(true))),
hasTrueExpression(integerLiteral(equals(1))),
hasFalseExpression(
unaryOperator(hasUnaryOperand(integerLiteral(equals(5))))
)))))));
}
TEST(ImportExpr, ImportBinaryConditionalOperator) {
MatchVerifier<Decl> Verifier;
testImport(
"void declToImport() { 1 ?: -5; }", Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
binaryConditionalOperator(
hasCondition(
implicitCastExpr(
hasSourceExpression(
opaqueValueExpr(
hasSourceExpression(integerLiteral(equals(1))))),
hasType(booleanType()))),
hasTrueExpression(
opaqueValueExpr(hasSourceExpression(
integerLiteral(equals(1))))),
hasFalseExpression(
unaryOperator(hasOperatorName("-"),
hasUnaryOperand(integerLiteral(equals(5)))))
))))));
}
TEST(ImportExpr, ImportDesignatedInitExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() {"
" struct point { double x; double y; };"
" struct point ptarray[10] = "
"{ [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }",
Lang_C, "", Lang_C, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
declStmt(
hasSingleDecl(
varDecl(
hasInitializer(
initListExpr(
hasSyntacticForm(
initListExpr(
has(
designatedInitExpr(
designatorCountIs(2),
has(floatLiteral(
equals(1.0))),
has(integerLiteral(
equals(2))))),
has(
designatedInitExpr(
designatorCountIs(2),
has(floatLiteral(
equals(2.0))),
has(integerLiteral(
equals(2))))),
has(
designatedInitExpr(
designatorCountIs(2),
has(floatLiteral(
equals(1.0))),
has(integerLiteral(
equals(0)))))
))))))))))));
}
TEST(ImportExpr, ImportPredefinedExpr) {
MatchVerifier<Decl> Verifier;
// __func__ expands as StringLiteral("declToImport")
testImport("void declToImport() { __func__; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
predefinedExpr(
hasType(
asString("const char [13]")),
has(
stringLiteral(
hasType(
asString("const char [13]"))))))))));
}
TEST(ImportExpr, ImportInitListExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"void declToImport() {"
" struct point { double x; double y; };"
" point ptarray[10] = { [2].y = 1.0, [2].x = 2.0,"
" [0].x = 1.0 }; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
declStmt(
hasSingleDecl(
varDecl(
hasInitializer(
initListExpr(
has(
cxxConstructExpr(
requiresZeroInitialization())),
has(
initListExpr(
hasType(asString("struct point")),
has(floatLiteral(equals(1.0))),
has(implicitValueInitExpr(
hasType(asString("double")))))),
has(
initListExpr(
hasType(asString("struct point")),
has(floatLiteral(equals(2.0))),
has(floatLiteral(equals(1.0)))))
))))))))));
}
const internal::VariadicDynCastAllOfMatcher<Expr, VAArgExpr> vaArgExpr;
TEST(ImportExpr, ImportVAArgExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport(__builtin_va_list list, ...) {"
" (void)__builtin_va_arg(list, int); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
cStyleCastExpr(
hasSourceExpression(
vaArgExpr())))))));
}
TEST(ImportExpr, CXXTemporaryObjectExpr) {
MatchVerifier<Decl> Verifier;
testImport("struct C {};"
"void declToImport() { C c = C(); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(hasBody(compoundStmt(has(
declStmt(has(varDecl(has(exprWithCleanups(has(cxxConstructExpr(
has(materializeTemporaryExpr(has(implicitCastExpr(
has(cxxTemporaryObjectExpr())))))))))))))))));
}
TEST(ImportType, ImportAtomicType) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { typedef _Atomic(int) a_int; }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
declStmt(
has(
typedefDecl(
has(atomicType())))))))));
}
TEST(ImportDecl, ImportFunctionTemplateDecl) {
MatchVerifier<Decl> Verifier;
testImport("template <typename T> void declToImport() { };", Lang_CXX, "",
Lang_CXX, Verifier, functionTemplateDecl());
}
const internal::VariadicDynCastAllOfMatcher<Expr, CXXDependentScopeMemberExpr>
cxxDependentScopeMemberExpr;
TEST(ImportExpr, ImportCXXDependentScopeMemberExpr) {
MatchVerifier<Decl> Verifier;
testImport("template <typename T> struct C { T t; };"
"template <typename T> void declToImport() {"
" C<T> d;"
" d.t;"
"}"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionTemplateDecl(has(functionDecl(
has(compoundStmt(has(cxxDependentScopeMemberExpr())))))));
testImport("template <typename T> struct C { T t; };"
"template <typename T> void declToImport() {"
" C<T> d;"
" (&d)->t;"
"}"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionTemplateDecl(has(functionDecl(
has(compoundStmt(has(cxxDependentScopeMemberExpr())))))));
}
TEST(ImportType, ImportTypeAliasTemplate) {
MatchVerifier<Decl> Verifier;
testImport(
"template <int K>"
"struct dummy { static const int i = K; };"
"template <int K> using dummy2 = dummy<K>;"
"int declToImport() { return dummy2<3>::i; }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(
hasBody(compoundStmt(
has(returnStmt(has(implicitCastExpr(has(declRefExpr()))))))),
unless(hasAncestor(translationUnitDecl(has(typeAliasDecl()))))));
}
const internal::VariadicDynCastAllOfMatcher<Decl, VarTemplateSpecializationDecl>
varTemplateSpecializationDecl;
TEST(ImportDecl, ImportVarTemplate) {
MatchVerifier<Decl> Verifier;
testImport(
"template <typename T>"
"T pi = T(3.1415926535897932385L);"
"void declToImport() { pi<int>; }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(
hasBody(has(declRefExpr(to(varTemplateSpecializationDecl())))),
unless(hasAncestor(translationUnitDecl(has(varDecl(
hasName("pi"), unless(varTemplateSpecializationDecl()))))))));
}
TEST(ImportType, ImportPackExpansion) {
MatchVerifier<Decl> Verifier;
testImport("template <typename... Args>"
"struct dummy {"
" dummy(Args... args) {}"
" static const int i = 4;"
"};"
"int declToImport() { return dummy<int>::i; }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
returnStmt(
has(
implicitCastExpr(
has(
declRefExpr())))))))));
}
const internal::VariadicDynCastAllOfMatcher<Type,
DependentTemplateSpecializationType>
dependentTemplateSpecializationType;
TEST(ImportType, ImportDependentTemplateSpecialization) {
MatchVerifier<Decl> Verifier;
testImport("template<typename T>"
"struct A;"
"template<typename T>"
"struct declToImport {"
" typename A<T>::template B<T> a;"
"};",
Lang_CXX, "", Lang_CXX, Verifier,
classTemplateDecl(has(cxxRecordDecl(has(
fieldDecl(hasType(dependentTemplateSpecializationType())))))));
}
const internal::VariadicDynCastAllOfMatcher<Stmt, SizeOfPackExpr>
sizeOfPackExpr;
TEST(ImportExpr, ImportSizeOfPackExpr) {
MatchVerifier<Decl> Verifier;
testImport("template <typename... Ts>"
"void declToImport() {"
" const int i = sizeof...(Ts);"
"};"
"void g() { declToImport<int>(); }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionTemplateDecl(has(functionDecl(
hasBody(compoundStmt(has(declStmt(has(varDecl(hasInitializer(
implicitCastExpr(has(sizeOfPackExpr())))))))))))));
testImport(
"template <typename... Ts>"
"using X = int[sizeof...(Ts)];"
"template <typename... Us>"
"struct Y {"
" X<Us..., int, double, int, Us...> f;"
"};"
"Y<float, int> declToImport;",
Lang_CXX11, "", Lang_CXX11, Verifier,
varDecl(hasType(classTemplateSpecializationDecl(has(fieldDecl(hasType(
hasUnqualifiedDesugaredType(constantArrayType(hasSize(7))))))))));
}
/// \brief Matches __builtin_types_compatible_p:
/// GNU extension to check equivalent types
/// Given
/// \code
/// __builtin_types_compatible_p(int, int)
/// \endcode
// will generate TypeTraitExpr <...> 'int'
const internal::VariadicDynCastAllOfMatcher<Stmt, TypeTraitExpr> typeTraitExpr;
TEST(ImportExpr, ImportTypeTraitExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { "
" __builtin_types_compatible_p(int, int);"
"}",
Lang_C, "", Lang_C, Verifier,
functionDecl(
hasBody(
compoundStmt(
has(
typeTraitExpr(hasType(asString("int"))))))));
}
const internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> cxxTypeidExpr;
TEST(ImportExpr, ImportCXXTypeidExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"namespace std { class type_info {}; }"
"void declToImport() {"
" int x;"
" auto a = typeid(int); auto b = typeid(x);"
"}",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(
hasDescendant(varDecl(
hasName("a"), hasInitializer(hasDescendant(cxxTypeidExpr())))),
hasDescendant(varDecl(
hasName("b"), hasInitializer(hasDescendant(cxxTypeidExpr()))))));
}
TEST(ImportExpr, ImportTypeTraitExprValDep) {
MatchVerifier<Decl> Verifier;
testImport("template<typename T> struct declToImport {"
" void m() { __is_pod(T); }"
"};"
"void f() { declToImport<int>().m(); }",
Lang_CXX11, "", Lang_CXX11, Verifier,
classTemplateDecl(
has(
cxxRecordDecl(
has(
functionDecl(
hasBody(
compoundStmt(
has(
typeTraitExpr(
hasType(booleanType())
))))))))));
}
const internal::VariadicDynCastAllOfMatcher<Expr, CXXPseudoDestructorExpr>
cxxPseudoDestructorExpr;
TEST(ImportExpr, ImportCXXPseudoDestructorExpr) {
MatchVerifier<Decl> Verifier;
testImport("typedef int T;"
"void declToImport(int *p) {"
" T t;"
" p->T::~T();"
"}",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(has(compoundStmt(has(
callExpr(has(cxxPseudoDestructorExpr())))))));
}
TEST(ImportDecl, ImportUsingDecl) {
MatchVerifier<Decl> Verifier;
testImport("namespace foo { int bar; }"
"void declToImport() { using foo::bar; }",
Lang_CXX, "", Lang_CXX, Verifier,
functionDecl(
has(
compoundStmt(
has(
declStmt(
has(
usingDecl())))))));
}
/// \brief Matches shadow declarations introduced into a scope by a
/// (resolved) using declaration.
///
/// Given
/// \code
/// namespace n { int f; }
/// namespace declToImport { using n::f; }
/// \endcode
/// usingShadowDecl()
/// matches \code f \endcode
const internal::VariadicDynCastAllOfMatcher<Decl,
UsingShadowDecl> usingShadowDecl;
TEST(ImportDecl, ImportUsingShadowDecl) {
MatchVerifier<Decl> Verifier;
testImport("namespace foo { int bar; }"
"namespace declToImport { using foo::bar; }",
Lang_CXX, "", Lang_CXX, Verifier,
namespaceDecl(has(usingShadowDecl())));
}
TEST(ImportExpr, ImportUnresolvedLookupExpr) {
MatchVerifier<Decl> Verifier;
testImport("template<typename T> int foo();"
"template <typename T> void declToImport() {"
" ::foo<T>;"
" ::template foo<T>;"
"}"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionTemplateDecl(has(functionDecl(
has(compoundStmt(has(unresolvedLookupExpr())))))));
}
TEST(ImportExpr, ImportCXXUnresolvedConstructExpr) {
MatchVerifier<Decl> Verifier;
testImport("template <typename T> struct C { T t; };"
"template <typename T> void declToImport() {"
" C<T> d;"
" d.t = T();"
"}"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionTemplateDecl(has(functionDecl(has(compoundStmt(has(
binaryOperator(has(cxxUnresolvedConstructExpr())))))))));
testImport("template <typename T> struct C { T t; };"
"template <typename T> void declToImport() {"
" C<T> d;"
" (&d)->t = T();"
"}"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionTemplateDecl(has(functionDecl(has(compoundStmt(has(
binaryOperator(has(cxxUnresolvedConstructExpr())))))))));
}
/// Check that function "declToImport()" (which is the templated function
/// for corresponding FunctionTemplateDecl) is not added into DeclContext.
/// Same for class template declarations.
TEST(ImportDecl, ImportTemplatedDeclForTemplate) {
MatchVerifier<Decl> Verifier;
testImport("template <typename T> void declToImport() { T a = 1; }"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX, Verifier,
functionTemplateDecl(hasAncestor(translationUnitDecl(
unless(has(functionDecl(hasName("declToImport"))))))));
testImport("template <typename T> struct declToImport { T t; };"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX, Verifier,
classTemplateDecl(hasAncestor(translationUnitDecl(
unless(has(cxxRecordDecl(hasName("declToImport"))))))));
}
TEST(ImportExpr, CXXOperatorCallExpr) {
MatchVerifier<Decl> Verifier;
testImport("class declToImport {"
" void f() { *this = declToImport(); }"
"};",
Lang_CXX, "", Lang_CXX, Verifier,
cxxRecordDecl(has(cxxMethodDecl(hasBody(compoundStmt(
has(exprWithCleanups(has(cxxOperatorCallExpr())))))))));
}
TEST(ImportExpr, DependentSizedArrayType) {
MatchVerifier<Decl> Verifier;
testImport("template<typename T, int Size> class declToImport {"
" T data[Size];"
"};",
Lang_CXX, "", Lang_CXX, Verifier,
classTemplateDecl(has(cxxRecordDecl(
has(fieldDecl(hasType(dependentSizedArrayType())))))));
}
TEST_P(ASTImporterTestBase, DISABLED_ImportFunctionWithBackReferringParameter) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template <typename T> struct X {};
void declToImport(int y, X<int> &x) {}
template <> struct X<int> {
void g() {
X<int> x;
declToImport(0, x);
}
};
)",
Lang_CXX, "", Lang_CXX);
MatchVerifier<Decl> Verifier;
auto Matcher = functionDecl(hasName("declToImport"),
parameterCountIs(2),
hasParameter(0, hasName("y")),
hasParameter(1, hasName("x")),
hasParameter(1, hasType(asString("X<int> &"))));
ASSERT_TRUE(Verifier.match(From, Matcher));
EXPECT_TRUE(Verifier.match(To, Matcher));
}
TEST_P(ASTImporterTestBase,
TUshouldNotContainTemplatedDeclOfFunctionTemplates) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("template <typename T> void declToImport() { T a = 1; }"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX);
auto Check = [](Decl *D) -> bool {
auto TU = D->getTranslationUnitDecl();
for (auto Child : TU->decls()) {
if (auto *FD = dyn_cast<FunctionDecl>(Child)) {
if (FD->getNameAsString() == "declToImport") {
GTEST_NONFATAL_FAILURE_(
"TU should not contain any FunctionDecl with name declToImport");
return false;
}
}
}
return true;
};
ASSERT_TRUE(Check(From));
EXPECT_TRUE(Check(To));
}
TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfClassTemplates) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("template <typename T> struct declToImport { T t; };"
"void instantiate() { declToImport<int>(); }",
Lang_CXX, "", Lang_CXX);
auto Check = [](Decl *D) -> bool {
auto TU = D->getTranslationUnitDecl();
for (auto Child : TU->decls()) {
if (auto *RD = dyn_cast<CXXRecordDecl>(Child)) {
if (RD->getNameAsString() == "declToImport") {
GTEST_NONFATAL_FAILURE_(
"TU should not contain any CXXRecordDecl with name declToImport");
return false;
}
}
}
return true;
};
ASSERT_TRUE(Check(From));
EXPECT_TRUE(Check(To));
}
TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfTypeAlias) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl(
"template <typename T> struct X {};"
"template <typename T> using declToImport = X<T>;"
"void instantiate() { declToImport<int> a; }",
Lang_CXX11, "", Lang_CXX11);
auto Check = [](Decl *D) -> bool {
auto TU = D->getTranslationUnitDecl();
for (auto Child : TU->decls()) {
if (auto *AD = dyn_cast<TypeAliasDecl>(Child)) {
if (AD->getNameAsString() == "declToImport") {
GTEST_NONFATAL_FAILURE_(
"TU should not contain any TypeAliasDecl with name declToImport");
return false;
}
}
}
return true;
};
ASSERT_TRUE(Check(From));
EXPECT_TRUE(Check(To));
}
TEST_P(
ASTImporterTestBase,
DISABLED_TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template<class T>
class Base {};
class declToImport : public Base<declToImport> {};
)",
Lang_CXX, "", Lang_CXX);
// Check that the ClassTemplateSpecializationDecl is NOT the child of the TU.
auto Pattern =
translationUnitDecl(unless(has(classTemplateSpecializationDecl())));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
// Check that the ClassTemplateSpecializationDecl is the child of the
// ClassTemplateDecl.
Pattern = translationUnitDecl(has(classTemplateDecl(
hasName("Base"), has(classTemplateSpecializationDecl()))));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}
AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) {
size_t Index = 0;
for (FieldDecl *Field : Node.fields()) {
if (Index == Order.size())
return false;
if (Field->getName() != Order[Index])
return false;
++Index;
}
return Index == Order.size();
}
TEST_P(ASTImporterTestBase,
TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
namespace NS {
template<class T>
class X {};
template class X<int>;
}
)",
Lang_CXX, "", Lang_CXX, "NS");
// Check that the ClassTemplateSpecializationDecl is NOT the child of the
// ClassTemplateDecl.
auto Pattern = namespaceDecl(has(classTemplateDecl(
hasName("X"), unless(has(classTemplateSpecializationDecl())))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));
// Check that the ClassTemplateSpecializationDecl is the child of the
// NamespaceDecl.
Pattern = namespaceDecl(has(classTemplateSpecializationDecl(hasName("X"))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));
}
TEST_P(ASTImporterTestBase, CXXRecordDeclFieldsShouldBeInCorrectOrder) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl(
"struct declToImport { int a; int b; };",
Lang_CXX11, "", Lang_CXX11);
MatchVerifier<Decl> Verifier;
ASSERT_TRUE(Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
}
TEST_P(ASTImporterTestBase,
DISABLED_CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
// The original recursive algorithm of ASTImporter first imports 'c' then
// 'b' and lastly 'a'. Therefore we must restore the order somehow.
R"s(
struct declToImport {
int a = c + b;
int b = 1;
int c = 2;
};
)s",
Lang_CXX11, "", Lang_CXX11);
MatchVerifier<Decl> Verifier;
ASSERT_TRUE(
Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
EXPECT_TRUE(
Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
}
TEST_P(ASTImporterTestBase, DISABLED_ShouldImportImplicitCXXRecordDecl) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template <typename U>
struct declToImport {
};
)",
Lang_CXX, "", Lang_CXX);
MatchVerifier<Decl> Verifier;
// Match the implicit Decl.
auto Matcher = classTemplateDecl(has(cxxRecordDecl(has(cxxRecordDecl()))));
ASSERT_TRUE(Verifier.match(From, Matcher));
EXPECT_TRUE(Verifier.match(To, Matcher));
}
TEST_P(
ASTImporterTestBase,
DISABLED_ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template<class T>
class Base {};
class declToImport : public Base<declToImport> {};
)",
Lang_CXX, "", Lang_CXX);
auto hasImplicitClass = has(cxxRecordDecl());
auto Pattern = translationUnitDecl(has(classTemplateDecl(
hasName("Base"),
has(classTemplateSpecializationDecl(hasImplicitClass)))));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}
TEST_P(ASTImporterTestBase, IDNSOrdinary) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("void declToImport() {}", Lang_CXX, "", Lang_CXX);
MatchVerifier<Decl> Verifier;
auto Matcher = functionDecl();
ASSERT_TRUE(Verifier.match(From, Matcher));
EXPECT_TRUE(Verifier.match(To, Matcher));
EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
}
TEST_P(ASTImporterTestBase, DISABLED_IDNSOfNonmemberOperator) {
Decl *FromTU = getTuDecl(
R"(
struct X {};
void operator<<(int, X);
)",
Lang_CXX);
Decl *From = LastDeclMatcher<Decl>{}.match(FromTU, functionDecl());
const Decl *To = Import(From, Lang_CXX);
EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
}
TEST_P(ASTImporterTestBase,
ShouldImportMembersOfClassTemplateSpecializationDecl) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template<class T>
class Base { int a; };
class declToImport : Base<declToImport> {};
)",
Lang_CXX, "", Lang_CXX);
auto Pattern = translationUnitDecl(has(classTemplateDecl(
hasName("Base"),
has(classTemplateSpecializationDecl(has(fieldDecl(hasName("a"))))))));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}
INSTANTIATE_TEST_CASE_P(
ParameterizedTests, ASTImporterTestBase,
::testing::Values(ArgVector(), ArgVector{"-fdelayed-template-parsing"}),);
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,
DefinitionShouldBeImportedAsDefintionWhenThereIsAPrototype) {
Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX);
auto Pattern = functionDecl(hasName("f"));
FunctionDecl *FromD = // Definition
LastDeclMatcher<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, DefinitionShouldBeImportedAsADefinition) {
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, DISABLED_ImportPrototypeOfRecursiveFunction) {
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
auto Pattern = functionDecl(hasName("f"));
FunctionDecl *PrototypeFD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(PrototypeFD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) {
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
auto Pattern = functionDecl(hasName("f"));
FunctionDecl *DefinitionFD =
LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(DefinitionFD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
TEST_P(ImportFunctions, ImportPrototypes) {
auto Pattern = functionDecl(hasName("f"));
Decl *ImportedD;
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, Lang_CXX);
}
Decl *ImportedD1;
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD1 = Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
EXPECT_EQ(ImportedD, ImportedD1);
EXPECT_TRUE(!cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
auto Pattern = functionDecl(hasName("f"));
Decl *ImportedD;
{
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, Lang_CXX);
}
Decl *ImportedD1;
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD1 = Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
EXPECT_EQ(ImportedD, ImportedD1);
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
TEST_P(ImportFunctions, ImportPrototypeThenDefinition) {
auto Pattern = functionDecl(hasName("f"));
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input1.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(!ProtoD->doesThisDeclarationHaveABody());
FunctionDecl *DefinitionD =
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
}
TEST_P(ImportFunctions, DISABLED_ImportPrototypeThenProtoAndDefinition) {
auto Pattern = functionDecl(hasName("f"));
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl("void f(); void f(){}", Lang_CXX, "input1.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(!ProtoD->doesThisDeclarationHaveABody());
FunctionDecl *DefinitionD =
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
}
TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) {
auto Code =
R"(
struct B { virtual void f(); };
void B::f() {}
struct D : B { void f(); };
)";
auto Pattern =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
Decl *FromTU = getTuDecl(Code, Lang_CXX);
CXXMethodDecl *Proto =
FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
ASSERT_EQ(Proto->size_overridden_methods(), 1u);
CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX));
EXPECT_EQ(To->size_overridden_methods(), 1u);
}
TEST_P(ImportFunctions, VirtualFlagShouldBePreservedWhenImportingPrototype) {
auto Code =
R"(
struct B { virtual void f(); };
void B::f() {}
)";
auto Pattern =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
Decl *FromTU = getTuDecl(Code, Lang_CXX);
CXXMethodDecl *Proto =
FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
CXXMethodDecl *Def = LastDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
ASSERT_TRUE(Proto->isVirtual());
ASSERT_TRUE(Def->isVirtual());
CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX));
EXPECT_TRUE(To->isVirtual());
}
INSTANTIATE_TEST_CASE_P(
ParameterizedTests, ImportFunctions,
::testing::Values(ArgVector(), ArgVector{"-fdelayed-template-parsing"}),);
AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>,
InnerMatcher) {
if (auto *Typedef = Node.getTypedefNameForAnonDecl())
return InnerMatcher.matches(*Typedef, Finder, Builder);
return false;
}
TEST(ImportDecl, ImportEnumSequential) {
CodeFiles Samples{{"main.c",
{"void foo();"
"void moo();"
"int main() { foo(); moo(); }",
Lang_C}},
{"foo.c",
{"typedef enum { THING_VALUE } thing_t;"
"void conflict(thing_t type);"
"void foo() { (void)THING_VALUE; }"
"void conflict(thing_t type) {}",
Lang_C}},
{"moo.c",
{"typedef enum { THING_VALUE } thing_t;"
"void conflict(thing_t type);"
"void moo() { conflict(THING_VALUE); }",
Lang_C}}};
auto VerificationMatcher =
enumDecl(has(enumConstantDecl(hasName("THING_VALUE"))),
hasTypedefForAnonDecl(hasName("thing_t")));
ImportAction ImportFoo{"foo.c", "main.c", functionDecl(hasName("foo"))},
ImportMoo{"moo.c", "main.c", functionDecl(hasName("moo"))};
testImportSequence(
Samples, {ImportFoo, ImportMoo}, // "foo", them "moo".
// Just check that there is only one enum decl in the result AST.
"main.c", enumDecl(), VerificationMatcher);
// For different import order, result should be the same.
testImportSequence(
Samples, {ImportMoo, ImportFoo}, // "moo", them "foo".
// Check that there is only one enum decl in the result AST.
"main.c", enumDecl(), VerificationMatcher);
}
} // end namespace ast_matchers
} // end namespace clang