зеркало из https://github.com/microsoft/clang-1.git
Create initial support for matching and binding NestedNameSpecifier(Loc)s.
Review: http://llvm-reviews.chandlerc.com/D39 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163794 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
1824d54df8
Коммит
a756443319
|
@ -112,6 +112,10 @@ public:
|
|||
MatchCallback *Action);
|
||||
void addMatcher(const StatementMatcher &NodeMatch,
|
||||
MatchCallback *Action);
|
||||
void addMatcher(const NestedNameSpecifierMatcher &NodeMatch,
|
||||
MatchCallback *Action);
|
||||
void addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch,
|
||||
MatchCallback *Action);
|
||||
/// @}
|
||||
|
||||
/// \brief Creates a clang ASTConsumer that finds all matches.
|
||||
|
|
|
@ -110,6 +110,8 @@ internal::Matcher<T> id(const std::string &ID,
|
|||
typedef internal::Matcher<Decl> DeclarationMatcher;
|
||||
typedef internal::Matcher<QualType> TypeMatcher;
|
||||
typedef internal::Matcher<Stmt> StatementMatcher;
|
||||
typedef internal::Matcher<NestedNameSpecifier> NestedNameSpecifierMatcher;
|
||||
typedef internal::Matcher<NestedNameSpecifierLoc> NestedNameSpecifierLocMatcher;
|
||||
/// @}
|
||||
|
||||
/// \brief Matches any node.
|
||||
|
@ -2288,6 +2290,83 @@ isExplicitTemplateSpecialization() {
|
|||
internal::IsExplicitTemplateSpecializationMatcher>();
|
||||
}
|
||||
|
||||
/// \brief Matches nested name specifiers.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// namespace ns {
|
||||
/// struct A { static void f(); };
|
||||
/// void A::f() {}
|
||||
/// void g() { A::f(); }
|
||||
/// }
|
||||
/// ns::A a;
|
||||
/// \endcode
|
||||
/// nestedNameSpecifier()
|
||||
/// matches "ns::" and both "A::"
|
||||
const internal::VariadicAllOfMatcher<NestedNameSpecifier> nestedNameSpecifier;
|
||||
|
||||
/// \brief Same as \c nestedNameSpecifier but matches \c NestedNameSpecifierLoc.
|
||||
const internal::VariadicAllOfMatcher<
|
||||
NestedNameSpecifierLoc> nestedNameSpecifierLoc;
|
||||
|
||||
/// \brief Matches \c NestedNameSpecifierLocs for which the given inner
|
||||
/// NestedNameSpecifier-matcher matches.
|
||||
inline internal::BindableMatcher<NestedNameSpecifierLoc> loc(
|
||||
const internal::Matcher<NestedNameSpecifier> &InnerMatcher) {
|
||||
return internal::BindableMatcher<NestedNameSpecifierLoc>(
|
||||
new internal::LocMatcher<NestedNameSpecifierLoc, NestedNameSpecifier>(
|
||||
InnerMatcher));
|
||||
}
|
||||
|
||||
/// \brief Matches nested name specifiers that specify a type matching the
|
||||
/// given \c QualType matcher without qualifiers.
|
||||
/// FIXME: This is a temporary solution. Switch to using Type-matchers as soon
|
||||
/// as we have those.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// struct A { struct B { struct C {}; }; };
|
||||
/// A::B::C c;
|
||||
/// \endcode
|
||||
/// nestedNameSpecifier(specifiesType(hasDeclaration(recordDecl(hasName("A")))))
|
||||
/// matches "A::"
|
||||
AST_MATCHER_P(NestedNameSpecifier, specifiesType,
|
||||
internal::Matcher<QualType>, InnerMatcher) {
|
||||
if (Node.getAsType() == NULL)
|
||||
return false;
|
||||
return InnerMatcher.matches(QualType(Node.getAsType(), 0), Finder, Builder);
|
||||
}
|
||||
|
||||
/// \brief Matches on the prefix of a \c NestedNameSpecifier or
|
||||
/// \c NestedNameSpecifierLoc.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// struct A { struct B { struct C {}; }; };
|
||||
/// A::B::C c;
|
||||
/// \endcode
|
||||
/// nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))) and
|
||||
/// nestedNameSpecifierLoc(hasPrefix(loc(specifiesType(asString("struct A")))))
|
||||
/// both match "A::"
|
||||
LOC_TRAVERSE_MATCHER(hasPrefix, NestedNameSpecifier, getPrefix)
|
||||
|
||||
/// \brief Matches nested name specifiers that specify a namespace matching the
|
||||
/// given namespace matcher.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// namespace ns { struct A {}; }
|
||||
/// ns::A a;
|
||||
/// \endcode
|
||||
/// nestedNameSpecifier(specifiesNamespace(hasName("ns")))
|
||||
/// matches "ns::"
|
||||
AST_MATCHER_P(NestedNameSpecifier, specifiesNamespace,
|
||||
internal::Matcher<NamespaceDecl>, InnerMatcher) {
|
||||
if (Node.getAsNamespace() == NULL)
|
||||
return false;
|
||||
return InnerMatcher.matches(*Node.getAsNamespace(), Finder, Builder);
|
||||
}
|
||||
|
||||
} // end namespace ast_matchers
|
||||
} // end namespace clang
|
||||
|
||||
|
|
|
@ -380,6 +380,8 @@ struct IsBaseType {
|
|||
(llvm::is_same<T, Decl>::value ||
|
||||
llvm::is_same<T, Stmt>::value ||
|
||||
llvm::is_same<T, QualType>::value ||
|
||||
llvm::is_same<T, NestedNameSpecifier>::value ||
|
||||
llvm::is_same<T, NestedNameSpecifierLoc>::value ||
|
||||
llvm::is_same<T, CXXCtorInitializer>::value);
|
||||
};
|
||||
template <typename T>
|
||||
|
@ -652,9 +654,6 @@ public:
|
|||
/// The returned matcher is equivalent to this matcher, but will
|
||||
/// bind the matched node on a match.
|
||||
Matcher<T> bind(StringRef ID) const {
|
||||
TOOLING_COMPILE_ASSERT((llvm::is_base_of<Stmt, T>::value ||
|
||||
llvm::is_base_of<Decl, T>::value),
|
||||
trying_to_bind_unsupported_node_type__only_decl_and_stmt_supported);
|
||||
return Matcher<T>(new IdMatcher<T>(ID, *this));
|
||||
}
|
||||
};
|
||||
|
@ -779,6 +778,20 @@ private:
|
|||
const Matcher<T> InnertMatcher2;
|
||||
};
|
||||
|
||||
/// \brief Creates a Matcher<T> that matches if all inner matchers match.
|
||||
template<typename T>
|
||||
BindableMatcher<T> makeAllOfComposite(
|
||||
ArrayRef<const Matcher<T> *> InnerMatchers) {
|
||||
if (InnerMatchers.empty())
|
||||
return BindableMatcher<T>(new TrueMatcher<T>);
|
||||
MatcherInterface<T> *InnerMatcher = new TrueMatcher<T>;
|
||||
for (int i = InnerMatchers.size() - 1; i >= 0; --i) {
|
||||
InnerMatcher = new AllOfMatcher<T, Matcher<T>, Matcher<T> >(
|
||||
*InnerMatchers[i], makeMatcher(InnerMatcher));
|
||||
}
|
||||
return BindableMatcher<T>(InnerMatcher);
|
||||
}
|
||||
|
||||
/// \brief Creates a Matcher<T> that matches if
|
||||
/// T is dyn_cast'able into InnerT and all inner matchers match.
|
||||
///
|
||||
|
@ -788,17 +801,8 @@ private:
|
|||
template<typename T, typename InnerT>
|
||||
BindableMatcher<T> makeDynCastAllOfComposite(
|
||||
ArrayRef<const Matcher<InnerT> *> InnerMatchers) {
|
||||
if (InnerMatchers.empty()) {
|
||||
Matcher<InnerT> InnerMatcher = makeMatcher(new TrueMatcher<InnerT>);
|
||||
return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher));
|
||||
}
|
||||
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 BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher));
|
||||
return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(
|
||||
makeAllOfComposite(InnerMatchers)));
|
||||
}
|
||||
|
||||
/// \brief Matches nodes of type T that have at least one descendant node of
|
||||
|
@ -980,6 +984,99 @@ public:
|
|||
VariadicDynCastAllOfMatcher() {}
|
||||
};
|
||||
|
||||
/// \brief A \c VariadicAllOfMatcher<T> object is a variadic functor that takes
|
||||
/// a number of \c Matcher<T> and returns a \c Matcher<T> that matches \c T
|
||||
/// nodes that are matched by all of the given matchers.
|
||||
///
|
||||
/// For example:
|
||||
/// const VariadicAllOfMatcher<NestedNameSpecifier> nestedNameSpecifier;
|
||||
/// Creates a functor nestedNameSpecifier(...) that creates a
|
||||
/// \c Matcher<NestedNameSpecifier> given a variable number of arguments of type
|
||||
/// \c Matcher<NestedNameSpecifier>.
|
||||
/// The returned matcher matches if all given matchers match.
|
||||
template <typename T>
|
||||
class VariadicAllOfMatcher : public llvm::VariadicFunction<
|
||||
BindableMatcher<T>, Matcher<T>,
|
||||
makeAllOfComposite<T> > {
|
||||
public:
|
||||
VariadicAllOfMatcher() {}
|
||||
};
|
||||
|
||||
/// \brief Matches nodes of type \c TLoc for which the inner
|
||||
/// \c Matcher<T> matches.
|
||||
template <typename TLoc, typename T>
|
||||
class LocMatcher : public MatcherInterface<TLoc> {
|
||||
public:
|
||||
explicit LocMatcher(const Matcher<T> &InnerMatcher)
|
||||
: InnerMatcher(InnerMatcher) {}
|
||||
|
||||
virtual bool matches(const TLoc &Node,
|
||||
ASTMatchFinder *Finder,
|
||||
BoundNodesTreeBuilder *Builder) const {
|
||||
if (!Node)
|
||||
return false;
|
||||
return InnerMatcher.matches(*extract(Node), Finder, Builder);
|
||||
}
|
||||
|
||||
private:
|
||||
const NestedNameSpecifier *extract(const NestedNameSpecifierLoc &Loc) const {
|
||||
return Loc.getNestedNameSpecifier();
|
||||
}
|
||||
// FIXME: Add overload for TypeLoc when implementing TypeLoc-matchers.
|
||||
|
||||
const Matcher<T> InnerMatcher;
|
||||
};
|
||||
|
||||
/// \brief Matches nodes of type \c T for which the inner matcher matches on a
|
||||
/// another node of type \c T that can be reached using a given traverse
|
||||
/// function.
|
||||
template <typename T>
|
||||
class TraverseMatcher : public MatcherInterface<T> {
|
||||
public:
|
||||
explicit TraverseMatcher(const Matcher<T> &InnerMatcher,
|
||||
T *(T::*TraverseFunction)() const)
|
||||
: InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {}
|
||||
|
||||
virtual bool matches(const T &Node,
|
||||
ASTMatchFinder *Finder,
|
||||
BoundNodesTreeBuilder *Builder) const {
|
||||
T* NextNode = (Node.*TraverseFunction)();
|
||||
if (NextNode == NULL)
|
||||
return false;
|
||||
return InnerMatcher.matches(*NextNode, Finder, Builder);
|
||||
}
|
||||
|
||||
private:
|
||||
const Matcher<T> InnerMatcher;
|
||||
T *(T::*TraverseFunction)() const;
|
||||
};
|
||||
|
||||
/// \brief Matches nodes of type \c T in a ..Loc hierarchy, for which the inner
|
||||
/// matcher matches on a another node of type \c T that can be reached using a
|
||||
/// given traverse function.
|
||||
template <typename T>
|
||||
class LocTraverseMatcher : public MatcherInterface<T> {
|
||||
public:
|
||||
explicit LocTraverseMatcher(const Matcher<T> &InnerMatcher,
|
||||
T (T::*TraverseFunction)() const)
|
||||
: InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {}
|
||||
|
||||
virtual bool matches(const T &Node,
|
||||
ASTMatchFinder *Finder,
|
||||
BoundNodesTreeBuilder *Builder) const {
|
||||
if (!Node)
|
||||
return false;
|
||||
T NextNode = (Node.*TraverseFunction)();
|
||||
if (!NextNode)
|
||||
return false;
|
||||
return InnerMatcher.matches(NextNode, Finder, Builder);
|
||||
}
|
||||
|
||||
private:
|
||||
const Matcher<T> InnerMatcher;
|
||||
T (T::*TraverseFunction)() const;
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace ast_matchers
|
||||
} // end namespace clang
|
||||
|
|
|
@ -221,4 +221,22 @@
|
|||
const NodeType &Node, ASTMatchFinder *Finder, \
|
||||
BoundNodesTreeBuilder *Builder) const
|
||||
|
||||
/// \brief LOC_TRAVERSE_MATCHER(MatcherName, NodeType, FunctionName)
|
||||
/// defines the matcher \c MatcherName that can be used to traverse
|
||||
/// a Type or NestedNameSpecifier as well as the corresponding ..Loc.
|
||||
///
|
||||
/// The traversal is done using the given \c FunctionName.
|
||||
#define LOC_TRAVERSE_MATCHER( \
|
||||
MatcherName, NodeType, FunctionName) \
|
||||
inline internal::Matcher<NodeType> hasPrefix( \
|
||||
const internal::Matcher<NodeType> &InnerMatcher) { \
|
||||
return makeMatcher(new internal::TraverseMatcher<NodeType>( \
|
||||
InnerMatcher, &NodeType::getPrefix)); \
|
||||
} \
|
||||
inline internal::Matcher<NodeType##Loc> hasPrefix( \
|
||||
const internal::Matcher<NodeType##Loc> &InnerMatcher) { \
|
||||
return makeMatcher(new internal::LocTraverseMatcher<NodeType##Loc>( \
|
||||
InnerMatcher, &NodeType##Loc::getPrefix)); \
|
||||
}
|
||||
|
||||
#endif // LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_MACROS_H
|
||||
|
|
|
@ -74,6 +74,8 @@ private:
|
|||
enum NodeTypeTag {
|
||||
NT_Decl,
|
||||
NT_Stmt,
|
||||
NT_NestedNameSpecifier,
|
||||
NT_NestedNameSpecifierLoc,
|
||||
NT_QualType
|
||||
} Tag;
|
||||
|
||||
|
@ -83,7 +85,7 @@ private:
|
|||
/// guaranteed to be unique pointers pointing to dedicated storage in the
|
||||
/// AST. \c QualTypes on the other hand do not have storage or unique
|
||||
/// pointers and thus need to be stored by value.
|
||||
llvm::AlignedCharArrayUnion<Decl*, Stmt*, QualType> Storage;
|
||||
llvm::AlignedCharArrayUnion<Decl*, Stmt*, NestedNameSpecifierLoc, QualType> Storage;
|
||||
};
|
||||
template<typename T> struct DynTypedNode::BaseConverter<T,
|
||||
typename llvm::enable_if<llvm::is_base_of<Decl, T> >::type> {
|
||||
|
@ -113,6 +115,33 @@ template<typename T> struct DynTypedNode::BaseConverter<T,
|
|||
return Result;
|
||||
}
|
||||
};
|
||||
template<> struct DynTypedNode::BaseConverter<NestedNameSpecifier, void> {
|
||||
static const NestedNameSpecifier *get(NodeTypeTag Tag, const char Storage[]) {
|
||||
if (Tag == NT_NestedNameSpecifier)
|
||||
return *reinterpret_cast<NestedNameSpecifier*const*>(Storage);
|
||||
return NULL;
|
||||
}
|
||||
static DynTypedNode create(const NestedNameSpecifier &Node) {
|
||||
DynTypedNode Result;
|
||||
Result.Tag = NT_NestedNameSpecifier;
|
||||
new (Result.Storage.buffer) const NestedNameSpecifier*(&Node);
|
||||
return Result;
|
||||
}
|
||||
};
|
||||
template<> struct DynTypedNode::BaseConverter<NestedNameSpecifierLoc, void> {
|
||||
static const NestedNameSpecifierLoc *get(NodeTypeTag Tag,
|
||||
const char Storage[]) {
|
||||
if (Tag == NT_NestedNameSpecifierLoc)
|
||||
return reinterpret_cast<const NestedNameSpecifierLoc*>(Storage);
|
||||
return NULL;
|
||||
}
|
||||
static DynTypedNode create(const NestedNameSpecifierLoc &Node) {
|
||||
DynTypedNode Result;
|
||||
Result.Tag = NT_NestedNameSpecifierLoc;
|
||||
new (Result.Storage.buffer) NestedNameSpecifierLoc(Node);
|
||||
return Result;
|
||||
}
|
||||
};
|
||||
template<> struct DynTypedNode::BaseConverter<QualType, void> {
|
||||
static const QualType *get(NodeTypeTag Tag, const char Storage[]) {
|
||||
if (Tag == NT_QualType)
|
||||
|
@ -146,4 +175,3 @@ inline const void *DynTypedNode::getMemoizationData() const {
|
|||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_MATCHERS_AST_TYPE_TRAITS_H
|
||||
|
||||
|
|
|
@ -313,6 +313,8 @@ public:
|
|||
bool TraverseStmt(Stmt *StmtNode);
|
||||
bool TraverseType(QualType TypeNode);
|
||||
bool TraverseTypeLoc(TypeLoc TypeNode);
|
||||
bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS);
|
||||
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS);
|
||||
|
||||
// Matches children or descendants of 'Node' with 'BaseMatcher'.
|
||||
bool memoizedMatchesRecursively(const ast_type_traits::DynTypedNode &Node,
|
||||
|
@ -556,6 +558,21 @@ bool MatchASTVisitor::TraverseTypeLoc(TypeLoc TypeLoc) {
|
|||
TraverseTypeLoc(TypeLoc);
|
||||
}
|
||||
|
||||
bool MatchASTVisitor::TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) {
|
||||
match(*NNS);
|
||||
return RecursiveASTVisitor<MatchASTVisitor>::TraverseNestedNameSpecifier(NNS);
|
||||
}
|
||||
|
||||
bool MatchASTVisitor::TraverseNestedNameSpecifierLoc(
|
||||
NestedNameSpecifierLoc NNS) {
|
||||
match(NNS);
|
||||
// We only match the nested name specifier here (as opposed to traversing it)
|
||||
// because the traversal is already done in the parallel "Loc"-hierarchy.
|
||||
match(*NNS.getNestedNameSpecifier());
|
||||
return
|
||||
RecursiveASTVisitor<MatchASTVisitor>::TraverseNestedNameSpecifierLoc(NNS);
|
||||
}
|
||||
|
||||
class MatchASTConsumer : public ASTConsumer {
|
||||
public:
|
||||
MatchASTConsumer(
|
||||
|
@ -619,6 +636,18 @@ void MatchFinder::addMatcher(const StatementMatcher &NodeMatch,
|
|||
new internal::Matcher<Stmt>(NodeMatch), Action));
|
||||
}
|
||||
|
||||
void MatchFinder::addMatcher(const NestedNameSpecifierMatcher &NodeMatch,
|
||||
MatchCallback *Action) {
|
||||
MatcherCallbackPairs.push_back(std::make_pair(
|
||||
new NestedNameSpecifierMatcher(NodeMatch), Action));
|
||||
}
|
||||
|
||||
void MatchFinder::addMatcher(const NestedNameSpecifierLocMatcher &NodeMatch,
|
||||
MatchCallback *Action) {
|
||||
MatcherCallbackPairs.push_back(std::make_pair(
|
||||
new NestedNameSpecifierLocMatcher(NodeMatch), Action));
|
||||
}
|
||||
|
||||
ASTConsumer *MatchFinder::newASTConsumer() {
|
||||
return new internal::MatchASTConsumer(&MatcherCallbackPairs, ParsingDone);
|
||||
}
|
||||
|
|
|
@ -607,36 +607,40 @@ TEST(TypeMatcher, MatchesClassType) {
|
|||
// Decl bound to Id that can be dynamically cast to T.
|
||||
// Optionally checks that the check succeeded a specific number of times.
|
||||
template <typename T>
|
||||
class VerifyIdIsBoundToDecl : public BoundNodesCallback {
|
||||
class VerifyIdIsBoundTo : public BoundNodesCallback {
|
||||
public:
|
||||
// Create an object that checks that a node of type \c T was bound to \c Id.
|
||||
// Does not check for a certain number of matches.
|
||||
explicit VerifyIdIsBoundToDecl(llvm::StringRef Id)
|
||||
explicit VerifyIdIsBoundTo(llvm::StringRef Id)
|
||||
: Id(Id), ExpectedCount(-1), Count(0) {}
|
||||
|
||||
// Create an object that checks that a node of type \c T was bound to \c Id.
|
||||
// Checks that there were exactly \c ExpectedCount matches.
|
||||
VerifyIdIsBoundToDecl(llvm::StringRef Id, int ExpectedCount)
|
||||
VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount)
|
||||
: Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
|
||||
|
||||
// Create an object that checks that a node of type \c T was bound to \c Id.
|
||||
// Checks that there was exactly one match with the name \c ExpectedDeclName.
|
||||
// Checks that there was exactly one match with the name \c ExpectedName.
|
||||
// Note that \c T must be a NamedDecl for this to work.
|
||||
VerifyIdIsBoundToDecl(llvm::StringRef Id, llvm::StringRef ExpectedDeclName)
|
||||
: Id(Id), ExpectedCount(1), Count(0), ExpectedDeclName(ExpectedDeclName) {}
|
||||
VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName)
|
||||
: Id(Id), ExpectedCount(1), Count(0), ExpectedName(ExpectedName) {}
|
||||
|
||||
~VerifyIdIsBoundToDecl() {
|
||||
~VerifyIdIsBoundTo() {
|
||||
if (ExpectedCount != -1)
|
||||
EXPECT_EQ(ExpectedCount, Count);
|
||||
if (!ExpectedDeclName.empty())
|
||||
EXPECT_EQ(ExpectedDeclName, DeclName);
|
||||
if (!ExpectedName.empty())
|
||||
EXPECT_EQ(ExpectedName, Name);
|
||||
}
|
||||
|
||||
virtual bool run(const BoundNodes *Nodes) {
|
||||
if (const Decl *Node = Nodes->getDeclAs<T>(Id)) {
|
||||
if (Nodes->getNodeAs<T>(Id)) {
|
||||
++Count;
|
||||
if (const NamedDecl *Named = llvm::dyn_cast<NamedDecl>(Node)) {
|
||||
DeclName = Named->getNameAsString();
|
||||
if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
|
||||
Name = Named->getNameAsString();
|
||||
} else if (const NestedNameSpecifier *NNS =
|
||||
Nodes->getNodeAs<NestedNameSpecifier>(Id)) {
|
||||
llvm::raw_string_ostream OS(Name);
|
||||
NNS->print(OS, PrintingPolicy(LangOptions()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -647,43 +651,32 @@ private:
|
|||
const std::string Id;
|
||||
const int ExpectedCount;
|
||||
int Count;
|
||||
const std::string ExpectedDeclName;
|
||||
std::string DeclName;
|
||||
};
|
||||
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;
|
||||
const std::string ExpectedName;
|
||||
std::string Name;
|
||||
};
|
||||
|
||||
TEST(Matcher, BindMatchedNodes) {
|
||||
DeclarationMatcher ClassX = has(recordDecl(hasName("::X")).bind("x"));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class X {};",
|
||||
ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("x")));
|
||||
ClassX, new VerifyIdIsBoundTo<CXXRecordDecl>("x")));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultFalse("class X {};",
|
||||
ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("other-id")));
|
||||
ClassX, new VerifyIdIsBoundTo<CXXRecordDecl>("other-id")));
|
||||
|
||||
TypeMatcher TypeAHasClassB = hasDeclaration(
|
||||
recordDecl(hasName("A"), has(recordDecl(hasName("B")).bind("b"))));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };",
|
||||
TypeAHasClassB,
|
||||
new VerifyIdIsBoundToDecl<Decl>("b")));
|
||||
new VerifyIdIsBoundTo<Decl>("b")));
|
||||
|
||||
StatementMatcher MethodX =
|
||||
callExpr(callee(methodDecl(hasName("x")))).bind("x");
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };",
|
||||
MethodX,
|
||||
new VerifyIdIsBoundToStmt<CXXMemberCallExpr>("x")));
|
||||
new VerifyIdIsBoundTo<CXXMemberCallExpr>("x")));
|
||||
}
|
||||
|
||||
TEST(Matcher, BindTheSameNameInAlternatives) {
|
||||
|
@ -700,7 +693,7 @@ TEST(Matcher, BindTheSameNameInAlternatives) {
|
|||
// The second branch binds x to f() and succeeds.
|
||||
"int f() { return 0 + f(); }",
|
||||
matcher,
|
||||
new VerifyIdIsBoundToStmt<CallExpr>("x")));
|
||||
new VerifyIdIsBoundTo<CallExpr>("x")));
|
||||
}
|
||||
|
||||
TEST(Matcher, BindsIDForMemoizedResults) {
|
||||
|
@ -712,7 +705,7 @@ TEST(Matcher, BindsIDForMemoizedResults) {
|
|||
DeclarationMatcher(anyOf(
|
||||
recordDecl(hasName("A"), hasDescendant(ClassX)),
|
||||
recordDecl(hasName("B"), hasDescendant(ClassX)))),
|
||||
new VerifyIdIsBoundToDecl<Decl>("x", 2)));
|
||||
new VerifyIdIsBoundTo<Decl>("x", 2)));
|
||||
}
|
||||
|
||||
TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) {
|
||||
|
@ -1918,13 +1911,13 @@ TEST(AstMatcherPMacro, Works) {
|
|||
DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b")));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
|
||||
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
|
||||
HasClassB, new VerifyIdIsBoundTo<Decl>("b")));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
|
||||
HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
|
||||
HasClassB, new VerifyIdIsBoundTo<Decl>("a")));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
|
||||
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
|
||||
HasClassB, new VerifyIdIsBoundTo<Decl>("b")));
|
||||
}
|
||||
|
||||
AST_POLYMORPHIC_MATCHER_P(
|
||||
|
@ -1943,13 +1936,13 @@ TEST(AstPolymorphicMatcherPMacro, Works) {
|
|||
polymorphicHas(recordDecl(hasName("B")).bind("b"));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
|
||||
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
|
||||
HasClassB, new VerifyIdIsBoundTo<Decl>("b")));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
|
||||
HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
|
||||
HasClassB, new VerifyIdIsBoundTo<Decl>("a")));
|
||||
|
||||
EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
|
||||
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
|
||||
HasClassB, new VerifyIdIsBoundTo<Decl>("b")));
|
||||
|
||||
StatementMatcher StatementHasClassB =
|
||||
polymorphicHas(recordDecl(hasName("B")));
|
||||
|
@ -2579,13 +2572,13 @@ TEST(HasConditionVariableStatement, MatchesConditionVariables) {
|
|||
TEST(ForEach, BindsOneNode) {
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; };",
|
||||
recordDecl(hasName("C"), forEach(fieldDecl(hasName("x")).bind("x"))),
|
||||
new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
|
||||
new VerifyIdIsBoundTo<FieldDecl>("x", 1)));
|
||||
}
|
||||
|
||||
TEST(ForEach, BindsMultipleNodes) {
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; int y; int z; };",
|
||||
recordDecl(hasName("C"), forEach(fieldDecl().bind("f"))),
|
||||
new VerifyIdIsBoundToDecl<FieldDecl>("f", 3)));
|
||||
new VerifyIdIsBoundTo<FieldDecl>("f", 3)));
|
||||
}
|
||||
|
||||
TEST(ForEach, BindsRecursiveCombinations) {
|
||||
|
@ -2593,14 +2586,14 @@ TEST(ForEach, BindsRecursiveCombinations) {
|
|||
"class C { class D { int x; int y; }; class E { int y; int z; }; };",
|
||||
recordDecl(hasName("C"),
|
||||
forEach(recordDecl(forEach(fieldDecl().bind("f"))))),
|
||||
new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
|
||||
new VerifyIdIsBoundTo<FieldDecl>("f", 4)));
|
||||
}
|
||||
|
||||
TEST(ForEachDescendant, BindsOneNode) {
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue("class C { class D { int x; }; };",
|
||||
recordDecl(hasName("C"),
|
||||
forEachDescendant(fieldDecl(hasName("x")).bind("x"))),
|
||||
new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
|
||||
new VerifyIdIsBoundTo<FieldDecl>("x", 1)));
|
||||
}
|
||||
|
||||
TEST(ForEachDescendant, BindsMultipleNodes) {
|
||||
|
@ -2608,7 +2601,7 @@ TEST(ForEachDescendant, BindsMultipleNodes) {
|
|||
"class C { class D { int x; int y; }; "
|
||||
" class E { class F { int y; int z; }; }; };",
|
||||
recordDecl(hasName("C"), forEachDescendant(fieldDecl().bind("f"))),
|
||||
new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
|
||||
new VerifyIdIsBoundTo<FieldDecl>("f", 4)));
|
||||
}
|
||||
|
||||
TEST(ForEachDescendant, BindsRecursiveCombinations) {
|
||||
|
@ -2617,7 +2610,7 @@ TEST(ForEachDescendant, BindsRecursiveCombinations) {
|
|||
" class E { class F { class G { int y; int z; }; }; }; }; };",
|
||||
recordDecl(hasName("C"), forEachDescendant(recordDecl(
|
||||
forEachDescendant(fieldDecl().bind("f"))))),
|
||||
new VerifyIdIsBoundToDecl<FieldDecl>("f", 8)));
|
||||
new VerifyIdIsBoundTo<FieldDecl>("f", 8)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -2775,7 +2768,7 @@ TEST(HasAncestor, BindsRecursiveCombinations) {
|
|||
EXPECT_TRUE(matchAndVerifyResultTrue(
|
||||
"class C { class D { class E { class F { int y; }; }; }; };",
|
||||
fieldDecl(hasAncestor(recordDecl(hasAncestor(recordDecl().bind("r"))))),
|
||||
new VerifyIdIsBoundToDecl<CXXRecordDecl>("r", 1)));
|
||||
new VerifyIdIsBoundTo<CXXRecordDecl>("r", 1)));
|
||||
}
|
||||
|
||||
TEST(HasAncestor, BindsCombinationsWithHasDescendant) {
|
||||
|
@ -2787,7 +2780,7 @@ TEST(HasAncestor, BindsCombinationsWithHasDescendant) {
|
|||
hasAncestor(recordDecl())))
|
||||
).bind("d")
|
||||
)),
|
||||
new VerifyIdIsBoundToDecl<CXXRecordDecl>("d", "E")));
|
||||
new VerifyIdIsBoundTo<CXXRecordDecl>("d", "E")));
|
||||
}
|
||||
|
||||
TEST(HasAncestor, MatchesInTemplateInstantiations) {
|
||||
|
@ -2806,5 +2799,62 @@ TEST(HasAncestor, MatchesInImplicitCode) {
|
|||
hasAncestor(recordDecl(hasName("A")))))))));
|
||||
}
|
||||
|
||||
TEST(NNS, MatchesNestedNameSpecifiers) {
|
||||
EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;",
|
||||
nestedNameSpecifier()));
|
||||
EXPECT_TRUE(matches("template <typename T> class A { typename T::B b; };",
|
||||
nestedNameSpecifier()));
|
||||
EXPECT_TRUE(matches("struct A { void f(); }; void A::f() {}",
|
||||
nestedNameSpecifier()));
|
||||
|
||||
EXPECT_TRUE(matches(
|
||||
"struct A { static void f() {} }; void g() { A::f(); }",
|
||||
nestedNameSpecifier()));
|
||||
EXPECT_TRUE(notMatches(
|
||||
"struct A { static void f() {} }; void g(A* a) { a->f(); }",
|
||||
nestedNameSpecifier()));
|
||||
}
|
||||
|
||||
TEST(NNS, MatchesTypes) {
|
||||
NestedNameSpecifierMatcher Matcher = nestedNameSpecifier(
|
||||
specifiesType(hasDeclaration(recordDecl(hasName("A")))));
|
||||
EXPECT_TRUE(matches("struct A { struct B {}; }; A::B b;", Matcher));
|
||||
EXPECT_TRUE(matches("struct A { struct B { struct C {}; }; }; A::B::C c;",
|
||||
Matcher));
|
||||
EXPECT_TRUE(notMatches("namespace A { struct B {}; } A::B b;", Matcher));
|
||||
}
|
||||
|
||||
TEST(NNS, MatchesNamespaceDecls) {
|
||||
NestedNameSpecifierMatcher Matcher = nestedNameSpecifier(
|
||||
specifiesNamespace(hasName("ns")));
|
||||
EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", Matcher));
|
||||
EXPECT_TRUE(notMatches("namespace xx { struct A {}; } xx::A a;", Matcher));
|
||||
EXPECT_TRUE(notMatches("struct ns { struct A {}; }; ns::A a;", Matcher));
|
||||
}
|
||||
|
||||
TEST(NNS, BindsNestedNameSpecifiers) {
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue(
|
||||
"namespace ns { struct E { struct B {}; }; } ns::E::B b;",
|
||||
nestedNameSpecifier(specifiesType(asString("struct ns::E"))).bind("nns"),
|
||||
new VerifyIdIsBoundTo<NestedNameSpecifier>("nns", "ns::struct E::")));
|
||||
}
|
||||
|
||||
TEST(NNS, BindsNestedNameSpecifierLocs) {
|
||||
EXPECT_TRUE(matchAndVerifyResultTrue(
|
||||
"namespace ns { struct B {}; } ns::B b;",
|
||||
loc(nestedNameSpecifier()).bind("loc"),
|
||||
new VerifyIdIsBoundTo<NestedNameSpecifierLoc>("loc", 1)));
|
||||
}
|
||||
|
||||
TEST(NNS, MatchesNestedNameSpecifierPrefixes) {
|
||||
EXPECT_TRUE(matches(
|
||||
"struct A { struct B { struct C {}; }; }; A::B::C c;",
|
||||
nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A"))))));
|
||||
EXPECT_TRUE(matches(
|
||||
"struct A { struct B { struct C {}; }; }; A::B::C c;",
|
||||
nestedNameSpecifierLoc(hasPrefix(loc(
|
||||
specifiesType(asString("struct A")))))));
|
||||
}
|
||||
|
||||
} // end namespace ast_matchers
|
||||
} // end namespace clang
|
||||
|
|
Загрузка…
Ссылка в новой задаче