389 changes: 385 additions & 4 deletions clang/docs/LibASTMatchersReference.html

Large diffs are not rendered by default.

43 changes: 37 additions & 6 deletions clang/include/clang/ASTMatchers/ASTMatchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -3896,20 +3896,51 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
return false;
}

/// Matches if the type location of the declarator decl's type matches
/// the inner matcher.
/// Matches if the type location of a node matches the inner matcher.
///
/// Given
/// Examples:
/// \code
/// int x;
/// \endcode
/// declaratorDecl(hasTypeLoc(loc(asString("int"))))
/// matches int x
AST_MATCHER_P(DeclaratorDecl, hasTypeLoc, internal::Matcher<TypeLoc>, Inner) {
if (!Node.getTypeSourceInfo())
///
/// \code
/// auto x = int(3);
/// \code
/// cxxTemporaryObjectExpr(hasTypeLoc(loc(asString("int"))))
/// matches int(3)
///
/// \code
/// struct Foo { Foo(int, int); };
/// auto x = Foo(1, 2);
/// \code
/// cxxFunctionalCastExpr(hasTypeLoc(loc(asString("struct Foo"))))
/// matches Foo(1, 2)
///
/// Usable as: Matcher<BlockDecl>, Matcher<CXXBaseSpecifier>,
/// Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
/// Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
/// Matcher<CXXUnresolvedConstructExpr>,
/// Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
/// Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
/// Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
/// Matcher<TypedefNameDecl>
AST_POLYMORPHIC_MATCHER_P(
hasTypeLoc,
AST_POLYMORPHIC_SUPPORTED_TYPES(
BlockDecl, CXXBaseSpecifier, CXXCtorInitializer, CXXFunctionalCastExpr,
CXXNewExpr, CXXTemporaryObjectExpr, CXXUnresolvedConstructExpr,
ClassTemplateSpecializationDecl, CompoundLiteralExpr, DeclaratorDecl,
ExplicitCastExpr, ObjCPropertyDecl, TemplateArgumentLoc,
TypedefNameDecl),
internal::Matcher<TypeLoc>, Inner) {
TypeSourceInfo *source = internal::GetTypeSourceInfo(Node);
if (source == nullptr) {
// This happens for example for implicit destructors.
return false;
return Inner.matches(Node.getTypeSourceInfo()->getTypeLoc(), Finder, Builder);
}
return Inner.matches(source->getTypeLoc(), Finder, Builder);
}

/// Matches if the matched type is represented by the given string.
Expand Down
93 changes: 60 additions & 33 deletions clang/include/clang/ASTMatchers/ASTMatchersInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,37 @@ class BoundNodes;

namespace internal {

/// A type-list implementation.
///
/// A "linked list" of types, accessible by using the ::head and ::tail
/// typedefs.
template <typename... Ts> struct TypeList {}; // Empty sentinel type list.

template <typename T1, typename... Ts> struct TypeList<T1, Ts...> {
/// The first type on the list.
using head = T1;

/// A sublist with the tail. ie everything but the head.
///
/// This type is used to do recursion. TypeList<>/EmptyTypeList indicates the
/// end of the list.
using tail = TypeList<Ts...>;
};

/// The empty type list.
using EmptyTypeList = TypeList<>;

/// Helper meta-function to determine if some type \c T is present or
/// a parent type in the list.
template <typename AnyTypeList, typename T> struct TypeListContainsSuperOf {
static const bool value =
std::is_base_of<typename AnyTypeList::head, T>::value ||
TypeListContainsSuperOf<typename AnyTypeList::tail, T>::value;
};
template <typename T> struct TypeListContainsSuperOf<EmptyTypeList, T> {
static const bool value = false;
};

/// Variadic function object.
///
/// Most of the functions below that use VariadicFunction could be implemented
Expand Down Expand Up @@ -135,6 +166,35 @@ inline QualType getUnderlyingType(const CXXBaseSpecifier &Node) {
return Node.getType();
}

/// Unifies obtaining a `TypeSourceInfo` from different node types.
template <typename T,
std::enable_if_t<TypeListContainsSuperOf<
TypeList<CXXBaseSpecifier, CXXCtorInitializer,
CXXTemporaryObjectExpr, CXXUnresolvedConstructExpr,
CompoundLiteralExpr, DeclaratorDecl, ObjCPropertyDecl,
TemplateArgumentLoc, TypedefNameDecl>,
T>::value> * = nullptr>
inline TypeSourceInfo *GetTypeSourceInfo(const T &Node) {
return Node.getTypeSourceInfo();
}
template <typename T,
std::enable_if_t<TypeListContainsSuperOf<
TypeList<CXXFunctionalCastExpr, ExplicitCastExpr>, T>::value> * =
nullptr>
inline TypeSourceInfo *GetTypeSourceInfo(const T &Node) {
return Node.getTypeInfoAsWritten();
}
inline TypeSourceInfo *GetTypeSourceInfo(const BlockDecl &Node) {
return Node.getSignatureAsWritten();
}
inline TypeSourceInfo *GetTypeSourceInfo(const CXXNewExpr &Node) {
return Node.getAllocatedTypeSourceInfo();
}
inline TypeSourceInfo *
GetTypeSourceInfo(const ClassTemplateSpecializationDecl &Node) {
return Node.getTypeAsWritten();
}

/// Unifies obtaining the FunctionProtoType pointer from both
/// FunctionProtoType and FunctionDecl nodes..
inline const FunctionProtoType *
Expand Down Expand Up @@ -1120,39 +1180,6 @@ struct IsBaseType {
template <typename T>
const bool IsBaseType<T>::value;

/// A type-list implementation.
///
/// A "linked list" of types, accessible by using the ::head and ::tail
/// typedefs.
template <typename... Ts> struct TypeList {}; // Empty sentinel type list.

template <typename T1, typename... Ts> struct TypeList<T1, Ts...> {
/// The first type on the list.
using head = T1;

/// A sublist with the tail. ie everything but the head.
///
/// This type is used to do recursion. TypeList<>/EmptyTypeList indicates the
/// end of the list.
using tail = TypeList<Ts...>;
};

/// The empty type list.
using EmptyTypeList = TypeList<>;

/// Helper meta-function to determine if some type \c T is present or
/// a parent type in the list.
template <typename AnyTypeList, typename T>
struct TypeListContainsSuperOf {
static const bool value =
std::is_base_of<typename AnyTypeList::head, T>::value ||
TypeListContainsSuperOf<typename AnyTypeList::tail, T>::value;
};
template <typename T>
struct TypeListContainsSuperOf<EmptyTypeList, T> {
static const bool value = false;
};

/// A "type list" that contains all types.
///
/// Useful for matchers like \c anything and \c unless.
Expand Down
92 changes: 91 additions & 1 deletion clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Host.h"
#include "gtest/gtest.h"
Expand Down Expand Up @@ -375,15 +376,104 @@ TEST(HasType, MatchesTypedefNameDecl) {
typedefNameDecl(hasType(asString("foo")), hasName("bar"))));
}

TEST(HasTypeLoc, MatchesDeclaratorDecls) {
TEST(HasTypeLoc, MatchesBlockDecl) {
EXPECT_TRUE(matchesConditionally(
"auto x = ^int (int a, int b) { return a + b; };",
blockDecl(hasTypeLoc(loc(asString("int (int, int)")))), true,
{"-fblocks"}));
}

TEST(HasTypeLoc, MatchesCXXBaseSpecifierAndCtorInitializer) {
llvm::StringRef code = R"cpp(
class Foo {};
class Bar : public Foo {
Bar() : Foo() {}
};
)cpp";

EXPECT_TRUE(matches(
code, cxxRecordDecl(hasAnyBase(hasTypeLoc(loc(asString("class Foo")))))));
EXPECT_TRUE(matches(
code, cxxCtorInitializer(hasTypeLoc(loc(asString("class Foo"))))));
}

TEST(HasTypeLoc, MatchesCXXFunctionalCastExpr) {
EXPECT_TRUE(matches("auto x = int(3);",
cxxFunctionalCastExpr(hasTypeLoc(loc(asString("int"))))));
}

TEST(HasTypeLoc, MatchesCXXNewExpr) {
EXPECT_TRUE(matches("auto* x = new int(3);",
cxxNewExpr(hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("class Foo{}; auto* x = new Foo();",
cxxNewExpr(hasTypeLoc(loc(asString("class Foo"))))));
}

TEST(HasTypeLoc, MatchesCXXTemporaryObjectExpr) {
EXPECT_TRUE(
matches("struct Foo { Foo(int, int); }; auto x = Foo(1, 2);",
cxxTemporaryObjectExpr(hasTypeLoc(loc(asString("struct Foo"))))));
}

TEST(HasTypeLoc, MatchesCXXUnresolvedConstructExpr) {
EXPECT_TRUE(
matches("template <typename T> T make() { return T(); }",
cxxUnresolvedConstructExpr(hasTypeLoc(loc(asString("T"))))));
}

TEST(HasTypeLoc, MatchesClassTemplateSpecializationDecl) {
EXPECT_TRUE(matches(
"template <typename T> class Foo; template <> class Foo<int> {};",
classTemplateSpecializationDecl(hasTypeLoc(loc(asString("Foo<int>"))))));
}

TEST(HasTypeLoc, MatchesCompoundLiteralExpr) {
EXPECT_TRUE(
matches("int* x = (int [2]) { 0, 1 };",
compoundLiteralExpr(hasTypeLoc(loc(asString("int [2]"))))));
}

TEST(HasTypeLoc, MatchesDeclaratorDecl) {
EXPECT_TRUE(matches("int x;",
varDecl(hasName("x"), hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("int x(3);",
varDecl(hasName("x"), hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(
matches("struct Foo { Foo(int, int); }; Foo x(1, 2);",
varDecl(hasName("x"), hasTypeLoc(loc(asString("struct Foo"))))));

// Make sure we don't crash on implicit constructors.
EXPECT_TRUE(notMatches("class X {}; X x;",
declaratorDecl(hasTypeLoc(loc(asString("int"))))));
}

TEST(HasTypeLoc, MatchesExplicitCastExpr) {
EXPECT_TRUE(matches("auto x = (int) 3;",
explicitCastExpr(hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("auto x = static_cast<int>(3);",
explicitCastExpr(hasTypeLoc(loc(asString("int"))))));
}

TEST(HasTypeLoc, MatchesObjCPropertyDecl) {
EXPECT_TRUE(matchesObjC(R"objc(
@interface Foo
@property int enabled;
@end
)objc",
objcPropertyDecl(hasTypeLoc(loc(asString("int"))))));
}

TEST(HasTypeLoc, MatchesTemplateArgumentLoc) {
EXPECT_TRUE(matches("template <typename T> class Foo {}; Foo<int> x;",
templateArgumentLoc(hasTypeLoc(loc(asString("int"))))));
}

TEST(HasTypeLoc, MatchesTypedefNameDecl) {
EXPECT_TRUE(matches("typedef int X;",
typedefNameDecl(hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("using X = int;",
typedefNameDecl(hasTypeLoc(loc(asString("int"))))));
}

TEST(Callee, MatchesDeclarations) {
StatementMatcher CallMethodX = callExpr(callee(cxxMethodDecl(hasName("x"))));
Expand Down