Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,240 @@
// unittests/ASTMatchers/ASTMatchersInternalTest.cpp - AST matcher unit tests //
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ASTMatchersTest.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Host.h"
#include "gtest/gtest.h"
namespace clang {
namespace ast_matchers {
#if GTEST_HAS_DEATH_TEST
TEST(HasNameDeathTest, DiesOnEmptyName) {
ASSERT_DEBUG_DEATH({
DeclarationMatcher HasEmptyName = recordDecl(hasName(""));
EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
}, "");
}
TEST(HasNameDeathTest, DiesOnEmptyPattern) {
ASSERT_DEBUG_DEATH({
DeclarationMatcher HasEmptyName = recordDecl(matchesName(""));
EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
}, "");
}
TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) {
ASSERT_DEBUG_DEATH({
DeclarationMatcher IsDerivedFromEmpty = cxxRecordDecl(isDerivedFrom(""));
EXPECT_TRUE(notMatches("class X {};", IsDerivedFromEmpty));
}, "");
}
#endif
TEST(ConstructVariadic, MismatchedTypes_Regression) {
EXPECT_TRUE(
matches("const int a = 0;",
internal::DynTypedMatcher::constructVariadic(
internal::DynTypedMatcher::VO_AnyOf,
ast_type_traits::ASTNodeKind::getFromNodeKind<QualType>(),
{isConstQualified(), arrayType()})
.convertTo<QualType>()));
}
// For testing AST_MATCHER_P().
AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) {
// Make sure all special variables are used: node, match_finder,
// bound_nodes_builder, and the parameter named 'AMatcher'.
return AMatcher.matches(Node, Finder, Builder);
}
TEST(AstMatcherPMacro, Works) {
DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b")));
EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
HasClassB, llvm::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
HasClassB, llvm::make_unique<VerifyIdIsBoundTo<Decl>>("a")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
HasClassB, llvm::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
}
AST_POLYMORPHIC_MATCHER_P(polymorphicHas,
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt),
internal::Matcher<Decl>, AMatcher) {
return Finder->matchesChildOf(
Node, AMatcher, Builder,
ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses,
ASTMatchFinder::BK_First);
}
TEST(AstPolymorphicMatcherPMacro, Works) {
DeclarationMatcher HasClassB =
polymorphicHas(recordDecl(hasName("B")).bind("b"));
EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
HasClassB, llvm::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
HasClassB, llvm::make_unique<VerifyIdIsBoundTo<Decl>>("a")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
HasClassB, llvm::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
StatementMatcher StatementHasClassB =
polymorphicHas(recordDecl(hasName("B")));
EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB));
}
TEST(MatchFinder, CheckProfiling) {
MatchFinder::MatchFinderOptions Options;
llvm::StringMap<llvm::TimeRecord> Records;
Options.CheckProfiling.emplace(Records);
MatchFinder Finder(std::move(Options));
struct NamedCallback : public MatchFinder::MatchCallback {
void run(const MatchFinder::MatchResult &Result) override {}
StringRef getID() const override { return "MyID"; }
} Callback;
Finder.addMatcher(decl(), &Callback);
std::unique_ptr<FrontendActionFactory> Factory(
newFrontendActionFactory(&Finder));
ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
EXPECT_EQ(1u, Records.size());
EXPECT_EQ("MyID", Records.begin()->getKey());
}
class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback {
public:
VerifyStartOfTranslationUnit() : Called(false) {}
void run(const MatchFinder::MatchResult &Result) override {
EXPECT_TRUE(Called);
}
void onStartOfTranslationUnit() override { Called = true; }
bool Called;
};
TEST(MatchFinder, InterceptsStartOfTranslationUnit) {
MatchFinder Finder;
VerifyStartOfTranslationUnit VerifyCallback;
Finder.addMatcher(decl(), &VerifyCallback);
std::unique_ptr<FrontendActionFactory> Factory(
newFrontendActionFactory(&Finder));
ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
EXPECT_TRUE(VerifyCallback.Called);
VerifyCallback.Called = false;
std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
ASSERT_TRUE(AST.get());
Finder.matchAST(AST->getASTContext());
EXPECT_TRUE(VerifyCallback.Called);
}
class VerifyEndOfTranslationUnit : public MatchFinder::MatchCallback {
public:
VerifyEndOfTranslationUnit() : Called(false) {}
void run(const MatchFinder::MatchResult &Result) override {
EXPECT_FALSE(Called);
}
void onEndOfTranslationUnit() override { Called = true; }
bool Called;
};
TEST(MatchFinder, InterceptsEndOfTranslationUnit) {
MatchFinder Finder;
VerifyEndOfTranslationUnit VerifyCallback;
Finder.addMatcher(decl(), &VerifyCallback);
std::unique_ptr<FrontendActionFactory> Factory(
newFrontendActionFactory(&Finder));
ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
EXPECT_TRUE(VerifyCallback.Called);
VerifyCallback.Called = false;
std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
ASSERT_TRUE(AST.get());
Finder.matchAST(AST->getASTContext());
EXPECT_TRUE(VerifyCallback.Called);
}
TEST(Matcher, matchOverEntireASTContext) {
std::unique_ptr<ASTUnit> AST =
clang::tooling::buildASTFromCode("struct { int *foo; };");
ASSERT_TRUE(AST.get());
auto PT = selectFirst<PointerType>(
"x", match(pointerType().bind("x"), AST->getASTContext()));
EXPECT_NE(nullptr, PT);
}
TEST(IsInlineMatcher, IsInline) {
EXPECT_TRUE(matches("void g(); inline void f();",
functionDecl(isInline(), hasName("f"))));
EXPECT_TRUE(matches("namespace n { inline namespace m {} }",
namespaceDecl(isInline(), hasName("m"))));
}
// FIXME: Figure out how to specify paths so the following tests pass on
// Windows.
#ifndef LLVM_ON_WIN32
TEST(Matcher, IsExpansionInMainFileMatcher) {
EXPECT_TRUE(matches("class X {};",
recordDecl(hasName("X"), isExpansionInMainFile())));
EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile())));
FileContentMappings M;
M.push_back(std::make_pair("/other", "class X {};"));
EXPECT_TRUE(matchesConditionally("#include <other>\n",
recordDecl(isExpansionInMainFile()), false,
"-isystem/", M));
}
TEST(Matcher, IsExpansionInSystemHeader) {
FileContentMappings M;
M.push_back(std::make_pair("/other", "class X {};"));
EXPECT_TRUE(matchesConditionally(
"#include \"other\"\n", recordDecl(isExpansionInSystemHeader()), true,
"-isystem/", M));
EXPECT_TRUE(matchesConditionally("#include \"other\"\n",
recordDecl(isExpansionInSystemHeader()),
false, "-I/", M));
EXPECT_TRUE(notMatches("class X {};",
recordDecl(isExpansionInSystemHeader())));
EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader())));
}
TEST(Matcher, IsExpansionInFileMatching) {
FileContentMappings M;
M.push_back(std::make_pair("/foo", "class A {};"));
M.push_back(std::make_pair("/bar", "class B {};"));
EXPECT_TRUE(matchesConditionally(
"#include <foo>\n"
"#include <bar>\n"
"class X {};",
recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true,
"-isystem/", M));
EXPECT_TRUE(matchesConditionally(
"#include <foo>\n"
"#include <bar>\n"
"class X {};",
recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false,
"-isystem/", M));
}
#endif // LLVM_ON_WIN32
} // end namespace ast_matchers
} // end namespace clang

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,376 @@
//===- unittest/Tooling/ASTMatchersTest.h - Matcher tests helpers ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H
#define LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
namespace clang {
namespace ast_matchers {
using clang::tooling::buildASTFromCodeWithArgs;
using clang::tooling::newFrontendActionFactory;
using clang::tooling::runToolOnCodeWithArgs;
using clang::tooling::FrontendActionFactory;
using clang::tooling::FileContentMappings;
class BoundNodesCallback {
public:
virtual ~BoundNodesCallback() {}
virtual bool run(const BoundNodes *BoundNodes) = 0;
virtual bool run(const BoundNodes *BoundNodes, ASTContext *Context) = 0;
virtual void onEndOfTranslationUnit() {}
};
// If 'FindResultVerifier' is not NULL, sets *Verified to the result of
// running 'FindResultVerifier' with the bound nodes as argument.
// If 'FindResultVerifier' is NULL, sets *Verified to true when Run is called.
class VerifyMatch : public MatchFinder::MatchCallback {
public:
VerifyMatch(std::unique_ptr<BoundNodesCallback> FindResultVerifier, bool *Verified)
: Verified(Verified), FindResultReviewer(std::move(FindResultVerifier)) {}
void run(const MatchFinder::MatchResult &Result) override {
if (FindResultReviewer != nullptr) {
*Verified |= FindResultReviewer->run(&Result.Nodes, Result.Context);
} else {
*Verified = true;
}
}
void onEndOfTranslationUnit() override {
if (FindResultReviewer)
FindResultReviewer->onEndOfTranslationUnit();
}
private:
bool *const Verified;
const std::unique_ptr<BoundNodesCallback> FindResultReviewer;
};
template <typename T>
testing::AssertionResult matchesConditionally(
const std::string &Code, const T &AMatcher, bool ExpectMatch,
llvm::ArrayRef<llvm::StringRef> CompileArgs,
const FileContentMappings &VirtualMappedFiles = FileContentMappings(),
const std::string &Filename = "input.cc") {
bool Found = false, DynamicFound = false;
MatchFinder Finder;
VerifyMatch VerifyFound(nullptr, &Found);
Finder.addMatcher(AMatcher, &VerifyFound);
VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound);
if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound))
return testing::AssertionFailure() << "Could not add dynamic matcher";
std::unique_ptr<FrontendActionFactory> Factory(
newFrontendActionFactory(&Finder));
// Some tests need rtti/exceptions on. Use an unknown-unknown triple so we
// don't instantiate the full system toolchain. On Linux, instantiating the
// toolchain involves stat'ing large portions of /usr/lib, and this slows down
// not only this test, but all other tests, via contention in the kernel.
//
// FIXME: This is a hack to work around the fact that there's no way to do the
// equivalent of runToolOnCodeWithArgs without instantiating a full Driver.
// We should consider having a function, at least for tests, that invokes cc1.
std::vector<std::string> Args(CompileArgs.begin(), CompileArgs.end());
Args.insert(Args.end(), {"-frtti", "-fexceptions",
"-target", "i386-unknown-unknown"});
if (!runToolOnCodeWithArgs(
Factory->create(), Code, Args, Filename, "clang-tool",
std::make_shared<PCHContainerOperations>(), VirtualMappedFiles)) {
return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
}
if (Found != DynamicFound) {
return testing::AssertionFailure() << "Dynamic match result ("
<< DynamicFound
<< ") does not match static result ("
<< Found << ")";
}
if (!Found && ExpectMatch) {
return testing::AssertionFailure()
<< "Could not find match in \"" << Code << "\"";
} else if (Found && !ExpectMatch) {
return testing::AssertionFailure()
<< "Found unexpected match in \"" << Code << "\"";
}
return testing::AssertionSuccess();
}
template <typename T>
testing::AssertionResult matchesConditionally(
const std::string &Code, const T &AMatcher, bool ExpectMatch,
llvm::StringRef CompileArg,
const FileContentMappings &VirtualMappedFiles = FileContentMappings(),
const std::string &Filename = "input.cc") {
return matchesConditionally(Code, AMatcher, ExpectMatch,
llvm::makeArrayRef(CompileArg),
VirtualMappedFiles, Filename);
}
template <typename T>
testing::AssertionResult matches(const std::string &Code, const T &AMatcher) {
return matchesConditionally(Code, AMatcher, true, "-std=c++11");
}
template <typename T>
testing::AssertionResult notMatches(const std::string &Code,
const T &AMatcher) {
return matchesConditionally(Code, AMatcher, false, "-std=c++11");
}
template <typename T>
testing::AssertionResult matchesObjC(const std::string &Code, const T &AMatcher,
bool ExpectMatch = true) {
return matchesConditionally(Code, AMatcher, ExpectMatch,
{"-fobjc-nonfragile-abi", "-Wno-objc-root-class",
"-Wno-incomplete-implementation"},
FileContentMappings(), "input.m");
}
template <typename T>
testing::AssertionResult matchesC(const std::string &Code, const T &AMatcher) {
return matchesConditionally(Code, AMatcher, true, "", FileContentMappings(),
"input.c");
}
template <typename T>
testing::AssertionResult matchesC99(const std::string &Code,
const T &AMatcher) {
return matchesConditionally(Code, AMatcher, true, "-std=c99",
FileContentMappings(), "input.c");
}
template <typename T>
testing::AssertionResult notMatchesC(const std::string &Code,
const T &AMatcher) {
return matchesConditionally(Code, AMatcher, false, "", FileContentMappings(),
"input.c");
}
template <typename T>
testing::AssertionResult notMatchesObjC(const std::string &Code,
const T &AMatcher) {
return matchesObjC(Code, AMatcher, false);
}
// Function based on matchesConditionally with "-x cuda" argument added and
// small CUDA header prepended to the code string.
template <typename T>
testing::AssertionResult matchesConditionallyWithCuda(
const std::string &Code, const T &AMatcher, bool ExpectMatch,
llvm::StringRef CompileArg) {
const std::string CudaHeader =
"typedef unsigned int size_t;\n"
"#define __constant__ __attribute__((constant))\n"
"#define __device__ __attribute__((device))\n"
"#define __global__ __attribute__((global))\n"
"#define __host__ __attribute__((host))\n"
"#define __shared__ __attribute__((shared))\n"
"struct dim3 {"
" unsigned x, y, z;"
" __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1)"
" : x(x), y(y), z(z) {}"
"};"
"typedef struct cudaStream *cudaStream_t;"
"int cudaConfigureCall(dim3 gridSize, dim3 blockSize,"
" size_t sharedSize = 0,"
" cudaStream_t stream = 0);";
bool Found = false, DynamicFound = false;
MatchFinder Finder;
VerifyMatch VerifyFound(nullptr, &Found);
Finder.addMatcher(AMatcher, &VerifyFound);
VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound);
if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound))
return testing::AssertionFailure() << "Could not add dynamic matcher";
std::unique_ptr<FrontendActionFactory> Factory(
newFrontendActionFactory(&Finder));
// Some tests use typeof, which is a gnu extension. Using an explicit
// unknown-unknown triple is good for a large speedup, because it lets us
// avoid constructing a full system triple.
std::vector<std::string> Args = {
"-xcuda", "-fno-ms-extensions", "--cuda-host-only", "-nocudainc",
"-target", "x86_64-unknown-unknown", CompileArg};
if (!runToolOnCodeWithArgs(Factory->create(),
CudaHeader + Code, Args)) {
return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
}
if (Found != DynamicFound) {
return testing::AssertionFailure() << "Dynamic match result ("
<< DynamicFound
<< ") does not match static result ("
<< Found << ")";
}
if (!Found && ExpectMatch) {
return testing::AssertionFailure()
<< "Could not find match in \"" << Code << "\"";
} else if (Found && !ExpectMatch) {
return testing::AssertionFailure()
<< "Found unexpected match in \"" << Code << "\"";
}
return testing::AssertionSuccess();
}
template <typename T>
testing::AssertionResult matchesWithCuda(const std::string &Code,
const T &AMatcher) {
return matchesConditionallyWithCuda(Code, AMatcher, true, "-std=c++11");
}
template <typename T>
testing::AssertionResult notMatchesWithCuda(const std::string &Code,
const T &AMatcher) {
return matchesConditionallyWithCuda(Code, AMatcher, false, "-std=c++11");
}
template <typename T>
testing::AssertionResult
matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher,
std::unique_ptr<BoundNodesCallback> FindResultVerifier,
bool ExpectResult) {
bool VerifiedResult = false;
MatchFinder Finder;
VerifyMatch VerifyVerifiedResult(std::move(FindResultVerifier), &VerifiedResult);
Finder.addMatcher(AMatcher, &VerifyVerifiedResult);
std::unique_ptr<FrontendActionFactory> Factory(
newFrontendActionFactory(&Finder));
// Some tests use typeof, which is a gnu extension. Using an explicit
// unknown-unknown triple is good for a large speedup, because it lets us
// avoid constructing a full system triple.
std::vector<std::string> Args = {"-std=gnu++98", "-target",
"i386-unknown-unknown"};
if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) {
return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
}
if (!VerifiedResult && ExpectResult) {
return testing::AssertionFailure()
<< "Could not verify result in \"" << Code << "\"";
} else if (VerifiedResult && !ExpectResult) {
return testing::AssertionFailure()
<< "Verified unexpected result in \"" << Code << "\"";
}
VerifiedResult = false;
std::unique_ptr<ASTUnit> AST(buildASTFromCodeWithArgs(Code, Args));
if (!AST.get())
return testing::AssertionFailure() << "Parsing error in \"" << Code
<< "\" while building AST";
Finder.matchAST(AST->getASTContext());
if (!VerifiedResult && ExpectResult) {
return testing::AssertionFailure()
<< "Could not verify result in \"" << Code << "\" with AST";
} else if (VerifiedResult && !ExpectResult) {
return testing::AssertionFailure()
<< "Verified unexpected result in \"" << Code << "\" with AST";
}
return testing::AssertionSuccess();
}
// FIXME: Find better names for these functions (or document what they
// do more precisely).
template <typename T>
testing::AssertionResult
matchAndVerifyResultTrue(const std::string &Code, const T &AMatcher,
std::unique_ptr<BoundNodesCallback> FindResultVerifier) {
return matchAndVerifyResultConditionally(
Code, AMatcher, std::move(FindResultVerifier), true);
}
template <typename T>
testing::AssertionResult
matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher,
std::unique_ptr<BoundNodesCallback> FindResultVerifier) {
return matchAndVerifyResultConditionally(
Code, AMatcher, std::move(FindResultVerifier), false);
}
// Implements a run method that returns whether BoundNodes contains a
// Decl bound to Id that can be dynamically cast to T.
// Optionally checks that the check succeeded a specific number of times.
template <typename T>
class VerifyIdIsBoundTo : public BoundNodesCallback {
public:
// Create an object that checks that a node of type \c T was bound to \c Id.
// Does not check for a certain number of matches.
explicit VerifyIdIsBoundTo(llvm::StringRef Id)
: Id(Id), ExpectedCount(-1), Count(0) {}
// Create an object that checks that a node of type \c T was bound to \c Id.
// Checks that there were exactly \c ExpectedCount matches.
VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount)
: Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
// Create an object that checks that a node of type \c T was bound to \c Id.
// Checks that there was exactly one match with the name \c ExpectedName.
// Note that \c T must be a NamedDecl for this to work.
VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName,
int ExpectedCount = 1)
: Id(Id), ExpectedCount(ExpectedCount), Count(0),
ExpectedName(ExpectedName) {}
void onEndOfTranslationUnit() override {
if (ExpectedCount != -1) {
EXPECT_EQ(ExpectedCount, Count);
}
if (!ExpectedName.empty()) {
EXPECT_EQ(ExpectedName, Name);
}
Count = 0;
Name.clear();
}
~VerifyIdIsBoundTo() override {
EXPECT_EQ(0, Count);
EXPECT_EQ("", Name);
}
bool run(const BoundNodes *Nodes) override {
const BoundNodes::IDToNodeMap &M = Nodes->getMap();
if (Nodes->getNodeAs<T>(Id)) {
++Count;
if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
Name = Named->getNameAsString();
} else if (const NestedNameSpecifier *NNS =
Nodes->getNodeAs<NestedNameSpecifier>(Id)) {
llvm::raw_string_ostream OS(Name);
NNS->print(OS, PrintingPolicy(LangOptions()));
}
BoundNodes::IDToNodeMap::const_iterator I = M.find(Id);
EXPECT_NE(M.end(), I);
if (I != M.end()) {
EXPECT_EQ(Nodes->getNodeAs<T>(Id), I->second.get<T>());
}
return true;
}
EXPECT_TRUE(M.count(Id) == 0 ||
M.find(Id)->second.template get<T>() == nullptr);
return false;
}
bool run(const BoundNodes *Nodes, ASTContext *Context) override {
return run(Nodes);
}
private:
const std::string Id;
const int ExpectedCount;
int Count;
const std::string ExpectedName;
std::string Name;
};
} // namespace ast_matchers
} // namespace clang
#endif // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
set(LLVM_LINK_COMPONENTS
Support
)
# By default MSVC has a 2^16 limit on the number of sections in an object file,
# and this needs more than that.
if (MSVC)
set_source_files_properties(InternalASTMatchersTest.cpp PROPERTIES COMPILE_FLAGS /bigobj)
set_source_files_properties(NodeMatchersTest.cpp PROPERTIES COMPILE_FLAGS /bigobj)
set_source_files_properties(NarrowingMatchersTest.cpp PROPERTIES COMPILE_FLAGS /bigobj)
set_source_files_properties(ASTTraversalMatchersTest.cpp PROPERTIES COMPILE_FLAGS /bigobj)
endif()
add_clang_unittest(ASTMatchersTests
ASTMatchersInternalTest.cpp
ASTMatchersNodeTest.cpp
ASTMatchersNarrowingTest.cpp
ASTMatchersTraversalTest.cpp)
target_link_libraries(ASTMatchersTests
PRIVATE
clangAST
clangASTMatchers
clangBasic
clangFrontend
clangTooling
)
add_subdirectory(Dynamic)

