Add more matchers and do cleanups.

Reviewers: klimek

    Differential Revision: http://ec2-50-18-127-156.us-west-1.compute.amazonaws.com/D2

llvm-svn: 160013
This commit is contained in:
Daniel Jasper 2012-07-10 20:20:19 +00:00
parent 8b95df290e
commit 1dad183b38
5 changed files with 884 additions and 358 deletions

File diff suppressed because it is too large Load Diff

View File

@ -84,8 +84,8 @@ public:
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,
BoundNodesTree(const std::map<std::string, const Decl*>& DeclBindings,
const std::map<std::string, const Stmt*>& StmtBindings,
const std::vector<BoundNodesTree> RecursiveBindings);
/// \brief Adds all bound nodes to bound_nodes_builder.
@ -99,8 +99,8 @@ public:
private:
void visitMatchesRecursively(
Visitor* ResultVistior,
std::map<std::string, const clang::Decl*> DeclBindings,
std::map<std::string, const clang::Stmt*> StmtBindings);
std::map<std::string, const Decl*> DeclBindings,
std::map<std::string, const Stmt*> StmtBindings);
template <typename T>
void copyBindingsTo(const T& bindings, BoundNodesTreeBuilder* Builder) const;
@ -108,8 +108,8 @@ private:
// 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::map<std::string, const Decl*> DeclBindings;
std::map<std::string, const Stmt*> StmtBindings;
std::vector<BoundNodesTree> RecursiveBindings;
};
@ -126,10 +126,8 @@ public:
///
/// 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);
void setBinding(const std::string &Id, const Decl *Node);
void setBinding(const std::string &Id, const Stmt *Node);
/// @}
/// \brief Adds a branch in the tree.
@ -142,8 +140,8 @@ 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::map<std::string, const Decl*> DeclBindings;
std::map<std::string, const Stmt*> StmtBindings;
std::vector<BoundNodesTree> RecursiveBindings;
};
@ -262,10 +260,10 @@ inline Matcher<T> makeMatcher(MatcherInterface<T> *Implementation) {
template <typename T, typename DeclMatcherT>
class HasDeclarationMatcher : public MatcherInterface<T> {
TOOLING_COMPILE_ASSERT((llvm::is_same< DeclMatcherT,
Matcher<clang::Decl> >::value),
Matcher<Decl> >::value),
instantiated_with_wrong_types);
public:
explicit HasDeclarationMatcher(const Matcher<clang::Decl> &InnerMatcher)
explicit HasDeclarationMatcher(const Matcher<Decl> &InnerMatcher)
: InnerMatcher(InnerMatcher) {}
virtual bool matches(const T &Node,
@ -277,34 +275,36 @@ public:
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,
bool matchesSpecialized(const QualType &Node, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
/// FIXME: Add other ways to convert...
clang::CXXRecordDecl *NodeAsRecordDecl = Node->getAsCXXRecordDecl();
if (Node.isNull())
return false;
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,
bool matchesSpecialized(const CallExpr &Node, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
const clang::Decl *NodeAsDecl = Node.getCalleeDecl();
const 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,
bool matchesSpecialized(const CXXConstructExpr &Node,
ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
const clang::Decl *NodeAsDecl = Node.getConstructor();
const Decl *NodeAsDecl = Node.getConstructor();
return NodeAsDecl != NULL &&
InnerMatcher.matches(*NodeAsDecl, Finder, Builder);
}
const Matcher<clang::Decl> InnerMatcher;
const Matcher<Decl> InnerMatcher;
};
/// \brief IsBaseType<T>::value is true if T is a "base" type in the AST
@ -312,10 +312,10 @@ private:
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);
(llvm::is_same<T, Decl>::value ||
llvm::is_same<T, Stmt>::value ||
llvm::is_same<T, QualType>::value ||
llvm::is_same<T, CXXCtorInitializer>::value);
};
template <typename T>
const bool IsBaseType<T>::value;
@ -326,19 +326,19 @@ class UntypedBaseMatcher : public llvm::RefCountedBaseVPTR {
public:
virtual ~UntypedBaseMatcher() {}
virtual bool matches(const clang::Decl &DeclNode, ASTMatchFinder *Finder,
virtual bool matches(const Decl &DeclNode, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
return false;
}
virtual bool matches(const clang::QualType &TypeNode, ASTMatchFinder *Finder,
virtual bool matches(const QualType &TypeNode, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
return false;
}
virtual bool matches(const clang::Stmt &StmtNode, ASTMatchFinder *Finder,
virtual bool matches(const Stmt &StmtNode, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
return false;
}
virtual bool matches(const clang::CXXCtorInitializer &CtorInitNode,
virtual bool matches(const CXXCtorInitializer &CtorInitNode,
ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
return false;
@ -414,26 +414,26 @@ public:
/// 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,
virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration,
StringRef BaseName) const = 0;
// FIXME: Implement for other base nodes.
virtual bool matchesChildOf(const clang::Decl &DeclNode,
virtual bool matchesChildOf(const Decl &DeclNode,
const UntypedBaseMatcher &BaseMatcher,
BoundNodesTreeBuilder *Builder,
TraversalKind Traverse,
BindKind Bind) = 0;
virtual bool matchesChildOf(const clang::Stmt &StmtNode,
virtual bool matchesChildOf(const Stmt &StmtNode,
const UntypedBaseMatcher &BaseMatcher,
BoundNodesTreeBuilder *Builder,
TraversalKind Traverse,
BindKind Bind) = 0;
virtual bool matchesDescendantOf(const clang::Decl &DeclNode,
virtual bool matchesDescendantOf(const Decl &DeclNode,
const UntypedBaseMatcher &BaseMatcher,
BoundNodesTreeBuilder *Builder,
BindKind Bind) = 0;
virtual bool matchesDescendantOf(const clang::Stmt &StmtNode,
virtual bool matchesDescendantOf(const Stmt &StmtNode,
const UntypedBaseMatcher &BaseMatcher,
BoundNodesTreeBuilder *Builder,
BindKind Bind) = 0;
@ -566,17 +566,17 @@ private:
const Matcher<To> InnerMatcher;
};
/// \brief Enables the user to pass a Matcher<clang::CXXMemberCallExpr> to
/// \brief Enables the user to pass a Matcher<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 {
inline Matcher<CXXMemberCallExpr>::
operator Matcher<CallExpr>() const {
return makeMatcher(
new DynCastMatcher<clang::CallExpr, clang::CXXMemberCallExpr>(*this));
new DynCastMatcher<CallExpr, CXXMemberCallExpr>(*this));
}
/// \brief Matcher<T> that wraps an inner Matcher<T> and binds the matched node
@ -594,7 +594,7 @@ public:
BoundNodesTreeBuilder *Builder) const {
bool Result = InnerMatcher.matches(Node, Finder, Builder);
if (Result) {
Builder->setBinding(std::pair<const std::string, const T*>(ID, &Node));
Builder->setBinding(ID, &Node);
}
return Result;
}
@ -795,11 +795,11 @@ private:
/// 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,
TOOLING_COMPILE_ASSERT((llvm::is_base_of<CharacterLiteral, T>::value ||
llvm::is_base_of<CXXBoolLiteralExpr,
T>::value ||
llvm::is_base_of<clang::FloatingLiteral, T>::value ||
llvm::is_base_of<clang::IntegerLiteral, T>::value),
llvm::is_base_of<FloatingLiteral, T>::value ||
llvm::is_base_of<IntegerLiteral, T>::value),
the_node_must_have_a_getValue_method);
public:
explicit ValueEqualsMatcher(const ValueT &ExpectedValue)
@ -816,9 +816,9 @@ private:
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),
(llvm::is_base_of<TagDecl, T>::value) ||
(llvm::is_base_of<VarDecl, T>::value) ||
(llvm::is_base_of<FunctionDecl, T>::value),
is_definition_requires_isThisDeclarationADefinition_method);
public:
virtual bool matchesNode(const T &Node) const {
@ -830,32 +830,32 @@ public:
/// 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),
TOOLING_COMPILE_ASSERT((llvm::is_base_of<FunctionDecl, T>::value) ||
(llvm::is_base_of<VarDecl, T>::value) ||
(llvm::is_base_of<CXXRecordDecl, T>::value),
requires_getTemplateSpecializationKind_method);
public:
virtual bool matches(const T& Node,
ASTMatchFinder* Finder,
BoundNodesTreeBuilder* Builder) const {
return (Node.getTemplateSpecializationKind() ==
clang::TSK_ImplicitInstantiation ||
TSK_ImplicitInstantiation ||
Node.getTemplateSpecializationKind() ==
clang::TSK_ExplicitInstantiationDefinition);
TSK_ExplicitInstantiationDefinition);
}
};
class IsArrowMatcher : public SingleNodeMatcherInterface<clang::MemberExpr> {
class IsArrowMatcher : public SingleNodeMatcherInterface<MemberExpr> {
public:
virtual bool matchesNode(const clang::MemberExpr &Node) const {
virtual bool matchesNode(const MemberExpr &Node) const {
return Node.isArrow();
}
};
class IsConstQualifiedMatcher
: public SingleNodeMatcherInterface<clang::QualType> {
: public SingleNodeMatcherInterface<QualType> {
public:
virtual bool matchesNode(const clang::QualType& Node) const {
virtual bool matchesNode(const QualType& Node) const {
return Node.isConstQualified();
}
};
@ -867,11 +867,11 @@ class IsConstQualifiedMatcher
///
/// 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.
/// Decl, CXXRecordDecl> record;
/// Creates a functor record(...) that creates a Matcher<Decl> given
/// a variable number of arguments of type Matcher<CXXRecordDecl>.
/// The returned matcher matches if the given Decl can by dynamically
/// casted to CXXRecordDecl and all given matchers match.
template <typename SourceT, typename TargetT>
class VariadicDynCastAllOfMatcher
: public llvm::VariadicFunction<

View File

@ -51,9 +51,9 @@ struct MemoizedMatchResult {
// A RecursiveASTVisitor that traverses all children or all descendants of
// a node.
class MatchChildASTVisitor
: public clang::RecursiveASTVisitor<MatchChildASTVisitor> {
: public RecursiveASTVisitor<MatchChildASTVisitor> {
public:
typedef clang::RecursiveASTVisitor<MatchChildASTVisitor> VisitorBase;
typedef 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
@ -95,21 +95,21 @@ public:
// 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) {
bool TraverseDecl(Decl *DeclNode) {
return (DeclNode == NULL) || traverse(*DeclNode);
}
bool TraverseStmt(clang::Stmt *StmtNode) {
const clang::Stmt *StmtToTraverse = StmtNode;
bool TraverseStmt(Stmt *StmtNode) {
const Stmt *StmtToTraverse = StmtNode;
if (Traversal ==
ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses) {
const clang::Expr *ExprNode = dyn_cast_or_null<clang::Expr>(StmtNode);
const Expr *ExprNode = dyn_cast_or_null<Expr>(StmtNode);
if (ExprNode != NULL) {
StmtToTraverse = ExprNode->IgnoreParenImpCasts();
}
}
return (StmtToTraverse == NULL) || traverse(*StmtToTraverse);
}
bool TraverseType(clang::QualType TypeNode) {
bool TraverseType(QualType TypeNode) {
return traverse(TypeNode);
}
@ -134,13 +134,13 @@ private:
// 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 Decl &DeclNode) {
return VisitorBase::TraverseDecl(const_cast<Decl*>(&DeclNode));
}
bool baseTraverse(const clang::Stmt &StmtNode) {
return VisitorBase::TraverseStmt(const_cast<clang::Stmt*>(&StmtNode));
bool baseTraverse(const Stmt &StmtNode) {
return VisitorBase::TraverseStmt(const_cast<Stmt*>(&StmtNode));
}
bool baseTraverse(clang::QualType TypeNode) {
bool baseTraverse(QualType TypeNode) {
return VisitorBase::TraverseType(TypeNode);
}
@ -197,7 +197,7 @@ private:
// Controls the outermost traversal of the AST and allows to match multiple
// matchers.
class MatchASTVisitor : public clang::RecursiveASTVisitor<MatchASTVisitor>,
class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
public ASTMatchFinder {
public:
MatchASTVisitor(std::vector< std::pair<const UntypedBaseMatcher*,
@ -206,14 +206,14 @@ public:
ActiveASTContext(NULL) {
}
void set_active_ast_context(clang::ASTContext *NewActiveASTContext) {
void set_active_ast_context(ASTContext *NewActiveASTContext) {
ActiveASTContext = NewActiveASTContext;
}
// The following Visit*() and Traverse*() functions "override"
// methods in RecursiveASTVisitor.
bool VisitTypedefDecl(clang::TypedefDecl *DeclNode) {
bool VisitTypedefDecl(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
@ -241,18 +241,18 @@ public:
// 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
const Type *TypeNode = DeclNode->getUnderlyingType().getTypePtr();
const 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);
bool TraverseDecl(Decl *DeclNode);
bool TraverseStmt(Stmt *StmtNode);
bool TraverseType(QualType TypeNode);
bool TraverseTypeLoc(TypeLoc TypeNode);
// Matches children or descendants of 'Node' with 'BaseMatcher'.
template <typename T>
@ -260,8 +260,8 @@ public:
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),
TOOLING_COMPILE_ASSERT((llvm::is_same<T, Decl>::value) ||
(llvm::is_same<T, Stmt>::value),
type_does_not_support_memoization);
const UntypedMatchInput input(BaseMatcher.getID(), &Node);
std::pair<MemoizationMap::iterator, bool> InsertResult
@ -288,11 +288,11 @@ public:
return Visitor.findMatch(Node);
}
virtual bool classIsDerivedFrom(const clang::CXXRecordDecl *Declaration,
virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration,
StringRef BaseName) const;
// Implements ASTMatchFinder::MatchesChildOf.
virtual bool matchesChildOf(const clang::Decl &DeclNode,
virtual bool matchesChildOf(const Decl &DeclNode,
const UntypedBaseMatcher &BaseMatcher,
BoundNodesTreeBuilder *Builder,
TraversalKind Traversal,
@ -300,7 +300,7 @@ public:
return matchesRecursively(DeclNode, BaseMatcher, Builder, 1, Traversal,
Bind);
}
virtual bool matchesChildOf(const clang::Stmt &StmtNode,
virtual bool matchesChildOf(const Stmt &StmtNode,
const UntypedBaseMatcher &BaseMatcher,
BoundNodesTreeBuilder *Builder,
TraversalKind Traversal,
@ -310,14 +310,14 @@ public:
}
// Implements ASTMatchFinder::MatchesDescendantOf.
virtual bool matchesDescendantOf(const clang::Decl &DeclNode,
virtual bool matchesDescendantOf(const 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,
virtual bool matchesDescendantOf(const Stmt &StmtNode,
const UntypedBaseMatcher &BaseMatcher,
BoundNodesTreeBuilder *Builder,
BindKind Bind) {
@ -333,7 +333,7 @@ private:
// the aggregated bound nodes for each match.
class MatchVisitor : public BoundNodesTree::Visitor {
public:
MatchVisitor(clang::ASTContext* Context,
MatchVisitor(ASTContext* Context,
MatchFinder::MatchCallback* Callback)
: Context(Context),
Callback(Callback) {}
@ -343,16 +343,16 @@ private:
}
private:
clang::ASTContext* Context;
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,
bool typeHasAlias(const Type *TypeNode,
StringRef Name) const {
const clang::Type *const CanonicalType =
const Type *const CanonicalType =
ActiveASTContext->getCanonicalType(TypeNode);
const std::set<std::string> *UnqualifiedAlias =
find(TypeToUnqualifiedAliases, CanonicalType);
@ -378,10 +378,10 @@ private:
std::vector< std::pair<const UntypedBaseMatcher*,
MatchFinder::MatchCallback*> > *const Triggers;
clang::ASTContext *ActiveASTContext;
ASTContext *ActiveASTContext;
// Maps a canonical type to the names of its typedefs.
llvm::DenseMap<const clang::Type*, std::set<std::string> >
llvm::DenseMap<const Type*, std::set<std::string> >
TypeToUnqualifiedAliases;
// Maps (matcher, node) -> the match result for memoization.
@ -393,7 +393,7 @@ private:
// 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,
MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration,
StringRef BaseName) const {
if (Declaration->getName() == BaseName) {
return true;
@ -401,24 +401,24 @@ MatchASTVisitor::classIsDerivedFrom(const clang::CXXRecordDecl *Declaration,
if (!Declaration->hasDefinition()) {
return false;
}
typedef clang::CXXRecordDecl::base_class_const_iterator BaseIterator;
typedef 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();
const 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) {
// Type::getAs<...>() drills through typedefs.
if (TypeNode->getAs<DependentNameType>() != NULL ||
TypeNode->getAs<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>();
CXXRecordDecl *ClassDecl = NULL;
TemplateSpecializationType const *TemplateType =
TypeNode->getAs<TemplateSpecializationType>();
if (TemplateType != NULL) {
if (TemplateType->getTemplateName().isDependent()) {
// Dependent template specializations will be matched when the
@ -434,12 +434,12 @@ MatchASTVisitor::classIsDerivedFrom(const clang::CXXRecordDecl *Declaration,
// 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 =
CXXRecordDecl *SpecializationDecl =
TemplateType->getAsCXXRecordDecl();
if (SpecializationDecl != NULL) {
ClassDecl = SpecializationDecl;
} else {
ClassDecl = llvm::dyn_cast<clang::CXXRecordDecl>(
ClassDecl = llvm::dyn_cast<CXXRecordDecl>(
TemplateType->getTemplateName()
.getAsTemplateDecl()->getTemplatedDecl());
}
@ -455,33 +455,33 @@ MatchASTVisitor::classIsDerivedFrom(const clang::CXXRecordDecl *Declaration,
return false;
}
bool MatchASTVisitor::TraverseDecl(clang::Decl *DeclNode) {
bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
if (DeclNode == NULL) {
return true;
}
match(*DeclNode);
return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode);
}
bool MatchASTVisitor::TraverseStmt(clang::Stmt *StmtNode) {
bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode) {
if (StmtNode == NULL) {
return true;
}
match(*StmtNode);
return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode);
}
bool MatchASTVisitor::TraverseType(clang::QualType TypeNode) {
bool MatchASTVisitor::TraverseType(QualType TypeNode) {
match(TypeNode);
return clang::RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseType(TypeNode);
}
bool MatchASTVisitor::TraverseTypeLoc(clang::TypeLoc TypeLoc) {
return clang::RecursiveASTVisitor<MatchASTVisitor>::
bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLoc) {
return RecursiveASTVisitor<MatchASTVisitor>::
TraverseType(TypeLoc.getType());
}
class MatchASTConsumer : public clang::ASTConsumer {
class MatchASTConsumer : public ASTConsumer {
public:
MatchASTConsumer(std::vector< std::pair<const UntypedBaseMatcher*,
MatchFinder::MatchCallback*> > *Triggers,
@ -490,7 +490,7 @@ public:
ParsingDone(ParsingDone) {}
private:
virtual void HandleTranslationUnit(clang::ASTContext &Context) {
virtual void HandleTranslationUnit(ASTContext &Context) {
if (ParsingDone != NULL) {
ParsingDone->run();
}
@ -507,7 +507,7 @@ private:
} // end namespace internal
MatchFinder::MatchResult::MatchResult(const BoundNodes &Nodes,
clang::ASTContext *Context)
ASTContext *Context)
: Nodes(Nodes), Context(Context),
SourceManager(&Context->getSourceManager()) {}
@ -528,22 +528,22 @@ MatchFinder::~MatchFinder() {
void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch,
MatchCallback *Action) {
Triggers.push_back(std::make_pair(
new internal::TypedBaseMatcher<clang::Decl>(NodeMatch), Action));
new internal::TypedBaseMatcher<Decl>(NodeMatch), Action));
}
void MatchFinder::addMatcher(const TypeMatcher &NodeMatch,
MatchCallback *Action) {
Triggers.push_back(std::make_pair(
new internal::TypedBaseMatcher<clang::QualType>(NodeMatch), Action));
new internal::TypedBaseMatcher<QualType>(NodeMatch), Action));
}
void MatchFinder::addMatcher(const StatementMatcher &NodeMatch,
MatchCallback *Action) {
Triggers.push_back(std::make_pair(
new internal::TypedBaseMatcher<clang::Stmt>(NodeMatch), Action));
new internal::TypedBaseMatcher<Stmt>(NodeMatch), Action));
}
clang::ASTConsumer *MatchFinder::newASTConsumer() {
ASTConsumer *MatchFinder::newASTConsumer() {
return new internal::MatchASTConsumer(&Triggers, ParsingDone);
}

View File

@ -21,8 +21,8 @@ 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::map<std::string, const Decl*>& DeclBindings,
const std::map<std::string, const Stmt*>& StmtBindings,
const std::vector<BoundNodesTree> RecursiveBindings)
: DeclBindings(DeclBindings), StmtBindings(StmtBindings),
RecursiveBindings(RecursiveBindings) {}
@ -44,22 +44,22 @@ void BoundNodesTree::copyBindingsTo(
for (typename T::const_iterator I = Bindings.begin(),
E = Bindings.end();
I != E; ++I) {
Builder->setBinding(*I);
Builder->setBinding(I->first, I->second);
}
}
void BoundNodesTree::visitMatches(Visitor* ResultVisitor) {
std::map<std::string, const clang::Decl*> AggregatedDeclBindings;
std::map<std::string, const clang::Stmt*> AggregatedStmtBindings;
std::map<std::string, const Decl*> AggregatedDeclBindings;
std::map<std::string, const Stmt*> AggregatedStmtBindings;
visitMatchesRecursively(ResultVisitor, AggregatedDeclBindings,
AggregatedStmtBindings);
}
void BoundNodesTree::
visitMatchesRecursively(Visitor* ResultVisitor,
std::map<std::string, const clang::Decl*>
std::map<std::string, const Decl*>
AggregatedDeclBindings,
std::map<std::string, const clang::Stmt*>
std::map<std::string, const Stmt*>
AggregatedStmtBindings) {
copy(DeclBindings.begin(), DeclBindings.end(),
inserter(AggregatedDeclBindings, AggregatedDeclBindings.begin()));
@ -79,14 +79,14 @@ visitMatchesRecursively(Visitor* ResultVisitor,
BoundNodesTreeBuilder::BoundNodesTreeBuilder() {}
void BoundNodesTreeBuilder::
setBinding(const std::pair<const std::string, const clang::Decl*>& Binding) {
DeclBindings.insert(Binding);
void BoundNodesTreeBuilder::setBinding(const std::string &Id,
const Decl *Node) {
DeclBindings[Id] = Node;
}
void BoundNodesTreeBuilder::
setBinding(const std::pair<const std::string, const clang::Stmt*>& Binding) {
StmtBindings.insert(Binding);
void BoundNodesTreeBuilder::setBinding(const std::string &Id,
const Stmt *Node) {
StmtBindings[Id] = Node;
}
void BoundNodesTreeBuilder::addMatch(const BoundNodesTree& Bindings) {

View File

@ -24,6 +24,13 @@ TEST(HasNameDeathTest, DiesOnEmptyName) {
}, "");
}
TEST(HasNameDeathTest, DiesOnEmptyPattern) {
ASSERT_DEBUG_DEATH({
DeclarationMatcher HasEmptyName = record(matchesName(""));
EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
}, "");
}
TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) {
ASSERT_DEBUG_DEATH({
DeclarationMatcher IsDerivedFromEmpty = record(isDerivedFrom(""));
@ -40,10 +47,34 @@ TEST(NameableDeclaration, MatchesVariousDecls) {
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(matches("enum X { A, B, C };", NamedX));
EXPECT_TRUE(notMatches("#define X 1", NamedX));
}
TEST(NameableDeclaration, REMatchesVariousDecls) {
DeclarationMatcher NamedX = nameableDeclaration(matchesName("::X"));
EXPECT_TRUE(matches("typedef int Xa;", NamedX));
EXPECT_TRUE(matches("int Xb;", NamedX));
EXPECT_TRUE(matches("class foo { virtual void Xc(); };", NamedX));
EXPECT_TRUE(matches("void foo() try { } catch(int Xdef) { }", NamedX));
EXPECT_TRUE(matches("void foo() { int Xgh; }", NamedX));
EXPECT_TRUE(matches("namespace Xij { }", NamedX));
EXPECT_TRUE(matches("enum X { A, B, C };", NamedX));
EXPECT_TRUE(notMatches("#define Xkl 1", NamedX));
DeclarationMatcher StartsWithNo = nameableDeclaration(matchesName("::no"));
EXPECT_TRUE(matches("int no_foo;", StartsWithNo));
EXPECT_TRUE(matches("class foo { virtual void nobody(); };", StartsWithNo));
DeclarationMatcher Abc = nameableDeclaration(matchesName("a.*b.*c"));
EXPECT_TRUE(matches("int abc;", Abc));
EXPECT_TRUE(matches("int aFOObBARc;", Abc));
EXPECT_TRUE(notMatches("int cab;", Abc));
EXPECT_TRUE(matches("int cabc;", Abc));
}
TEST(DeclarationMatcher, MatchClass) {
DeclarationMatcher ClassMatcher(record());
#if !defined(_MSC_VER)
@ -456,6 +487,21 @@ TEST(DeclarationMatcher, HasDescendant) {
"};", ZDescendantClassXDescendantClassY));
}
TEST(Enum, DoesNotMatchClasses) {
EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X"))));
}
TEST(Enum, MatchesEnums) {
EXPECT_TRUE(matches("enum X {};", enumDecl(hasName("X"))));
}
TEST(EnumConstant, Matches) {
DeclarationMatcher Matcher = enumConstant(hasName("A"));
EXPECT_TRUE(matches("enum X{ A };", Matcher));
EXPECT_TRUE(notMatches("enum X{ B };", Matcher));
EXPECT_TRUE(notMatches("enum X {};", Matcher));
}
TEST(StatementMatcher, Has) {
StatementMatcher HasVariableI =
expression(
@ -552,23 +598,40 @@ TEST(Matcher, BindMatchedNodes) {
DeclarationMatcher ClassX = has(id("x", record(hasName("X"))));
EXPECT_TRUE(matchAndVerifyResultTrue("class X {};",
ClassX, new VerifyIdIsBoundToDecl<clang::CXXRecordDecl>("x")));
ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("x")));
EXPECT_TRUE(matchAndVerifyResultFalse("class X {};",
ClassX, new VerifyIdIsBoundToDecl<clang::CXXRecordDecl>("other-id")));
ClassX, new VerifyIdIsBoundToDecl<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")));
new VerifyIdIsBoundToDecl<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")));
new VerifyIdIsBoundToStmt<CXXMemberCallExpr>("x")));
}
TEST(Matcher, BindTheSameNameInAlternatives) {
StatementMatcher matcher = anyOf(
binaryOperator(hasOperatorName("+"),
hasLHS(id("x", expression())),
hasRHS(integerLiteral(equals(0)))),
binaryOperator(hasOperatorName("+"),
hasLHS(integerLiteral(equals(0))),
hasRHS(id("x", expression()))));
EXPECT_TRUE(matchAndVerifyResultTrue(
// The first branch of the matcher binds x to 0 but then fails.
// The second branch binds x to f() and succeeds.
"int f() { return 0 + f(); }",
matcher,
new VerifyIdIsBoundToStmt<CallExpr>("x")));
}
TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) {
@ -613,7 +676,7 @@ TEST(HasType, TakesDeclMatcherAndMatchesValueDecl) {
TEST(Matcher, Call) {
// FIXME: Do we want to overload Call() to directly take
// Matcher<clang::Decl>, too?
// Matcher<Decl>, too?
StatementMatcher MethodX = call(hasDeclaration(method(hasName("x"))));
EXPECT_TRUE(matches("class Y { void x() { x(); } };", MethodX));
@ -657,6 +720,18 @@ TEST(Matcher, Call) {
MethodOnYPointer));
}
TEST(HasType, MatchesAsString) {
EXPECT_TRUE(
matches("class Y { public: void x(); }; void z() {Y* y; y->x(); }",
call(on(hasType(asString("class Y *"))))));
EXPECT_TRUE(matches("class X { void x(int x) {} };",
method(hasParameter(0, hasType(asString("int"))))));
EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };",
field(hasType(asString("ns::A")))));
EXPECT_TRUE(matches("namespace { struct A {}; } struct B { A a; };",
field(hasType(asString("struct <anonymous>::A")))));
}
TEST(Matcher, OverloadedOperatorCall) {
StatementMatcher OpCall = overloadedOperatorCall();
// Unary operator
@ -772,6 +847,30 @@ TEST(Matcher, CalledVariable) {
CallOnVariableY));
}
TEST(UnaryExprOrTypeTraitExpr, MatchesSizeOfAndAlignOf) {
EXPECT_TRUE(matches("void x() { int a = sizeof(a); }",
unaryExprOrTypeTraitExpr()));
EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }",
alignOfExpr(anything())));
// FIXME: Uncomment once alignof is enabled.
// EXPECT_TRUE(matches("void x() { int a = alignof(a); }",
// unaryExprOrTypeTraitExpr()));
// EXPECT_TRUE(notMatches("void x() { int a = alignof(a); }",
// sizeOfExpr()));
}
TEST(UnaryExpressionOrTypeTraitExpression, MatchesCorrectType) {
EXPECT_TRUE(matches("void x() { int a = sizeof(a); }", sizeOfExpr(
hasArgumentOfType(asString("int")))));
EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(
hasArgumentOfType(asString("float")))));
EXPECT_TRUE(matches(
"struct A {}; void x() { A a; int b = sizeof(a); }",
sizeOfExpr(hasArgumentOfType(hasDeclaration(record(hasName("A")))))));
EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(
hasArgumentOfType(hasDeclaration(record(hasName("string")))))));
}
TEST(MemberExpression, DoesNotMatchClasses) {
EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpression()));
}
@ -939,6 +1038,15 @@ TEST(HasAnyParameter, MatchesIndependentlyOfPosition) {
method(hasAnyParameter(hasType(record(hasName("X")))))));
}
TEST(Returns, MatchesReturnTypes) {
EXPECT_TRUE(matches("class Y { int f() { return 1; } };",
function(returns(asString("int")))));
EXPECT_TRUE(notMatches("class Y { int f() { return 1; } };",
function(returns(asString("float")))));
EXPECT_TRUE(matches("class Y { Y getMe() { return *this; } };",
function(returns(hasDeclaration(record(hasName("Y")))))));
}
TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) {
EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };",
method(hasAnyParameter(hasType(record(hasName("X")))))));
@ -1062,6 +1170,15 @@ TEST(ConstructorDeclaration, IsImplicit) {
constructor(unless(isImplicit()))));
}
TEST(DestructorDeclaration, MatchesVirtualDestructor) {
EXPECT_TRUE(matches("class Foo { virtual ~Foo(); };",
destructor(ofClass(hasName("Foo")))));
}
TEST(DestructorDeclaration, DoesNotMatchImplicitDestructor) {
EXPECT_TRUE(notMatches("class Foo {};", destructor(ofClass(hasName("Foo")))));
}
TEST(HasAnyConstructorInitializer, SimpleCase) {
EXPECT_TRUE(notMatches(
"class Foo { Foo() { } };",
@ -1162,6 +1279,11 @@ TEST(Matcher, NewExpressionArgumentCount) {
New));
}
TEST(Matcher, DeleteExpression) {
EXPECT_TRUE(matches("struct A {}; void f(A* a) { delete a; }",
deleteExpression()));
}
TEST(Matcher, DefaultArgument) {
StatementMatcher Arg = defaultArgument();
@ -1412,6 +1534,32 @@ TEST(Matcher, ConditionalOperator) {
notMatches("void x() { true ? false : true; }", ConditionalFalse));
}
TEST(ArraySubscriptMatchers, ArraySubscripts) {
EXPECT_TRUE(matches("int i[2]; void f() { i[1] = 1; }",
arraySubscriptExpr()));
EXPECT_TRUE(notMatches("int i; void f() { i = 1; }",
arraySubscriptExpr()));
}
TEST(ArraySubscriptMatchers, ArrayIndex) {
EXPECT_TRUE(matches(
"int i[2]; void f() { i[1] = 1; }",
arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
EXPECT_TRUE(matches(
"int i[2]; void f() { 1[i] = 1; }",
arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
EXPECT_TRUE(notMatches(
"int i[2]; void f() { i[1] = 1; }",
arraySubscriptExpr(hasIndex(integerLiteral(equals(0))))));
}
TEST(ArraySubscriptMatchers, MatchesArrayBase) {
EXPECT_TRUE(matches(
"int i[2]; void f() { i[1] = 2; }",
arraySubscriptExpr(hasBase(implicitCast(
hasSourceExpression(declarationReference()))))));
}
TEST(Matcher, HasNameSupportsNamespaces) {
EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
record(hasName("a::b::C"))));
@ -1519,8 +1667,33 @@ TEST(Matcher, VisitsTemplateInstantiations) {
hasDescendant(call(callee(method(hasName("x"))))))));
}
TEST(Matcher, HandlesNullQualTypes) {
// FIXME: Add a Type matcher so we can replace uses of this
// variable with Type(True())
const TypeMatcher AnyType = anything();
// We don't really care whether this matcher succeeds; we're testing that
// it completes without crashing.
EXPECT_TRUE(matches(
"struct A { };"
"template <typename T>"
"void f(T t) {"
" T local_t(t /* this becomes a null QualType in the AST */);"
"}"
"void g() {"
" f(0);"
"}",
expression(hasType(TypeMatcher(
anyOf(
TypeMatcher(hasDeclaration(anything())),
pointsTo(AnyType),
references(AnyType)
// Other QualType matchers should go here.
))))));
}
// For testing AST_MATCHER_P().
AST_MATCHER_P(clang::Decl, just, internal::Matcher<clang::Decl>, AMatcher) {
AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) {
// Make sure all special variables are used: node, match_finder,
// bound_nodes_builder, and the parameter named 'AMatcher'.
return AMatcher.matches(Node, Finder, Builder);
@ -1530,21 +1703,21 @@ 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")));
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("a")));
HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("b")));
HasClassB, new VerifyIdIsBoundToDecl<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),
polymorphicHas, internal::Matcher<Decl>, AMatcher) {
TOOLING_COMPILE_ASSERT((llvm::is_same<NodeType, Decl>::value) ||
(llvm::is_same<NodeType, Stmt>::value),
assert_node_type_is_accessible);
internal::TypedBaseMatcher<clang::Decl> ChildMatcher(AMatcher);
internal::TypedBaseMatcher<Decl> ChildMatcher(AMatcher);
return Finder->matchesChildOf(
Node, ChildMatcher, Builder,
ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses,
@ -1555,13 +1728,13 @@ TEST(AstPolymorphicMatcherPMacro, Works) {
DeclarationMatcher HasClassB = polymorphicHas(id("b", record(hasName("B"))));
EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("b")));
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("a")));
HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
HasClassB, new VerifyIdIsBoundToDecl<clang::Decl>("b")));
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
StatementMatcher StatementHasClassB =
polymorphicHas(record(hasName("B")));
@ -1831,6 +2004,41 @@ TEST(DeclarationStatement, MatchesVariableDeclarationStatements) {
EXPECT_TRUE(matches("void x() { int a; }", declarationStatement()));
}
TEST(InitListExpression, MatchesInitListExpression) {
EXPECT_TRUE(matches("int a[] = { 1, 2 };",
initListExpr(hasType(asString("int [2]")))));
EXPECT_TRUE(matches("struct B { int x, y; }; B b = { 5, 6 };",
initListExpr(hasType(record(hasName("B"))))));
}
TEST(UsingDeclaration, MatchesUsingDeclarations) {
EXPECT_TRUE(matches("namespace X { int x; } using X::x;",
usingDecl()));
}
TEST(UsingDeclaration, MatchesShadowUsingDelcarations) {
EXPECT_TRUE(matches("namespace f { int a; } using f::a;",
usingDecl(hasAnyUsingShadowDecl(hasName("a")))));
}
TEST(UsingDeclaration, MatchesSpecificTarget) {
EXPECT_TRUE(matches("namespace f { int a; void b(); } using f::b;",
usingDecl(hasAnyUsingShadowDecl(
hasTargetDecl(function())))));
EXPECT_TRUE(notMatches("namespace f { int a; void b(); } using f::a;",
usingDecl(hasAnyUsingShadowDecl(
hasTargetDecl(function())))));
}
TEST(UsingDeclaration, ThroughUsingDeclaration) {
EXPECT_TRUE(matches(
"namespace a { void f(); } using a::f; void g() { f(); }",
declarationReference(throughUsingDecl(anything()))));
EXPECT_TRUE(notMatches(
"namespace a { void f(); } using a::f; void g() { a::f(); }",
declarationReference(throughUsingDecl(anything()))));
}
TEST(While, MatchesWhileLoops) {
EXPECT_TRUE(notMatches("void x() {}", whileStmt()));
EXPECT_TRUE(matches("void x() { while(true); }", whileStmt()));
@ -1871,26 +2079,26 @@ TEST(HasConditionVariableStatement, MatchesConditionVariables) {
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)));
new VerifyIdIsBoundToDecl<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)));
new VerifyIdIsBoundToDecl<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)));
new VerifyIdIsBoundToDecl<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)));
new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
}
TEST(ForEachDescendant, BindsMultipleNodes) {
@ -1898,7 +2106,7 @@ TEST(ForEachDescendant, BindsMultipleNodes) {
"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)));
new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
}
TEST(ForEachDescendant, BindsRecursiveCombinations) {
@ -1907,7 +2115,7 @@ TEST(ForEachDescendant, BindsRecursiveCombinations) {
" 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)));
new VerifyIdIsBoundToDecl<FieldDecl>("f", 8)));
}