diff options
author | Manuel Klimek <klimek@google.com> | 2012-07-06 05:48:52 +0000 |
---|---|---|
committer | Manuel Klimek <klimek@google.com> | 2012-07-06 05:48:52 +0000 |
commit | 4da216637fa1ad4bdfd31bc265edb57ca35c2c12 (patch) | |
tree | 08b93fbc99de82a43b0bb4dba96dbfbf96abac45 | |
parent | ea7eb071166c69e11bfda4f1d89d5c2b6bbcdca3 (diff) | |
download | clang-4da216637fa1ad4bdfd31bc265edb57ca35c2c12.tar.gz clang-4da216637fa1ad4bdfd31bc265edb57ca35c2c12.tar.bz2 clang-4da216637fa1ad4bdfd31bc265edb57ca35c2c12.tar.xz |
Adds the AST Matcher library, which provides a in-C++ DSL to express
matches on interesting parts of the AST, and callback mechanisms to
act on them.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159805 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchFinder.h | 141 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchers.h | 1403 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchersInternal.h | 888 | ||||
-rw-r--r-- | include/clang/ASTMatchers/ASTMatchersMacros.h | 224 | ||||
-rw-r--r-- | lib/ASTMatchers/ASTMatchFinder.cpp | 556 | ||||
-rw-r--r-- | lib/ASTMatchers/ASTMatchersInternal.cpp | 102 | ||||
-rw-r--r-- | lib/ASTMatchers/CMakeLists.txt | 7 | ||||
-rw-r--r-- | lib/ASTMatchers/Makefile | 15 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 1 | ||||
-rwxr-xr-x | lib/Makefile | 2 | ||||
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.cpp | 1979 | ||||
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.h | 128 | ||||
-rw-r--r-- | unittests/ASTMatchers/CMakeLists.txt | 5 | ||||
-rw-r--r-- | unittests/ASTMatchers/Makefile | 19 | ||||
-rw-r--r-- | unittests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | unittests/Makefile | 2 |
16 files changed, 5471 insertions, 2 deletions
diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h new file mode 100644 index 0000000000..dd237eece3 --- /dev/null +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -0,0 +1,141 @@ +//===--- ASTMatchFinder.h - Structural query framework ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Provides a way to construct an ASTConsumer that runs given matchers +// over the AST and invokes a given callback on every match. +// +// The general idea is to construct a matcher expression that describes a +// subtree match on the AST. Next, a callback that is executed every time the +// expression matches is registered, and the matcher is run over the AST of +// some code. Matched subexpressions can be bound to string IDs and easily +// be accessed from the registered callback. The callback can than use the +// AST nodes that the subexpressions matched on to output information about +// the match or construct changes that can be applied to the code. +// +// Example: +// class HandleMatch : public MatchFinder::MatchCallback { +// public: +// virtual void Run(const MatchFinder::MatchResult &Result) { +// const CXXRecordDecl *Class = +// Result.Nodes.GetDeclAs<CXXRecordDecl>("id"); +// ... +// } +// }; +// +// int main(int argc, char **argv) { +// ClangTool Tool(argc, argv); +// MatchFinder finder; +// finder.AddMatcher(Id("id", record(hasName("::a_namespace::AClass"))), +// new HandleMatch); +// return Tool.Run(newFrontendActionFactory(&finder)); +// } +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCH_FINDER_H +#define LLVM_CLANG_AST_MATCHERS_AST_MATCH_FINDER_H + +#include "clang/ASTMatchers/ASTMatchers.h" + +namespace clang { + +namespace ast_matchers { + +/// \brief A class to allow finding matches over the Clang AST. +/// +/// After creation, you can add multiple matchers to the MatchFinder via +/// calls to addMatcher(...). +/// +/// Once all matchers are added, newASTConsumer() returns an ASTConsumer +/// that will trigger the callbacks specified via addMatcher(...) when a match +/// is found. +/// +/// See ASTMatchers.h for more information about how to create matchers. +/// +/// Not intended to be subclassed. +class MatchFinder { +public: + /// \brief Contains all information for a given match. + /// + /// Every time a match is found, the MatchFinder will invoke the registered + /// MatchCallback with a MatchResult containing information about the match. + struct MatchResult { + MatchResult(const BoundNodes &Nodes, clang::ASTContext *Context); + + /// \brief Contains the nodes bound on the current match. + /// + /// This allows user code to easily extract matched AST nodes. + const BoundNodes Nodes; + + /// \brief Utilities for interpreting the matched AST structures. + /// @{ + clang::ASTContext * const Context; + clang::SourceManager * const SourceManager; + /// @} + }; + + /// \brief Called when the Match registered for it was successfully found + /// in the AST. + class MatchCallback { + public: + virtual ~MatchCallback(); + virtual void run(const MatchResult &Result) = 0; + }; + + /// \brief Called when parsing is finished. Intended for testing only. + class ParsingDoneTestCallback { + public: + virtual ~ParsingDoneTestCallback(); + virtual void run() = 0; + }; + + MatchFinder(); + ~MatchFinder(); + + /// \brief Adds a matcher to execute when running over the AST. + /// + /// Calls 'Action' with the BoundNodes on every match. + /// Adding more than one 'NodeMatch' allows finding different matches in a + /// single pass over the AST. + /// + /// Does not take ownership of 'Action'. + /// @{ + void addMatcher(const DeclarationMatcher &NodeMatch, + MatchCallback *Action); + void addMatcher(const TypeMatcher &NodeMatch, + MatchCallback *Action); + void addMatcher(const StatementMatcher &NodeMatch, + MatchCallback *Action); + /// @} + + /// \brief Creates a clang ASTConsumer that finds all matches. + clang::ASTConsumer *newASTConsumer(); + + /// \brief Registers a callback to notify the end of parsing. + /// + /// The provided closure is called after parsing is done, before the AST is + /// traversed. Useful for benchmarking. + /// Each call to FindAll(...) will call the closure once. + void registerTestCallbackAfterParsing(ParsingDoneTestCallback *ParsingDone); + +private: + /// \brief The MatchCallback*'s will be called every time the + /// UntypedBaseMatcher matches on the AST. + std::vector< std::pair< + const internal::UntypedBaseMatcher*, + MatchCallback*> > Triggers; + + /// \brief Called when parsing is done. + ParsingDoneTestCallback *ParsingDone; +}; + +} // end namespace ast_matchers +} // end namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCH_FINDER_H diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h new file mode 100644 index 0000000000..69e9c4b53d --- /dev/null +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -0,0 +1,1403 @@ +//===--- ASTMatchers.h - Structural query framework -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements matchers to be used together with the MatchFinder to +// match AST nodes. +// +// Matchers are created by generator functions, which can be combined in +// a functional in-language DSL to express queries over the C++ AST. +// +// For example, to match a class with a certain name, one would call: +// record(hasName("MyClass")) +// which returns a matcher that can be used to find all AST nodes that declare +// a class named 'MyClass'. +// +// For more complicated match expressions we're often interested in accessing +// multiple parts of the matched AST nodes once a match is found. In that case, +// use the id(...) matcher around the match expressions that match the nodes +// you want to access. +// +// For example, when we're interested in child classes of a certain class, we +// would write: +// record(hasName("MyClass"), hasChild(id("child", record()))) +// When the match is found via the MatchFinder, a user provided callback will +// be called with a BoundNodes instance that contains a mapping from the +// strings that we provided for the id(...) calls to the nodes that were +// matched. +// In the given example, each time our matcher finds a match we get a callback +// where "child" is bound to the CXXRecordDecl node of the matching child +// class declaration. +// +// See ASTMatchersInternal.h for a more in-depth explanation of the +// implementation details of the matcher framework. +// +// See ASTMatchFinder.h for how to use the generated matchers to run over +// an AST. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_H +#define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_H + +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" +#include "llvm/ADT/Twine.h" + +namespace clang { +namespace ast_matchers { + +/// \brief Maps string IDs to AST nodes matched by parts of a matcher. +/// +/// The bound nodes are generated by adding id(...) matchers into the +/// match expression around the matchers for the nodes we want to access later. +/// +/// The instances of BoundNodes are created by MatchFinder when the user's +/// callbacks are executed every time a match is found. +class BoundNodes { +public: + /// \brief Returns the AST node bound to 'ID'. + /// Returns NULL if there was no node bound to 'ID' or if there is a node but + /// it cannot be converted to the specified type. + /// FIXME: We'll need one of those for every base type. + /// @{ + template <typename T> + const T *getDeclAs(StringRef ID) const { + return getNodeAs<T>(DeclBindings, ID); + } + template <typename T> + const T *getStmtAs(StringRef ID) const { + return getNodeAs<T>(StmtBindings, ID); + } + /// @} + +private: + /// \brief Create BoundNodes from a pre-filled map of bindings. + BoundNodes(const std::map<std::string, const clang::Decl*> &DeclBindings, + const std::map<std::string, const clang::Stmt*> &StmtBindings) + : DeclBindings(DeclBindings), StmtBindings(StmtBindings) {} + + template <typename T, typename MapT> + const T *getNodeAs(const MapT &Bindings, StringRef ID) const { + typename MapT::const_iterator It = Bindings.find(ID); + if (It == Bindings.end()) { + return NULL; + } + return llvm::dyn_cast<T>(It->second); + } + + std::map<std::string, const clang::Decl*> DeclBindings; + std::map<std::string, const clang::Stmt*> StmtBindings; + + friend class internal::BoundNodesTree; +}; + +/// \brief If the provided matcher matches a node, binds the node to 'ID'. +/// +/// FIXME: Add example for accessing it. +template <typename T> +internal::Matcher<T> id(const std::string &ID, + const internal::Matcher<T> &InnerMatcher) { + return internal::Matcher<T>(new internal::IdMatcher<T>(ID, InnerMatcher)); +} + +/// \brief Types of matchers for the top-level classes in the AST class +/// hierarchy. +/// @{ +typedef internal::Matcher<clang::Decl> DeclarationMatcher; +typedef internal::Matcher<clang::QualType> TypeMatcher; +typedef internal::Matcher<clang::Stmt> StatementMatcher; +/// @} + +/// \brief Matches any node. +/// +/// Useful when another matcher requires a child matcher, but there's no +/// additional constraint. This will often be used with an explicit conversion +/// to a internal::Matcher<> type such as TypeMatcher. +/// +/// Example: DeclarationMatcher(anything()) matches all declarations, e.g., +/// "int* p" and "void f()" in +/// int* p; +/// void f(); +inline internal::PolymorphicMatcherWithParam0<internal::TrueMatcher> anything() { + return internal::PolymorphicMatcherWithParam0<internal::TrueMatcher>(); +} + +/// \brief Matches a declaration of anything that could have a name. +/// +/// Example matches X, S, the anonymous union type, i, and U; +/// typedef int X; +/// struct S { +/// union { +/// int i; +/// } U; +/// }; +const internal::VariadicDynCastAllOfMatcher< + clang::Decl, + clang::NamedDecl> nameableDeclaration; + +/// \brief Matches C++ class declarations. +/// +/// Example matches X, Z +/// class X; +/// template<class T> class Z {}; +const internal::VariadicDynCastAllOfMatcher< + clang::Decl, + clang::CXXRecordDecl> record; + +/// \brief Matches C++ constructor declarations. +/// +/// Example matches Foo::Foo() and Foo::Foo(int) +/// class Foo { +/// public: +/// Foo(); +/// Foo(int); +/// int DoSomething(); +/// }; +const internal::VariadicDynCastAllOfMatcher< + clang::Decl, + clang::CXXConstructorDecl> constructor; + +/// \brief Matches method declarations. +/// +/// Example matches y +/// class X { void y() }; +const internal::VariadicDynCastAllOfMatcher< + clang::Decl, + clang::CXXMethodDecl> method; + +/// \brief Matches variable declarations. +/// +/// Note: this does not match declarations of member variables, which are +/// "field" declarations in Clang parlance. +/// +/// Example matches a +/// int a; +const internal::VariadicDynCastAllOfMatcher< + clang::Decl, + clang::VarDecl> variable; + +/// \brief Matches field declarations. +/// +/// Given +/// class X { int m; }; +/// field() +/// matches 'm'. +const internal::VariadicDynCastAllOfMatcher< + clang::Decl, + clang::FieldDecl> field; + +/// \brief Matches function declarations. +/// +/// Example matches f +/// void f(); +const internal::VariadicDynCastAllOfMatcher< + clang::Decl, + clang::FunctionDecl> function; + + +/// \brief Matches statements. +/// +/// Given +/// { ++a; } +/// statement() +/// matches both the compound statement '{ ++a; }' and '++a'. +const internal::VariadicDynCastAllOfMatcher<clang::Stmt, clang::Stmt> statement; + +/// \brief Matches declaration statements. +/// +/// Given +/// int a; +/// declarationStatement() +/// matches 'int a'. +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::DeclStmt> declarationStatement; + +/// \brief Matches member expressions. +/// +/// Given +/// class Y { +/// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; } +/// int a; static int b; +/// }; +/// memberExpression() +/// matches this->x, x, y.x, a, this->b +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::MemberExpr> memberExpression; + +/// \brief Matches call expressions. +/// +/// Example matches x.y() +/// X x; +/// x.y(); +const internal::VariadicDynCastAllOfMatcher<clang::Stmt, clang::CallExpr> call; + +/// \brief Matches constructor call expressions (including implicit ones). +/// +/// Example matches string(ptr, n) and ptr within arguments of f +/// (matcher = constructorCall()) +/// void f(const string &a, const string &b); +/// char *ptr; +/// int n; +/// f(string(ptr, n), ptr); +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::CXXConstructExpr> constructorCall; + +/// \brief Matches nodes where temporaries are created. +/// +/// Example matches FunctionTakesString(GetStringByValue()) +/// (matcher = bindTemporaryExpr()) +/// FunctionTakesString(GetStringByValue()); +/// FunctionTakesStringByPointer(GetStringPointer()); +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::CXXBindTemporaryExpr> bindTemporaryExpression; + +/// \brief Matches new expressions. +/// +/// Given +/// new X; +/// newExpression() +/// matches 'new X'. +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::CXXNewExpr> newExpression; + +/// \brief Matches the value of a default argument at the call site. +/// +/// Example matches the CXXDefaultArgExpr placeholder inserted for the +/// default value of the second parameter in the call expression f(42) +/// (matcher = defaultArgument()) +/// void f(int x, int y = 0); +/// f(42); +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::CXXDefaultArgExpr> defaultArgument; + +/// \brief Matches overloaded operator calls. +/// +/// Note that if an operator isn't overloaded, it won't match. Instead, use +/// binaryOperator matcher. +/// Currently it does not match operators such as new delete. +/// FIXME: figure out why these do not match? +/// +/// Example matches both operator<<((o << b), c) and operator<<(o, b) +/// (matcher = overloadedOperatorCall()) +/// ostream &operator<< (ostream &out, int i) { }; +/// ostream &o; int b = 1, c = 1; +/// o << b << c; +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::CXXOperatorCallExpr> overloadedOperatorCall; + +/// \brief Matches expressions. +/// +/// Example matches x() +/// void f() { x(); } +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::Expr> expression; + +/// \brief Matches expressions that refer to declarations. +/// +/// Example matches x in if (x) +/// bool x; +/// if (x) {} +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::DeclRefExpr> declarationReference; + +/// \brief Matches if statements. +/// +/// Example matches 'if (x) {}' +/// if (x) {} +const internal::VariadicDynCastAllOfMatcher<clang::Stmt, clang::IfStmt> ifStmt; + +/// \brief Matches for statements. +/// +/// Example matches 'for (;;) {}' +/// for (;;) {} +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, clang::ForStmt> forStmt; + +/// \brief Matches while statements. +/// +/// Given +/// while (true) {} +/// whileStmt() +/// matches 'while (true) {}'. +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::WhileStmt> whileStmt; + +/// \brief Matches do statements. +/// +/// Given +/// do {} while (true); +/// doStmt() +/// matches 'do {} while(true)' +const internal::VariadicDynCastAllOfMatcher<clang::Stmt, clang::DoStmt> doStmt; + +/// \brief Matches case and default statements inside switch statements. +/// +/// Given +/// switch(a) { case 42: break; default: break; } +/// switchCase() +/// matches 'case 42: break;' and 'default: break;'. +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::SwitchCase> switchCase; + +/// \brief Matches compound statements. +/// +/// Example matches '{}' and '{{}}'in 'for (;;) {{}}' +/// for (;;) {{}} +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::CompoundStmt> compoundStatement; + +/// \brief Matches bool literals. +/// +/// Example matches true +/// true +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::CXXBoolLiteralExpr> boolLiteral; + +/// \brief Matches string literals (also matches wide string literals). +/// +/// Example matches "abcd", L"abcd" +/// char *s = "abcd"; wchar_t *ws = L"abcd" +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::StringLiteral> stringLiteral; + +/// \brief Matches character literals (also matches wchar_t). +/// +/// Not matching Hex-encoded chars (e.g. 0x1234, which is a IntegerLiteral), +/// though. +/// +/// Example matches 'a', L'a' +/// char ch = 'a'; wchar_t chw = L'a'; +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::CharacterLiteral> characterLiteral; + +/// \brief Matches integer literals of all sizes / encodings. +/// +/// Not matching character-encoded integers such as L'a'. +/// +/// Example matches 1, 1L, 0x1, 1U +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::IntegerLiteral> integerLiteral; + +/// \brief Matches binary operator expressions. +/// +/// Example matches a || b +/// !(a || b) +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::BinaryOperator> binaryOperator; + +/// \brief Matches unary operator expressions. +/// +/// Example matches !a +/// !a || b +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::UnaryOperator> unaryOperator; + +/// \brief Matches conditional operator expressions. +/// +/// Example matches a ? b : c +/// (a ? b : c) + 42 +const internal::VariadicDynCastAllOfMatcher< + clang::Stmt, + clang::ConditionalOperator> conditionalOperator; + +/// \brief Matches a reinterpret_cast expression. +/// +/// Either the source expression or the destination type can be matched +/// using has(), but hasDestinationType() is more specific and can be +/// more readable. +/// +/// Example matches reinterpret_cast<char*>(&p) in +/// void* p = reinterpret_cast<char*>(&p); +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::CXXReinterpretCastExpr> reinterpretCast; + +/// \brief Matches a C++ static_cast expression. +/// +/// \see hasDestinationType +/// \see reinterpretCast +/// +/// Example: +/// staticCast() +/// matches +/// static_cast<long>(8) +/// in +/// long eight(static_cast<long>(8)); +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::CXXStaticCastExpr> staticCast; + +/// \brief Matches a dynamic_cast expression. +/// +/// Example: +/// dynamicCast() +/// matches +/// dynamic_cast<D*>(&b); +/// in +/// struct B { virtual ~B() {} }; struct D : B {}; +/// B b; +/// D* p = dynamic_cast<D*>(&b); +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::CXXDynamicCastExpr> dynamicCast; + +/// \brief Matches a const_cast expression. +/// +/// Example: Matches const_cast<int*>(&r) in +/// int n = 42; +/// const int& r(n); +/// int* p = const_cast<int*>(&r); +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::CXXConstCastExpr> constCast; + +/// \brief Matches explicit cast expressions. +/// +/// Matches any cast expression written in user code, whether it be a +/// C-style cast, a functional-style cast, or a keyword cast. +/// +/// Does not match implicit conversions. +/// +/// Note: the name "explicitCast" is chosen to match Clang's terminology, as +/// Clang uses the term "cast" to apply to implicit conversions as well as to +/// actual cast expressions. +/// +/// \see hasDestinationType. +/// +/// Example: matches all five of the casts in +/// int((int)(reinterpret_cast<int>(static_cast<int>(const_cast<int>(42))))) +/// but does not match the implicit conversion in +/// long ell = 42; +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::ExplicitCastExpr> explicitCast; + +/// \brief Matches the implicit cast nodes of Clang's AST. +/// +/// This matches many different places, including function call return value +/// eliding, as well as any type conversions. +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::ImplicitCastExpr> implicitCast; + +/// \brief Matches functional cast expressions +/// +/// Example: Matches Foo(bar); +/// Foo f = bar; +/// Foo g = (Foo) bar; +/// Foo h = Foo(bar); +const internal::VariadicDynCastAllOfMatcher< + clang::Expr, + clang::CXXFunctionalCastExpr> functionalCast; + +/// \brief Various overloads for the anyOf matcher. +/// @{ +template<typename C1, typename C2> +internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1, C2> +anyOf(const C1 &P1, const C2 &P2) { + return internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, + C1, C2 >(P1, P2); +} +template<typename C1, typename C2, typename C3> +internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1, + internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C2, C3> > +anyOf(const C1 &P1, const C2 &P2, const C3 &P3) { + return anyOf(P1, anyOf(P2, P3)); +} +template<typename C1, typename C2, typename C3, typename C4> +internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1, + internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C2, + internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, + C3, C4> > > +anyOf(const C1 &P1, const C2 &P2, const C3 &P3, const C4 &P4) { + return AnyOf(P1, AnyOf(P2, AnyOf(P3, P4))); +} +template<typename C1, typename C2, typename C3, typename C4, typename C5> +internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C1, + internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C2, + internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, C3, + internal::PolymorphicMatcherWithParam2<internal::AnyOfMatcher, + C4, C5> > > > +anyOf(const C1& P1, const C2& P2, const C3& P3, const C4& P4, const C5& P5) { + return anyOf(P1, anyOf(P2, anyOf(P3, anyOf(P4, P5)))); +} +/// @} + +/// \brief Various overloads for the allOf matcher. +/// @{ +template<typename C1, typename C2> +internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, C1, C2> +allOf(const C1 &P1, const C2 &P2) { + return internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, + C1, C2>(P1, P2); +} +template<typename C1, typename C2, typename C3> +internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, C1, + internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, C2, C3> > +allOf(const C1& P1, const C2& P2, const C3& P3) { + return AllOf(P1, AllOf(P2, P3)); +} +/// @} + +/// \brief Matches NamedDecl nodes that have the specified name. +/// +/// Supports specifying enclosing namespaces or classes by prefixing the name +/// with '<enclosing>::'. +/// Does not match typedefs of an underlying type with the given name. +/// +/// Example matches X (Name == "X") +/// class X; +/// +/// Example matches X (Name is one of "::a::b::X", "a::b::X", "b::X", "X") +/// namespace a { namespace b { class X; } } +AST_MATCHER_P(clang::NamedDecl, hasName, std::string, Name) { + assert(!Name.empty()); + const std::string FullNameString = "::" + Node.getQualifiedNameAsString(); + const llvm::StringRef FullName = FullNameString; + const llvm::StringRef Pattern = Name; + if (Pattern.startswith("::")) { + return FullName == Pattern; + } else { + return FullName.endswith(("::" + Pattern).str()); + } +} + +/// \brief Matches overloaded operator names. +/// +/// Matches overloaded operator names specified in strings without the +/// "operator" prefix, such as "<<", for OverloadedOperatorCall's. +/// +/// Example matches a << b +/// (matcher == overloadedOperatorCall(hasOverloadedOperatorName("<<"))) +/// a << b; +/// c && d; // assuming both operator<< +/// // and operator&& are overloaded somewhere. +AST_MATCHER_P(clang::CXXOperatorCallExpr, + hasOverloadedOperatorName, std::string, Name) { + return clang::getOperatorSpelling(Node.getOperator()) == Name; +} + +/// \brief Matches C++ classes that are directly or indirectly derived from +/// the given base class. +/// +/// Note that a class is considered to be also derived from itself. +/// The parameter specified the name of the base type (either a class or a +/// typedef), and does not allow structural matches for namespaces or template +/// type parameters. +/// +/// Example matches X, Y, Z, C (Base == "X") +/// class X; // A class is considered to be derived from itself +/// class Y : public X {}; // directly derived +/// class Z : public Y {}; // indirectly derived +/// typedef X A; +/// typedef A B; +/// class C : public B {}; // derived from a typedef of X +/// +/// In the following example, Bar matches isDerivedFrom("X"): +/// class Foo; +/// typedef Foo X; +/// class Bar : public Foo {}; // derived from a type that X is a typedef of +AST_MATCHER_P(clang::CXXRecordDecl, isDerivedFrom, std::string, Base) { + assert(!Base.empty()); + return Finder->classIsDerivedFrom(&Node, Base); +} + +/// \brief Matches AST nodes that have child AST nodes that match the +/// provided matcher. +/// +/// Example matches X, Y (matcher = record(has(record(hasName("X"))) +/// class X {}; // Matches X, because X::X is a class of name X inside X. +/// class Y { class X {}; }; +/// class Z { class Y { class X {}; }; }; // Does not match Z. +/// +/// ChildT must be an AST base type. +template <typename ChildT> +internal::ArgumentAdaptingMatcher<internal::HasMatcher, ChildT> has( + const internal::Matcher<ChildT> &ChildMatcher) { + return internal::ArgumentAdaptingMatcher<internal::HasMatcher, + ChildT>(ChildMatcher); +} + +/// \brief Matches AST nodes that have descendant AST nodes that match the +/// provided matcher. +/// +/// Example matches X, Y, Z +/// (matcher = record(hasDescendant(record(hasName("X"))))) +/// class X {}; // Matches X, because X::X is a class of name X inside X. +/// class Y { class X {}; }; +/// class Z { class Y { class X {}; }; }; +/// +/// DescendantT must be an AST base type. +template <typename DescendantT> +internal::ArgumentAdaptingMatcher<internal::HasDescendantMatcher, DescendantT> +hasDescendant(const internal::Matcher<DescendantT> &DescendantMatcher) { + return internal::ArgumentAdaptingMatcher< + internal::HasDescendantMatcher, + DescendantT>(DescendantMatcher); +} + + +/// \brief Matches AST nodes that have child AST nodes that match the +/// provided matcher. +/// +/// Example matches X, Y (matcher = record(forEach(record(hasName("X"))) +/// class X {}; // Matches X, because X::X is a class of name X inside X. +/// class Y { class X {}; }; +/// class Z { class Y { class X {}; }; }; // Does not match Z. +/// +/// ChildT must be an AST base type. +/// +/// As opposed to 'has', 'forEach' will cause a match for each result that +/// matches instead of only on the first one. +template <typename ChildT> +internal::ArgumentAdaptingMatcher<internal::ForEachMatcher, ChildT> forEach( + const internal::Matcher<ChildT>& ChildMatcher) { + return internal::ArgumentAdaptingMatcher< + internal::ForEachMatcher, + ChildT>(ChildMatcher); +} + +/// \brief Matches AST nodes that have descendant AST nodes that match the +/// provided matcher. +/// +/// Example matches X, A, B, C +/// (matcher = record(forEachDescendant(record(hasName("X"))))) +/// class X {}; // Matches X, because X::X is a class of name X inside X. +/// class A { class X {}; }; +/// class B { class C { class X {}; }; }; +/// +/// DescendantT must be an AST base type. +/// +/// As opposed to 'hasDescendant', 'forEachDescendant' will cause a match for +/// each result that matches instead of only on the first one. +/// +/// Note: Recursively combined ForEachDescendant can cause many matches: +/// record(forEachDescendant(record(forEachDescendant(record())))) +/// will match 10 times (plus injected class name matches) on: +/// class A { class B { class C { class D { class E {}; }; }; }; }; +template <typename DescendantT> +internal::ArgumentAdaptingMatcher<internal::ForEachDescendantMatcher, DescendantT> +forEachDescendant( + const internal::Matcher<DescendantT>& DescendantMatcher) { + return internal::ArgumentAdaptingMatcher< + internal::ForEachDescendantMatcher, + DescendantT>(DescendantMatcher); +} + +/// \brief Matches if the provided matcher does not match. +/// +/// Example matches Y (matcher = record(unless(hasName("X")))) +/// class X {}; +/// class Y {}; +template <typename M> +internal::PolymorphicMatcherWithParam1<internal::NotMatcher, M> unless(const M &InnerMatcher) { + return internal::PolymorphicMatcherWithParam1< + internal::NotMatcher, M>(InnerMatcher); +} + +/// \brief Matches a type if the declaration of the type matches the given +/// matcher. +inline internal::PolymorphicMatcherWithParam1< internal::HasDeclarationMatcher, + internal::Matcher<clang::Decl> > + hasDeclaration(const internal::Matcher<clang::Decl> &InnerMatcher) { + return internal::PolymorphicMatcherWithParam1< + internal::HasDeclarationMatcher, + internal::Matcher<clang::Decl> >(InnerMatcher); +} + +/// \brief Matches on the implicit object argument of a member call expression. +/// +/// Example matches y.x() (matcher = call(on(hasType(record(hasName("Y")))))) +/// class Y { public: void x(); }; +/// void z() { Y y; y.x(); }", +/// +/// FIXME: Overload to allow directly matching types? +AST_MATCHER_P(clang::CXXMemberCallExpr, on, internal::Matcher<clang::Expr>, + InnerMatcher) { + const clang::Expr *ExprNode = const_cast<clang::CXXMemberCallExpr&>(Node) + .getImplicitObjectArgument() + ->IgnoreParenImpCasts(); + return (ExprNode != NULL && + InnerMatcher.matches(*ExprNode, Finder, Builder)); +} + +/// \brief Matches if the call expression's callee expression matches. +/// +/// Given +/// class Y { void x() { this->x(); x(); Y y; y.x(); } }; +/// void f() { f(); } +/// call(callee(expression())) +/// matches this->x(), x(), y.x(), f() +/// with callee(...) +/// matching this->x, x, y.x, f respectively +/// +/// Note: Callee cannot take the more general internal::Matcher<clang::Expr> +/// because this introduces ambiguous overloads with calls to Callee taking a +/// internal::Matcher<clang::Decl>, as the matcher hierarchy is purely +/// implemented in terms of implicit casts. +AST_MATCHER_P(clang::CallExpr, callee, internal::Matcher<clang::Stmt>, + InnerMatcher) { + const clang::Expr *ExprNode = Node.getCallee(); + return (ExprNode != NULL && + InnerMatcher.matches(*ExprNode, Finder, Builder)); +} + +/// \brief Matches if the call expression's callee's declaration matches the +/// given matcher. +/// +/// Example matches y.x() (matcher = call(callee(method(hasName("x"))))) +/// class Y { public: void x(); }; +/// void z() { Y y; y.x(); +inline internal::Matcher<clang::CallExpr> callee( + const internal::Matcher<clang::Decl> &InnerMatcher) { + return internal::Matcher<clang::CallExpr>(hasDeclaration(InnerMatcher)); +} + +/// \brief Matches if the expression's or declaration's type matches a type +/// matcher. +/// +/// Example matches x (matcher = expression(hasType( +/// hasDeclaration(record(hasName("X")))))) +/// and z (matcher = variable(hasType( +/// hasDeclaration(record(hasName("X")))))) +/// class X {}; +/// void y(X &x) { x; X z; } +AST_POLYMORPHIC_MATCHER_P(hasType, internal::Matcher<clang::QualType>, + InnerMatcher) { + TOOLING_COMPILE_ASSERT((llvm::is_base_of<clang::Expr, NodeType>::value || + llvm::is_base_of<clang::ValueDecl, NodeType>::value), + instantiated_with_wrong_types); + return InnerMatcher.matches(Node.getType(), Finder, Builder); +} + +/// \brief Overloaded to match the declaration of the expression's or value +/// declaration's type. +/// +/// In case of a value declaration (for example a variable declaration), +/// this resolves one layer of indirection. For example, in the value +/// declaration "X x;", record(hasName("X")) matches the declaration of X, +/// while variable(hasType(record(hasName("X")))) matches the declaration +/// of x." +/// +/// Example matches x (matcher = expression(hasType(record(hasName("X"))))) +/// and z (matcher = variable(hasType(record(hasName("X"))))) +/// class X {}; +/// void y(X &x) { x; X z; } +inline internal::PolymorphicMatcherWithParam1< + internal::matcher_hasTypeMatcher, + internal::Matcher<clang::QualType> > +hasType(const internal::Matcher<clang::Decl> &InnerMatcher) { + return hasType(internal::Matcher<clang::QualType>( + hasDeclaration(InnerMatcher))); +} + +/// \brief Matches if the matched type is a pointer type and the pointee type +/// matches the specified matcher. +/// +/// Example matches y->x() +/// (matcher = call(on(hasType(pointsTo(record(hasName("Y"))))))) +/// class Y { public: void x(); }; +/// void z() { Y *y; y->x(); } +AST_MATCHER_P( + clang::QualType, pointsTo, internal::Matcher<clang::QualType>, + InnerMatcher) { + return (Node->isPointerType() && + InnerMatcher.matches(Node->getPointeeType(), Finder, Builder)); +} + +/// \brief Overloaded to match the pointee type's declaration. +inline internal::Matcher<clang::QualType> pointsTo( + const internal::Matcher<clang::Decl> &InnerMatcher) { + return pointsTo(internal::Matcher<clang::QualType>( + hasDeclaration(InnerMatcher))); +} + +/// \brief Matches if the matched type is a reference type and the referenced +/// type matches the specified matcher. +/// +/// Example matches X &x and const X &y +/// (matcher = variable(hasType(references(record(hasName("X")))))) +/// class X { +/// void a(X b) { +/// X &x = b; +/// const X &y = b; +/// }; +AST_MATCHER_P(clang::QualType, references, internal::Matcher<clang::QualType>, + InnerMatcher) { + return (Node->isReferenceType() && + InnerMatcher.matches(Node->getPointeeType(), Finder, Builder)); +} + +/// \brief Overloaded to match the referenced type's declaration. +inline internal::Matcher<clang::QualType> references( + const internal::Matcher<clang::Decl> &InnerMatcher) { + return references(internal::Matcher<clang::QualType>( + hasDeclaration(InnerMatcher))); +} + +AST_MATCHER_P(clang::CXXMemberCallExpr, onImplicitObjectArgument, + internal::Matcher<clang::Expr>, InnerMatcher) { + const clang::Expr *ExprNode = + const_cast<clang::CXXMemberCallExpr&>(Node).getImplicitObjectArgument(); + return (ExprNode != NULL && + InnerMatcher.matches(*ExprNode, Finder, Builder)); +} + +/// \brief Matches if the expression's type either matches the specified +/// matcher, or is a pointer to a type that matches the InnerMatcher. +inline internal::Matcher<clang::CallExpr> thisPointerType( + const internal::Matcher<clang::QualType> &InnerMatcher) { + return onImplicitObjectArgument( + anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher)))); +} + +/// \brief Overloaded to match the type's declaration. +inline internal::Matcher<clang::CallExpr> thisPointerType( + const internal::Matcher<clang::Decl> &InnerMatcher) { + return onImplicitObjectArgument( + anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher)))); +} + +/// \brief Matches a DeclRefExpr that refers to a declaration that matches the +/// specified matcher. +/// +/// Example matches x in if(x) +/// (matcher = declarationReference(to(variable(hasName("x"))))) +/// bool x; +/// if (x) {} +AST_MATCHER_P(clang::DeclRefExpr, to, internal::Matcher<clang::Decl>, + InnerMatcher) { + const clang::Decl *DeclNode = Node.getDecl(); + return (DeclNode != NULL && + InnerMatcher.matches(*DeclNode, Finder, Builder)); +} + +/// \brief Matches a variable declaration that has an initializer expression +/// that matches the given matcher. +/// +/// Example matches x (matcher = variable(hasInitializer(call()))) +/// bool y() { return true; } +/// bool x = y(); +AST_MATCHER_P( + clang::VarDecl, hasInitializer, internal::Matcher<clang::Expr>, + InnerMatcher) { + const clang::Expr *Initializer = Node.getAnyInitializer(); + return (Initializer != NULL && + InnerMatcher.matches(*Initializer, Finder, Builder)); +} + +/// \brief Checks that a call expression or a constructor call expression has +/// a specific number of arguments (including absent default arguments). +/// +/// Example matches f(0, 0) (matcher = call(argumentCountIs(2))) +/// void f(int x, int y); +/// f(0, 0); +AST_POLYMORPHIC_MATCHER_P(argumentCountIs, unsigned, N) { + TOOLING_COMPILE_ASSERT((llvm::is_base_of<clang::CallExpr, NodeType>::value || + llvm::is_base_of<clang::CXXConstructExpr, + NodeType>::value), + instantiated_with_wrong_types); + return Node.getNumArgs() == N; +} + +/// \brief Matches the n'th argument of a call expression or a constructor +/// call expression. +/// +/// Example matches y in x(y) +/// (matcher = call(hasArgument(0, declarationReference()))) +/// void x(int) { int y; x(y); } +AST_POLYMORPHIC_MATCHER_P2( + hasArgument, unsigned, N, internal::Matcher<clang::Expr>, InnerMatcher) { + TOOLING_COMPILE_ASSERT((llvm::is_base_of<clang::CallExpr, NodeType>::value || + llvm::is_base_of<clang::CXXConstructExpr, + NodeType>::value), + instantiated_with_wrong_types); + return (N < Node.getNumArgs() && + InnerMatcher.matches( + *Node.getArg(N)->IgnoreParenImpCasts(), Finder, Builder)); +} + +/// \brief Matches a constructor initializer. +/// +/// Given +/// struct Foo { +/// Foo() : foo_(1) { } +/// int foo_; +/// }; +/// record(Has(Constructor(hasAnyConstructorInitializer(anything())))) +/// Class matches Foo, hasAnyConstructorInitializer matches foo_(1) +AST_MATCHER_P(clang::CXXConstructorDecl, hasAnyConstructorInitializer, + internal::Matcher<clang::CXXCtorInitializer>, InnerMatcher) { + for (clang::CXXConstructorDecl::init_const_iterator I = Node.init_begin(); + I != Node.init_end(); ++I) { + if (InnerMatcher.matches(**I, Finder, Builder)) { + return true; + } + } + return false; +} + +/// \brief Matches the field declaration of a constructor initializer. +/// +/// Given +/// struct Foo { +/// Foo() : foo_(1) { } +/// int foo_; +/// }; +/// record(has(constructor(hasAnyConstructorInitializer( +/// forField(hasName("foo_")))))) +/// matches Foo +/// with forField matching foo_ +AST_MATCHER_P(clang::CXXCtorInitializer, forField, + internal::Matcher<clang::FieldDecl>, InnerMatcher) { + const clang::FieldDecl *NodeAsDecl = Node.getMember(); + return (NodeAsDecl != NULL && + InnerMatcher.matches(*NodeAsDecl, Finder, Builder)); +} + +/// \brief Matches the initializer expression of a constructor initializer. +/// +/// Given +/// struct Foo { +/// Foo() : foo_(1) { } +/// int foo_; +/// }; +/// record(has(constructor(hasAnyConstructorInitializer( +/// withInitializer(integerLiteral(equals(1))))))) +/// matches Foo +/// with withInitializer matching (1) +AST_MATCHER_P(clang::CXXCtorInitializer, withInitializer, + internal::Matcher<clang::Expr>, InnerMatcher) { + const clang::Expr* NodeAsExpr = Node.getInit(); + return (NodeAsExpr != NULL && + InnerMatcher.matches(*NodeAsExpr, Finder, Builder)); +} + +/// \brief Matches a contructor initializer if it is explicitly written in +/// code (as opposed to implicitly added by the compiler). +/// +/// Given +/// struct Foo { +/// Foo() { } +/// Foo(int) : foo_("A") { } +/// string foo_; +/// }; +/// constructor(hasAnyConstructorInitializer(isWritten())) +/// will match Foo(int), but not Foo() +AST_MATCHER(clang::CXXCtorInitializer, isWritten) { + return Node.isWritten(); +} + +/// \brief Matches a constructor declaration that has been implicitly added +/// by the compiler (eg. implicit default/copy constructors). +AST_MATCHER(clang::CXXConstructorDecl, isImplicit) { + return Node.isImplicit(); +} + +/// \brief Matches any argument of a call expression or a constructor call +/// expression. +/// +/// Given +/// void x(int, int, int) { int y; x(1, y, 42); } +/// call(hasAnyArgument(declarationReference())) +/// matches x(1, y, 42) +/// with hasAnyArgument(...) +/// matching y +AST_POLYMORPHIC_MATCHER_P(hasAnyArgument, internal::Matcher<clang::Expr>, + InnerMatcher) { + TOOLING_COMPILE_ASSERT((llvm::is_base_of<clang::CallExpr, NodeType>::value || + llvm::is_base_of<clang::CXXConstructExpr, + NodeType>::value), + instantiated_with_wrong_types); + for (unsigned I = 0; I < Node.getNumArgs(); ++I) { + if (InnerMatcher.matches(*Node.getArg(I)->IgnoreParenImpCasts(), + Finder, Builder)) { + return true; + } + } + return false; +} + +/// \brief Matches the n'th parameter of a function declaration. +/// +/// Given +/// class X { void f(int x) {} }; +/// method(hasParameter(0, hasType(variable()))) +/// matches f(int x) {} +/// with hasParameter(...) +/// matching int x +AST_MATCHER_P2(clang::FunctionDecl, hasParameter, + unsigned, N, internal::Matcher<clang::ParmVarDecl>, + InnerMatcher) { + return (N < Node.getNumParams() && + InnerMatcher.matches( + *Node.getParamDecl(N), Finder, Builder)); +} + +/// \brief Matches any parameter of a function declaration. +/// +/// Does not match the 'this' parameter of a method. +/// +/// Given +/// class X { void f(int x, int y, int z) {} }; +/// method(hasAnyParameter(hasName("y"))) +/// matches f(int x, int y, int z) {} +/// with hasAnyParameter(...) +/// matching int y +AST_MATCHER_P(clang::FunctionDecl, hasAnyParameter, + internal::Matcher<clang::ParmVarDecl>, InnerMatcher) { + for (unsigned I = 0; I < Node.getNumParams(); ++I) { + if (InnerMatcher.matches(*Node.getParamDecl(I), Finder, Builder)) { + return true; + } + } + return false; +} + +/// \brief Matches the condition expression of an if statement or conditional +/// operator. +/// +/// Example matches true (matcher = hasCondition(boolLiteral(equals(true)))) +/// if (true) {} +AST_POLYMORPHIC_MATCHER_P(hasCondition, internal::Matcher<clang::Expr>, + InnerMatcher) { + TOOLING_COMPILE_ASSERT( + (llvm::is_base_of<clang::IfStmt, NodeType>::value) || + (llvm::is_base_of<clang::ConditionalOperator, NodeType>::value), + has_condition_requires_if_statement_or_conditional_operator); + const clang::Expr *const Condition = Node.getCond(); + return (Condition != NULL && + InnerMatcher.matches(*Condition, Finder, Builder)); +} + +/// \brief Matches the condition variable statement in an if statement. +/// +/// Given +/// if (A* a = GetAPointer()) {} +/// hasConditionVariableStatment(...) +/// matches 'A* a = GetAPointer()'. +AST_MATCHER_P(clang::IfStmt, hasConditionVariableStatement, + internal::Matcher<clang::DeclStmt>, InnerMatcher) { + const clang::DeclStmt* const DeclarationStatement = + Node.getConditionVariableDeclStmt(); + return DeclarationStatement != NULL && + InnerMatcher.matches(*DeclarationStatement, Finder, Builder); +} + +/// \brief Matches a 'for' statement that has a given body. +/// +/// Given +/// for (;;) {} +/// hasBody(compoundStatement()) +/// matches 'for (;;) {}' +/// with compoundStatement() +/// matching '{}' +AST_MATCHER_P(clang::ForStmt, hasBody, internal::Matcher<clang::Stmt>, + InnerMatcher) { + const clang::Stmt *const Statement = Node.getBody(); + return (Statement != NULL && + InnerMatcher.matches(*Statement, Finder, Builder)); +} + +/// \brief Matches compound statements where at least one substatement matches +/// a given matcher. +/// +/// Given +/// { {}; 1+2; } +/// hasAnySubstatement(compoundStatement()) +/// matches '{ {}; 1+2; }' +/// with compoundStatement() +/// matching '{}' +AST_MATCHER_P(clang::CompoundStmt, hasAnySubstatement, + internal::Matcher<clang::Stmt>, InnerMatcher) { + for (clang::CompoundStmt::const_body_iterator It = Node.body_begin(); + It != Node.body_end(); + ++It) { + if (InnerMatcher.matches(**It, Finder, Builder)) return true; + } + return false; +} + +/// \brief Checks that a compound statement contains a specific number of +/// child statements. +/// +/// Example: Given +/// { for (;;) {} } +/// compoundStatement(statementCountIs(0))) +/// matches '{}' +/// but does not match the outer compound statement. +AST_MATCHER_P(clang::CompoundStmt, statementCountIs, unsigned, N) { + return Node.size() == N; +} + +/// \brief Matches literals that are equal to the given value. +/// +/// Example matches true (matcher = boolLiteral(equals(true))) +/// true +template <typename ValueT> +internal::PolymorphicMatcherWithParam1<internal::ValueEqualsMatcher, ValueT> +equals(const ValueT &Value) { + return internal::PolymorphicMatcherWithParam1< + internal::ValueEqualsMatcher, + ValueT>(Value); +} + +/// \brief Matches the operator Name of operator expressions (binary or +/// unary). +/// +/// Example matches a || b (matcher = binaryOperator(hasOperatorName("||"))) +/// !(a || b) +AST_POLYMORPHIC_MATCHER_P(hasOperatorName, std::string, Name) { + TOOLING_COMPILE_ASSERT( + (llvm::is_base_of<clang::BinaryOperator, NodeType>::value) || + (llvm::is_base_of<clang::UnaryOperator, NodeType>::value), + has_condition_requires_if_statement_or_conditional_operator); + return Name == Node.getOpcodeStr(Node.getOpcode()); +} + +/// \brief Matches the left hand side of binary operator expressions. +/// +/// Example matches a (matcher = binaryOperator(hasLHS())) +/// a || b +AST_MATCHER_P(clang::BinaryOperator, hasLHS, + internal::Matcher<clang::Expr>, InnerMatcher) { + clang::Expr *LeftHandSide = Node.getLHS(); + return (LeftHandSide != NULL && + InnerMatcher.matches(*LeftHandSide, Finder, Builder)); +} + +/// \brief Matches the right hand side of binary operator expressions. +/// +/// Example matches b (matcher = binaryOperator(hasRHS())) +/// a || b +AST_MATCHER_P(clang::BinaryOperator, hasRHS, + internal::Matcher<clang::Expr>, InnerMatcher) { + clang::Expr *RightHandSide = Node.getRHS(); + return (RightHandSide != NULL && + InnerMatcher.matches(*RightHandSide, Finder, Builder)); +} + +/// \brief Matches if either the left hand side or the right hand side of a +/// binary operator matches. +inline internal::Matcher<clang::BinaryOperator> hasEitherOperand( + const internal::Matcher<clang::Expr> &InnerMatcher) { + return anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)); +} + +/// \brief Matches if the operand of a unary operator matches. +/// +/// Example matches true (matcher = hasOperand(boolLiteral(equals(true)))) +/// !true +AST_MATCHER_P(clang::UnaryOperator, hasUnaryOperand, + internal::Matcher<clang::Expr>, InnerMatcher) { + const clang::Expr * const Operand = Node.getSubExpr(); + return (Operand != NULL && + InnerMatcher.matches(*Operand, Finder, Builder)); +} + +/// Matches if the implicit cast's source expression matches the given matcher. +/// +/// Example: matches "a string" (matcher = +/// hasSourceExpression(constructorCall())) +/// +/// class URL { URL(string); }; +/// URL url = "a string"; +AST_MATCHER_P(clang::ImplicitCastExpr, hasSourceExpression, + internal::Matcher<clang::Expr>, InnerMatcher) { + const clang::Expr* const SubExpression = Node.getSubExpr(); + return (SubExpression != NULL && + InnerMatcher.matches(*SubExpression, Finder, Builder)); +} + +/// \brief Matches casts whose destination type matches a given matcher. +/// +/// (Note: Clang's AST refers to other conversions as "casts" too, and calls +/// actual casts "explicit" casts.) +AST_MATCHER_P(clang::ExplicitCastExpr, hasDestinationType, + internal::Matcher<clang::QualType>, InnerMatcher) { + const clang::QualType NodeType = Node.getTypeAsWritten(); + return InnerMatcher.matches(NodeType, Finder, Builder); +} + +/// \brief Matches implicit casts whose destination type matches a given +/// matcher. +/// +/// FIXME: Unit test this matcher +AST_MATCHER_P(clang::ImplicitCastExpr, hasImplicitDestinationType, + internal::Matcher<clang::QualType>, InnerMatcher) { + return InnerMatcher.matches(Node.getType(), Finder, Builder); +} + +/// \brief Matches the true branch expression of a conditional operator. +/// +/// Example matches a +/// condition ? a : b +AST_MATCHER_P(clang::ConditionalOperator, hasTrueExpression, + internal::Matcher<clang::Expr>, InnerMatcher) { + clang::Expr *Expression = Node.getTrueExpr(); + return (Expression != NULL && + InnerMatcher.matches(*Expression, Finder, Builder)); +} + +/// \brief Matches the false branch expression of a conditional operator. +/// +/// Example matches b +/// condition ? a : b +AST_MATCHER_P(clang::ConditionalOperator, hasFalseExpression, + internal::Matcher<clang::Expr>, InnerMatcher) { + clang::Expr *Expression = Node.getFalseExpr(); + return (Expression != NULL && + InnerMatcher.matches(*Expression, Finder, Builder)); +} + +/// \brief Matches if a declaration has a body attached. +/// +/// Example matches A, va, fa +/// class A {}; +/// class B; // Doesn't match, as it has no body. +/// int va; +/// extern int vb; // Doesn't match, as it doesn't define the variable. +/// void fa() {} +/// void fb(); // Doesn't match, as it has no body. +inline internal::PolymorphicMatcherWithParam0<internal::IsDefinitionMatcher> +isDefinition() { + return internal::PolymorphicMatcherWithParam0< + internal::IsDefinitionMatcher>(); +} + +/// \brief Matches the class declaration that the given method declaration +/// belongs to. +/// +/// FIXME: Generalize this for other kinds of declarations. +/// FIXME: What other kind of declarations would we need to generalize +/// this to? +/// +/// Example matches A() in the last line +/// (matcher = constructorCall(hasDeclaration(method( +/// ofClass(hasName("A")))))) +/// class A { +/// public: +/// A(); +/// }; +/// A a = A(); +AST_MATCHER_P(clang::CXXMethodDecl, ofClass, + internal::Matcher<clang::CXXRecordDecl>, InnerMatcher) { + const clang::CXXRecordDecl *Parent = Node.getParent(); + return (Parent != NULL && + InnerMatcher.matches(*Parent, Finder, Builder)); +} + +/// \brief Matches member expressions that are called with '->' as opposed +/// to '.'. +/// +/// Member calls on the implicit this pointer match as called with '->'. +/// +/// Given +/// class Y { +/// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; } +/// int a; +/// static int b; +/// }; +/// memberExpression(isArrow()) +/// matches this->x, x, y.x, a, this->b +inline internal::Matcher<clang::MemberExpr> isArrow() { + return makeMatcher(new internal::IsArrowMatcher()); +} + +/// \brief Matches clang::QualType nodes that are const-qualified, i.e., that +/// include "top-level" const. +/// +/// Given +/// void a(int); +/// void b(int const); +/// void c(const int); +/// void d(const int*); +/// void e(int const) {}; +/// function(hasAnyParameter(hasType(isConstQualified()))) +/// matches "void b(int const)", "void c(const int)" and +/// "void e(int const) {}". It does not match d as there +/// is no top-level const on the parameter type "const int *". +inline internal::Matcher<clang::QualType> isConstQualified() { + return makeMatcher(new internal::IsConstQualifiedMatcher()); +} + +/// \brief Matches a member expression where the member is matched by a +/// given matcher. +/// +/// Given +/// struct { int first, second; } first, second; +/// int i(second.first); +/// int j(first.second); +/// memberExpression(member(hasName("first"))) +/// matches second.first +/// but not first.second (because the member name there is "second"). +AST_MATCHER_P(clang::MemberExpr, member, + internal::Matcher<clang::ValueDecl>, InnerMatcher) { + return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder); +} + +/// \brief Matches a member expression where the object expression is +/// matched by a given matcher. +/// +/// Given +/// struct X { int m; }; +/// void f(X x) { x.m; m; } +/// memberExpression(hasObjectExpression(hasType(record(hasName("X"))))))) +/// matches "x.m" and "m" +/// with hasObjectExpression(...) +/// matching "x" and the implicit object expression of "m" which has type X*. +AST_MATCHER_P(clang::MemberExpr, hasObjectExpression, + internal::Matcher<clang::Expr>, InnerMatcher) { + return InnerMatcher.matches(*Node.getBase(), Finder, Builder); +} + +/// \brief Matches template instantiations of function, class, or static +/// member variable template instantiations. +/// +/// Given +/// template <typename T> class X {}; class A {}; X<A> x; +/// or +/// template <typename T> class X {}; class A {}; template class X<A>; +/// record(hasName("::X"), isTemplateInstantiation()) +/// matches the template instantiation of X<A>. +/// +/// But given +/// template <typename T> class X {}; class A {}; +/// template <> class X<A> {}; X<A> x; +/// record(hasName("::X"), isTemplateInstantiation()) +/// does not match, as X<A> is an explicit template specialization. +inline internal::PolymorphicMatcherWithParam0< + internal::IsTemplateInstantiationMatcher> +isTemplateInstantiation() { + return internal::PolymorphicMatcherWithParam0< + internal::IsTemplateInstantiationMatcher>(); +} + +} // end namespace ast_matchers +} // end namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_H diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h new file mode 100644 index 0000000000..d9a557e78c --- /dev/null +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -0,0 +1,888 @@ +//===--- ASTMatchersInternal.h - Structural query framework -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the base layer of the matcher framework. +// +// Matchers are methods that return a Matcher<T> which provides a method +// Matches(...) which is a predicate on an AST node. The Matches method's +// parameters define the context of the match, which allows matchers to recurse +// or store the current node as bound to a specific string, so that it can be +// retrieved later. +// +// In general, matchers have two parts: +// 1. A function Matcher<T> MatcherName(<arguments>) which returns a Matcher<T> +// based on the arguments and optionally on template type deduction based +// on the arguments. Matcher<T>s form an implicit reverse hierarchy +// to clang's AST class hierarchy, meaning that you can use a Matcher<Base> +// everywhere a Matcher<Derived> is required. +// 2. An implementation of a class derived from MatcherInterface<T>. +// +// The matcher functions are defined in ASTMatchers.h. To make it possible +// to implement both the matcher function and the implementation of the matcher +// interface in one place, ASTMatcherMacros.h defines macros that allow +// implementing a matcher in a single place. +// +// This file contains the base classes needed to construct the actual matchers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H +#define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" +#include "llvm/ADT/VariadicFunction.h" +#include <map> +#include <string> +#include <vector> + +/// FIXME: Move into the llvm support library. +template <bool> struct CompileAssert {}; +#define TOOLING_COMPILE_ASSERT(Expr, Msg) \ + typedef CompileAssert<(bool(Expr))> Msg[bool(Expr) ? 1 : -1] + +namespace clang { +namespace ast_matchers { + +class BoundNodes; + +namespace internal { + +class BoundNodesTreeBuilder; + +/// \brief A tree of bound nodes in match results. +/// +/// If a match can contain multiple matches on the same node with different +/// matching subexpressions, BoundNodesTree contains a branch for each of +/// those matching subexpressions. +/// +/// BoundNodesTree's are created during the matching process; when a match +/// is found, we iterate over the tree and create a BoundNodes object containing +/// the union of all bound nodes on the path from the root to a each leaf. +class BoundNodesTree { +public: + /// \brief A visitor interface to visit all BoundNodes results for a + /// BoundNodesTree. + class Visitor { + public: + virtual ~Visitor() {} + + /// \brief Called multiple times during a single call to VisitMatches(...). + /// + /// 'BoundNodesView' contains the bound nodes for a single match. + virtual void visitMatch(const BoundNodes& BoundNodesView) = 0; + }; + + BoundNodesTree(); + + /// \brief Create a BoundNodesTree from pre-filled maps of bindings. + BoundNodesTree(const std::map<std::string, const clang::Decl*>& DeclBindings, + const std::map<std::string, const clang::Stmt*>& StmtBindings, + const std::vector<BoundNodesTree> RecursiveBindings); + + /// \brief Adds all bound nodes to bound_nodes_builder. + void copyTo(BoundNodesTreeBuilder* Builder) const; + + /// \brief Visits all matches that this BoundNodesTree represents. + /// + /// The ownership of 'ResultVisitor' remains at the caller. + void visitMatches(Visitor* ResultVisitor); + +private: + void visitMatchesRecursively( + Visitor* ResultVistior, + std::map<std::string, const clang::Decl*> DeclBindings, + std::map<std::string, const clang::Stmt*> StmtBindings); + + template <typename T> + void copyBindingsTo(const T& bindings, BoundNodesTreeBuilder* Builder) const; + + // FIXME: Find out whether we want to use different data structures here - + // first benchmarks indicate that it doesn't matter though. + + std::map<std::string, const clang::Decl*> DeclBindings; + std::map<std::string, const clang::Stmt*> StmtBindings; + + std::vector<BoundNodesTree> RecursiveBindings; +}; + +/// \brief Creates BoundNodesTree objects. +/// +/// The tree builder is used during the matching process to insert the bound +/// nodes from the Id matcher. +class BoundNodesTreeBuilder { +public: + BoundNodesTreeBuilder(); + + /// \brief Add a binding from an id to a node. + /// + /// FIXME: Add overloads for all AST base types. + /// @{ + void setBinding(const std::pair<const std::string, + const clang::Decl*>& binding); + void setBinding(const std::pair<const std::string, + const clang::Stmt*>& binding); + /// @} + + /// \brief Adds a branch in the tree. + void addMatch(const BoundNodesTree& Bindings); + + /// \brief Returns a BoundNodes object containing all current bindings. + BoundNodesTree build() const; + +private: + BoundNodesTreeBuilder(const BoundNodesTreeBuilder&); // DO NOT IMPLEMENT + void operator=(const BoundNodesTreeBuilder&); // DO NOT IMPLEMENT + + std::map<std::string, const clang::Decl*> DeclBindings; + std::map<std::string, const clang::Stmt*> StmtBindings; + + std::vector<BoundNodesTree> RecursiveBindings; +}; + +class ASTMatchFinder; + +/// \brief Generic interface for matchers on an AST node of type T. +/// +/// Implement this if your matcher may need to inspect the children or +/// descendants of the node or bind matched nodes to names. If you are +/// writing a simple matcher that only inspects properties of the +/// current node and doesn't care about its children or descendants, +/// implement SingleNodeMatcherInterface instead. +template <typename T> +class MatcherInterface : public llvm::RefCountedBaseVPTR { +public: + virtual ~MatcherInterface() {} + + /// \brief Returns true if 'Node' can be matched. + /// + /// May bind 'Node' to an ID via 'Builder', or recurse into + /// the AST via 'Finder'. + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const = 0; +}; + +/// \brief Interface for matchers that only evaluate properties on a single node. +template <typename T> +class SingleNodeMatcherInterface : public MatcherInterface<T> { +public: + /// \brief Returns true if the matcher matches the provided node. + /// + /// A subclass must implement this instead of Matches(). + virtual bool matchesNode(const T &Node) const = 0; + +private: + /// Implements MatcherInterface::Matches. + virtual bool matches(const T &Node, + ASTMatchFinder * /* Finder */, + BoundNodesTreeBuilder * /* Builder */) const { + return matchesNode(Node); + } +}; + +/// \brief Wrapper of a MatcherInterface<T> *that allows copying. +/// +/// A Matcher<Base> can be used anywhere a Matcher<Derived> is +/// required. This establishes an is-a relationship which is reverse +/// to the AST hierarchy. In other words, Matcher<T> is contravariant +/// with respect to T. The relationship is built via a type conversion +/// operator rather than a type hierarchy to be able to templatize the +/// type hierarchy instead of spelling it out. +template <typename T> +class Matcher { +public: + /// \brief Takes ownership of the provided implementation pointer. + explicit Matcher(MatcherInterface<T> *Implementation) + : Implementation(Implementation) {} + + /// \brief Forwards the call to the underlying MatcherInterface<T> pointer. + bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return Implementation->matches(Node, Finder, Builder); + } + + /// \brief Implicitly converts this object to a Matcher<Derived>. + /// + /// Requires Derived to be derived from T. + template <typename Derived> + operator Matcher<Derived>() const { + return Matcher<Derived>(new ImplicitCastMatcher<Derived>(*this)); + } + + /// \brief Returns an ID that uniquely identifies the matcher. + uint64_t getID() const { + /// FIXME: Document the requirements this imposes on matcher + /// implementations (no new() implementation_ during a Matches()). + return reinterpret_cast<uint64_t>(Implementation.getPtr()); + } + +private: + /// \brief Allows conversion from Matcher<T> to Matcher<Derived> if Derived + /// is derived from T. + template <typename Derived> + class ImplicitCastMatcher : public MatcherInterface<Derived> { + public: + explicit ImplicitCastMatcher(const Matcher<T> &From) + : From(From) {} + + virtual bool matches(const Derived &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return From.matches(Node, Finder, Builder); + } + + private: + const Matcher<T> From; + }; + + llvm::IntrusiveRefCntPtr< MatcherInterface<T> > Implementation; +}; // class Matcher + +/// \brief A convenient helper for creating a Matcher<T> without specifying +/// the template type argument. +template <typename T> +inline Matcher<T> makeMatcher(MatcherInterface<T> *Implementation) { + return Matcher<T>(Implementation); +} + +/// \brief Matches declarations for QualType and CallExpr. +/// +/// Type argument DeclMatcherT is required by PolymorphicMatcherWithParam1 but +/// not actually used. +template <typename T, typename DeclMatcherT> +class HasDeclarationMatcher : public MatcherInterface<T> { + TOOLING_COMPILE_ASSERT((llvm::is_same< DeclMatcherT, + Matcher<clang::Decl> >::value), + instantiated_with_wrong_types); +public: + explicit HasDeclarationMatcher(const Matcher<clang::Decl> &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return matchesSpecialized(Node, Finder, Builder); + } + +private: + /// \brief Extracts the CXXRecordDecl of a QualType and returns whether the + /// inner matcher matches on it. + bool matchesSpecialized(const clang::QualType &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + /// FIXME: Add other ways to convert... + clang::CXXRecordDecl *NodeAsRecordDecl = Node->getAsCXXRecordDecl(); + return NodeAsRecordDecl != NULL && + InnerMatcher.matches(*NodeAsRecordDecl, Finder, Builder); + } + + /// \brief Extracts the Decl of the callee of a CallExpr and returns whether + /// the inner matcher matches on it. + bool matchesSpecialized(const clang::CallExpr &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + const clang::Decl *NodeAsDecl = Node.getCalleeDecl(); + return NodeAsDecl != NULL && + InnerMatcher.matches(*NodeAsDecl, Finder, Builder); + } + + /// \brief Extracts the Decl of the constructor call and returns whether the + /// inner matcher matches on it. + bool matchesSpecialized(const clang::CXXConstructExpr &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + const clang::Decl *NodeAsDecl = Node.getConstructor(); + return NodeAsDecl != NULL && + InnerMatcher.matches(*NodeAsDecl, Finder, Builder); + } + + const Matcher<clang::Decl> InnerMatcher; +}; + +/// \brief IsBaseType<T>::value is true if T is a "base" type in the AST +/// node class hierarchies (i.e. if T is Decl, Stmt, or QualType). +template <typename T> +struct IsBaseType { + static const bool value = + (llvm::is_same<T, clang::Decl>::value || + llvm::is_same<T, clang::Stmt>::value || + llvm::is_same<T, clang::QualType>::value || + llvm::is_same<T, clang::CXXCtorInitializer>::value); +}; +template <typename T> +const bool IsBaseType<T>::value; + +/// \brief Interface that can match any AST base node type and contains default +/// implementations returning false. +class UntypedBaseMatcher : public llvm::RefCountedBaseVPTR { +public: + virtual ~UntypedBaseMatcher() {} + + virtual bool matches(const clang::Decl &DeclNode, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return false; + } + virtual bool matches(const clang::QualType &TypeNode, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return false; + } + virtual bool matches(const clang::Stmt &StmtNode, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return false; + } + virtual bool matches(const clang::CXXCtorInitializer &CtorInitNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return false; + } + + /// \brief Returns a unique ID for the matcher. + virtual uint64_t getID() const = 0; +}; + +/// \brief An UntypedBaseMatcher that overwrites the Matches(...) method for +/// node type T. T must be an AST base type. +template <typename T> +class TypedBaseMatcher : public UntypedBaseMatcher { + TOOLING_COMPILE_ASSERT(IsBaseType<T>::value, + typed_base_matcher_can_only_be_used_with_base_type); +public: + explicit TypedBaseMatcher(const Matcher<T> &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + using UntypedBaseMatcher::matches; + /// \brief Implements UntypedBaseMatcher::Matches. + /// + /// Since T is guaranteed to be a "base" AST node type, this method is + /// guaranteed to override one of the matches() methods from + /// UntypedBaseMatcher. + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return InnerMatcher.matches(Node, Finder, Builder); + } + + /// \brief Implements UntypedBaseMatcher::getID. + virtual uint64_t getID() const { + return InnerMatcher.getID(); + } + +private: + Matcher<T> InnerMatcher; +}; + +/// \brief Interface that allows matchers to traverse the AST. +/// FIXME: Find a better name. +/// +/// This provides two entry methods for each base node type in the AST: +/// - matchesChildOf: +/// Matches a matcher on every child node of the given node. Returns true +/// if at least one child node could be matched. +/// - matchesDescendantOf: +/// Matches a matcher on all descendant nodes of the given node. Returns true +/// if at least one descendant matched. +class ASTMatchFinder { +public: + /// \brief Defines how we descend a level in the AST when we pass + /// through expressions. + enum TraversalKind { + /// Will traverse any child nodes. + TK_AsIs, + /// Will not traverse implicit casts and parentheses. + TK_IgnoreImplicitCastsAndParentheses + }; + + /// \brief Defines how bindings are processed on recursive matches. + enum BindKind { + /// Stop at the first match and only bind the first match. + BK_First, + /// Create results for all combinations of bindings that match. + BK_All + }; + + virtual ~ASTMatchFinder() {} + + /// \brief Returns true if the given class is directly or indirectly derived + /// from a base type with the given name. + /// + /// A class is considered to be also derived from itself. + virtual bool classIsDerivedFrom(const clang::CXXRecordDecl *Declaration, + StringRef BaseName) const = 0; + + // FIXME: Implement for other base nodes. + virtual bool matchesChildOf(const clang::Decl &DeclNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traverse, + BindKind Bind) = 0; + virtual bool matchesChildOf(const clang::Stmt &StmtNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traverse, + BindKind Bind) = 0; + + virtual bool matchesDescendantOf(const clang::Decl &DeclNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) = 0; + virtual bool matchesDescendantOf(const clang::Stmt &StmtNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) = 0; +}; + +/// \brief Converts a Matcher<T> to a matcher of desired type To by "adapting" +/// a To into a T. +/// +/// The ArgumentAdapterT argument specifies how the adaptation is done. +/// +/// For example: +/// ArgumentAdaptingMatcher<DynCastMatcher, T>(InnerMatcher); +/// returns a matcher that can be used where a Matcher<To> is required, if +/// To and T are in the same type hierarchy, and thus dyn_cast can be +/// called to convert a To to a T. +/// +/// FIXME: Make sure all our applications of this class actually require +/// knowledge about the inner type. DynCastMatcher obviously does, but the +/// Has *matchers require the inner type solely for COMPILE_ASSERT purposes. +template <template <typename ToArg, typename FromArg> class ArgumentAdapterT, + typename T> +class ArgumentAdaptingMatcher { +public: + explicit ArgumentAdaptingMatcher(const Matcher<T> &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + template <typename To> + operator Matcher<To>() const { + return Matcher<To>(new ArgumentAdapterT<To, T>(InnerMatcher)); + } + +private: + const Matcher<T> InnerMatcher; +}; + +/// \brief A PolymorphicMatcherWithParamN<MatcherT, P1, ..., PN> object can be +/// created from N parameters p1, ..., pN (of type P1, ..., PN) and +/// used as a Matcher<T> where a MatcherT<T, P1, ..., PN>(p1, ..., pN) +/// can be constructed. +/// +/// For example: +/// - PolymorphicMatcherWithParam0<IsDefinitionMatcher>() +/// creates an object that can be used as a Matcher<T> for any type T +/// where an IsDefinitionMatcher<T>() can be constructed. +/// - PolymorphicMatcherWithParam1<ValueEqualsMatcher, int>(42) +/// creates an object that can be used as a Matcher<T> for any type T +/// where a ValueEqualsMatcher<T, int>(42) can be constructed. +template <template <typename T> class MatcherT> +class PolymorphicMatcherWithParam0 { +public: + template <typename T> + operator Matcher<T>() const { + return Matcher<T>(new MatcherT<T>()); + } +}; + +template <template <typename T, typename P1> class MatcherT, + typename P1> +class PolymorphicMatcherWithParam1 { +public: + explicit PolymorphicMatcherWithParam1(const P1 &Param1) + : Param1(Param1) {} + + template <typename T> + operator Matcher<T>() const { + return Matcher<T>(new MatcherT<T, P1>(Param1)); + } + +private: + const P1 Param1; +}; + +template <template <typename T, typename P1, typename P2> class MatcherT, + typename P1, typename P2> +class PolymorphicMatcherWithParam2 { +public: + PolymorphicMatcherWithParam2(const P1 &Param1, const P2 &Param2) + : Param1(Param1), Param2(Param2) {} + + template <typename T> + operator Matcher<T>() const { + return Matcher<T>(new MatcherT<T, P1, P2>(Param1, Param2)); + } + +private: + const P1 Param1; + const P2 Param2; +}; + +/// \brief Matches any instance of the given NodeType. +/// +/// This is useful when a matcher syntactically requires a child matcher, +/// but the context doesn't care. See for example: anything(). +/// +/// FIXME: Alternatively we could also create a IsAMatcher or something +/// that checks that a dyn_cast is possible. This is purely needed for the +/// difference between calling for example: +/// record() +/// and +/// record(SomeMatcher) +/// In the second case we need the correct type we were dyn_cast'ed to in order +/// to get the right type for the inner matcher. In the first case we don't need +/// that, but we use the type conversion anyway and insert a TrueMatcher. +template <typename T> +class TrueMatcher : public SingleNodeMatcherInterface<T> { +public: + virtual bool matchesNode(const T &Node) const { + return true; + } +}; + +/// \brief Provides a MatcherInterface<T> for a Matcher<To> that matches if T is +/// dyn_cast'able into To and the given Matcher<To> matches on the dyn_cast'ed +/// node. +template <typename T, typename To> +class DynCastMatcher : public MatcherInterface<T> { +public: + explicit DynCastMatcher(const Matcher<To> &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + const To *InnerMatchValue = llvm::dyn_cast<To>(&Node); + return InnerMatchValue != NULL && + InnerMatcher.matches(*InnerMatchValue, Finder, Builder); + } + +private: + const Matcher<To> InnerMatcher; +}; + +/// \brief Enables the user to pass a Matcher<clang::CXXMemberCallExpr> to +/// Call(). +/// +/// FIXME: Alternatives are using more specific methods than Call, like +/// MemberCall, or not using VariadicFunction for Call and overloading it. +template <> +template <> +inline Matcher<clang::CXXMemberCallExpr>:: +operator Matcher<clang::CallExpr>() const { + return makeMatcher( + new DynCastMatcher<clang::CallExpr, clang::CXXMemberCallExpr>(*this)); +} + +/// \brief Matcher<T> that wraps an inner Matcher<T> and binds the matched node +/// to an ID if the inner matcher matches on the node. +template <typename T> +class IdMatcher : public MatcherInterface<T> { +public: + /// \brief Creates an IdMatcher that binds to 'ID' if 'InnerMatcher' matches + /// the node. + IdMatcher(StringRef ID, const Matcher<T> &InnerMatcher) + : ID(ID), InnerMatcher(InnerMatcher) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + bool Result = InnerMatcher.matches(Node, Finder, Builder); + if (Result) { + Builder->setBinding(std::pair<const std::string, const T*>(ID, &Node)); + } + return Result; + } + +private: + const std::string ID; + const Matcher<T> InnerMatcher; +}; + +/// \brief Matches nodes of type T that have child nodes of type ChildT for +/// which a specified child matcher matches. +/// +/// ChildT must be an AST base type. +template <typename T, typename ChildT> +class HasMatcher : public MatcherInterface<T> { + TOOLING_COMPILE_ASSERT(IsBaseType<ChildT>::value, + has_only_accepts_base_type_matcher); +public: + explicit HasMatcher(const Matcher<ChildT> &ChildMatcher) + : ChildMatcher(ChildMatcher) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return Finder->matchesChildOf( + Node, ChildMatcher, Builder, + ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses, + ASTMatchFinder::BK_First); + } + + private: + const TypedBaseMatcher<ChildT> ChildMatcher; +}; + +/// \brief Matches nodes of type T that have child nodes of type ChildT for +/// which a specified child matcher matches. ChildT must be an AST base +/// type. +/// As opposed to the HasMatcher, the ForEachMatcher will produce a match +/// for each child that matches. +template <typename T, typename ChildT> +class ForEachMatcher : public MatcherInterface<T> { + TOOLING_COMPILE_ASSERT(IsBaseType<ChildT>::value, + for_each_only_accepts_base_type_matcher); + public: + explicit ForEachMatcher(const Matcher<ChildT> &ChildMatcher) + : ChildMatcher(ChildMatcher) {} + + virtual bool matches(const T& Node, + ASTMatchFinder* Finder, + BoundNodesTreeBuilder* Builder) const { + return Finder->matchesChildOf( + Node, ChildMatcher, Builder, + ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses, + ASTMatchFinder::BK_All); + } + +private: + const TypedBaseMatcher<ChildT> ChildMatcher; +}; + +/// \brief Matches nodes of type T if the given Matcher<T> does not match. +/// +/// Type argument MatcherT is required by PolymorphicMatcherWithParam1 +/// but not actually used. It will always be instantiated with a type +/// convertible to Matcher<T>. +template <typename T, typename MatcherT> +class NotMatcher : public MatcherInterface<T> { +public: + explicit NotMatcher(const Matcher<T> &InnerMatcher) + : InnerMatcher(InnerMatcher) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return !InnerMatcher.matches(Node, Finder, Builder); + } + +private: + const Matcher<T> InnerMatcher; +}; + +/// \brief Matches nodes of type T for which both provided matchers match. +/// +/// Type arguments MatcherT1 and MatcherT2 are required by +/// PolymorphicMatcherWithParam2 but not actually used. They will +/// always be instantiated with types convertible to Matcher<T>. +template <typename T, typename MatcherT1, typename MatcherT2> +class AllOfMatcher : public MatcherInterface<T> { +public: + AllOfMatcher(const Matcher<T> &InnerMatcher1, const Matcher<T> &InnerMatcher2) + : InnerMatcher1(InnerMatcher1), InnerMatcher2(InnerMatcher2) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return InnerMatcher1.matches(Node, Finder, Builder) && + InnerMatcher2.matches(Node, Finder, Builder); + } + +private: + const Matcher<T> InnerMatcher1; + const Matcher<T> InnerMatcher2; +}; + +/// \brief Matches nodes of type T for which at least one of the two provided +/// matchers matches. +/// +/// Type arguments MatcherT1 and MatcherT2 are +/// required by PolymorphicMatcherWithParam2 but not actually +/// used. They will always be instantiated with types convertible to +/// Matcher<T>. +template <typename T, typename MatcherT1, typename MatcherT2> +class AnyOfMatcher : public MatcherInterface<T> { +public: + AnyOfMatcher(const Matcher<T> &InnerMatcher1, const Matcher<T> &InnerMatcher2) + : InnerMatcher1(InnerMatcher1), InnertMatcher2(InnerMatcher2) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return InnerMatcher1.matches(Node, Finder, Builder) || + InnertMatcher2.matches(Node, Finder, Builder); + } + +private: + const Matcher<T> InnerMatcher1; + const Matcher<T> InnertMatcher2; +}; + +/// \brief Creates a Matcher<T> that matches if +/// T is dyn_cast'able into InnerT and all inner matchers match. +template<typename T, typename InnerT> +Matcher<T> makeDynCastAllOfComposite( + ArrayRef<const Matcher<InnerT> *> InnerMatchers) { + if (InnerMatchers.empty()) { + return ArgumentAdaptingMatcher<DynCastMatcher, InnerT>( + makeMatcher(new TrueMatcher<InnerT>)); + } + Matcher<InnerT> InnerMatcher = *InnerMatchers.back(); + for (int i = InnerMatchers.size() - 2; i >= 0; --i) { + InnerMatcher = makeMatcher( + new AllOfMatcher<InnerT, Matcher<InnerT>, Matcher<InnerT> >( + *InnerMatchers[i], InnerMatcher)); + } + return ArgumentAdaptingMatcher<DynCastMatcher, InnerT>(InnerMatcher); +} + +/// \brief Matches nodes of type T that have at least one descendant node of +/// type DescendantT for which the given inner matcher matches. +/// +/// DescendantT must be an AST base type. +template <typename T, typename DescendantT> +class HasDescendantMatcher : public MatcherInterface<T> { + TOOLING_COMPILE_ASSERT(IsBaseType<DescendantT>::value, + has_descendant_only_accepts_base_type_matcher); +public: + explicit HasDescendantMatcher(const Matcher<DescendantT> &DescendantMatcher) + : DescendantMatcher(DescendantMatcher) {} + + virtual bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return Finder->matchesDescendantOf( + Node, DescendantMatcher, Builder, ASTMatchFinder::BK_First); + } + + private: + const TypedBaseMatcher<DescendantT> DescendantMatcher; +}; + +/// \brief Matches nodes of type T that have at least one descendant node of +/// type DescendantT for which the given inner matcher matches. +/// +/// DescendantT must be an AST base type. +/// As opposed to HasDescendantMatcher, ForEachDescendantMatcher will match +/// for each descendant node that matches instead of only for the first. +template <typename T, typename DescendantT> +class ForEachDescendantMatcher : public MatcherInterface<T> { + TOOLING_COMPILE_ASSERT(IsBaseType<DescendantT>::value, + for_each_descendant_only_accepts_base_type_matcher); + public: + explicit ForEachDescendantMatcher( + const Matcher<DescendantT>& DescendantMatcher) + : DescendantMatcher(DescendantMatcher) {} + + virtual bool matches(const T& Node, + ASTMatchFinder* Finder, + BoundNodesTreeBuilder* Builder) const { + return Finder->matchesDescendantOf(Node, DescendantMatcher, Builder, + ASTMatchFinder::BK_All); + } + +private: + const TypedBaseMatcher<DescendantT> DescendantMatcher; +}; + +/// \brief Matches on nodes that have a getValue() method if getValue() equals +/// the value the ValueEqualsMatcher was constructed with. +template <typename T, typename ValueT> +class ValueEqualsMatcher : public SingleNodeMatcherInterface<T> { + TOOLING_COMPILE_ASSERT((llvm::is_base_of<clang::CharacterLiteral, T>::value || + llvm::is_base_of<clang::CXXBoolLiteralExpr, + T>::value || + llvm::is_base_of<clang::FloatingLiteral, T>::value || + llvm::is_base_of<clang::IntegerLiteral, T>::value), + the_node_must_have_a_getValue_method); +public: + explicit ValueEqualsMatcher(const ValueT &ExpectedValue) + : ExpectedValue(ExpectedValue) {} + + virtual bool matchesNode(const T &Node) const { + return Node.getValue() == ExpectedValue; + } + +private: + const ValueT ExpectedValue; +}; + +template <typename T> +class IsDefinitionMatcher : public SingleNodeMatcherInterface<T> { + TOOLING_COMPILE_ASSERT( + (llvm::is_base_of<clang::TagDecl, T>::value) || + (llvm::is_base_of<clang::VarDecl, T>::value) || + (llvm::is_base_of<clang::FunctionDecl, T>::value), + is_definition_requires_isThisDeclarationADefinition_method); +public: + virtual bool matchesNode(const T &Node) const { + return Node.isThisDeclarationADefinition(); + } +}; + +/// \brief Matches on template instantiations for FunctionDecl, VarDecl or +/// CXXRecordDecl nodes. +template <typename T> +class IsTemplateInstantiationMatcher : public MatcherInterface<T> { + TOOLING_COMPILE_ASSERT((llvm::is_base_of<clang::FunctionDecl, T>::value) || + (llvm::is_base_of<clang::VarDecl, T>::value) || + (llvm::is_base_of<clang::CXXRecordDecl, T>::value), + requires_getTemplateSpecializationKind_method); + public: + virtual bool matches(const T& Node, + ASTMatchFinder* Finder, + BoundNodesTreeBuilder* Builder) const { + return (Node.getTemplateSpecializationKind() == + clang::TSK_ImplicitInstantiation || + Node.getTemplateSpecializationKind() == + clang::TSK_ExplicitInstantiationDefinition); + } +}; + +class IsArrowMatcher : public SingleNodeMatcherInterface<clang::MemberExpr> { +public: + virtual bool matchesNode(const clang::MemberExpr &Node) const { + return Node.isArrow(); + } +}; + +class IsConstQualifiedMatcher + : public SingleNodeMatcherInterface<clang::QualType> { + public: + virtual bool matchesNode(const clang::QualType& Node) const { + return Node.isConstQualified(); + } +}; + +/// \brief A VariadicDynCastAllOfMatcher<SourceT, TargetT> object is a +/// variadic functor that takes a number of Matcher<TargetT> and returns a +/// Matcher<SourceT> that matches TargetT nodes that are matched by all of the +/// given matchers, if SourceT can be dynamically casted into TargetT. +/// +/// For example: +/// const VariadicDynCastAllOfMatcher< +/// clang::Decl, clang::CXXRecordDecl> record; +/// Creates a functor record(...) that creates a Matcher<clang::Decl> given +/// a variable number of arguments of type Matcher<clang::CXXRecordDecl>. +/// The returned matcher matches if the given clang::Decl can by dynamically +/// casted to clang::CXXRecordDecl and all given matchers match. +template <typename SourceT, typename TargetT> +class VariadicDynCastAllOfMatcher + : public llvm::VariadicFunction< + Matcher<SourceT>, Matcher<TargetT>, + makeDynCastAllOfComposite<SourceT, TargetT> > { +public: + VariadicDynCastAllOfMatcher() {} +}; + +} // end namespace internal +} // end namespace ast_matchers +} // end namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H diff --git a/include/clang/ASTMatchers/ASTMatchersMacros.h b/include/clang/ASTMatchers/ASTMatchersMacros.h new file mode 100644 index 0000000000..c68534acae --- /dev/null +++ b/include/clang/ASTMatchers/ASTMatchersMacros.h @@ -0,0 +1,224 @@ +//===--- ASTMatchersMacros.h - Structural query framework -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines macros that enable us to define new matchers in a single place. +// Since a matcher is a function which returns a Matcher<T> object, where +// T is the type of the actual implementation of the matcher, the macros allow +// us to write matchers like functions and take care of the definition of the +// class boilerplate. +// +// Note that when you define a matcher with an AST_MATCHER* macro, only the +// function which creates the matcher goes into the current namespace - the +// class that implements the actual matcher, which gets returned by the +// generator function, is put into the 'internal' namespace. This allows us +// to only have the functions (which is all the user cares about) in the +// 'ast_matchers' namespace and hide the boilerplate. +// +// To define a matcher in user code, always put it into the clang::ast_matchers +// namespace and refer to the internal types via the 'internal::': +// +// namespace clang { +// namespace ast_matchers { +// AST_MATCHER_P(MemberExpr, Member, +// internal::Matcher<ValueDecl>, InnerMatcher) { +// return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder); +// } +// } // end namespace ast_matchers +// } // end namespace clang +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H +#define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H + +/// \brief AST_MATCHER(Type, DefineMatcher) { ... } +/// defines a zero parameter function named DefineMatcher() that returns a +/// Matcher<Type> object. +/// +/// The code between the curly braces has access to the following variables: +/// +/// Node: the AST node being matched; its type is Type. +/// Finder: an ASTMatchFinder*. +/// Builder: a BoundNodesTreeBuilder*. +/// +/// The code should return true if 'Node' matches. +#define AST_MATCHER(Type, DefineMatcher) \ + namespace internal { \ + class matcher_##DefineMatcher##Matcher \ + : public MatcherInterface<Type> { \ + public: \ + explicit matcher_##DefineMatcher##Matcher() {} \ + virtual bool matches( \ + const Type &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const; \ + }; \ + } \ + inline internal::Matcher<Type> DefineMatcher() { \ + return internal::makeMatcher( \ + new internal::matcher_##DefineMatcher##Matcher()); \ + } \ + inline bool internal::matcher_##DefineMatcher##Matcher::matches( \ + const Type &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const + +/// \brief AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } +/// defines a single-parameter function named DefineMatcher() that returns a +/// Matcher<Type> object. +/// +/// The code between the curly braces has access to the following variables: +/// +/// Node: the AST node being matched; its type is Type. +/// Param: the parameter passed to the function; its type +/// is ParamType. +/// Finder: an ASTMatchFinder*. +/// Builder: a BoundNodesTreeBuilder*. +/// +/// The code should return true if 'Node' matches. +#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) \ + namespace internal { \ + class matcher_##DefineMatcher##Matcher \ + : public MatcherInterface<Type> { \ + public: \ + explicit matcher_##DefineMatcher##Matcher( \ + const ParamType &A##Param) : Param(A##Param) {} \ + virtual bool matches( \ + const Type &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const; \ + private: \ + const ParamType Param; \ + }; \ + } \ + inline internal::Matcher<Type> DefineMatcher(const ParamType &Param) { \ + return internal::makeMatcher( \ + new internal::matcher_##DefineMatcher##Matcher(Param)); \ + } \ + inline bool internal::matcher_##DefineMatcher##Matcher::matches( \ + const Type &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const + +/// \brief AST_MATCHER_P2( +/// Type, DefineMatcher, ParamType1, Param1, ParamType2, Param2) { ... } +/// defines a two-parameter function named DefineMatcher() that returns a +/// Matcher<Type> object. +/// +/// The code between the curly braces has access to the following variables: +/// +/// Node: the AST node being matched; its type is Type. +/// Param1, Param2: the parameters passed to the function; their types +/// are ParamType1 and ParamType2. +/// Finder: an ASTMatchFinder*. +/// Builder: a BoundNodesTreeBuilder*. +/// +/// The code should return true if 'Node' matches. +#define AST_MATCHER_P2( \ + Type, DefineMatcher, ParamType1, Param1, ParamType2, Param2) \ + namespace internal { \ + class matcher_##DefineMatcher##Matcher \ + : public MatcherInterface<Type> { \ + public: \ + matcher_##DefineMatcher##Matcher( \ + const ParamType1 &A##Param1, const ParamType2 &A##Param2) \ + : Param1(A##Param1), Param2(A##Param2) {} \ + virtual bool matches( \ + const Type &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const; \ + private: \ + const ParamType1 Param1; \ + const ParamType2 Param2; \ + }; \ + } \ + inline internal::Matcher<Type> DefineMatcher( \ + const ParamType1 &Param1, const ParamType2 &Param2) { \ + return internal::makeMatcher( \ + new internal::matcher_##DefineMatcher##Matcher( \ + Param1, Param2)); \ + } \ + inline bool internal::matcher_##DefineMatcher##Matcher::matches( \ + const Type &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const + +/// \brief AST_POLYMORPHIC_MATCHER_P(DefineMatcher, ParamType, Param) { ... } +/// defines a single-parameter function named DefineMatcher() that is +/// polymorphic in the return type. +/// +/// The variables are the same as for +/// AST_MATCHER_P, with the addition of NodeType, which specifies the node type +/// of the matcher Matcher<NodeType> returned by the function matcher(). +/// +/// FIXME: Pull out common code with above macro? +#define AST_POLYMORPHIC_MATCHER_P(DefineMatcher, ParamType, Param) \ + namespace internal { \ + template <typename NodeType, typename ParamT> \ + class matcher_##DefineMatcher##Matcher \ + : public MatcherInterface<NodeType> { \ + public: \ + explicit matcher_##DefineMatcher##Matcher( \ + const ParamType &A##Param) : Param(A##Param) {} \ + virtual bool matches( \ + const NodeType &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const; \ + private: \ + const ParamType Param; \ + }; \ + } \ + inline internal::PolymorphicMatcherWithParam1< \ + internal::matcher_##DefineMatcher##Matcher, \ + ParamType > \ + DefineMatcher(const ParamType &Param) { \ + return internal::PolymorphicMatcherWithParam1< \ + internal::matcher_##DefineMatcher##Matcher, \ + ParamType >(Param); \ + } \ + template <typename NodeType, typename ParamT> \ + bool internal::matcher_##DefineMatcher##Matcher<NodeType, ParamT>::matches( \ + const NodeType &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const + +/// \brief AST_POLYMORPHIC_MATCHER_P2( +/// DefineMatcher, ParamType1, Param1, ParamType2, Param2) { ... } +/// defines a two-parameter function named matcher() that is polymorphic in +/// the return type. +/// +/// The variables are the same as for AST_MATCHER_P2, with the +/// addition of NodeType, which specifies the node type of the matcher +/// Matcher<NodeType> returned by the function DefineMatcher(). +#define AST_POLYMORPHIC_MATCHER_P2( \ + DefineMatcher, ParamType1, Param1, ParamType2, Param2) \ + namespace internal { \ + template <typename NodeType, typename ParamT1, typename ParamT2> \ + class matcher_##DefineMatcher##Matcher \ + : public MatcherInterface<NodeType> { \ + public: \ + matcher_##DefineMatcher##Matcher( \ + const ParamType1 &A##Param1, const ParamType2 &A##Param2) \ + : Param1(A##Param1), Param2(A##Param2) {} \ + virtual bool matches( \ + const NodeType &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const; \ + private: \ + const ParamType1 Param1; \ + const ParamType2 Param2; \ + }; \ + } \ + inline internal::PolymorphicMatcherWithParam2< \ + internal::matcher_##DefineMatcher##Matcher, \ + ParamType1, ParamType2 > \ + DefineMatcher(const ParamType1 &Param1, const ParamType2 &Param2) { \ + return internal::PolymorphicMatcherWithParam2< \ + internal::matcher_##DefineMatcher##Matcher, \ + ParamType1, ParamType2 >( \ + Param1, Param2); \ + } \ + template <typename NodeType, typename ParamT1, typename ParamT2> \ + bool internal::matcher_##DefineMatcher##Matcher< \ + NodeType, ParamT1, ParamT2>::matches( \ + const NodeType &Node, ASTMatchFinder *Finder, \ + BoundNodesTreeBuilder *Builder) const + +#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp new file mode 100644 index 0000000000..e4e44a32b6 --- /dev/null +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -0,0 +1,556 @@ +//===--- ASTMatchFinder.cpp - Structural query framework ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements an algorithm to efficiently search for matches on AST nodes. +// Uses memoization to support recursive matches like HasDescendant. +// +// The general idea is to visit all AST nodes with a RecursiveASTVisitor, +// calling the Matches(...) method of each matcher we are running on each +// AST node. The matcher can recurse via the ASTMatchFinder interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include <set> + +namespace clang { +namespace ast_matchers { +namespace internal { +namespace { + +// Returns the value that 'AMap' maps 'Key' to, or NULL if 'Key' is +// not in 'AMap'. +template <typename Map> +static const typename Map::mapped_type * +find(const Map &AMap, const typename Map::key_type &Key) { + typename Map::const_iterator It = AMap.find(Key); + return It == AMap.end() ? NULL : &It->second; +} + +// We use memoization to avoid running the same matcher on the same +// AST node twice. This pair is the key for looking up match +// result. It consists of an ID of the MatcherInterface (for +// identifying the matcher) and a pointer to the AST node. +typedef std::pair<uint64_t, const void*> UntypedMatchInput; + +// Used to store the result of a match and possibly bound nodes. +struct MemoizedMatchResult { + bool ResultOfMatch; + BoundNodesTree Nodes; +}; + +// A RecursiveASTVisitor that traverses all children or all descendants of +// a node. +class MatchChildASTVisitor + : public clang::RecursiveASTVisitor<MatchChildASTVisitor> { +public: + typedef clang::RecursiveASTVisitor<MatchChildASTVisitor> VisitorBase; + + // Creates an AST visitor that matches 'matcher' on all children or + // descendants of a traversed node. max_depth is the maximum depth + // to traverse: use 1 for matching the children and INT_MAX for + // matching the descendants. + MatchChildASTVisitor(const UntypedBaseMatcher *BaseMatcher, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder, + int MaxDepth, + ASTMatchFinder::TraversalKind Traversal, + ASTMatchFinder::BindKind Bind) + : BaseMatcher(BaseMatcher), + Finder(Finder), + Builder(Builder), + CurrentDepth(-1), + MaxDepth(MaxDepth), + Traversal(Traversal), + Bind(Bind), + Matches(false) {} + + // Returns true if a match is found in the subtree rooted at the + // given AST node. This is done via a set of mutually recursive + // functions. Here's how the recursion is done (the *wildcard can + // actually be Decl, Stmt, or Type): + // + // - Traverse(node) calls BaseTraverse(node) when it needs + // to visit the descendants of node. + // - BaseTraverse(node) then calls (via VisitorBase::Traverse*(node)) + // Traverse*(c) for each child c of 'node'. + // - Traverse*(c) in turn calls Traverse(c), completing the + // recursion. + template <typename T> + bool findMatch(const T &Node) { + reset(); + traverse(Node); + return Matches; + } + + // The following are overriding methods from the base visitor class. + // They are public only to allow CRTP to work. They are *not *part + // of the public API of this class. + bool TraverseDecl(clang::Decl *DeclNode) { + return (DeclNode == NULL) || traverse(*DeclNode); + } + bool TraverseStmt(clang::Stmt *StmtNode) { + const clang::Stmt *StmtToTraverse = StmtNode; + if (Traversal == + ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses) { + const clang::Expr *ExprNode = dyn_cast_or_null<clang::Expr>(StmtNode); + if (ExprNode != NULL) { + StmtToTraverse = ExprNode->IgnoreParenImpCasts(); + } + } + return (StmtToTraverse == NULL) || traverse(*StmtToTraverse); + } + bool TraverseType(clang::QualType TypeNode) { + return traverse(TypeNode); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } + +private: + // Used for updating the depth during traversal. + struct ScopedIncrement { + explicit ScopedIncrement(int *Depth) : Depth(Depth) { ++(*Depth); } + ~ScopedIncrement() { --(*Depth); } + + private: + int *Depth; + }; + + // Resets the state of this object. + void reset() { + Matches = false; + CurrentDepth = -1; + } + + // Forwards the call to the corresponding Traverse*() method in the + // base visitor class. + bool baseTraverse(const clang::Decl &DeclNode) { + return VisitorBase::TraverseDecl(const_cast<clang::Decl*>(&DeclNode)); + } + bool baseTraverse(const clang::Stmt &StmtNode) { + return VisitorBase::TraverseStmt(const_cast<clang::Stmt*>(&StmtNode)); + } + bool baseTraverse(clang::QualType TypeNode) { + return VisitorBase::TraverseType(TypeNode); + } + + // Traverses the subtree rooted at 'node'; returns true if the + // traversal should continue after this function returns; also sets + // matched_ to true if a match is found during the traversal. + template <typename T> + bool traverse(const T &Node) { + TOOLING_COMPILE_ASSERT(IsBaseType<T>::value, + traverse_can_only_be_instantiated_with_base_type); + ScopedIncrement ScopedDepth(&CurrentDepth); + if (CurrentDepth == 0) { + // We don't want to match the root node, so just recurse. + return baseTraverse(Node); + } + if (Bind != ASTMatchFinder::BK_All) { + if (BaseMatcher->matches(Node, Finder, Builder)) { + Matches = true; + return false; // Abort as soon as a match is found. + } + if (CurrentDepth < MaxDepth) { + // The current node doesn't match, and we haven't reached the + // maximum depth yet, so recurse. + return baseTraverse(Node); + } + // The current node doesn't match, and we have reached the + // maximum depth, so don't recurse (but continue the traversal + // such that other nodes at the current level can be visited). + return true; + } else { + BoundNodesTreeBuilder RecursiveBuilder; + if (BaseMatcher->matches(Node, Finder, &RecursiveBuilder)) { + // After the first match the matcher succeeds. + Matches = true; + Builder->addMatch(RecursiveBuilder.build()); + } + if (CurrentDepth < MaxDepth) { + baseTraverse(Node); + } + // In kBindAll mode we always search for more matches. + return true; + } + } + + const UntypedBaseMatcher *const BaseMatcher; + ASTMatchFinder *const Finder; + BoundNodesTreeBuilder *const Builder; + int CurrentDepth; + const int MaxDepth; + const ASTMatchFinder::TraversalKind Traversal; + const ASTMatchFinder::BindKind Bind; + bool Matches; +}; + +// Controls the outermost traversal of the AST and allows to match multiple +// matchers. +class MatchASTVisitor : public clang::RecursiveASTVisitor<MatchASTVisitor>, + public ASTMatchFinder { +public: + MatchASTVisitor(std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> > *Triggers) + : Triggers(Triggers), + ActiveASTContext(NULL) { + } + + void set_active_ast_context(clang::ASTContext *NewActiveASTContext) { + ActiveASTContext = NewActiveASTContext; + } + + // The following Visit*() and Traverse*() functions "override" + // methods in RecursiveASTVisitor. + + bool VisitTypedefDecl(clang::TypedefDecl *DeclNode) { + // When we see 'typedef A B', we add name 'B' to the set of names + // A's canonical type maps to. This is necessary for implementing + // IsDerivedFrom(x) properly, where x can be the name of the base + // class or any of its aliases. + // + // In general, the is-alias-of (as defined by typedefs) relation + // is tree-shaped, as you can typedef a type more than once. For + // example, + // + // typedef A B; + // typedef A C; + // typedef C D; + // typedef C E; + // + // gives you + // + // A + // |- B + // `- C + // |- D + // `- E + // + // It is wrong to assume that the relation is a chain. A correct + // implementation of IsDerivedFrom() needs to recognize that B and + // E are aliases, even though neither is a typedef of the other. + // Therefore, we cannot simply walk through one typedef chain to + // find out whether the type name matches. + const clang::Type *TypeNode = DeclNode->getUnderlyingType().getTypePtr(); + const clang::Type *CanonicalType = // root of the typedef tree + ActiveASTContext->getCanonicalType(TypeNode); + TypeToUnqualifiedAliases[CanonicalType].insert( + DeclNode->getName().str()); + return true; + } + + bool TraverseDecl(clang::Decl *DeclNode); + bool TraverseStmt(clang::Stmt *StmtNode); + bool TraverseType(clang::QualType TypeNode); + bool TraverseTypeLoc(clang::TypeLoc TypeNode); + + // Matches children or descendants of 'Node' with 'BaseMatcher'. + template <typename T> + bool memoizedMatchesRecursively(const T &Node, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, int MaxDepth, + TraversalKind Traversal, BindKind Bind) { + TOOLING_COMPILE_ASSERT((llvm::is_same<T, clang::Decl>::value) || + (llvm::is_same<T, clang::Stmt>::value), + type_does_not_support_memoization); + const UntypedMatchInput input(BaseMatcher.getID(), &Node); + std::pair<MemoizationMap::iterator, bool> InsertResult + = ResultCache.insert(std::make_pair(input, MemoizedMatchResult())); + if (InsertResult.second) { + BoundNodesTreeBuilder DescendantBoundNodesBuilder; + InsertResult.first->second.ResultOfMatch = + matchesRecursively(Node, BaseMatcher, &DescendantBoundNodesBuilder, + MaxDepth, Traversal, Bind); + InsertResult.first->second.Nodes = + DescendantBoundNodesBuilder.build(); + } + InsertResult.first->second.Nodes.copyTo(Builder); + return InsertResult.first->second.ResultOfMatch; + } + + // Matches children or descendants of 'Node' with 'BaseMatcher'. + template <typename T> + bool matchesRecursively(const T &Node, const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, int MaxDepth, + TraversalKind Traversal, BindKind Bind) { + MatchChildASTVisitor Visitor( + &BaseMatcher, this, Builder, MaxDepth, Traversal, Bind); + return Visitor.findMatch(Node); + } + + virtual bool classIsDerivedFrom(const clang::CXXRecordDecl *Declaration, + StringRef BaseName) const; + + // Implements ASTMatchFinder::MatchesChildOf. + virtual bool matchesChildOf(const clang::Decl &DeclNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traversal, + BindKind Bind) { + return matchesRecursively(DeclNode, BaseMatcher, Builder, 1, Traversal, + Bind); + } + virtual bool matchesChildOf(const clang::Stmt &StmtNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traversal, + BindKind Bind) { + return matchesRecursively(StmtNode, BaseMatcher, Builder, 1, Traversal, + Bind); + } + + // Implements ASTMatchFinder::MatchesDescendantOf. + virtual bool matchesDescendantOf(const clang::Decl &DeclNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) { + return memoizedMatchesRecursively(DeclNode, BaseMatcher, Builder, INT_MAX, + TK_AsIs, Bind); + } + virtual bool matchesDescendantOf(const clang::Stmt &StmtNode, + const UntypedBaseMatcher &BaseMatcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) { + return memoizedMatchesRecursively(StmtNode, BaseMatcher, Builder, INT_MAX, + TK_AsIs, Bind); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } + +private: + // Implements a BoundNodesTree::Visitor that calls a MatchCallback with + // the aggregated bound nodes for each match. + class MatchVisitor : public BoundNodesTree::Visitor { + public: + MatchVisitor(clang::ASTContext* Context, + MatchFinder::MatchCallback* Callback) + : Context(Context), + Callback(Callback) {} + + virtual void visitMatch(const BoundNodes& BoundNodesView) { + Callback->run(MatchFinder::MatchResult(BoundNodesView, Context)); + } + + private: + clang::ASTContext* Context; + MatchFinder::MatchCallback* Callback; + }; + + // Returns true if 'TypeNode' is also known by the name 'Name'. In other + // words, there is a type (including typedef) with the name 'Name' + // that is equal to 'TypeNode'. + bool typeHasAlias(const clang::Type *TypeNode, + StringRef Name) const { + const clang::Type *const CanonicalType = + ActiveASTContext->getCanonicalType(TypeNode); + const std::set<std::string> *UnqualifiedAlias = + find(TypeToUnqualifiedAliases, CanonicalType); + return UnqualifiedAlias != NULL && UnqualifiedAlias->count(Name) > 0; + } + + // Matches all registered matchers on the given node and calls the + // result callback for every node that matches. + template <typename T> + void match(const T &node) { + for (std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> >::const_iterator + It = Triggers->begin(), End = Triggers->end(); + It != End; ++It) { + BoundNodesTreeBuilder Builder; + if (It->first->matches(node, this, &Builder)) { + BoundNodesTree BoundNodes = Builder.build(); + MatchVisitor Visitor(ActiveASTContext, It->second); + BoundNodes.visitMatches(&Visitor); + } + } + } + + std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> > *const Triggers; + clang::ASTContext *ActiveASTContext; + + // Maps a canonical type to the names of its typedefs. + llvm::DenseMap<const clang::Type*, std::set<std::string> > + TypeToUnqualifiedAliases; + + // Maps (matcher, node) -> the match result for memoization. + typedef llvm::DenseMap<UntypedMatchInput, MemoizedMatchResult> MemoizationMap; + MemoizationMap ResultCache; +}; + +// Returns true if the given class is directly or indirectly derived +// from a base type with the given name. A class is considered to be +// also derived from itself. +bool +MatchASTVisitor::classIsDerivedFrom(const clang::CXXRecordDecl *Declaration, + StringRef BaseName) const { + if (Declaration->getName() == BaseName) { + return true; + } + if (!Declaration->hasDefinition()) { + return false; + } + typedef clang::CXXRecordDecl::base_class_const_iterator BaseIterator; + for (BaseIterator It = Declaration->bases_begin(), + End = Declaration->bases_end(); It != End; ++It) { + const clang::Type *TypeNode = It->getType().getTypePtr(); + + if (typeHasAlias(TypeNode, BaseName)) + return true; + + // clang::Type::getAs<...>() drills through typedefs. + if (TypeNode->getAs<clang::DependentNameType>() != NULL || + TypeNode->getAs<clang::TemplateTypeParmType>() != NULL) { + // Dependent names and template TypeNode parameters will be matched when + // the template is instantiated. + continue; + } + clang::CXXRecordDecl *ClassDecl = NULL; + clang::TemplateSpecializationType const *TemplateType = + TypeNode->getAs<clang::TemplateSpecializationType>(); + if (TemplateType != NULL) { + if (TemplateType->getTemplateName().isDependent()) { + // Dependent template specializations will be matched when the + // template is instantiated. + continue; + } + // For template specialization types which are specializing a template + // declaration which is an explicit or partial specialization of another + // template declaration, getAsCXXRecordDecl() returns the corresponding + // ClassTemplateSpecializationDecl. + // + // For template specialization types which are specializing a template + // declaration which is neither an explicit nor partial specialization of + // another template declaration, getAsCXXRecordDecl() returns NULL and + // we get the CXXRecordDecl of the templated declaration. + clang::CXXRecordDecl *SpecializationDecl = + TemplateType->getAsCXXRecordDecl(); + if (SpecializationDecl != NULL) { + ClassDecl = SpecializationDecl; + } else { + ClassDecl = llvm::dyn_cast<clang::CXXRecordDecl>( + TemplateType->getTemplateName() + .getAsTemplateDecl()->getTemplatedDecl()); + } + } else { + ClassDecl = TypeNode->getAsCXXRecordDecl(); + } + assert(ClassDecl != NULL); + assert(ClassDecl != Declaration); + if (classIsDerivedFrom(ClassDecl, BaseName)) { + return true; + } + } + return false; +} + +bool MatchASTVisitor::TraverseDecl(clang::Decl *DeclNode) { + if (DeclNode == NULL) { + return true; + } + match(*DeclNode); + return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode); +} + +bool MatchASTVisitor::TraverseStmt(clang::Stmt *StmtNode) { + if (StmtNode == NULL) { + return true; + } + match(*StmtNode); + return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode); +} + +bool MatchASTVisitor::TraverseType(clang::QualType TypeNode) { + match(TypeNode); + return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode); +} + +bool MatchASTVisitor::TraverseTypeLoc(clang::TypeLoc TypeLoc) { + return clang::RecursiveASTVisitor<MatchASTVisitor>:: + TraverseType(TypeLoc.getType()); +} + +class MatchASTConsumer : public clang::ASTConsumer { +public: + MatchASTConsumer(std::vector< std::pair<const UntypedBaseMatcher*, + MatchFinder::MatchCallback*> > *Triggers, + MatchFinder::ParsingDoneTestCallback *ParsingDone) + : Visitor(Triggers), + ParsingDone(ParsingDone) {} + +private: + virtual void HandleTranslationUnit(clang::ASTContext &Context) { + if (ParsingDone != NULL) { + ParsingDone->run(); + } + Visitor.set_active_ast_context(&Context); + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + Visitor.set_active_ast_context(NULL); + } + + MatchASTVisitor Visitor; + MatchFinder::ParsingDoneTestCallback *ParsingDone; +}; + +} // end namespace +} // end namespace internal + +MatchFinder::MatchResult::MatchResult(const BoundNodes &Nodes, + clang::ASTContext *Context) + : Nodes(Nodes), Context(Context), + SourceManager(&Context->getSourceManager()) {} + +MatchFinder::MatchCallback::~MatchCallback() {} +MatchFinder::ParsingDoneTestCallback::~ParsingDoneTestCallback() {} + +MatchFinder::MatchFinder() : ParsingDone(NULL) {} + +MatchFinder::~MatchFinder() { + for (std::vector< std::pair<const internal::UntypedBaseMatcher*, + MatchFinder::MatchCallback*> >::const_iterator + It = Triggers.begin(), End = Triggers.end(); + It != End; ++It) { + delete It->first; + } +} + +void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch, + MatchCallback *Action) { + Triggers.push_back(std::make_pair( + new internal::TypedBaseMatcher<clang::Decl>(NodeMatch), Action)); +} + +void MatchFinder::addMatcher(const TypeMatcher &NodeMatch, + MatchCallback *Action) { + Triggers.push_back(std::make_pair( + new internal::TypedBaseMatcher<clang::QualType>(NodeMatch), Action)); +} + +void MatchFinder::addMatcher(const StatementMatcher &NodeMatch, + MatchCallback *Action) { + Triggers.push_back(std::make_pair( + new internal::TypedBaseMatcher<clang::Stmt>(NodeMatch), Action)); +} + +clang::ASTConsumer *MatchFinder::newASTConsumer() { + return new internal::MatchASTConsumer(&Triggers, ParsingDone); +} + +void MatchFinder::registerTestCallbackAfterParsing( + MatchFinder::ParsingDoneTestCallback *NewParsingDone) { + ParsingDone = NewParsingDone; +} + +} // end namespace ast_matchers +} // end namespace clang diff --git a/lib/ASTMatchers/ASTMatchersInternal.cpp b/lib/ASTMatchers/ASTMatchersInternal.cpp new file mode 100644 index 0000000000..c864e31cb1 --- /dev/null +++ b/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -0,0 +1,102 @@ +//===--- ASTMatchersInternal.cpp - Structural query framework -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the base layer of the matcher framework. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" + +namespace clang { +namespace ast_matchers { +namespace internal { + +BoundNodesTree::BoundNodesTree() {} + +BoundNodesTree::BoundNodesTree( + const std::map<std::string, const clang::Decl*>& DeclBindings, + const std::map<std::string, const clang::Stmt*>& StmtBindings, + const std::vector<BoundNodesTree> RecursiveBindings) + : DeclBindings(DeclBindings), StmtBindings(StmtBindings), + RecursiveBindings(RecursiveBindings) {} + +void BoundNodesTree::copyTo(BoundNodesTreeBuilder* Builder) const { + copyBindingsTo(DeclBindings, Builder); + copyBindingsTo(StmtBindings, Builder); + for (std::vector<BoundNodesTree>::const_iterator + I = RecursiveBindings.begin(), + E = RecursiveBindings.end(); + I != E; ++I) { + Builder->addMatch(*I); + } +} + +template <typename T> +void BoundNodesTree::copyBindingsTo( + const T& Bindings, BoundNodesTreeBuilder* Builder) const { + for (typename T::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) { + Builder->setBinding(*I); + } +} + +void BoundNodesTree::visitMatches(Visitor* ResultVisitor) { + std::map<std::string, const clang::Decl*> AggregatedDeclBindings; + std::map<std::string, const clang::Stmt*> AggregatedStmtBindings; + visitMatchesRecursively(ResultVisitor, AggregatedDeclBindings, + AggregatedStmtBindings); +} + +void BoundNodesTree:: +visitMatchesRecursively(Visitor* ResultVisitor, + std::map<std::string, const clang::Decl*> + AggregatedDeclBindings, + std::map<std::string, const clang::Stmt*> + AggregatedStmtBindings) { + copy(DeclBindings.begin(), DeclBindings.end(), + inserter(AggregatedDeclBindings, AggregatedDeclBindings.begin())); + copy(StmtBindings.begin(), StmtBindings.end(), + inserter(AggregatedStmtBindings, AggregatedStmtBindings.begin())); + if (RecursiveBindings.empty()) { + ResultVisitor->visitMatch(BoundNodes(AggregatedDeclBindings, + AggregatedStmtBindings)); + } else { + for (unsigned I = 0; I < RecursiveBindings.size(); ++I) { + RecursiveBindings[I].visitMatchesRecursively(ResultVisitor, + AggregatedDeclBindings, + AggregatedStmtBindings); + } + } +} + +BoundNodesTreeBuilder::BoundNodesTreeBuilder() {} + +void BoundNodesTreeBuilder:: +setBinding(const std::pair<const std::string, const clang::Decl*>& Binding) { + DeclBindings.insert(Binding); +} + +void BoundNodesTreeBuilder:: +setBinding(const std::pair<const std::string, const clang::Stmt*>& Binding) { + StmtBindings.insert(Binding); +} + +void BoundNodesTreeBuilder::addMatch(const BoundNodesTree& Bindings) { + RecursiveBindings.push_back(Bindings); +} + +BoundNodesTree BoundNodesTreeBuilder::build() const { + return BoundNodesTree(DeclBindings, StmtBindings, RecursiveBindings); +} + +} // end namespace internal +} // end namespace ast_matchers +} // end namespace clang diff --git a/lib/ASTMatchers/CMakeLists.txt b/lib/ASTMatchers/CMakeLists.txt new file mode 100644 index 0000000000..c27a37edd8 --- /dev/null +++ b/lib/ASTMatchers/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS support) +set(LLVM_USED_LIBS clangBasic clangAST) + +add_clang_library(clangASTMatchers + ASTMatchFinder.cpp + ASTMatchersInternal.cpp + ) diff --git a/lib/ASTMatchers/Makefile b/lib/ASTMatchers/Makefile new file mode 100644 index 0000000000..3ee9ccb580 --- /dev/null +++ b/lib/ASTMatchers/Makefile @@ -0,0 +1,15 @@ +##===- clang/lib/ASTMatchers/Makefile ----------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangASTMatchers + +PARALLEL_DIRS = Dynamic + +include $(CLANG_LEVEL)/Makefile diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index dfb9d61ff5..206c22818b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(Basic) add_subdirectory(Lex) add_subdirectory(Parse) add_subdirectory(AST) +add_subdirectory(ASTMatchers) add_subdirectory(Sema) add_subdirectory(CodeGen) add_subdirectory(Analysis) diff --git a/lib/Makefile b/lib/Makefile index 2eb72a91e6..1f14aa078e 100755 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,7 +8,7 @@ ##===----------------------------------------------------------------------===## CLANG_LEVEL := .. -PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \ +PARALLEL_DIRS = Headers Basic Lex Parse AST ASTMatchers Sema CodeGen Analysis \ StaticAnalyzer Edit Rewrite ARCMigrate Serialization Frontend \ FrontendTool Tooling Driver diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp new file mode 100644 index 0000000000..290da9005d --- /dev/null +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -0,0 +1,1979 @@ +//===- unittest/Tooling/ASTMatchersTest.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/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { + +TEST(HasNameDeathTest, DiesOnEmptyName) { + ASSERT_DEBUG_DEATH({ + DeclarationMatcher HasEmptyName = record(hasName("")); + EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); + }, ""); +} + +TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) { + ASSERT_DEBUG_DEATH({ + DeclarationMatcher IsDerivedFromEmpty = record(isDerivedFrom("")); + EXPECT_TRUE(notMatches("class X {};", IsDerivedFromEmpty)); + }, ""); +} + +TEST(NameableDeclaration, MatchesVariousDecls) { + DeclarationMatcher NamedX = nameableDeclaration(hasName("X")); + EXPECT_TRUE(matches("typedef int X;", NamedX)); + EXPECT_TRUE(matches("int X;", NamedX)); + EXPECT_TRUE(matches("class foo { virtual void X(); };", NamedX)); + EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", NamedX)); + EXPECT_TRUE(matches("void foo() { int X; }", NamedX)); + EXPECT_TRUE(matches("namespace X { }", NamedX)); + + EXPECT_TRUE(notMatches("#define X 1", NamedX)); +} + +TEST(DeclarationMatcher, MatchClass) { + DeclarationMatcher ClassMatcher(record()); + EXPECT_FALSE(matches("", ClassMatcher)); + + DeclarationMatcher ClassX = record(record(hasName("X"))); + EXPECT_TRUE(matches("class X;", ClassX)); + EXPECT_TRUE(matches("class X {};", ClassX)); + EXPECT_TRUE(matches("template<class T> class X {};", ClassX)); + EXPECT_TRUE(notMatches("", ClassX)); +} + +TEST(DeclarationMatcher, ClassIsDerived) { + DeclarationMatcher IsDerivedFromX = record(isDerivedFrom("X")); + + EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX)); + EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX)); + EXPECT_TRUE(matches("class X {};", IsDerivedFromX)); + EXPECT_TRUE(matches("class X;", IsDerivedFromX)); + EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX)); + EXPECT_TRUE(notMatches("", IsDerivedFromX)); + + DeclarationMatcher ZIsDerivedFromX = + record(hasName("Z"), isDerivedFrom("X")); + EXPECT_TRUE( + matches("class X {}; class Y : public X {}; class Z : public Y {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {};" + "template<class T> class Y : public X {};" + "class Z : public Y<int> {};", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class X {}; template<class T> class Z : public X {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class T> class X {}; " + "template<class T> class Z : public X<T> {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class T, class U=T> class X {}; " + "template<class T> class Z : public X<T> {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class X> class A { class Z : public X {}; };", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class X> class A { public: class Z : public X {}; }; " + "class X{}; void y() { A<X>::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template <class T> class X {}; " + "template<class Y> class A { class Z : public X<Y> {}; };", + ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<template<class T> class X> class A { " + " class Z : public X<int> {}; };", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<template<class T> class X> class A { " + " public: class Z : public X<int> {}; }; " + "template<class T> class X {}; void y() { A<X>::Z z; }", + ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class X> class A { class Z : public X::D {}; };", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class X> class A { public: " + " class Z : public X::D {}; }; " + "class Y { public: class X {}; typedef X D; }; " + "void y() { A<Y>::Z z; }", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {}; typedef X Y; class Z : public Y {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class T> class Y { typedef typename T::U X; " + " class Z : public X {}; };", ZIsDerivedFromX)); + EXPECT_TRUE(matches("class X {}; class Z : public ::X {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class T> class X {}; " + "template<class T> class A { class Z : public X<T>::D {}; };", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class T> class X { public: typedef X<T> D; }; " + "template<class T> class A { public: " + " class Z : public X<T>::D {}; }; void y() { A<int>::Z z; }", + ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class X> class A { class Z : public X::D::E {}; };", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {}; typedef X V; typedef V W; class Z : public W {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {}; class Y : public X {}; " + "typedef Y V; typedef V W; class Z : public W {};", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("template<class T, class U> class X {}; " + "template<class T> class A { class Z : public X<T, int> {}; };", + ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template<class X> class D { typedef X A; typedef A B; " + " typedef B C; class Z : public C {}; };", + ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {}; typedef X A; typedef A B; " + "class Z : public B {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class X {}; typedef X A; typedef A B; typedef B C; " + "class Z : public C {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class U {}; typedef U X; typedef X V; " + "class Z : public V {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class Base {}; typedef Base X; " + "class Z : public Base {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class Base {}; typedef Base Base2; typedef Base2 X; " + "class Z : public Base {};", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("class Base {}; class Base2 {}; typedef Base2 X; " + "class Z : public Base {};", ZIsDerivedFromX)); + EXPECT_TRUE( + matches("class A {}; typedef A X; typedef A Y; " + "class Z : public Y {};", ZIsDerivedFromX)); + EXPECT_TRUE( + notMatches("template <typename T> class Z;" + "template <> class Z<void> {};" + "template <typename T> class Z : public Z<void> {};", + IsDerivedFromX)); + EXPECT_TRUE( + matches("template <typename T> class X;" + "template <> class X<void> {};" + "template <typename T> class X : public X<void> {};", + IsDerivedFromX)); + EXPECT_TRUE(matches( + "class X {};" + "template <typename T> class Z;" + "template <> class Z<void> {};" + "template <typename T> class Z : public Z<void>, public X {};", + ZIsDerivedFromX)); + + // FIXME: Once we have better matchers for template type matching, + // get rid of the Variable(...) matching and match the right template + // declarations directly. + const char *RecursiveTemplateOneParameter = + "class Base1 {}; class Base2 {};" + "template <typename T> class Z;" + "template <> class Z<void> : public Base1 {};" + "template <> class Z<int> : public Base2 {};" + "template <> class Z<float> : public Z<void> {};" + "template <> class Z<double> : public Z<int> {};" + "template <typename T> class Z : public Z<float>, public Z<double> {};" + "void f() { Z<float> z_float; Z<double> z_double; Z<char> z_char; }"; + EXPECT_TRUE(matches( + RecursiveTemplateOneParameter, + variable(hasName("z_float"), + hasInitializer(hasType(record(isDerivedFrom("Base1"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateOneParameter, + variable( + hasName("z_float"), + hasInitializer(hasType(record(isDerivedFrom("Base2"))))))); + EXPECT_TRUE(matches( + RecursiveTemplateOneParameter, + variable( + hasName("z_char"), + hasInitializer(hasType(record(isDerivedFrom("Base1"), + isDerivedFrom("Base2"))))))); + + const char *RecursiveTemplateTwoParameters = + "class Base1 {}; class Base2 {};" + "template <typename T1, typename T2> class Z;" + "template <typename T> class Z<void, T> : public Base1 {};" + "template <typename T> class Z<int, T> : public Base2 {};" + "template <typename T> class Z<float, T> : public Z<void, T> {};" + "template <typename T> class Z<double, T> : public Z<int, T> {};" + "template <typename T1, typename T2> class Z : " + " public Z<float, T2>, public Z<double, T2> {};" + "void f() { Z<float, void> z_float; Z<double, void> z_double; " + " Z<char, void> z_char; }"; + EXPECT_TRUE(matches( + RecursiveTemplateTwoParameters, + variable( + hasName("z_float"), + hasInitializer(hasType(record(isDerivedFrom("Base1"))))))); + EXPECT_TRUE(notMatches( + RecursiveTemplateTwoParameters, + variable( + hasName("z_float"), + hasInitializer(hasType(record(isDerivedFrom("Base2"))))))); + EXPECT_TRUE(matches( + RecursiveTemplateTwoParameters, + variable( + hasName("z_char"), + hasInitializer(hasType(record(isDerivedFrom("Base1"), + isDerivedFrom("Base2"))))))); +} + +TEST(DeclarationMatcher, MatchAnyOf) { + DeclarationMatcher YOrZDerivedFromX = + record(anyOf(hasName("Y"), allOf(isDerivedFrom("X"), hasName("Z")))); + + EXPECT_TRUE( + matches("class X {}; class Z : public X {};", YOrZDerivedFromX)); + EXPECT_TRUE(matches("class Y {};", YOrZDerivedFromX)); + EXPECT_TRUE( + notMatches("class X {}; class W : public X {};", YOrZDerivedFromX)); + EXPECT_TRUE(notMatches("class Z {};", YOrZDerivedFromX)); + + DeclarationMatcher XOrYOrZOrUOrV = + record(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U"), + hasName("V"))); + + EXPECT_TRUE(matches("class X {};", XOrYOrZOrUOrV)); + EXPECT_TRUE(matches("class Y {};", XOrYOrZOrUOrV)); + EXPECT_TRUE(matches("class Z {};", XOrYOrZOrUOrV)); + EXPECT_TRUE(matches("class U {};", XOrYOrZOrUOrV)); + EXPECT_TRUE(matches("class V {};", XOrYOrZOrUOrV)); + EXPECT_TRUE(notMatches("class A {};", XOrYOrZOrUOrV)); +} + +TEST(DeclarationMatcher, MatchHas) { + DeclarationMatcher HasClassX = record(has(record(hasName("X")))); + + EXPECT_TRUE(matches("class Y { class X {}; };", HasClassX)); + EXPECT_TRUE(matches("class X {};", HasClassX)); + + DeclarationMatcher YHasClassX = + record(hasName("Y"), has(record(hasName("X")))); + EXPECT_TRUE(matches("class Y { class X {}; };", YHasClassX)); + EXPECT_TRUE(notMatches("class X {};", YHasClassX)); + EXPECT_TRUE( + notMatches("class Y { class Z { class X {}; }; };", YHasClassX)); +} + +TEST(DeclarationMatcher, MatchHasRecursiveAllOf) { + DeclarationMatcher Recursive = + record( + has(record( + has(record(hasName("X"))), + has(record(hasName("Y"))), + hasName("Z"))), + has(record( + has(record(hasName("A"))), + has(record(hasName("B"))), + hasName("C"))), + hasName("F")); + + EXPECT_TRUE(matches( + "class F {" + " class Z {" + " class X {};" + " class Y {};" + " };" + " class C {" + " class A {};" + " class B {};" + " };" + "};", Recursive)); + + EXPECT_TRUE(matches( + "class F {" + " class Z {" + " class A {};" + " class X {};" + " class Y {};" + " };" + " class C {" + " class X {};" + " class A {};" + " class B {};" + " };" + "};", Recursive)); + + EXPECT_TRUE(matches( + "class O1 {" + " class O2 {" + " class F {" + " class Z {" + " class A {};" + " class X {};" + " class Y {};" + " };" + " class C {" + " class X {};" + " class A {};" + " class B {};" + " };" + " };" + " };" + "};", Recursive)); +} + +TEST(DeclarationMatcher, MatchHasRecursiveAnyOf) { + DeclarationMatcher Recursive = + record( + anyOf( + has(record( + anyOf( + has(record( + hasName("X"))), + has(record( + hasName("Y"))), + hasName("Z")))), + has(record( + anyOf( + hasName("C"), + has(record( + hasName("A"))), + has(record( + hasName("B")))))), + hasName("F"))); + + EXPECT_TRUE(matches("class F {};", Recursive)); + EXPECT_TRUE(matches("class Z {};", Recursive)); + EXPECT_TRUE(matches("class C {};", Recursive)); + EXPECT_TRUE(matches("class M { class N { class X {}; }; };", Recursive)); + EXPECT_TRUE(matches("class M { class N { class B {}; }; };", Recursive)); + EXPECT_TRUE( + matches("class O1 { class O2 {" + " class M { class N { class B {}; }; }; " + "}; };", Recursive)); +} + +TEST(DeclarationMatcher, MatchNot) { + DeclarationMatcher NotClassX = + record( + isDerivedFrom("Y"), + unless(hasName("Y")), + unless(hasName("X"))); + EXPECT_TRUE(notMatches("", NotClassX)); + EXPECT_TRUE(notMatches("class Y {};", NotClassX)); + EXPECT_TRUE(matches("class Y {}; class Z : public Y {};", NotClassX)); + EXPECT_TRUE(notMatches("class Y {}; class X : public Y {};", NotClassX)); + EXPECT_TRUE( + notMatches("class Y {}; class Z {}; class X : public Y {};", + NotClassX)); + + DeclarationMatcher ClassXHasNotClassY = + record( + hasName("X"), + has(record(hasName("Z"))), + unless( + has(record(hasName("Y"))))); + EXPECT_TRUE(matches("class X { class Z {}; };", ClassXHasNotClassY)); + EXPECT_TRUE(notMatches("class X { class Y {}; class Z {}; };", + ClassXHasNotClassY)); +} + +TEST(DeclarationMatcher, HasDescendant) { + DeclarationMatcher ZDescendantClassX = + record( + hasDescendant(record(hasName("X"))), + hasName("Z")); + EXPECT_TRUE(matches("class Z { class X {}; };", ZDescendantClassX)); + EXPECT_TRUE( + matches("class Z { class Y { class X {}; }; };", ZDescendantClassX)); + EXPECT_TRUE( + matches("class Z { class A { class Y { class X {}; }; }; };", + ZDescendantClassX)); + EXPECT_TRUE( + matches("class Z { class A { class B { class Y { class X {}; }; }; }; };", + ZDescendantClassX)); + EXPECT_TRUE(notMatches("class Z {};", ZDescendantClassX)); + + DeclarationMatcher ZDescendantClassXHasClassY = + record( + hasDescendant(record(has(record(hasName("Y"))), + hasName("X"))), + hasName("Z")); + EXPECT_TRUE(matches("class Z { class X { class Y {}; }; };", + ZDescendantClassXHasClassY)); + EXPECT_TRUE( + matches("class Z { class A { class B { class X { class Y {}; }; }; }; };", + ZDescendantClassXHasClassY)); + EXPECT_TRUE(notMatches( + "class Z {" + " class A {" + " class B {" + " class X {" + " class C {" + " class Y {};" + " };" + " };" + " }; " + " };" + "};", ZDescendantClassXHasClassY)); + + DeclarationMatcher ZDescendantClassXDescendantClassY = + record( + hasDescendant(record(hasDescendant(record(hasName("Y"))), + hasName("X"))), + hasName("Z")); + EXPECT_TRUE( + matches("class Z { class A { class X { class B { class Y {}; }; }; }; };", + ZDescendantClassXDescendantClassY)); + EXPECT_TRUE(matches( + "class Z {" + " class A {" + " class X {" + " class B {" + " class Y {};" + " };" + " class Y {};" + " };" + " };" + "};", ZDescendantClassXDescendantClassY)); +} + +TEST(StatementMatcher, Has) { + StatementMatcher HasVariableI = + expression( + hasType(pointsTo(record(hasName("X")))), + has(declarationReference(to(variable(hasName("i")))))); + + EXPECT_TRUE(matches( + "class X; X *x(int); void c() { int i; x(i); }", HasVariableI)); + EXPECT_TRUE(notMatches( + "class X; X *x(int); void c() { int i; x(42); }", HasVariableI)); +} + +TEST(StatementMatcher, HasDescendant) { + StatementMatcher HasDescendantVariableI = + expression( + hasType(pointsTo(record(hasName("X")))), + hasDescendant(declarationReference(to(variable(hasName("i")))))); + + EXPECT_TRUE(matches( + "class X; X *x(bool); bool b(int); void c() { int i; x(b(i)); }", + HasDescendantVariableI)); + EXPECT_TRUE(notMatches( + "class X; X *x(bool); bool b(int); void c() { int i; x(b(42)); }", + HasDescendantVariableI)); +} + +TEST(TypeMatcher, MatchesClassType) { + TypeMatcher TypeA = hasDeclaration(record(hasName("A"))); + + EXPECT_TRUE(matches("class A { public: A *a; };", TypeA)); + EXPECT_TRUE(notMatches("class A {};", TypeA)); + + TypeMatcher TypeDerivedFromA = hasDeclaration(record(isDerivedFrom("A"))); + + EXPECT_TRUE(matches("class A {}; class B : public A { public: B *b; };", + TypeDerivedFromA)); + EXPECT_TRUE(notMatches("class A {};", TypeA)); + + TypeMatcher TypeAHasClassB = hasDeclaration( + record(hasName("A"), has(record(hasName("B"))))); + + EXPECT_TRUE( + matches("class A { public: A *a; class B {}; };", TypeAHasClassB)); +} + +// Returns from Run whether 'bound_nodes' contain a Decl bound to 'Id', which +// can be dynamically casted to T. +// Optionally checks that the check succeeded a specific number of times. +template <typename T> +class VerifyIdIsBoundToDecl : public BoundNodesCallback { +public: + // Create an object that checks that a node of type 'T' was bound to 'Id'. + // Does not check for a certain number of matches. + explicit VerifyIdIsBoundToDecl(const std::string& Id) + : Id(Id), ExpectedCount(-1), Count(0) {} + + // Create an object that checks that a node of type 'T' was bound to 'Id'. + // Checks that there were exactly 'ExpectedCount' matches. + explicit VerifyIdIsBoundToDecl(const std::string& Id, int ExpectedCount) + : Id(Id), ExpectedCount(ExpectedCount), Count(0) {} + + ~VerifyIdIsBoundToDecl() { + if (ExpectedCount != -1) { + EXPECT_EQ(ExpectedCount, Count); + } + } + + virtual bool run(const BoundNodes *Nodes) { + if (Nodes->getDeclAs<T>(Id) != NULL) { + ++Count; + return true; + } + return false; + } + +private: + const std::string Id; + const int ExpectedCount; + int Count; +}; +template <typename T> +class VerifyIdIsBoundToStmt : public BoundNodesCallback { +public: + explicit VerifyIdIsBoundToStmt(const std::string &Id) : Id(Id) {} + virtual bool run(const BoundNodes *Nodes) { + const T *Node = Nodes->getStmtAs<T>(Id); + return Node != NULL; + } +private: + const std::string Id; +}; + +TEST(Matcher, BindMatchedNodes) { + DeclarationMatcher ClassX = has(id("x", record(hasName("X")))); + + EXPECT_TRUE(matchAndVerifyResultTrue("class X {};", + ClassX, new VerifyIdIsBoundToDecl<clang::CXXRecordDecl>("x"))); + + EXPECT_TRUE(matchAndVerifyResultFalse("class X {};", + ClassX, new VerifyIdIsBoundToDecl<clang::CXXRecordDecl>("other-id"))); + + TypeMatcher TypeAHasClassB = hasDeclaration( + record(hasName("A"), has(id("b", record(hasName("B")))))); + + EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };", + TypeAHasClassB, + new VerifyIdIsBoundToDecl<clang::Decl>("b"))); + + StatementMatcher MethodX = id("x", call(callee(method(hasName("x"))))); + + EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };", + MethodX, + new VerifyIdIsBoundToStmt<clang::CXXMemberCallExpr>("x"))); +} + +TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) { + TypeMatcher ClassX = hasDeclaration(record(hasName("X"))); + EXPECT_TRUE( + matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX)))); + EXPECT_TRUE( + notMatches("class X {}; void y(X *x) { x; }", + expression(hasType(ClassX)))); + EXPECT_TRUE( + matches("class X {}; void y(X *x) { x; }", + expression(hasType(pointsTo(ClassX))))); +} + +TEST(HasType, TakesQualTypeMatcherAndMatchesValueDecl) { + TypeMatcher ClassX = hasDeclaration(record(hasName("X"))); + EXPECT_TRUE( + matches("class X {}; void y() { X x; }", variable(hasType(ClassX)))); + EXPECT_TRUE( + notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX)))); + EXPECT_TRUE( + matches("class X {}; void y() { X *x; }", + variable(hasType(pointsTo(ClassX))))); +} + +TEST(HasType, TakesDeclMatcherAndMatchesExpr) { + DeclarationMatcher ClassX = record(hasName("X")); + EXPECT_TRUE( + matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX)))); + EXPECT_TRUE( + notMatches("class X {}; void y(X *x) { x; }", + expression(hasType(ClassX)))); +} + +TEST(HasType, TakesDeclMatcherAndMatchesValueDecl) { + DeclarationMatcher ClassX = record(hasName("X")); + EXPECT_TRUE( + matches("class X {}; void y() { X x; }", variable(hasType(ClassX)))); + EXPECT_TRUE( + notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX)))); +} + +TEST(Matcher, Call) { + // FIXME: Do we want to overload Call() to directly take + // Matcher<clang::Decl>, too? + StatementMatcher MethodX = call(hasDeclaration(method(hasName("x")))); + + EXPECT_TRUE(matches("class Y { void x() { x(); } };", MethodX)); + EXPECT_TRUE(notMatches("class Y { void x() {} };", MethodX)); + + StatementMatcher MethodOnY = call(on(hasType(record(hasName("Y"))))); + + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z() { Y y; y.x(); }", + MethodOnY)); + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z(Y &y) { y.x(); }", + MethodOnY)); + EXPECT_TRUE( + notMatches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }", + MethodOnY)); + EXPECT_TRUE( + notMatches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }", + MethodOnY)); + EXPECT_TRUE( + notMatches("class Y { public: void x(); }; void z() { Y *y; y->x(); }", + MethodOnY)); + + StatementMatcher MethodOnYPointer = + call(on(hasType(pointsTo(record(hasName("Y")))))); + + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }", + MethodOnYPointer)); + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }", + MethodOnYPointer)); + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }", + MethodOnYPointer)); + EXPECT_TRUE( + notMatches("class Y { public: void x(); }; void z() { Y y; y.x(); }", + MethodOnYPointer)); + EXPECT_TRUE( + notMatches("class Y { public: void x(); }; void z(Y &y) { y.x(); }", + MethodOnYPointer)); +} + +TEST(Matcher, OverloadedOperatorCall) { + StatementMatcher OpCall = overloadedOperatorCall(); + // Unary operator + EXPECT_TRUE(matches("class Y { }; " + "bool operator!(Y x) { return false; }; " + "Y y; bool c = !y;", OpCall)); + // No match -- special operators like "new", "delete" + // FIXME: operator new takes size_t, for which we need stddef.h, for which + // we need to figure out include paths in the test. + // EXPECT_TRUE(NotMatches("#include <stddef.h>\n" + // "class Y { }; " + // "void *operator new(size_t size) { return 0; } " + // "Y *y = new Y;", OpCall)); + EXPECT_TRUE(notMatches("class Y { }; " + "void operator delete(void *p) { } " + "void a() {Y *y = new Y; delete y;}", OpCall)); + // Binary operator + EXPECT_TRUE(matches("class Y { }; " + "bool operator&&(Y x, Y y) { return true; }; " + "Y a; Y b; bool c = a && b;", + OpCall)); + // No match -- normal operator, not an overloaded one. + EXPECT_TRUE(notMatches("bool x = true, y = true; bool t = x && y;", OpCall)); + EXPECT_TRUE(notMatches("int t = 5 << 2;", OpCall)); +} + +TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) { + StatementMatcher OpCallAndAnd = + overloadedOperatorCall(hasOverloadedOperatorName("&&")); + EXPECT_TRUE(matches("class Y { }; " + "bool operator&&(Y x, Y y) { return true; }; " + "Y a; Y b; bool c = a && b;", OpCallAndAnd)); + StatementMatcher OpCallLessLess = + overloadedOperatorCall(hasOverloadedOperatorName("<<")); + EXPECT_TRUE(notMatches("class Y { }; " + "bool operator&&(Y x, Y y) { return true; }; " + "Y a; Y b; bool c = a && b;", + OpCallLessLess)); +} + +TEST(Matcher, ThisPointerType) { + StatementMatcher MethodOnY = call(thisPointerType(record(hasName("Y")))); + + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z() { Y y; y.x(); }", + MethodOnY)); + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z(Y &y) { y.x(); }", + MethodOnY)); + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }", + MethodOnY)); + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }", + MethodOnY)); + EXPECT_TRUE( + matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }", + MethodOnY)); + + EXPECT_TRUE(matches( + "class Y {" + " public: virtual void x();" + "};" + "class X : public Y {" + " public: virtual void x();" + "};" + "void z() { X *x; x->Y::x(); }", MethodOnY)); +} + +TEST(Matcher, VariableUsage) { + StatementMatcher Reference = + declarationReference(to( + variable(hasInitializer( + call(thisPointerType(record(hasName("Y")))))))); + + EXPECT_TRUE(matches( + "class Y {" + " public:" + " bool x() const;" + "};" + "void z(const Y &y) {" + " bool b = y.x();" + " if (b) {}" + "}", Reference)); + + EXPECT_TRUE(notMatches( + "class Y {" + " public:" + " bool x() const;" + "};" + "void z(const Y &y) {" + " bool b = y.x();" + "}", Reference)); +} + +TEST(Matcher, CalledVariable) { + StatementMatcher CallOnVariableY = expression( + call(on(declarationReference(to(variable(hasName("y"))))))); + + EXPECT_TRUE(matches( + "class Y { public: void x() { Y y; y.x(); } };", CallOnVariableY)); + EXPECT_TRUE(matches( + "class Y { public: void x() const { Y y; y.x(); } };", CallOnVariableY)); + EXPECT_TRUE(matches( + "class Y { public: void x(); };" + "class X : public Y { void z() { X y; y.x(); } };", CallOnVariableY)); + EXPECT_TRUE(matches( + "class Y { public: void x(); };" + "class X : public Y { void z() { X *y; y->x(); } };", CallOnVariableY)); + EXPECT_TRUE(notMatches( + "class Y { public: void x(); };" + "class X : public Y { void z() { unsigned long y; ((X*)y)->x(); } };", + CallOnVariableY)); +} + +TEST(MemberExpression, DoesNotMatchClasses) { + EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpression())); +} + +TEST(MemberExpression, MatchesMemberFunctionCall) { + EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpression())); +} + +TEST(MemberExpression, MatchesVariable) { + EXPECT_TRUE( + matches("class Y { void x() { this->y; } int y; };", memberExpression())); + EXPECT_TRUE( + matches("class Y { void x() { y; } int y; };", memberExpression())); + EXPECT_TRUE( + matches("class Y { void x() { Y y; y.y; } int y; };", + memberExpression())); +} + +TEST(MemberExpression, MatchesStaticVariable) { + EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };", + memberExpression())); + EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };", + memberExpression())); + EXPECT_TRUE(notMatches("class Y { void x() { Y::y; } static int y; };", + memberExpression())); +} + +TEST(IsArrow, MatchesMemberVariablesViaArrow) { + EXPECT_TRUE(matches("class Y { void x() { this->y; } int y; };", + memberExpression(isArrow()))); + EXPECT_TRUE(matches("class Y { void x() { y; } int y; };", + memberExpression(isArrow()))); + EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };", + memberExpression(isArrow()))); +} + +TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) { + EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };", + memberExpression(isArrow()))); + EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };", + memberExpression(isArrow()))); + EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } static int y; };", + memberExpression(isArrow()))); +} + +TEST(IsArrow, MatchesMemberCallsViaArrow) { + EXPECT_TRUE(matches("class Y { void x() { this->x(); } };", + memberExpression(isArrow()))); + EXPECT_TRUE(matches("class Y { void x() { x(); } };", + memberExpression(isArrow()))); + EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };", + memberExpression(isArrow()))); +} + +TEST(Callee, MatchesDeclarations) { + StatementMatcher CallMethodX = call(callee(method(hasName("x")))); + + EXPECT_TRUE(matches("class Y { void x() { x(); } };", CallMethodX)); + EXPECT_TRUE(notMatches("class Y { void x() {} };", CallMethodX)); +} + +TEST(Callee, MatchesMemberExpressions) { + EXPECT_TRUE(matches("class Y { void x() { this->x(); } };", + call(callee(memberExpression())))); + EXPECT_TRUE( + notMatches("class Y { void x() { this->x(); } };", call(callee(call())))); +} + +TEST(Function, MatchesFunctionDeclarations) { + StatementMatcher CallFunctionF = call(callee(function(hasName("f")))); + + EXPECT_TRUE(matches("void f() { f(); }", CallFunctionF)); + EXPECT_TRUE(notMatches("void f() { }", CallFunctionF)); + + // Dependent contexts, but a non-dependent call. + EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }", + CallFunctionF)); + EXPECT_TRUE( + matches("void f(); template <int N> struct S { void g() { f(); } };", + CallFunctionF)); + + // Depedent calls don't match. + EXPECT_TRUE( + notMatches("void f(int); template <typename T> void g(T t) { f(t); }", + CallFunctionF)); + EXPECT_TRUE( + notMatches("void f(int);" + "template <typename T> struct S { void g(T t) { f(t); } };", + CallFunctionF)); +} + +TEST(Matcher, Argument) { + StatementMatcher CallArgumentY = expression(call( + hasArgument(0, declarationReference(to(variable(hasName("y"))))))); + + EXPECT_TRUE(matches("void x(int) { int y; x(y); }", CallArgumentY)); + EXPECT_TRUE( + matches("class X { void x(int) { int y; x(y); } };", CallArgumentY)); + EXPECT_TRUE(notMatches("void x(int) { int z; x(z); }", CallArgumentY)); + + StatementMatcher WrongIndex = expression(call( + hasArgument(42, declarationReference(to(variable(hasName("y"))))))); + EXPECT_TRUE(notMatches("void x(int) { int y; x(y); }", WrongIndex)); +} + +TEST(Matcher, AnyArgument) { + StatementMatcher CallArgumentY = expression(call( + hasAnyArgument(declarationReference(to(variable(hasName("y"))))))); + EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY)); + EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY)); + EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY)); +} + +TEST(Matcher, ArgumentCount) { + StatementMatcher Call1Arg = expression(call(argumentCountIs(1))); + + EXPECT_TRUE(matches("void x(int) { x(0); }", Call1Arg)); + EXPECT_TRUE(matches("class X { void x(int) { x(0); } };", Call1Arg)); + EXPECT_TRUE(notMatches("void x(int, int) { x(0, 0); }", Call1Arg)); +} + +TEST(Matcher, References) { + DeclarationMatcher ReferenceClassX = variable( + hasType(references(record(hasName("X"))))); + EXPECT_TRUE(matches("class X {}; void y(X y) { X &x = y; }", + ReferenceClassX)); + EXPECT_TRUE( + matches("class X {}; void y(X y) { const X &x = y; }", ReferenceClassX)); + EXPECT_TRUE( + notMatches("class X {}; void y(X y) { X x = y; }", ReferenceClassX)); + EXPECT_TRUE( + notMatches("class X {}; void y(X *y) { X *&x = y; }", ReferenceClassX)); +} + +TEST(HasParameter, CallsInnerMatcher) { + EXPECT_TRUE(matches("class X { void x(int) {} };", + method(hasParameter(0, variable())))); + EXPECT_TRUE(notMatches("class X { void x(int) {} };", + method(hasParameter(0, hasName("x"))))); +} + +TEST(HasParameter, DoesNotMatchIfIndexOutOfBounds) { + EXPECT_TRUE(notMatches("class X { void x(int) {} };", + method(hasParameter(42, variable())))); +} + +TEST(HasType, MatchesParameterVariableTypesStrictly) { + EXPECT_TRUE(matches("class X { void x(X x) {} };", + method(hasParameter(0, hasType(record(hasName("X"))))))); + EXPECT_TRUE(notMatches("class X { void x(const X &x) {} };", + method(hasParameter(0, hasType(record(hasName("X"))))))); + EXPECT_TRUE(matches("class X { void x(const X *x) {} };", + method(hasParameter(0, hasType(pointsTo(record(hasName("X")))))))); + EXPECT_TRUE(matches("class X { void x(const X &x) {} };", + method(hasParameter(0, hasType(references(record(hasName("X")))))))); +} + +TEST(HasAnyParameter, MatchesIndependentlyOfPosition) { + EXPECT_TRUE(matches("class Y {}; class X { void x(X x, Y y) {} };", + method(hasAnyParameter(hasType(record(hasName("X"))))))); + EXPECT_TRUE(matches("class Y {}; class X { void x(Y y, X x) {} };", + method(hasAnyParameter(hasType(record(hasName("X"))))))); +} + +TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) { + EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };", + method(hasAnyParameter(hasType(record(hasName("X"))))))); +} + +TEST(HasAnyParameter, DoesNotMatchThisPointer) { + EXPECT_TRUE(notMatches("class Y {}; class X { void x() {} };", + method(hasAnyParameter(hasType(pointsTo(record(hasName("X")))))))); +} + +TEST(HasName, MatchesParameterVariableDeclartions) { + EXPECT_TRUE(matches("class Y {}; class X { void x(int x) {} };", + method(hasAnyParameter(hasName("x"))))); + EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };", + method(hasAnyParameter(hasName("x"))))); +} + +TEST(Matcher, ConstructorCall) { + StatementMatcher Constructor = expression(constructorCall()); + + EXPECT_TRUE( + matches("class X { public: X(); }; void x() { X x; }", Constructor)); + EXPECT_TRUE( + matches("class X { public: X(); }; void x() { X x = X(); }", + Constructor)); + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { X x = 0; }", + Constructor)); + EXPECT_TRUE(matches("class X {}; void x(int) { X x; }", Constructor)); +} + +TEST(Matcher, ConstructorArgument) { + StatementMatcher Constructor = expression(constructorCall( + hasArgument(0, declarationReference(to(variable(hasName("y"))))))); + + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { int y; X x(y); }", + Constructor)); + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { int y; X x = X(y); }", + Constructor)); + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { int y; X x = y; }", + Constructor)); + EXPECT_TRUE( + notMatches("class X { public: X(int); }; void x() { int z; X x(z); }", + Constructor)); + + StatementMatcher WrongIndex = expression(constructorCall( + hasArgument(42, declarationReference(to(variable(hasName("y"))))))); + EXPECT_TRUE( + notMatches("class X { public: X(int); }; void x() { int y; X x(y); }", + WrongIndex)); +} + +TEST(Matcher, ConstructorArgumentCount) { + StatementMatcher Constructor1Arg = + expression(constructorCall(argumentCountIs(1))); + + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { X x(0); }", + Constructor1Arg)); + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { X x = X(0); }", + Constructor1Arg)); + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { X x = 0; }", + Constructor1Arg)); + EXPECT_TRUE( + notMatches("class X { public: X(int, int); }; void x() { X x(0, 0); }", + Constructor1Arg)); +} + +TEST(Matcher, BindTemporaryExpression) { + StatementMatcher TempExpression = expression(bindTemporaryExpression()); + + std::string ClassString = "class string { public: string(); ~string(); }; "; + + EXPECT_TRUE( + matches(ClassString + + "string GetStringByValue();" + "void FunctionTakesString(string s);" + "void run() { FunctionTakesString(GetStringByValue()); }", + TempExpression)); + + EXPECT_TRUE( + notMatches(ClassString + + "string* GetStringPointer(); " + "void FunctionTakesStringPtr(string* s);" + "void run() {" + " string* s = GetStringPointer();" + " FunctionTakesStringPtr(GetStringPointer());" + " FunctionTakesStringPtr(s);" + "}", + TempExpression)); + + EXPECT_TRUE( + notMatches("class no_dtor {};" + "no_dtor GetObjByValue();" + "void ConsumeObj(no_dtor param);" + "void run() { ConsumeObj(GetObjByValue()); }", + TempExpression)); +} + +TEST(ConstructorDeclaration, SimpleCase) { + EXPECT_TRUE(matches("class Foo { Foo(int i); };", + constructor(ofClass(hasName("Foo"))))); + EXPECT_TRUE(notMatches("class Foo { Foo(int i); };", + constructor(ofClass(hasName("Bar"))))); +} + +TEST(ConstructorDeclaration, IsImplicit) { + // This one doesn't match because the constructor is not added by the + // compiler (it is not needed). + EXPECT_TRUE(notMatches("class Foo { };", + constructor(isImplicit()))); + // The compiler added the implicit default constructor. + EXPECT_TRUE(matches("class Foo { }; Foo* f = new Foo();", + constructor(isImplicit()))); + EXPECT_TRUE(matches("class Foo { Foo(){} };", + constructor(unless(isImplicit())))); +} + +TEST(HasAnyConstructorInitializer, SimpleCase) { + EXPECT_TRUE(notMatches( + "class Foo { Foo() { } };", + constructor(hasAnyConstructorInitializer(anything())))); + EXPECT_TRUE(matches( + "class Foo {" + " Foo() : foo_() { }" + " int foo_;" + "};", + constructor(hasAnyConstructorInitializer(anything())))); +} + +TEST(HasAnyConstructorInitializer, ForField) { + static const char Code[] = + "class Baz { };" + "class Foo {" + " Foo() : foo_() { }" + " Baz foo_;" + " Baz bar_;" + "};"; + EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + forField(hasType(record(hasName("Baz")))))))); + EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + forField(hasName("foo_")))))); + EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer( + forField(hasType(record(hasName("Bar")))))))); +} + +TEST(HasAnyConstructorInitializer, WithInitializer) { + static const char Code[] = + "class Foo {" + " Foo() : foo_(0) { }" + " int foo_;" + "};"; + EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + withInitializer(integerLiteral(equals(0))))))); + EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer( + withInitializer(integerLiteral(equals(1))))))); +} + +TEST(HasAnyConstructorInitializer, IsWritten) { + static const char Code[] = + "struct Bar { Bar(){} };" + "class Foo {" + " Foo() : foo_() { }" + " Bar foo_;" + " Bar bar_;" + "};"; + EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + allOf(forField(hasName("foo_")), isWritten()))))); + EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer( + allOf(forField(hasName("bar_")), isWritten()))))); + EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + allOf(forField(hasName("bar_")), unless(isWritten())))))); +} + +TEST(Matcher, NewExpression) { + StatementMatcher New = expression(newExpression()); + + EXPECT_TRUE(matches("class X { public: X(); }; void x() { new X; }", New)); + EXPECT_TRUE( + matches("class X { public: X(); }; void x() { new X(); }", New)); + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { new X(0); }", New)); + EXPECT_TRUE(matches("class X {}; void x(int) { new X; }", New)); +} + +TEST(Matcher, NewExpressionArgument) { + StatementMatcher New = expression(constructorCall( + hasArgument( + 0, declarationReference(to(variable(hasName("y"))))))); + + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { int y; new X(y); }", + New)); + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { int y; new X(y); }", + New)); + EXPECT_TRUE( + notMatches("class X { public: X(int); }; void x() { int z; new X(z); }", + New)); + + StatementMatcher WrongIndex = expression(constructorCall( + hasArgument( + 42, declarationReference(to(variable(hasName("y"))))))); + EXPECT_TRUE( + notMatches("class X { public: X(int); }; void x() { int y; new X(y); }", + WrongIndex)); +} + +TEST(Matcher, NewExpressionArgumentCount) { + StatementMatcher New = constructorCall(argumentCountIs(1)); + + EXPECT_TRUE( + matches("class X { public: X(int); }; void x() { new X(0); }", New)); + EXPECT_TRUE( + notMatches("class X { public: X(int, int); }; void x() { new X(0, 0); }", + New)); +} + +TEST(Matcher, DefaultArgument) { + StatementMatcher Arg = defaultArgument(); + + EXPECT_TRUE(matches("void x(int, int = 0) { int y; x(y); }", Arg)); + EXPECT_TRUE( + matches("class X { void x(int, int = 0) { int y; x(y); } };", Arg)); + EXPECT_TRUE(notMatches("void x(int, int = 0) { int y; x(y, 0); }", Arg)); +} + +TEST(Matcher, StringLiterals) { + StatementMatcher Literal = expression(stringLiteral()); + EXPECT_TRUE(matches("const char *s = \"string\";", Literal)); + // wide string + EXPECT_TRUE(matches("const wchar_t *s = L\"string\";", Literal)); + // with escaped characters + EXPECT_TRUE(matches("const char *s = \"\x05five\";", Literal)); + // no matching -- though the data type is the same, there is no string literal + EXPECT_TRUE(notMatches("const char s[1] = {'a'};", Literal)); +} + +TEST(Matcher, CharacterLiterals) { + StatementMatcher CharLiteral = expression(characterLiteral()); + EXPECT_TRUE(matches("const char c = 'c';", CharLiteral)); + // wide character + EXPECT_TRUE(matches("const char c = L'c';", CharLiteral)); + // wide character, Hex encoded, NOT MATCHED! + EXPECT_TRUE(notMatches("const wchar_t c = 0x2126;", CharLiteral)); + EXPECT_TRUE(notMatches("const char c = 0x1;", CharLiteral)); +} + +TEST(Matcher, IntegerLiterals) { + StatementMatcher HasIntLiteral = expression(integerLiteral()); + EXPECT_TRUE(matches("int i = 10;", HasIntLiteral)); + EXPECT_TRUE(matches("int i = 0x1AB;", HasIntLiteral)); + EXPECT_TRUE(matches("int i = 10L;", HasIntLiteral)); + EXPECT_TRUE(matches("int i = 10U;", HasIntLiteral)); + + // Non-matching cases (character literals, float and double) + EXPECT_TRUE(notMatches("int i = L'a';", + HasIntLiteral)); // this is actually a character + // literal cast to int + EXPECT_TRUE(notMatches("int i = 'a';", HasIntLiteral)); + EXPECT_TRUE(notMatches("int i = 1e10;", HasIntLiteral)); + EXPECT_TRUE(notMatches("int i = 10.0;", HasIntLiteral)); +} + +TEST(Matcher, Conditions) { + StatementMatcher Condition = ifStmt(hasCondition(boolLiteral(equals(true)))); + + EXPECT_TRUE(matches("void x() { if (true) {} }", Condition)); + EXPECT_TRUE(notMatches("void x() { if (false) {} }", Condition)); + EXPECT_TRUE(notMatches("void x() { bool a = true; if (a) {} }", Condition)); + EXPECT_TRUE(notMatches("void x() { if (true || false) {} }", Condition)); + EXPECT_TRUE(notMatches("void x() { if (1) {} }", Condition)); +} + +TEST(MatchBinaryOperator, HasOperatorName) { + StatementMatcher OperatorOr = binaryOperator(hasOperatorName("||")); + + EXPECT_TRUE(matches("void x() { true || false; }", OperatorOr)); + EXPECT_TRUE(notMatches("void x() { true && false; }", OperatorOr)); +} + +TEST(MatchBinaryOperator, HasLHSAndHasRHS) { + StatementMatcher OperatorTrueFalse = + binaryOperator(hasLHS(boolLiteral(equals(true))), + hasRHS(boolLiteral(equals(false)))); + + EXPECT_TRUE(matches("void x() { true || false; }", OperatorTrueFalse)); + EXPECT_TRUE(matches("void x() { true && false; }", OperatorTrueFalse)); + EXPECT_TRUE(notMatches("void x() { false || true; }", OperatorTrueFalse)); +} + +TEST(MatchBinaryOperator, HasEitherOperand) { + StatementMatcher HasOperand = + binaryOperator(hasEitherOperand(boolLiteral(equals(false)))); + + EXPECT_TRUE(matches("void x() { true || false; }", HasOperand)); + EXPECT_TRUE(matches("void x() { false && true; }", HasOperand)); + EXPECT_TRUE(notMatches("void x() { true || true; }", HasOperand)); +} + +TEST(Matcher, BinaryOperatorTypes) { + // Integration test that verifies the AST provides all binary operators in + // a way we expect. + // FIXME: Operator ',' + EXPECT_TRUE( + matches("void x() { 3, 4; }", binaryOperator(hasOperatorName(",")))); + EXPECT_TRUE( + matches("bool b; bool c = (b = true);", + binaryOperator(hasOperatorName("=")))); + EXPECT_TRUE( + matches("bool b = 1 != 2;", binaryOperator(hasOperatorName("!=")))); + EXPECT_TRUE( + matches("bool b = 1 == 2;", binaryOperator(hasOperatorName("==")))); + EXPECT_TRUE(matches("bool b = 1 < 2;", binaryOperator(hasOperatorName("<")))); + EXPECT_TRUE( + matches("bool b = 1 <= 2;", binaryOperator(hasOperatorName("<=")))); + EXPECT_TRUE( + matches("int i = 1 << 2;", binaryOperator(hasOperatorName("<<")))); + EXPECT_TRUE( + matches("int i = 1; int j = (i <<= 2);", + binaryOperator(hasOperatorName("<<=")))); + EXPECT_TRUE(matches("bool b = 1 > 2;", binaryOperator(hasOperatorName(">")))); + EXPECT_TRUE( + matches("bool b = 1 >= 2;", binaryOperator(hasOperatorName(">=")))); + EXPECT_TRUE( + matches("int i = 1 >> 2;", binaryOperator(hasOperatorName(">>")))); + EXPECT_TRUE( + matches("int i = 1; int j = (i >>= 2);", + binaryOperator(hasOperatorName(">>=")))); + EXPECT_TRUE( + matches("int i = 42 ^ 23;", binaryOperator(hasOperatorName("^")))); + EXPECT_TRUE( + matches("int i = 42; int j = (i ^= 42);", + binaryOperator(hasOperatorName("^=")))); + EXPECT_TRUE( + matches("int i = 42 % 23;", binaryOperator(hasOperatorName("%")))); + EXPECT_TRUE( + matches("int i = 42; int j = (i %= 42);", + binaryOperator(hasOperatorName("%=")))); + EXPECT_TRUE( + matches("bool b = 42 &23;", binaryOperator(hasOperatorName("&")))); + EXPECT_TRUE( + matches("bool b = true && false;", + binaryOperator(hasOperatorName("&&")))); + EXPECT_TRUE( + matches("bool b = true; bool c = (b &= false);", + binaryOperator(hasOperatorName("&=")))); + EXPECT_TRUE( + matches("bool b = 42 | 23;", binaryOperator(hasOperatorName("|")))); + EXPECT_TRUE( + matches("bool b = true || false;", + binaryOperator(hasOperatorName("||")))); + EXPECT_TRUE( + matches("bool b = true; bool c = (b |= false);", + binaryOperator(hasOperatorName("|=")))); + EXPECT_TRUE( + matches("int i = 42 *23;", binaryOperator(hasOperatorName("*")))); + EXPECT_TRUE( + matches("int i = 42; int j = (i *= 23);", + binaryOperator(hasOperatorName("*=")))); + EXPECT_TRUE( + matches("int i = 42 / 23;", binaryOperator(hasOperatorName("/")))); + EXPECT_TRUE( + matches("int i = 42; int j = (i /= 23);", + binaryOperator(hasOperatorName("/=")))); + EXPECT_TRUE( + matches("int i = 42 + 23;", binaryOperator(hasOperatorName("+")))); + EXPECT_TRUE( + matches("int i = 42; int j = (i += 23);", + binaryOperator(hasOperatorName("+=")))); + EXPECT_TRUE( + matches("int i = 42 - 23;", binaryOperator(hasOperatorName("-")))); + EXPECT_TRUE( + matches("int i = 42; int j = (i -= 23);", + binaryOperator(hasOperatorName("-=")))); + EXPECT_TRUE( + matches("struct A { void x() { void (A::*a)(); (this->*a)(); } };", + binaryOperator(hasOperatorName("->*")))); + EXPECT_TRUE( + matches("struct A { void x() { void (A::*a)(); ((*this).*a)(); } };", + binaryOperator(hasOperatorName(".*")))); + + // Member expressions as operators are not supported in matches. + EXPECT_TRUE( + notMatches("struct A { void x(A *a) { a->x(this); } };", + binaryOperator(hasOperatorName("->")))); + + // Initializer assignments are not represented as operator equals. + EXPECT_TRUE( + notMatches("bool b = true;", binaryOperator(hasOperatorName("=")))); + + // Array indexing is not represented as operator. + EXPECT_TRUE(notMatches("int a[42]; void x() { a[23]; }", unaryOperator())); + + // Overloaded operators do not match at all. + EXPECT_TRUE(notMatches( + "struct A { bool operator&&(const A &a) const { return false; } };" + "void x() { A a, b; a && b; }", + binaryOperator())); +} + +TEST(MatchUnaryOperator, HasOperatorName) { + StatementMatcher OperatorNot = unaryOperator(hasOperatorName("!")); + + EXPECT_TRUE(matches("void x() { !true; } ", OperatorNot)); + EXPECT_TRUE(notMatches("void x() { true; } ", OperatorNot)); +} + +TEST(MatchUnaryOperator, HasUnaryOperand) { + StatementMatcher OperatorOnFalse = + unaryOperator(hasUnaryOperand(boolLiteral(equals(false)))); + + EXPECT_TRUE(matches("void x() { !false; }", OperatorOnFalse)); + EXPECT_TRUE(notMatches("void x() { !true; }", OperatorOnFalse)); +} + +TEST(Matcher, UnaryOperatorTypes) { + // Integration test that verifies the AST provides all unary operators in + // a way we expect. + EXPECT_TRUE(matches("bool b = !true;", unaryOperator(hasOperatorName("!")))); + EXPECT_TRUE( + matches("bool b; bool *p = &b;", unaryOperator(hasOperatorName("&")))); + EXPECT_TRUE(matches("int i = ~ 1;", unaryOperator(hasOperatorName("~")))); + EXPECT_TRUE( + matches("bool *p; bool b = *p;", unaryOperator(hasOperatorName("*")))); + EXPECT_TRUE( + matches("int i; int j = +i;", unaryOperator(hasOperatorName("+")))); + EXPECT_TRUE( + matches("int i; int j = -i;", unaryOperator(hasOperatorName("-")))); + EXPECT_TRUE( + matches("int i; int j = ++i;", unaryOperator(hasOperatorName("++")))); + EXPECT_TRUE( + matches("int i; int j = i++;", unaryOperator(hasOperatorName("++")))); + EXPECT_TRUE( + matches("int i; int j = --i;", unaryOperator(hasOperatorName("--")))); + EXPECT_TRUE( + matches("int i; int j = i--;", unaryOperator(hasOperatorName("--")))); + + // We don't match conversion operators. + EXPECT_TRUE(notMatches("int i; double d = (double)i;", unaryOperator())); + + // Function calls are not represented as operator. + EXPECT_TRUE(notMatches("void f(); void x() { f(); }", unaryOperator())); + + // Overloaded operators do not match at all. + // FIXME: We probably want to add that. + EXPECT_TRUE(notMatches( + "struct A { bool operator!() const { return false; } };" + "void x() { A a; !a; }", unaryOperator(hasOperatorName("!")))); +} + +TEST(Matcher, ConditionalOperator) { + StatementMatcher Conditional = conditionalOperator( + hasCondition(boolLiteral(equals(true))), + hasTrueExpression(boolLiteral(equals(false)))); + + EXPECT_TRUE(matches("void x() { true ? false : true; }", Conditional)); + EXPECT_TRUE(notMatches("void x() { false ? false : true; }", Conditional)); + EXPECT_TRUE(notMatches("void x() { true ? true : false; }", Conditional)); + + StatementMatcher ConditionalFalse = conditionalOperator( + hasFalseExpression(boolLiteral(equals(false)))); + + EXPECT_TRUE(matches("void x() { true ? true : false; }", ConditionalFalse)); + EXPECT_TRUE( + notMatches("void x() { true ? false : true; }", ConditionalFalse)); +} + +TEST(Matcher, HasNameSupportsNamespaces) { + EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", + record(hasName("a::b::C")))); + EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", + record(hasName("::a::b::C")))); + EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", + record(hasName("b::C")))); + EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", + record(hasName("C")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", + record(hasName("c::b::C")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", + record(hasName("a::c::C")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", + record(hasName("a::b::A")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", + record(hasName("::C")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", + record(hasName("::b::C")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", + record(hasName("z::a::b::C")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", + record(hasName("a+b::C")))); + EXPECT_TRUE(notMatches("namespace a { namespace b { class AC; } }", + record(hasName("C")))); +} + +TEST(Matcher, HasNameSupportsOuterClasses) { + EXPECT_TRUE( + matches("class A { class B { class C; }; };", record(hasName("A::B::C")))); + EXPECT_TRUE( + matches("class A { class B { class C; }; };", + record(hasName("::A::B::C")))); + EXPECT_TRUE( + matches("class A { class B { class C; }; };", record(hasName("B::C")))); + EXPECT_TRUE( + matches("class A { class B { class C; }; };", record(hasName("C")))); + EXPECT_TRUE( + notMatches("class A { class B { class C; }; };", + record(hasName("c::B::C")))); + EXPECT_TRUE( + notMatches("class A { class B { class C; }; };", + record(hasName("A::c::C")))); + EXPECT_TRUE( + notMatches("class A { class B { class C; }; };", + record(hasName("A::B::A")))); + EXPECT_TRUE( + notMatches("class A { class B { class C; }; };", record(hasName("::C")))); + EXPECT_TRUE( + notMatches("class A { class B { class C; }; };", + record(hasName("::B::C")))); + EXPECT_TRUE(notMatches("class A { class B { class C; }; };", + record(hasName("z::A::B::C")))); + EXPECT_TRUE( + notMatches("class A { class B { class C; }; };", + record(hasName("A+B::C")))); +} + +TEST(Matcher, IsDefinition) { + DeclarationMatcher DefinitionOfClassA = + record(hasName("A"), isDefinition()); + EXPECT_TRUE(matches("class A {};", DefinitionOfClassA)); + EXPECT_TRUE(notMatches("class A;", DefinitionOfClassA)); + + DeclarationMatcher DefinitionOfVariableA = + variable(hasName("a"), isDefinition()); + EXPECT_TRUE(matches("int a;", DefinitionOfVariableA)); + EXPECT_TRUE(notMatches("extern int a;", DefinitionOfVariableA)); + + DeclarationMatcher DefinitionOfMethodA = + method(hasName("a"), isDefinition()); + EXPECT_TRUE(matches("class A { void a() {} };", DefinitionOfMethodA)); + EXPECT_TRUE(notMatches("class A { void a(); };", DefinitionOfMethodA)); +} + +TEST(Matcher, OfClass) { + StatementMatcher Constructor = constructorCall(hasDeclaration(method( + ofClass(hasName("X"))))); + + EXPECT_TRUE( + matches("class X { public: X(); }; void x(int) { X x; }", Constructor)); + EXPECT_TRUE( + matches("class X { public: X(); }; void x(int) { X x = X(); }", + Constructor)); + EXPECT_TRUE( + notMatches("class Y { public: Y(); }; void x(int) { Y y; }", + Constructor)); +} + +TEST(Matcher, VisitsTemplateInstantiations) { + EXPECT_TRUE(matches( + "class A { public: void x(); };" + "template <typename T> class B { public: void y() { T t; t.x(); } };" + "void f() { B<A> b; b.y(); }", call(callee(method(hasName("x")))))); + + EXPECT_TRUE(matches( + "class A { public: void x(); };" + "class C {" + " public:" + " template <typename T> class B { public: void y() { T t; t.x(); } };" + "};" + "void f() {" + " C::B<A> b; b.y();" + "}", record(hasName("C"), + hasDescendant(call(callee(method(hasName("x")))))))); +} + +// For testing AST_MATCHER_P(). +AST_MATCHER_P(clang::Decl, just, internal::Matcher<clang::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(id("b", record(hasName("B"))))); + + EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", + HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("b"))); + + EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", + HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("a"))); + + EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", + HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("b"))); +} + +AST_POLYMORPHIC_MATCHER_P( + polymorphicHas, internal::Matcher<clang::Decl>, AMatcher) { + TOOLING_COMPILE_ASSERT((llvm::is_same<NodeType, clang::Decl>::value) || + (llvm::is_same<NodeType, clang::Stmt>::value), + assert_node_type_is_accessible); + internal::TypedBaseMatcher<clang::Decl> ChildMatcher(AMatcher); + return Finder->matchesChildOf( + Node, ChildMatcher, Builder, + ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses, + ASTMatchFinder::BK_First); +} + +TEST(AstPolymorphicMatcherPMacro, Works) { + DeclarationMatcher HasClassB = polymorphicHas(id("b", record(hasName("B")))); + + EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", + HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("b"))); + + EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", + HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("a"))); + + EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", + HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("b"))); + + StatementMatcher StatementHasClassB = + polymorphicHas(record(hasName("B"))); + + EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB)); +} + +TEST(For, FindsForLoops) { + EXPECT_TRUE(matches("void f() { for(;;); }", forStmt())); + EXPECT_TRUE(matches("void f() { if(true) for(;;); }", forStmt())); +} + +TEST(For, ReportsNoFalsePositives) { + EXPECT_TRUE(notMatches("void f() { ; }", forStmt())); + EXPECT_TRUE(notMatches("void f() { if(true); }", forStmt())); +} + +TEST(CompoundStatement, HandlesSimpleCases) { + EXPECT_TRUE(notMatches("void f();", compoundStatement())); + EXPECT_TRUE(matches("void f() {}", compoundStatement())); + EXPECT_TRUE(matches("void f() {{}}", compoundStatement())); +} + +TEST(CompoundStatement, DoesNotMatchEmptyStruct) { + // It's not a compound statement just because there's "{}" in the source + // text. This is an AST search, not grep. + EXPECT_TRUE(notMatches("namespace n { struct S {}; }", + compoundStatement())); + EXPECT_TRUE(matches("namespace n { struct S { void f() {{}} }; }", + compoundStatement())); +} + +TEST(HasBody, FindsBodyOfForLoop) { + StatementMatcher HasCompoundStatementBody = + forStmt(hasBody(compoundStatement())); + EXPECT_TRUE(matches("void f() { for(;;) {} }", + HasCompoundStatementBody)); + EXPECT_TRUE(notMatches("void f() { for(;;); }", + HasCompoundStatementBody)); +} + +TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) { + // The simplest case: every compound statement is in a function + // definition, and the function body itself must be a compound + // statement. + EXPECT_TRUE(matches("void f() { for (;;); }", + compoundStatement(hasAnySubstatement(forStmt())))); +} + +TEST(HasAnySubstatement, IsNotRecursive) { + // It's really "has any immediate substatement". + EXPECT_TRUE(notMatches("void f() { if (true) for (;;); }", + compoundStatement(hasAnySubstatement(forStmt())))); +} + +TEST(HasAnySubstatement, MatchesInNestedCompoundStatements) { + EXPECT_TRUE(matches("void f() { if (true) { for (;;); } }", + compoundStatement(hasAnySubstatement(forStmt())))); +} + +TEST(HasAnySubstatement, FindsSubstatementBetweenOthers) { + EXPECT_TRUE(matches("void f() { 1; 2; 3; for (;;); 4; 5; 6; }", + compoundStatement(hasAnySubstatement(forStmt())))); +} + +TEST(StatementCountIs, FindsNoStatementsInAnEmptyCompoundStatement) { + EXPECT_TRUE(matches("void f() { }", + compoundStatement(statementCountIs(0)))); + EXPECT_TRUE(notMatches("void f() {}", + compoundStatement(statementCountIs(1)))); +} + +TEST(StatementCountIs, AppearsToMatchOnlyOneCount) { + EXPECT_TRUE(matches("void f() { 1; }", + compoundStatement(statementCountIs(1)))); + EXPECT_TRUE(notMatches("void f() { 1; }", + compoundStatement(statementCountIs(0)))); + EXPECT_TRUE(notMatches("void f() { 1; }", + compoundStatement(statementCountIs(2)))); +} + +TEST(StatementCountIs, WorksWithMultipleStatements) { + EXPECT_TRUE(matches("void f() { 1; 2; 3; }", + compoundStatement(statementCountIs(3)))); +} + +TEST(StatementCountIs, WorksWithNestedCompoundStatements) { + EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", + compoundStatement(statementCountIs(1)))); + EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", + compoundStatement(statementCountIs(2)))); + EXPECT_TRUE(notMatches("void f() { { 1; } { 1; 2; 3; 4; } }", + compoundStatement(statementCountIs(3)))); + EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", + compoundStatement(statementCountIs(4)))); +} + +TEST(Member, WorksInSimplestCase) { + EXPECT_TRUE(matches("struct { int first; } s; int i(s.first);", + memberExpression(member(hasName("first"))))); +} + +TEST(Member, DoesNotMatchTheBaseExpression) { + // Don't pick out the wrong part of the member expression, this should + // be checking the member (name) only. + EXPECT_TRUE(notMatches("struct { int i; } first; int i(first.i);", + memberExpression(member(hasName("first"))))); +} + +TEST(Member, MatchesInMemberFunctionCall) { + EXPECT_TRUE(matches("void f() {" + " struct { void first() {}; } s;" + " s.first();" + "};", + memberExpression(member(hasName("first"))))); +} + +TEST(HasObjectExpression, DoesNotMatchMember) { + EXPECT_TRUE(notMatches( + "class X {}; struct Z { X m; }; void f(Z z) { z.m; }", + memberExpression(hasObjectExpression(hasType(record(hasName("X"))))))); +} + +TEST(HasObjectExpression, MatchesBaseOfVariable) { + EXPECT_TRUE(matches( + "struct X { int m; }; void f(X x) { x.m; }", + memberExpression(hasObjectExpression(hasType(record(hasName("X"))))))); + EXPECT_TRUE(matches( + "struct X { int m; }; void f(X* x) { x->m; }", + memberExpression(hasObjectExpression( + hasType(pointsTo(record(hasName("X")))))))); +} + +TEST(HasObjectExpression, + MatchesObjectExpressionOfImplicitlyFormedMemberExpression) { + EXPECT_TRUE(matches( + "class X {}; struct S { X m; void f() { this->m; } };", + memberExpression(hasObjectExpression( + hasType(pointsTo(record(hasName("S")))))))); + EXPECT_TRUE(matches( + "class X {}; struct S { X m; void f() { m; } };", + memberExpression(hasObjectExpression( + hasType(pointsTo(record(hasName("S")))))))); +} + +TEST(Field, DoesNotMatchNonFieldMembers) { + EXPECT_TRUE(notMatches("class X { void m(); };", field(hasName("m")))); + EXPECT_TRUE(notMatches("class X { class m {}; };", field(hasName("m")))); + EXPECT_TRUE(notMatches("class X { enum { m }; };", field(hasName("m")))); + EXPECT_TRUE(notMatches("class X { enum m {}; };", field(hasName("m")))); +} + +TEST(Field, MatchesField) { + EXPECT_TRUE(matches("class X { int m; };", field(hasName("m")))); +} + +TEST(IsConstQualified, MatchesConstInt) { + EXPECT_TRUE(matches("const int i = 42;", + variable(hasType(isConstQualified())))); +} + +TEST(IsConstQualified, MatchesConstPointer) { + EXPECT_TRUE(matches("int i = 42; int* const p(&i);", + variable(hasType(isConstQualified())))); +} + +TEST(IsConstQualified, MatchesThroughTypedef) { + EXPECT_TRUE(matches("typedef const int const_int; const_int i = 42;", + variable(hasType(isConstQualified())))); + EXPECT_TRUE(matches("typedef int* int_ptr; const int_ptr p(0);", + variable(hasType(isConstQualified())))); +} + +TEST(IsConstQualified, DoesNotMatchInappropriately) { + EXPECT_TRUE(notMatches("typedef int nonconst_int; nonconst_int i = 42;", + variable(hasType(isConstQualified())))); + EXPECT_TRUE(notMatches("int const* p;", + variable(hasType(isConstQualified())))); +} + +TEST(ReinterpretCast, MatchesSimpleCase) { + EXPECT_TRUE(matches("char* p = reinterpret_cast<char*>(&p);", + expression(reinterpretCast()))); +} + +TEST(ReinterpretCast, DoesNotMatchOtherCasts) { + EXPECT_TRUE(notMatches("char* p = (char*)(&p);", + expression(reinterpretCast()))); + EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);", + expression(reinterpretCast()))); + EXPECT_TRUE(notMatches("void* p = static_cast<void*>(&p);", + expression(reinterpretCast()))); + EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};" + "B b;" + "D* p = dynamic_cast<D*>(&b);", + expression(reinterpretCast()))); +} + +TEST(FunctionalCast, MatchesSimpleCase) { + std::string foo_class = "class Foo { public: Foo(char*); };"; + EXPECT_TRUE(matches(foo_class + "void r() { Foo f = Foo(\"hello world\"); }", + expression(functionalCast()))); +} + +TEST(FunctionalCast, DoesNotMatchOtherCasts) { + std::string FooClass = "class Foo { public: Foo(char*); };"; + EXPECT_TRUE( + notMatches(FooClass + "void r() { Foo f = (Foo) \"hello world\"; }", + expression(functionalCast()))); + EXPECT_TRUE( + notMatches(FooClass + "void r() { Foo f = \"hello world\"; }", + expression(functionalCast()))); +} + +TEST(DynamicCast, MatchesSimpleCase) { + EXPECT_TRUE(matches("struct B { virtual ~B() {} }; struct D : B {};" + "B b;" + "D* p = dynamic_cast<D*>(&b);", + expression(dynamicCast()))); +} + +TEST(StaticCast, MatchesSimpleCase) { + EXPECT_TRUE(matches("void* p(static_cast<void*>(&p));", + expression(staticCast()))); +} + +TEST(StaticCast, DoesNotMatchOtherCasts) { + EXPECT_TRUE(notMatches("char* p = (char*)(&p);", + expression(staticCast()))); + EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);", + expression(staticCast()))); + EXPECT_TRUE(notMatches("void* p = reinterpret_cast<char*>(&p);", + expression(staticCast()))); + EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};" + "B b;" + "D* p = dynamic_cast<D*>(&b);", + expression(staticCast()))); +} + +TEST(HasDestinationType, MatchesSimpleCase) { + EXPECT_TRUE(matches("char* p = static_cast<char*>(0);", + expression( + staticCast(hasDestinationType( + pointsTo(TypeMatcher(anything()))))))); +} + +TEST(HasSourceExpression, MatchesSimpleCase) { + EXPECT_TRUE(matches("class string {}; class URL { public: URL(string s); };" + "void r() {string a_string; URL url = a_string; }", + expression(implicitCast( + hasSourceExpression(constructorCall()))))); +} + +TEST(Statement, DoesNotMatchDeclarations) { + EXPECT_TRUE(notMatches("class X {};", statement())); +} + +TEST(Statement, MatchesCompoundStatments) { + EXPECT_TRUE(matches("void x() {}", statement())); +} + +TEST(DeclarationStatement, DoesNotMatchCompoundStatements) { + EXPECT_TRUE(notMatches("void x() {}", declarationStatement())); +} + +TEST(DeclarationStatement, MatchesVariableDeclarationStatements) { + EXPECT_TRUE(matches("void x() { int a; }", declarationStatement())); +} + +TEST(While, MatchesWhileLoops) { + EXPECT_TRUE(notMatches("void x() {}", whileStmt())); + EXPECT_TRUE(matches("void x() { while(true); }", whileStmt())); + EXPECT_TRUE(notMatches("void x() { do {} while(true); }", whileStmt())); +} + +TEST(Do, MatchesDoLoops) { + EXPECT_TRUE(matches("void x() { do {} while(true); }", doStmt())); + EXPECT_TRUE(matches("void x() { do ; while(false); }", doStmt())); +} + +TEST(Do, DoesNotMatchWhileLoops) { + EXPECT_TRUE(notMatches("void x() { while(true) {} }", doStmt())); +} + +TEST(SwitchCase, MatchesCase) { + EXPECT_TRUE(matches("void x() { switch(42) { case 42:; } }", switchCase())); + EXPECT_TRUE(matches("void x() { switch(42) { default:; } }", switchCase())); + EXPECT_TRUE(matches("void x() { switch(42) default:; }", switchCase())); + EXPECT_TRUE(notMatches("void x() { switch(42) {} }", switchCase())); +} + +TEST(HasConditionVariableStatement, DoesNotMatchCondition) { + EXPECT_TRUE(notMatches( + "void x() { if(true) {} }", + ifStmt(hasConditionVariableStatement(declarationStatement())))); + EXPECT_TRUE(notMatches( + "void x() { int x; if((x = 42)) {} }", + ifStmt(hasConditionVariableStatement(declarationStatement())))); +} + +TEST(HasConditionVariableStatement, MatchesConditionVariables) { + EXPECT_TRUE(matches( + "void x() { if(int* a = 0) {} }", + ifStmt(hasConditionVariableStatement(declarationStatement())))); +} + +TEST(ForEach, BindsOneNode) { + EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; };", + record(hasName("C"), forEach(id("x", field(hasName("x"))))), + new VerifyIdIsBoundToDecl<clang::FieldDecl>("x", 1))); +} + +TEST(ForEach, BindsMultipleNodes) { + EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; int y; int z; };", + record(hasName("C"), forEach(id("f", field()))), + new VerifyIdIsBoundToDecl<clang::FieldDecl>("f", 3))); +} + +TEST(ForEach, BindsRecursiveCombinations) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { class D { int x; int y; }; class E { int y; int z; }; };", + record(hasName("C"), forEach(record(forEach(id("f", field()))))), + new VerifyIdIsBoundToDecl<clang::FieldDecl>("f", 4))); +} + +TEST(ForEachDescendant, BindsOneNode) { + EXPECT_TRUE(matchAndVerifyResultTrue("class C { class D { int x; }; };", + record(hasName("C"), forEachDescendant(id("x", field(hasName("x"))))), + new VerifyIdIsBoundToDecl<clang::FieldDecl>("x", 1))); +} + +TEST(ForEachDescendant, BindsMultipleNodes) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { class D { int x; int y; }; " + " class E { class F { int y; int z; }; }; };", + record(hasName("C"), forEachDescendant(id("f", field()))), + new VerifyIdIsBoundToDecl<clang::FieldDecl>("f", 4))); +} + +TEST(ForEachDescendant, BindsRecursiveCombinations) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { class D { " + " class E { class F { class G { int y; int z; }; }; }; }; };", + record(hasName("C"), forEachDescendant(record( + forEachDescendant(id("f", field()))))), + new VerifyIdIsBoundToDecl<clang::FieldDecl>("f", 8))); +} + + +TEST(IsTemplateInstantiation, MatchesImplicitClassTemplateInstantiation) { + // Make sure that we can both match the class by name (::X) and by the type + // the template was instantiated with (via a field). + + EXPECT_TRUE(matches( + "template <typename T> class X {}; class A {}; X<A> x;", + record(hasName("::X"), isTemplateInstantiation()))); + + EXPECT_TRUE(matches( + "template <typename T> class X { T t; }; class A {}; X<A> x;", + record(isTemplateInstantiation(), hasDescendant( + field(hasType(record(hasName("A")))))))); +} + +TEST(IsTemplateInstantiation, MatchesImplicitFunctionTemplateInstantiation) { + EXPECT_TRUE(matches( + "template <typename T> void f(T t) {} class A {}; void g() { f(A()); }", + function(hasParameter(0, hasType(record(hasName("A")))), + isTemplateInstantiation()))); +} + +TEST(IsTemplateInstantiation, MatchesExplicitClassTemplateInstantiation) { + EXPECT_TRUE(matches( + "template <typename T> class X { T t; }; class A {};" + "template class X<A>;", + record(isTemplateInstantiation(), hasDescendant( + field(hasType(record(hasName("A")))))))); +} + +TEST(IsTemplateInstantiation, + MatchesInstantiationOfPartiallySpecializedClassTemplate) { + EXPECT_TRUE(matches( + "template <typename T> class X {};" + "template <typename T> class X<T*> {}; class A {}; X<A*> x;", + record(hasName("::X"), isTemplateInstantiation()))); +} + +TEST(IsTemplateInstantiation, + MatchesInstantiationOfClassTemplateNestedInNonTemplate) { + EXPECT_TRUE(matches( + "class A {};" + "class X {" + " template <typename U> class Y { U u; };" + " Y<A> y;" + "};", + record(hasName("::X::Y"), isTemplateInstantiation()))); +} + +TEST(IsTemplateInstantiation, DoesNotMatchInstantiationsInsideOfInstantiation) { + // FIXME: Figure out whether this makes sense. It doesn't affect the + // normal use case as long as the uppermost instantiation always is marked + // as template instantiation, but it might be confusing as a predicate. + EXPECT_TRUE(matches( + "class A {};" + "template <typename T> class X {" + " template <typename U> class Y { U u; };" + " Y<T> y;" + "}; X<A> x;", + record(hasName("::X<A>::Y"), unless(isTemplateInstantiation())))); +} + +TEST(IsTemplateInstantiation, DoesNotMatchExplicitClassTemplateSpecialization) { + EXPECT_TRUE(notMatches( + "template <typename T> class X {}; class A {};" + "template <> class X<A> {}; X<A> x;", + record(hasName("::X"), isTemplateInstantiation()))); +} + +TEST(IsTemplateInstantiation, DoesNotMatchNonTemplate) { + EXPECT_TRUE(notMatches( + "class A {}; class Y { A a; };", + record(isTemplateInstantiation()))); +} + +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h new file mode 100644 index 0000000000..64816f5d60 --- /dev/null +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -0,0 +1,128 @@ +//===- 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_AST_MATCHERS_AST_MATCHERS_TEST_H +#define LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { + +using clang::tooling::newFrontendActionFactory; +using clang::tooling::runToolOnCode; +using clang::tooling::FrontendActionFactory; + +class BoundNodesCallback { +public: + virtual ~BoundNodesCallback() {} + virtual bool run(const BoundNodes *BoundNodes) = 0; +}; + +// 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(BoundNodesCallback *FindResultVerifier, bool *Verified) + : Verified(Verified), FindResultReviewer(FindResultVerifier) {} + + virtual void run(const MatchFinder::MatchResult &Result) { + if (FindResultReviewer != NULL) { + *Verified = FindResultReviewer->run(&Result.Nodes); + } else { + *Verified = true; + } + } + +private: + bool *const Verified; + BoundNodesCallback *const FindResultReviewer; +}; + +template <typename T> +testing::AssertionResult matchesConditionally(const std::string &Code, + const T &AMatcher, + bool ExpectMatch) { + bool Found = false; + MatchFinder Finder; + Finder.addMatcher(AMatcher, new VerifyMatch(0, &Found)); + OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + if (!runToolOnCode(Factory->create(), Code)) { + return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; + } + 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 matches(const std::string &Code, const T &AMatcher) { + return matchesConditionally(Code, AMatcher, true); +} + +template <typename T> +testing::AssertionResult notMatches(const std::string &Code, + const T &AMatcher) { + return matchesConditionally(Code, AMatcher, false); +} + +template <typename T> +testing::AssertionResult +matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, + BoundNodesCallback *FindResultVerifier, + bool ExpectResult) { + llvm::OwningPtr<BoundNodesCallback> ScopedVerifier(FindResultVerifier); + bool VerifiedResult = false; + MatchFinder Finder; + Finder.addMatcher( + AMatcher, new VerifyMatch(FindResultVerifier, &VerifiedResult)); + OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + if (!runToolOnCode(Factory->create(), Code)) { + 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 << "\""; + } + 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, + BoundNodesCallback *FindResultVerifier) { + return matchAndVerifyResultConditionally( + Code, AMatcher, FindResultVerifier, true); +} + +template <typename T> +testing::AssertionResult +matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher, + BoundNodesCallback *FindResultVerifier) { + return matchAndVerifyResultConditionally( + Code, AMatcher, FindResultVerifier, false); +} + +} // end namespace ast_matchers +} // end namespace clang + +#endif // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H diff --git a/unittests/ASTMatchers/CMakeLists.txt b/unittests/ASTMatchers/CMakeLists.txt new file mode 100644 index 0000000000..8e61732141 --- /dev/null +++ b/unittests/ASTMatchers/CMakeLists.txt @@ -0,0 +1,5 @@ +add_clang_unittest(ASTMatchersTests + ASTMatchersTest.cpp) + +target_link_libraries(ASTMatchersTests + gtest gtest_main clangASTMatchers clangTooling) diff --git a/unittests/ASTMatchers/Makefile b/unittests/ASTMatchers/Makefile new file mode 100644 index 0000000000..2448a056da --- /dev/null +++ b/unittests/ASTMatchers/Makefile @@ -0,0 +1,19 @@ +##===- unittests/ASTMatchers/Makefile ----------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../.. +PARALLEL_DIRS = Dynamic + +TESTNAME = ASTMatchers +LINK_COMPONENTS := support mc +USEDLIBS = clangEdit.a clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \ + clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 3dd927d9b9..aed97c3627 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -9,6 +9,7 @@ function(add_clang_unittest test_dirname) add_unittest(ClangUnitTests ${test_dirname} ${ARGN}) endfunction() +add_subdirectory(ASTMatchers) add_subdirectory(Basic) add_subdirectory(Lex) add_subdirectory(Frontend) diff --git a/unittests/Makefile b/unittests/Makefile index b9f3c3f63d..f74820b649 100644 --- a/unittests/Makefile +++ b/unittests/Makefile @@ -14,7 +14,7 @@ ifndef CLANG_LEVEL IS_UNITTEST_LEVEL := 1 CLANG_LEVEL := .. -PARALLEL_DIRS = Basic AST Frontend Lex Tooling +PARALLEL_DIRS = ASTMatchers Basic AST Frontend Lex Tooling endif # CLANG_LEVEL |