View File

@@ -0,0 +1,18 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_unittest(DynamicASTMatchersTests
VariantValueTest.cpp
ParserTest.cpp
RegistryTest.cpp)
target_link_libraries(DynamicASTMatchersTests
PRIVATE
clangAST
clangASTMatchers
clangBasic
clangDynamicASTMatchers
clangFrontend
clangTooling
)

View File

@@ -0,0 +1,353 @@
//===- unittest/ASTMatchers/Dynamic/ParserTest.cpp - Parser unit tests -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===-------------------------------------------------------------------===//
#include "../ASTMatchersTest.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/ASTMatchers/Dynamic/Registry.h"
#include "llvm/ADT/Optional.h"
#include "gtest/gtest.h"
#include <string>
#include <vector>
namespace clang {
namespace ast_matchers {
namespace dynamic {
namespace {
class MockSema : public Parser::Sema {
public:
~MockSema() override {}
uint64_t expectMatcher(StringRef MatcherName) {
// Optimizations on the matcher framework make simple matchers like
// 'stmt()' to be all the same matcher.
// Use a more complex expression to prevent that.
ast_matchers::internal::Matcher<Stmt> M = stmt(stmt(), stmt());
ExpectedMatchers.insert(std::make_pair(MatcherName, M));
return M.getID().second;
}
void parse(StringRef Code) {
Diagnostics Error;
VariantValue Value;
Parser::parseExpression(Code, this, &Value, &Error);
Values.push_back(Value);
Errors.push_back(Error.toStringFull());
}
llvm::Optional<MatcherCtor>
lookupMatcherCtor(StringRef MatcherName) override {
const ExpectedMatchersTy::value_type *Matcher =
&*ExpectedMatchers.find(MatcherName);
return reinterpret_cast<MatcherCtor>(Matcher);
}
VariantMatcher actOnMatcherExpression(MatcherCtor Ctor,
SourceRange NameRange,
StringRef BindID,
ArrayRef<ParserValue> Args,
Diagnostics *Error) override {
const ExpectedMatchersTy::value_type *Matcher =
reinterpret_cast<const ExpectedMatchersTy::value_type *>(Ctor);
MatcherInfo ToStore = { Matcher->first, NameRange, Args, BindID };
Matchers.push_back(ToStore);
return VariantMatcher::SingleMatcher(Matcher->second);
}
struct MatcherInfo {
StringRef MatcherName;
SourceRange NameRange;
std::vector<ParserValue> Args;
std::string BoundID;
};
std::vector<std::string> Errors;
std::vector<VariantValue> Values;
std::vector<MatcherInfo> Matchers;
typedef std::map<std::string, ast_matchers::internal::Matcher<Stmt> >
ExpectedMatchersTy;
ExpectedMatchersTy ExpectedMatchers;
};
TEST(ParserTest, ParseBoolean) {
MockSema Sema;
Sema.parse("true");
Sema.parse("false");
EXPECT_EQ(2U, Sema.Values.size());
EXPECT_TRUE(Sema.Values[0].getBoolean());
EXPECT_FALSE(Sema.Values[1].getBoolean());
}
TEST(ParserTest, ParseDouble) {
MockSema Sema;
Sema.parse("1.0");
Sema.parse("2.0f");
Sema.parse("34.56e-78");
Sema.parse("4.E+6");
Sema.parse("1");
EXPECT_EQ(5U, Sema.Values.size());
EXPECT_EQ(1.0, Sema.Values[0].getDouble());
EXPECT_EQ("1:1: Error parsing numeric literal: <2.0f>", Sema.Errors[1]);
EXPECT_EQ(34.56e-78, Sema.Values[2].getDouble());
EXPECT_EQ(4e+6, Sema.Values[3].getDouble());
EXPECT_FALSE(Sema.Values[4].isDouble());
}
TEST(ParserTest, ParseUnsigned) {
MockSema Sema;
Sema.parse("0");
Sema.parse("123");
Sema.parse("0x1f");
Sema.parse("12345678901");
Sema.parse("1a1");
EXPECT_EQ(5U, Sema.Values.size());
EXPECT_EQ(0U, Sema.Values[0].getUnsigned());
EXPECT_EQ(123U, Sema.Values[1].getUnsigned());
EXPECT_EQ(31U, Sema.Values[2].getUnsigned());
EXPECT_EQ("1:1: Error parsing numeric literal: <12345678901>", Sema.Errors[3]);
EXPECT_EQ("1:1: Error parsing numeric literal: <1a1>", Sema.Errors[4]);
}
TEST(ParserTest, ParseString) {
MockSema Sema;
Sema.parse("\"Foo\"");
Sema.parse("\"\"");
Sema.parse("\"Baz");
EXPECT_EQ(3ULL, Sema.Values.size());
EXPECT_EQ("Foo", Sema.Values[0].getString());
EXPECT_EQ("", Sema.Values[1].getString());
EXPECT_EQ("1:1: Error parsing string token: <\"Baz>", Sema.Errors[2]);
}
bool matchesRange(SourceRange Range, unsigned StartLine,
unsigned EndLine, unsigned StartColumn, unsigned EndColumn) {
EXPECT_EQ(StartLine, Range.Start.Line);
EXPECT_EQ(EndLine, Range.End.Line);
EXPECT_EQ(StartColumn, Range.Start.Column);
EXPECT_EQ(EndColumn, Range.End.Column);
return Range.Start.Line == StartLine && Range.End.Line == EndLine &&
Range.Start.Column == StartColumn && Range.End.Column == EndColumn;
}
llvm::Optional<DynTypedMatcher> getSingleMatcher(const VariantValue &Value) {
llvm::Optional<DynTypedMatcher> Result =
Value.getMatcher().getSingleMatcher();
EXPECT_TRUE(Result.hasValue());
return Result;
}
TEST(ParserTest, ParseMatcher) {
MockSema Sema;
const uint64_t ExpectedFoo = Sema.expectMatcher("Foo");
const uint64_t ExpectedBar = Sema.expectMatcher("Bar");
const uint64_t ExpectedBaz = Sema.expectMatcher("Baz");
Sema.parse(" Foo ( Bar ( 17), Baz( \n \"B A,Z\") ) .bind( \"Yo!\") ");
for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) {
EXPECT_EQ("", Sema.Errors[i]);
}
EXPECT_NE(ExpectedFoo, ExpectedBar);
EXPECT_NE(ExpectedFoo, ExpectedBaz);
EXPECT_NE(ExpectedBar, ExpectedBaz);
EXPECT_EQ(1ULL, Sema.Values.size());
EXPECT_EQ(ExpectedFoo, getSingleMatcher(Sema.Values[0])->getID().second);
EXPECT_EQ(3ULL, Sema.Matchers.size());
const MockSema::MatcherInfo Bar = Sema.Matchers[0];
EXPECT_EQ("Bar", Bar.MatcherName);
EXPECT_TRUE(matchesRange(Bar.NameRange, 1, 1, 8, 17));
EXPECT_EQ(1ULL, Bar.Args.size());
EXPECT_EQ(17U, Bar.Args[0].Value.getUnsigned());
const MockSema::MatcherInfo Baz = Sema.Matchers[1];
EXPECT_EQ("Baz", Baz.MatcherName);
EXPECT_TRUE(matchesRange(Baz.NameRange, 1, 2, 19, 10));
EXPECT_EQ(1ULL, Baz.Args.size());
EXPECT_EQ("B A,Z", Baz.Args[0].Value.getString());
const MockSema::MatcherInfo Foo = Sema.Matchers[2];
EXPECT_EQ("Foo", Foo.MatcherName);
EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12));
EXPECT_EQ(2ULL, Foo.Args.size());
EXPECT_EQ(ExpectedBar, getSingleMatcher(Foo.Args[0].Value)->getID().second);
EXPECT_EQ(ExpectedBaz, getSingleMatcher(Foo.Args[1].Value)->getID().second);
EXPECT_EQ("Yo!", Foo.BoundID);
}
using ast_matchers::internal::Matcher;
Parser::NamedValueMap getTestNamedValues() {
Parser::NamedValueMap Values;
Values["nameX"] = llvm::StringRef("x");
Values["hasParamA"] =
VariantMatcher::SingleMatcher(hasParameter(0, hasName("a")));
return Values;
}
TEST(ParserTest, FullParserTest) {
Diagnostics Error;
llvm::Optional<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression(
"varDecl(hasInitializer(binaryOperator(hasLHS(integerLiteral()),"
" hasOperatorName(\"+\"))))",
&Error));
EXPECT_EQ("", Error.toStringFull());
Matcher<Decl> M = VarDecl->unconditionalConvertTo<Decl>();
EXPECT_TRUE(matches("int x = 1 + false;", M));
EXPECT_FALSE(matches("int x = true + 1;", M));
EXPECT_FALSE(matches("int x = 1 - false;", M));
EXPECT_FALSE(matches("int x = true - 1;", M));
llvm::Optional<DynTypedMatcher> HasParameter(Parser::parseMatcherExpression(
"functionDecl(hasParameter(1, hasName(\"x\")))", &Error));
EXPECT_EQ("", Error.toStringFull());
M = HasParameter->unconditionalConvertTo<Decl>();
EXPECT_TRUE(matches("void f(int a, int x);", M));
EXPECT_FALSE(matches("void f(int x, int a);", M));
// Test named values.
auto NamedValues = getTestNamedValues();
llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues(
Parser::parseMatcherExpression(
"functionDecl(hasParamA, hasParameter(1, hasName(nameX)))",
nullptr, &NamedValues, &Error));
EXPECT_EQ("", Error.toStringFull());
M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>();
EXPECT_TRUE(matches("void f(int a, int x);", M));
EXPECT_FALSE(matches("void f(int x, int a);", M));
EXPECT_TRUE(!Parser::parseMatcherExpression(
"hasInitializer(\n binaryOperator(hasLHS(\"A\")))",
&Error).hasValue());
EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n"
"2:5: Error parsing argument 1 for matcher binaryOperator.\n"
"2:20: Error building matcher hasLHS.\n"
"2:27: Incorrect type for arg 1. "
"(Expected = Matcher<Expr>) != (Actual = String)",
Error.toStringFull());
}
std::string ParseWithError(StringRef Code) {
Diagnostics Error;
VariantValue Value;
Parser::parseExpression(Code, &Value, &Error);
return Error.toStringFull();
}
std::string ParseMatcherWithError(StringRef Code) {
Diagnostics Error;
Parser::parseMatcherExpression(Code, &Error);
return Error.toStringFull();
}
TEST(ParserTest, Errors) {
EXPECT_EQ(
"1:5: Error parsing matcher. Found token <123> while looking for '('.",
ParseWithError("Foo 123"));
EXPECT_EQ(
"1:1: Matcher not found: Foo\n"
"1:9: Error parsing matcher. Found token <123> while looking for ','.",
ParseWithError("Foo(\"A\" 123)"));
EXPECT_EQ(
"1:1: Error parsing argument 1 for matcher stmt.\n"
"1:6: Value not found: someValue",
ParseWithError("stmt(someValue)"));
EXPECT_EQ(
"1:1: Matcher not found: Foo\n"
"1:4: Error parsing matcher. Found end-of-code while looking for ')'.",
ParseWithError("Foo("));
EXPECT_EQ("1:1: End of code found while looking for token.",
ParseWithError(""));
EXPECT_EQ("Input value is not a matcher expression.",
ParseMatcherWithError("\"A\""));
EXPECT_EQ("1:1: Matcher not found: Foo\n"
"1:1: Error parsing argument 1 for matcher Foo.\n"
"1:5: Invalid token <(> found when looking for a value.",
ParseWithError("Foo(("));
EXPECT_EQ("1:7: Expected end of code.", ParseWithError("expr()a"));
EXPECT_EQ("1:11: Malformed bind() expression.",
ParseWithError("isArrow().biind"));
EXPECT_EQ("1:15: Malformed bind() expression.",
ParseWithError("isArrow().bind"));
EXPECT_EQ("1:16: Malformed bind() expression.",
ParseWithError("isArrow().bind(foo"));
EXPECT_EQ("1:21: Malformed bind() expression.",
ParseWithError("isArrow().bind(\"foo\""));
EXPECT_EQ("1:1: Error building matcher isArrow.\n"
"1:1: Matcher does not support binding.",
ParseWithError("isArrow().bind(\"foo\")"));
EXPECT_EQ("Input value has unresolved overloaded type: "
"Matcher<DoStmt|ForStmt|WhileStmt|CXXForRangeStmt|FunctionDecl>",
ParseMatcherWithError("hasBody(stmt())"));
}
TEST(ParserTest, OverloadErrors) {
EXPECT_EQ("1:1: Error building matcher callee.\n"
"1:8: Candidate 1: Incorrect type for arg 1. "
"(Expected = Matcher<Stmt>) != (Actual = String)\n"
"1:8: Candidate 2: Incorrect type for arg 1. "
"(Expected = Matcher<Decl>) != (Actual = String)",
ParseWithError("callee(\"A\")"));
}
TEST(ParserTest, CompletionRegistry) {
std::vector<MatcherCompletion> Comps =
Parser::completeExpression("while", 5);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("Stmt(", Comps[0].TypedText);
EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
Comps[0].MatcherDecl);
Comps = Parser::completeExpression("whileStmt().", 12);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("bind(\"", Comps[0].TypedText);
EXPECT_EQ("bind", Comps[0].MatcherDecl);
}
TEST(ParserTest, CompletionNamedValues) {
// Can complete non-matcher types.
auto NamedValues = getTestNamedValues();
StringRef Code = "functionDecl(hasName(";
std::vector<MatcherCompletion> Comps =
Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("nameX", Comps[0].TypedText);
EXPECT_EQ("String nameX", Comps[0].MatcherDecl);
// Can complete if there are names in the expression.
Code = "cxxMethodDecl(hasName(nameX), ";
Comps = Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues);
EXPECT_LT(0u, Comps.size());
// Can complete names and registry together.
Code = "cxxMethodDecl(hasP";
Comps = Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues);
ASSERT_EQ(3u, Comps.size());
EXPECT_EQ("aramA", Comps[0].TypedText);
EXPECT_EQ("Matcher<FunctionDecl> hasParamA", Comps[0].MatcherDecl);
EXPECT_EQ("arameter(", Comps[1].TypedText);
EXPECT_EQ(
"Matcher<FunctionDecl> hasParameter(unsigned, Matcher<ParmVarDecl>)",
Comps[1].MatcherDecl);
EXPECT_EQ("arent(", Comps[2].TypedText);
EXPECT_EQ(
"Matcher<Decl> "
"hasParent(Matcher<NestedNameSpecifierLoc|TypeLoc|Decl|...>)",
Comps[2].MatcherDecl);
}
} // end anonymous namespace
} // end namespace dynamic
} // end namespace ast_matchers
} // end namespace clang

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,191 @@
//===- unittest/ASTMatchers/Dynamic/VariantValueTest.cpp - VariantValue unit tests -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===-----------------------------------------------------------------------------===//
#include "../ASTMatchersTest.h"
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
#include "gtest/gtest.h"
namespace clang {
namespace ast_matchers {
namespace dynamic {
namespace {
TEST(VariantValueTest, Unsigned) {
const unsigned kUnsigned = 17;
VariantValue Value = kUnsigned;
EXPECT_TRUE(Value.isUnsigned());
EXPECT_EQ(kUnsigned, Value.getUnsigned());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isString());
EXPECT_FALSE(Value.isMatcher());
}
TEST(VariantValueTest, String) {
const StringRef kString = "string";
VariantValue Value = kString;
EXPECT_TRUE(Value.isString());
EXPECT_EQ(kString, Value.getString());
EXPECT_EQ("String", Value.getTypeAsString());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isMatcher());
}
TEST(VariantValueTest, DynTypedMatcher) {
VariantValue Value = VariantMatcher::SingleMatcher(stmt());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isString());
EXPECT_TRUE(Value.isMatcher());
EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<Decl>());
EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<UnaryOperator>());
EXPECT_EQ("Matcher<Stmt>", Value.getTypeAsString());
// Can only convert to compatible matchers.
Value = VariantMatcher::SingleMatcher(recordDecl());
EXPECT_TRUE(Value.isMatcher());
EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<Decl>());
EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<UnaryOperator>());
EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString());
Value = VariantMatcher::SingleMatcher(ignoringImpCasts(expr()));
EXPECT_TRUE(Value.isMatcher());
EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<Decl>());
EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<Stmt>());
EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<Expr>());
EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<IntegerLiteral>());
EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<GotoStmt>());
EXPECT_EQ("Matcher<Expr>", Value.getTypeAsString());
}
TEST(VariantValueTest, Assignment) {
VariantValue Value = StringRef("A");
EXPECT_TRUE(Value.isString());
EXPECT_EQ("A", Value.getString());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isBoolean());
EXPECT_FALSE(Value.isDouble());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isMatcher());
EXPECT_EQ("String", Value.getTypeAsString());
Value = VariantMatcher::SingleMatcher(recordDecl());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isBoolean());
EXPECT_FALSE(Value.isDouble());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isString());
EXPECT_TRUE(Value.isMatcher());
EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<Decl>());
EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<UnaryOperator>());
EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString());
Value = true;
EXPECT_TRUE(Value.isBoolean());
EXPECT_EQ(true, Value.getBoolean());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isMatcher());
EXPECT_FALSE(Value.isString());
Value = 3.14;
EXPECT_TRUE(Value.isDouble());
EXPECT_EQ(3.14, Value.getDouble());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isBoolean());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isMatcher());
EXPECT_FALSE(Value.isString());
Value = 17;
EXPECT_TRUE(Value.isUnsigned());
EXPECT_EQ(17U, Value.getUnsigned());
EXPECT_FALSE(Value.isBoolean());
EXPECT_FALSE(Value.isDouble());
EXPECT_TRUE(Value.hasValue());
EXPECT_FALSE(Value.isMatcher());
EXPECT_FALSE(Value.isString());
Value = VariantValue();
EXPECT_FALSE(Value.hasValue());
EXPECT_FALSE(Value.isBoolean());
EXPECT_FALSE(Value.isDouble());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isString());
EXPECT_FALSE(Value.isMatcher());
EXPECT_EQ("Nothing", Value.getTypeAsString());
}
TEST(VariantValueTest, ImplicitBool) {
VariantValue Value;
bool IfTrue = false;
if (Value) {
IfTrue = true;
}
EXPECT_FALSE(IfTrue);
EXPECT_TRUE(!Value);
Value = StringRef();
IfTrue = false;
if (Value) {
IfTrue = true;
}
EXPECT_TRUE(IfTrue);
EXPECT_FALSE(!Value);
}
TEST(VariantValueTest, Matcher) {
EXPECT_TRUE(matches("class X {};", VariantValue(VariantMatcher::SingleMatcher(
recordDecl(hasName("X"))))
.getMatcher()
.getTypedMatcher<Decl>()));
EXPECT_TRUE(
matches("int x;", VariantValue(VariantMatcher::SingleMatcher(varDecl()))
.getMatcher()
.getTypedMatcher<Decl>()));
EXPECT_TRUE(
matches("int foo() { return 1 + 1; }",
VariantValue(VariantMatcher::SingleMatcher(functionDecl()))
.getMatcher()
.getTypedMatcher<Decl>()));
// Can't get the wrong matcher.
EXPECT_FALSE(VariantValue(VariantMatcher::SingleMatcher(varDecl()))
.getMatcher()
.hasTypedMatcher<Stmt>());
#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
// Trying to get the wrong matcher fails an assertion in Matcher<T>. We don't
// do this test when building with MSVC because its debug C runtime prints the
// assertion failure message as a wide string, which gtest doesn't understand.
EXPECT_DEATH(VariantValue(VariantMatcher::SingleMatcher(varDecl()))
.getMatcher()
.getTypedMatcher<Stmt>(),
"hasTypedMatcher");
#endif
EXPECT_FALSE(matches(
"int x;", VariantValue(VariantMatcher::SingleMatcher(functionDecl()))
.getMatcher()
.getTypedMatcher<Decl>()));
EXPECT_FALSE(
matches("int foo() { return 1 + 1; }",
VariantValue(VariantMatcher::SingleMatcher(declRefExpr()))
.getMatcher()
.getTypedMatcher<Stmt>()));
}
} // end anonymous namespace
} // end namespace dynamic
} // end namespace ast_matchers
} // end namespace clang