MatchCallback *Action);
/// @}
+ /// \brief Adds a matcher to execute when running over the AST.
+ ///
+ /// This is similar to \c addMatcher(), but it uses the dynamic interface. It
+ /// is more flexible, but the lost type information enables a caller to pass
+ /// a matcher that cannot match anything.
+ ///
+ /// \returns \c true if the matcher is a valid top-level matcher, \c false
+ /// otherwise.
+ bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
+ MatchCallback *Action);
+
/// \brief Creates a clang ASTConsumer that finds all matches.
clang::ASTConsumer *newASTConsumer();
ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const = 0;
+ /// \brief Makes a copy of this matcher object.
+ virtual DynTypedMatcher *clone() const = 0;
+
/// \brief Returns a unique ID for the matcher.
virtual uint64_t getID() const = 0;
};
return matches(*Node, Finder, Builder);
}
+ /// \brief Makes a copy of this matcher object.
+ virtual Matcher<T> *clone() const { return new Matcher<T>(*this); }
+
/// \brief Allows the conversion of a \c Matcher<Type> to a \c
/// Matcher<QualType>.
///
--- /dev/null
+//===--- Diagnostics.h - Helper class for error diagnostics -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Diagnostics class to manage error messages.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_DIAGNOSTICS_H
+#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_DIAGNOSTICS_H
+
+#include <string>
+#include <vector>
+
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+struct SourceLocation {
+ SourceLocation() : Line(), Column() {}
+ unsigned Line;
+ unsigned Column;
+};
+
+struct SourceRange {
+ SourceLocation Start;
+ SourceLocation End;
+};
+
+/// \brief A VariantValue instance annotated with its parser context.
+struct ParserValue {
+ ParserValue() : Text(), Range(), Value() {}
+ StringRef Text;
+ SourceRange Range;
+ VariantValue Value;
+};
+
+/// \brief Helper class to manage error messages.
+class Diagnostics {
+ public:
+ /// \brief All errors from the system.
+ enum ErrorType {
+ ET_None = 0,
+
+ ET_RegistryNotFound = 1,
+ ET_RegistryWrongArgCount = 2,
+ ET_RegistryWrongArgType = 3,
+
+ ET_ParserStringError = 100,
+ ET_ParserMatcherArgFailure = 101,
+ ET_ParserMatcherFailure = 102,
+ ET_ParserNoOpenParen = 103,
+ ET_ParserNoCloseParen = 104,
+ ET_ParserNoComma = 105,
+ ET_ParserNoCode = 106,
+ ET_ParserNotAMatcher = 107,
+ ET_ParserInvalidToken = 108
+ };
+
+ /// \brief Helper stream class.
+ struct ArgStream {
+ template <class T> ArgStream &operator<<(const T &Arg) {
+ return operator<<(Twine(Arg));
+ }
+ ArgStream &operator<<(const Twine &Arg);
+ std::vector<std::string> *Out;
+ };
+
+ /// \brief Push a frame to the beginning of the list
+ ///
+ /// Returns a helper class to allow the caller to pass the arguments for the
+ /// error message, using the << operator.
+ ArgStream pushErrorFrame(const SourceRange &Range, ErrorType Error);
+
+ struct ErrorFrame {
+ SourceRange Range;
+ ErrorType Type;
+ std::vector<std::string> Args;
+
+ std::string ToString() const;
+ };
+ ArrayRef<ErrorFrame> frames() const { return Frames; }
+
+ /// \brief Returns a string representation of the last frame.
+ std::string ToString() const;
+ /// \brief Returns a string representation of the whole frame stack.
+ std::string ToStringFull() const;
+
+ private:
+ std::vector<ErrorFrame> Frames;
+};
+
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_DIAGNOSTICS_H
--- /dev/null
+//===--- Parser.h - Matcher expression parser -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Simple matcher expression parser.
+///
+/// The parser understands matcher expressions of the form:
+/// MatcherName(Arg0, Arg1, ..., ArgN)
+/// as well as simple types like strings.
+/// The parser does not know how to process the matchers. It delegates this task
+/// to a Sema object received as an argument.
+///
+/// \code
+/// Grammar for the expressions supported:
+/// <Expression> := <StringLiteral> | <MatcherExpression>
+/// <StringLiteral> := "quoted string"
+/// <MatcherExpression> := <MatcherName>(<ArgumentList>)
+/// <MatcherName> := [a-zA-Z]+
+/// <ArgumentList> := <Expression> | <Expression>,<ArgumentList>
+/// \endcode
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_PARSER_H
+#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_PARSER_H
+
+#include "clang/ASTMatchers/Dynamic/Diagnostics.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+/// \brief Matcher expression parser.
+class Parser {
+public:
+ /// \brief Interface to connect the parser with the registry and more.
+ ///
+ /// The parser uses the Sema instance passed into
+ /// parseMatcherExpression() to handle all matcher tokens. The simplest
+ /// processor implementation would simply call into the registry to create
+ /// the matchers.
+ /// However, a more complex processor might decide to intercept the matcher
+ /// creation and do some extra work. For example, it could apply some
+ /// transformation to the matcher by adding some id() nodes, or could detect
+ /// specific matcher nodes for more efficient lookup.
+ class Sema {
+ public:
+ virtual ~Sema();
+
+ /// \brief Process a matcher expression.
+ ///
+ /// All the arguments passed here have already been processed.
+ ///
+ /// \param MatcherName The matcher name found by the parser.
+ ///
+ /// \param NameRange The location of the name in the matcher source.
+ /// Useful for error reporting.
+ ///
+ /// \param Args The argument list for the matcher.
+ ///
+ /// \return The matcher object constructed by the processor, or NULL
+ /// if an error occurred. In that case, \c Error will contain a
+ /// description of the error.
+ /// The caller takes ownership of the DynTypedMatcher object returned.
+ virtual DynTypedMatcher *
+ actOnMatcherExpression(StringRef MatcherName, const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args, Diagnostics *Error) = 0;
+ };
+
+ /// \brief Parse a matcher expression, creating matchers from the registry.
+ ///
+ /// This overload creates matchers calling directly into the registry. If the
+ /// caller needs more control over how the matchers are created, then it can
+ /// use the overload below that takes a Sema.
+ ///
+ /// \param MatcherCode The matcher expression to parse.
+ ///
+ /// \return The matcher object constructed, or NULL if an error occurred.
+ // In that case, \c Error will contain a description of the error.
+ /// The caller takes ownership of the DynTypedMatcher object returned.
+ static DynTypedMatcher *parseMatcherExpression(StringRef MatcherCode,
+ Diagnostics *Error);
+
+ /// \brief Parse a matcher expression.
+ ///
+ /// \param MatcherCode The matcher expression to parse.
+ ///
+ /// \param S The Sema instance that will help the parser
+ /// construct the matchers.
+ /// \return The matcher object constructed by the processor, or NULL
+ /// if an error occurred. In that case, \c Error will contain a
+ /// description of the error.
+ /// The caller takes ownership of the DynTypedMatcher object returned.
+ static DynTypedMatcher *parseMatcherExpression(StringRef MatcherCode,
+ Sema *S,
+ Diagnostics *Error);
+
+ /// \brief Parse an expression, creating matchers from the registry.
+ ///
+ /// Parses any expression supported by this parser. In general, the
+ /// \c parseMatcherExpression function is a better approach to get a matcher
+ /// object.
+ static bool parseExpression(StringRef Code, VariantValue *Value,
+ Diagnostics *Error);
+
+ /// \brief Parse an expression.
+ ///
+ /// Parses any expression supported by this parser. In general, the
+ /// \c parseMatcherExpression function is a better approach to get a matcher
+ /// object.
+ static bool parseExpression(StringRef Code, Sema *S,
+ VariantValue *Value, Diagnostics *Error);
+
+private:
+ class CodeTokenizer;
+ struct TokenInfo;
+
+ Parser(CodeTokenizer *Tokenizer, Sema *S,
+ Diagnostics *Error);
+
+ bool parseExpressionImpl(VariantValue *Value);
+ bool parseMatcherExpressionImpl(VariantValue *Value);
+
+ CodeTokenizer *const Tokenizer;
+ Sema *const S;
+ Diagnostics *const Error;
+};
+
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_PARSER_H
--- /dev/null
+//===--- Registry.h - Matcher registry -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Registry of all known matchers.
+///
+/// The registry provides a generic interface to construct any matcher by name.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_REGISTRY_H
+#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_REGISTRY_H
+
+#include "clang/ASTMatchers/Dynamic/Diagnostics.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+class Registry {
+public:
+ /// \brief Construct a matcher from the registry by name.
+ ///
+ /// Consult the registry of known matchers and construct the appropriate
+ /// matcher by name.
+ ///
+ /// \param MatcherName The name of the matcher to instantiate.
+ ///
+ /// \param NameRange The location of the name in the matcher source.
+ /// Useful for error reporting.
+ ///
+ /// \param Args The argument list for the matcher. The number and types of the
+ /// values must be valid for the matcher requested. Otherwise, the function
+ /// will return an error.
+ ///
+ /// \return The matcher if no error was found. NULL if the matcher is not
+ // found, or if the number of arguments or argument types do not
+ /// match the signature. In that case \c Error will contain the description
+ /// of the error.
+ static DynTypedMatcher *constructMatcher(StringRef MatcherName,
+ const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error);
+
+private:
+ Registry() LLVM_DELETED_FUNCTION;
+};
+
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_REGISTRY_H
--- /dev/null
+//===--- VariantValue.h - Polymorphic value type -*- C++ -*-===/
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Polymorphic value type.
+///
+/// Supports all the types required for dynamic Matcher construction.
+/// Used by the registry to construct matchers in a generic way.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H
+#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/type_traits.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+typedef ast_matchers::internal::DynTypedMatcher DynTypedMatcher;
+
+/// \brief Variant value class.
+///
+/// Basically, a tagged union with value type semantics.
+/// It is used by the registry as the return value and argument type for the
+/// matcher factory methods.
+/// It can be constructed from any of the supported types. It supports
+/// copy/assignment.
+///
+/// Supported types:
+/// - \c std::string
+/// - \c DynTypedMatcher, and any \c Matcher<T>
+class VariantValue {
+public:
+ VariantValue() : Type(VT_Nothing) {}
+
+ VariantValue(const VariantValue &Other);
+ ~VariantValue();
+ VariantValue &operator=(const VariantValue &Other);
+
+ /// \brief Specific constructors for each supported type.
+ VariantValue(const std::string &String);
+ VariantValue(const DynTypedMatcher &Matcher);
+
+ /// \brief String value functions.
+ bool isString() const;
+ const std::string &getString() const;
+ void setString(const std::string &String);
+
+ /// \brief Matcher value functions.
+ bool isMatcher() const;
+ const DynTypedMatcher &getMatcher() const;
+ void setMatcher(const DynTypedMatcher &Matcher);
+ /// \brief Set the value to be \c Matcher by taking ownership of the object.
+ void takeMatcher(DynTypedMatcher *Matcher);
+
+ /// \brief Specialized Matcher<T> is/get functions.
+ template <class T>
+ bool isTypedMatcher() const {
+ // TODO: Add some logic to test if T is actually valid for the underlying
+ // type of the matcher.
+ return isMatcher();
+ }
+
+ template <class T>
+ ast_matchers::internal::Matcher<T> getTypedMatcher() const {
+ return ast_matchers::internal::makeMatcher(
+ new DerivedTypeMatcher<T>(getMatcher()));
+ }
+
+private:
+ void reset();
+
+ /// \brief Matcher bridge between a Matcher<T> and a generic DynTypedMatcher.
+ template <class T>
+ class DerivedTypeMatcher :
+ public ast_matchers::internal::MatcherInterface<T> {
+ public:
+ explicit DerivedTypeMatcher(const DynTypedMatcher &DynMatcher)
+ : DynMatcher(DynMatcher.clone()) {}
+ virtual ~DerivedTypeMatcher() {}
+
+ typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder;
+ typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
+ bool matches(const T &Node, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return DynMatcher->matches(ast_type_traits::DynTypedNode::create(Node),
+ Finder, Builder);
+ }
+
+ private:
+ const OwningPtr<DynTypedMatcher> DynMatcher;
+ };
+
+ /// \brief All supported value types.
+ enum ValueType {
+ VT_Nothing,
+ VT_String,
+ VT_Matcher
+ };
+
+ /// \brief All supported value types.
+ union AllValues {
+ std::string *String;
+ DynTypedMatcher *Matcher;
+ };
+
+ ValueType Type;
+ AllValues Value;
+};
+
+} // end namespace dynamic
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H
new TypeLocMatcher(NodeMatch), Action));
}
+bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch,
+ MatchCallback *Action) {
+ MatcherCallbackPairs.push_back(std::make_pair(NodeMatch.clone(), Action));
+ // TODO: Do runtime type checking to make sure the matcher is one of the valid
+ // top-level matchers.
+ return true;
+}
+
ASTConsumer *MatchFinder::newASTConsumer() {
return new internal::MatchASTConsumer(&MatcherCallbackPairs, ParsingDone);
}
+add_subdirectory(Dynamic)
+
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangASTMatchers
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangDynamicASTMatchers
+ Diagnostics.cpp
+ VariantValue.cpp
+ Parser.cpp
+ Registry.cpp
+ )
+
+add_dependencies(clangDynamicASTMatchers
+ clangASTMatchers
+ )
--- /dev/null
+//===--- Diagnostics.cpp - Helper class for error diagnostics -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/Dynamic/Diagnostics.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+Diagnostics::ArgStream &
+Diagnostics::ArgStream::operator<<(const Twine &Arg) {
+ Out->push_back(Arg.str());
+ return *this;
+}
+
+Diagnostics::ArgStream Diagnostics::pushErrorFrame(const SourceRange &Range,
+ ErrorType Error) {
+ Frames.insert(Frames.begin(), ErrorFrame());
+ ErrorFrame &Last = Frames.front();
+ Last.Range = Range;
+ Last.Type = Error;
+ ArgStream Out = { &Last.Args };
+ return Out;
+}
+
+StringRef ErrorTypeToString(Diagnostics::ErrorType Type) {
+ switch (Type) {
+ case Diagnostics::ET_RegistryNotFound:
+ return "Matcher not found: $0";
+ case Diagnostics::ET_RegistryWrongArgCount:
+ return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
+ case Diagnostics::ET_RegistryWrongArgType:
+ return "Incorrect type on function $0 for arg $1.";
+
+ case Diagnostics::ET_ParserStringError:
+ return "Error parsing string token: <$0>";
+ case Diagnostics::ET_ParserMatcherArgFailure:
+ return "Error parsing argument $0 for matcher $1.";
+ case Diagnostics::ET_ParserMatcherFailure:
+ return "Error building matcher $0.";
+ case Diagnostics::ET_ParserNoOpenParen:
+ return "Error parsing matcher. Found token <$0> while looking for '('.";
+ case Diagnostics::ET_ParserNoCloseParen:
+ return "Error parsing matcher. Found end-of-code while looking for ')'.";
+ case Diagnostics::ET_ParserNoComma:
+ return "Error parsing matcher. Found token <$0> while looking for ','.";
+ case Diagnostics::ET_ParserNoCode:
+ return "End of code found while looking for token.";
+ case Diagnostics::ET_ParserNotAMatcher:
+ return "Input value is not a matcher expression.";
+ case Diagnostics::ET_ParserInvalidToken:
+ return "Invalid token <$0> found when looking for a value.";
+
+ case Diagnostics::ET_None:
+ return "<N/A>";
+ }
+ llvm_unreachable("Unknown ErrorType value.");
+}
+
+std::string FormatErrorString(StringRef FormatString,
+ ArrayRef<std::string> Args) {
+ std::string Out;
+ while (!FormatString.empty()) {
+ std::pair<StringRef, StringRef> Pieces = FormatString.split("$");
+ Out += Pieces.first.str();
+ if (Pieces.second.empty()) break;
+
+ const char Next = Pieces.second.front();
+ FormatString = Pieces.second.drop_front();
+ if (Next >= '0' && Next <= '9') {
+ const unsigned Index = Next - '0';
+ if (Index < Args.size()) {
+ Out += Args[Index];
+ } else {
+ Out += "<Argument_Not_Provided>";
+ }
+ }
+ }
+ return Out;
+}
+
+std::string Diagnostics::ErrorFrame::ToString() const {
+ StringRef FormatString = ErrorTypeToString(Type);
+ std::string ErrorOut = FormatErrorString(FormatString, Args);
+ if (Range.Start.Line > 0 && Range.Start.Column > 0)
+ return (Twine(Range.Start.Line) + ":" + Twine(Range.Start.Column) + ": " +
+ ErrorOut).str();
+ return ErrorOut;
+}
+
+std::string Diagnostics::ToString() const {
+ if (Frames.empty()) return "";
+ return Frames[Frames.size() - 1].ToString();
+}
+
+std::string Diagnostics::ToStringFull() const {
+ std::string Result;
+ for (size_t i = 0, end = Frames.size(); i != end; ++i) {
+ if (i > 0) Result += "\n";
+ Result += Frames[i].ToString();
+ }
+ return Result;
+}
+
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
--- /dev/null
+##===- clang/lib/ASTMatchers/Dynamic/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 := clangDynamicASTMatchers
+
+include $(CLANG_LEVEL)/Makefile
--- /dev/null
+//===--- Marshallers.h - Generic matcher function marshallers -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Functions templates and classes to wrap matcher construct functions.
+///
+/// A collection of template function and classes that provide a generic
+/// marshalling layer on top of matcher construct functions.
+/// These are used by the registry to export all marshaller constructors with
+/// the same generic interface.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_MARSHALLERS_H
+#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_MARSHALLERS_H
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/Dynamic/Diagnostics.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/Support/type_traits.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+namespace internal {
+
+/// \brief Helper template class to just from argument type to the right is/get
+/// functions in VariantValue.
+/// Used to verify and extract the matcher arguments below.
+template <class T> struct ArgTypeTraits;
+template <class T> struct ArgTypeTraits<const T &> : public ArgTypeTraits<T> {
+};
+
+template <> struct ArgTypeTraits<std::string> {
+ static bool is(const VariantValue &Value) { return Value.isString(); }
+ static const std::string &get(const VariantValue &Value) {
+ return Value.getString();
+ }
+};
+
+template <class T> struct ArgTypeTraits<ast_matchers::internal::Matcher<T> > {
+ static bool is(const VariantValue &Value) { return Value.isMatcher(); }
+ static ast_matchers::internal::Matcher<T> get(const VariantValue &Value) {
+ return Value.getTypedMatcher<T>();
+ }
+
+};
+
+/// \brief Generic MatcherCreate interface.
+///
+/// Provides a \c run() method that constructs the matcher from the provided
+/// arguments.
+class MatcherCreateCallback {
+public:
+ virtual ~MatcherCreateCallback() {}
+ virtual DynTypedMatcher *run(const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error) const = 0;
+};
+
+/// \brief Simple callback implementation. Marshaller and function are provided.
+///
+/// \param Marshaller Function to unpack the arguments and call \c Func
+/// \param Func Matcher construct function. This is the function that
+/// compile-time matcher expressions would use to create the matcher.
+template <typename MarshallerType, typename FuncType>
+class FixedArgCountMatcherCreateCallback : public MatcherCreateCallback {
+public:
+ FixedArgCountMatcherCreateCallback(MarshallerType Marshaller, FuncType Func,
+ StringRef MatcherName)
+ : Marshaller(Marshaller), Func(Func), MatcherName(MatcherName.str()) {}
+
+ DynTypedMatcher *run(const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args, Diagnostics *Error) const {
+ return Marshaller(Func, MatcherName, NameRange, Args, Error);
+ }
+
+private:
+ const MarshallerType Marshaller;
+ const FuncType Func;
+ const std::string MatcherName;
+};
+
+/// \brief Helper function to do template argument deduction.
+template <typename MarshallerType, typename FuncType>
+MatcherCreateCallback *
+createMarshallerCallback(MarshallerType Marshaller, FuncType Func,
+ StringRef MatcherName) {
+ return new FixedArgCountMatcherCreateCallback<MarshallerType, FuncType>(
+ Marshaller, Func, MatcherName);
+}
+
+/// \brief Helper macros to check the arguments on all marshaller functions.
+#define CHECK_ARG_COUNT(count) \
+ if (Args.size() != count) { \
+ Error->pushErrorFrame(NameRange, Error->ET_RegistryWrongArgCount) \
+ << count << Args.size(); \
+ return NULL; \
+ }
+
+#define CHECK_ARG_TYPE(index, type) \
+ if (!ArgTypeTraits<type>::is(Args[index].Value)) { \
+ Error->pushErrorFrame(Args[index].Range, Error->ET_RegistryWrongArgType) \
+ << MatcherName << (index + 1); \
+ return NULL; \
+ }
+
+/// \brief Metafunction to normalize argument types.
+///
+/// We need to remove the const& out of the function parameters to be able to
+/// find values on VariantValue.
+template <typename T>
+struct remove_const_ref :
+ public llvm::remove_const<typename llvm::remove_reference<T>::type> {
+};
+
+/// \brief 0-arg marshaller function.
+template <typename ReturnType>
+DynTypedMatcher *matcherMarshall0(ReturnType (*Func)(), StringRef MatcherName,
+ const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error) {
+ CHECK_ARG_COUNT(0);
+ return Func().clone();
+}
+
+/// \brief 1-arg marshaller function.
+template <typename ReturnType, typename InArgType1>
+DynTypedMatcher *matcherMarshall1(ReturnType (*Func)(InArgType1),
+ StringRef MatcherName,
+ const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error) {
+ typedef typename remove_const_ref<InArgType1>::type ArgType1;
+ CHECK_ARG_COUNT(1);
+ CHECK_ARG_TYPE(0, ArgType1);
+ return Func(ArgTypeTraits<ArgType1>::get(Args[0].Value)).clone();
+}
+
+/// \brief Variadic marshaller function.
+template <typename BaseType, typename DerivedType>
+class VariadicMatcherCreateCallback : public MatcherCreateCallback {
+public:
+ explicit VariadicMatcherCreateCallback(StringRef MatcherName)
+ : MatcherName(MatcherName.str()) {}
+
+ typedef ast_matchers::internal::Matcher<DerivedType> DerivedMatcherType;
+
+ DynTypedMatcher *run(const SourceRange &NameRange, ArrayRef<ParserValue> Args,
+ Diagnostics *Error) const {
+ std::list<DerivedMatcherType> References;
+ std::vector<const DerivedMatcherType *> InnerArgs(Args.size());
+ for (size_t i = 0, e = Args.size(); i != e; ++i) {
+ CHECK_ARG_TYPE(i, DerivedMatcherType);
+ References.push_back(
+ ArgTypeTraits<DerivedMatcherType>::get(Args[i].Value));
+ InnerArgs[i] = &References.back();
+ }
+ return ast_matchers::internal::makeDynCastAllOfComposite<BaseType>(
+ ArrayRef<const DerivedMatcherType *>(InnerArgs)).clone();
+ }
+
+private:
+ const std::string MatcherName;
+};
+
+#undef CHECK_ARG_COUNT
+#undef CHECK_ARG_TYPE
+
+/// Helper functions to select the appropriate marshaller functions.
+/// They detects the number of arguments, arguments types and return type.
+
+/// \brief 0-arg overload
+template <typename ReturnType>
+MatcherCreateCallback *makeMatcherAutoMarshall(ReturnType (*Func)(),
+ StringRef MatcherName) {
+ return createMarshallerCallback(matcherMarshall0<ReturnType>, Func,
+ MatcherName);
+}
+
+/// \brief 1-arg overload
+template <typename ReturnType, typename ArgType1>
+MatcherCreateCallback *makeMatcherAutoMarshall(ReturnType (*Func)(ArgType1),
+ StringRef MatcherName) {
+ return createMarshallerCallback(matcherMarshall1<ReturnType, ArgType1>, Func,
+ MatcherName);
+}
+
+/// \brief Variadic overload.
+template <typename MatcherType>
+MatcherCreateCallback *makeMatcherAutoMarshall(
+ ast_matchers::internal::VariadicAllOfMatcher<MatcherType> Func,
+ StringRef MatcherName) {
+ return new VariadicMatcherCreateCallback<MatcherType, MatcherType>(
+ MatcherName);
+}
+
+template <typename BaseType, typename MatcherType>
+MatcherCreateCallback *
+makeMatcherAutoMarshall(ast_matchers::internal::VariadicDynCastAllOfMatcher<
+ BaseType, MatcherType> Func,
+ StringRef MatcherName) {
+ return new VariadicMatcherCreateCallback<BaseType, MatcherType>(MatcherName);
+}
+
+} // namespace internal
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_MARSHALLERS_H
--- /dev/null
+//===--- Parser.cpp - Matcher expression parser -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Recursive parser implementation for the matcher expression grammar.
+///
+//===----------------------------------------------------------------------===//
+
+#include <string>
+#include <vector>
+
+#include "clang/ASTMatchers/Dynamic/Parser.h"
+#include "clang/ASTMatchers/Dynamic/Registry.h"
+#include "clang/Basic/CharInfo.h"
+#include "llvm/ADT/Twine.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+/// \brief Simple structure to hold information for one token from the parser.
+struct Parser::TokenInfo {
+ /// \brief Different possible tokens.
+ enum TokenKind {
+ TK_Eof = 0,
+ TK_OpenParen = 1,
+ TK_CloseParen = 2,
+ TK_Comma = 3,
+ TK_Literal = 4,
+ TK_Ident = 5,
+ TK_InvalidChar = 6,
+ TK_Error = 7
+ };
+
+ TokenInfo() : Text(), Kind(TK_Eof), Range(), Value() {}
+
+ StringRef Text;
+ TokenKind Kind;
+ SourceRange Range;
+ VariantValue Value;
+};
+
+/// \brief Simple tokenizer for the parser.
+class Parser::CodeTokenizer {
+public:
+ explicit CodeTokenizer(StringRef MatcherCode, Diagnostics *Error)
+ : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error) {
+ NextToken = getNextToken();
+ }
+
+ /// \brief Returns but doesn't consume the next token.
+ const TokenInfo &peekNextToken() const { return NextToken; }
+
+ /// \brief Consumes and returns the next token.
+ TokenInfo consumeNextToken() {
+ TokenInfo ThisToken = NextToken;
+ NextToken = getNextToken();
+ return ThisToken;
+ }
+
+ TokenInfo::TokenKind nextTokenKind() const { return NextToken.Kind; }
+
+private:
+ TokenInfo getNextToken() {
+ consumeWhitespace();
+ TokenInfo Result;
+ Result.Range.Start = currentLocation();
+
+ if (Code.empty()) {
+ Result.Kind = TokenInfo::TK_Eof;
+ Result.Text = "";
+ return Result;
+ }
+
+ switch (Code[0]) {
+ case ',':
+ Result.Kind = TokenInfo::TK_Comma;
+ Result.Text = Code.substr(0, 1);
+ Code = Code.drop_front();
+ break;
+ case '(':
+ Result.Kind = TokenInfo::TK_OpenParen;
+ Result.Text = Code.substr(0, 1);
+ Code = Code.drop_front();
+ break;
+ case ')':
+ Result.Kind = TokenInfo::TK_CloseParen;
+ Result.Text = Code.substr(0, 1);
+ Code = Code.drop_front();
+ break;
+
+ case '"':
+ case '\'':
+ // Parse a string literal.
+ consumeStringLiteral(&Result);
+ break;
+
+ default:
+ if (isAlphanumeric(Code[0])) {
+ // Parse an identifier
+ size_t TokenLength = 1;
+ while (TokenLength < Code.size() && isAlphanumeric(Code[TokenLength]))
+ ++TokenLength;
+ Result.Kind = TokenInfo::TK_Ident;
+ Result.Text = Code.substr(0, TokenLength);
+ Code = Code.drop_front(TokenLength);
+ } else {
+ Result.Kind = TokenInfo::TK_InvalidChar;
+ Result.Text = Code.substr(0, 1);
+ Code = Code.drop_front(1);
+ }
+ break;
+ }
+
+ Result.Range.End = currentLocation();
+ return Result;
+ }
+
+ /// \brief Consume a string literal.
+ ///
+ /// \c Code must be positioned at the start of the literal (the opening
+ /// quote). Consumed until it finds the same closing quote character.
+ void consumeStringLiteral(TokenInfo *Result) {
+ bool InEscape = false;
+ const char Marker = Code[0];
+ for (size_t Length = 1, Size = Code.size(); Length != Size; ++Length) {
+ if (InEscape) {
+ InEscape = false;
+ continue;
+ }
+ if (Code[Length] == '\\') {
+ InEscape = true;
+ continue;
+ }
+ if (Code[Length] == Marker) {
+ Result->Kind = TokenInfo::TK_Literal;
+ Result->Text = Code.substr(0, Length + 1);
+ Result->Value = Code.substr(1, Length - 1).str();
+ Code = Code.drop_front(Length + 1);
+ return;
+ }
+ }
+
+ StringRef ErrorText = Code;
+ Code = Code.drop_front(Code.size());
+ SourceRange Range;
+ Range.Start = Result->Range.Start;
+ Range.End = currentLocation();
+ Error->pushErrorFrame(Range, Error->ET_ParserStringError)
+ << ErrorText;
+ Result->Kind = TokenInfo::TK_Error;
+ }
+
+ /// \brief Consume all leading whitespace from \c Code.
+ void consumeWhitespace() {
+ while (!Code.empty() && isWhitespace(Code[0])) {
+ if (Code[0] == '\n') {
+ ++Line;
+ StartOfLine = Code.drop_front();
+ }
+ Code = Code.drop_front();
+ }
+ }
+
+ SourceLocation currentLocation() {
+ SourceLocation Location;
+ Location.Line = Line;
+ Location.Column = Code.data() - StartOfLine.data() + 1;
+ return Location;
+ }
+
+ StringRef Code;
+ StringRef StartOfLine;
+ unsigned Line;
+ Diagnostics *Error;
+ TokenInfo NextToken;
+};
+
+Parser::Sema::~Sema() {}
+
+/// \brief Parse and validate a matcher expression.
+/// \return \c true on success, in which case \c Value has the matcher parsed.
+/// If the input is malformed, or some argument has an error, it
+/// returns \c false.
+bool Parser::parseMatcherExpressionImpl(VariantValue *Value) {
+ const TokenInfo NameToken = Tokenizer->consumeNextToken();
+ assert(NameToken.Kind == TokenInfo::TK_Ident);
+ const TokenInfo OpenToken = Tokenizer->consumeNextToken();
+ if (OpenToken.Kind != TokenInfo::TK_OpenParen) {
+ Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserNoOpenParen)
+ << OpenToken.Text;
+ return false;
+ }
+
+ std::vector<ParserValue> Args;
+ TokenInfo EndToken;
+ while (Tokenizer->nextTokenKind() != TokenInfo::TK_Eof) {
+ if (Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen) {
+ // End of args.
+ EndToken = Tokenizer->consumeNextToken();
+ break;
+ }
+ if (Args.size() > 0) {
+ // We must find a , token to continue.
+ const TokenInfo CommaToken = Tokenizer->consumeNextToken();
+ if (CommaToken.Kind != TokenInfo::TK_Comma) {
+ Error->pushErrorFrame(CommaToken.Range, Error->ET_ParserNoComma)
+ << CommaToken.Text;
+ return false;
+ }
+ }
+
+ ParserValue ArgValue;
+ ArgValue.Text = Tokenizer->peekNextToken().Text;
+ ArgValue.Range = Tokenizer->peekNextToken().Range;
+ if (!parseExpressionImpl(&ArgValue.Value)) {
+ Error->pushErrorFrame(NameToken.Range,
+ Error->ET_ParserMatcherArgFailure)
+ << (Args.size() + 1) << NameToken.Text;
+ return false;
+ }
+
+ Args.push_back(ArgValue);
+ }
+
+ if (EndToken.Kind == TokenInfo::TK_Eof) {
+ Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserNoCloseParen);
+ return false;
+ }
+
+ // Merge the start and end infos.
+ SourceRange MatcherRange = NameToken.Range;
+ MatcherRange.End = EndToken.Range.End;
+ DynTypedMatcher *Result =
+ S->actOnMatcherExpression(NameToken.Text, MatcherRange, Args, Error);
+ if (Result == NULL) {
+ Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure)
+ << NameToken.Text;
+ return false;
+ }
+
+ Value->takeMatcher(Result);
+ return true;
+}
+
+/// \brief Parse an <Expresssion>
+bool Parser::parseExpressionImpl(VariantValue *Value) {
+ switch (Tokenizer->nextTokenKind()) {
+ case TokenInfo::TK_Literal:
+ *Value = Tokenizer->consumeNextToken().Value;
+ return true;
+
+ case TokenInfo::TK_Ident:
+ return parseMatcherExpressionImpl(Value);
+
+ case TokenInfo::TK_Eof:
+ Error->pushErrorFrame(Tokenizer->consumeNextToken().Range,
+ Error->ET_ParserNoCode);
+ return false;
+
+ case TokenInfo::TK_Error:
+ // This error was already reported by the tokenizer.
+ return false;
+
+ case TokenInfo::TK_OpenParen:
+ case TokenInfo::TK_CloseParen:
+ case TokenInfo::TK_Comma:
+ case TokenInfo::TK_InvalidChar:
+ const TokenInfo Token = Tokenizer->consumeNextToken();
+ Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken)
+ << Token.Text;
+ return false;
+ }
+
+ llvm_unreachable("Unknown token kind.");
+}
+
+Parser::Parser(CodeTokenizer *Tokenizer, Sema *S,
+ Diagnostics *Error)
+ : Tokenizer(Tokenizer), S(S), Error(Error) {}
+
+class RegistrySema : public Parser::Sema {
+public:
+ virtual ~RegistrySema() {}
+ DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
+ const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error) {
+ return Registry::constructMatcher(MatcherName, NameRange, Args, Error);
+ }
+};
+
+bool Parser::parseExpression(StringRef Code, VariantValue *Value,
+ Diagnostics *Error) {
+ RegistrySema S;
+ return parseExpression(Code, &S, Value, Error);
+}
+
+bool Parser::parseExpression(StringRef Code, Sema *S,
+ VariantValue *Value, Diagnostics *Error) {
+ CodeTokenizer Tokenizer(Code, Error);
+ return Parser(&Tokenizer, S, Error).parseExpressionImpl(Value);
+}
+
+DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code,
+ Diagnostics *Error) {
+ RegistrySema S;
+ return parseMatcherExpression(Code, &S, Error);
+}
+
+DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code,
+ Parser::Sema *S,
+ Diagnostics *Error) {
+ VariantValue Value;
+ if (!parseExpression(Code, S, &Value, Error))
+ return NULL;
+ if (!Value.isMatcher()) {
+ Error->pushErrorFrame(SourceRange(), Error->ET_ParserNotAMatcher);
+ return NULL;
+ }
+ return Value.getMatcher().clone();
+}
+
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
--- /dev/null
+//===--- Registry.cpp - Matcher registry ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Registry map populated at static initialization time.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/Dynamic/Registry.h"
+
+#include <utility>
+
+#include "Marshallers.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ManagedStatic.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+namespace {
+
+using internal::MatcherCreateCallback;
+
+typedef llvm::StringMap<const MatcherCreateCallback *> ConstructorMap;
+class RegistryMaps {
+public:
+ RegistryMaps();
+ ~RegistryMaps();
+
+ const ConstructorMap &constructors() const { return Constructors; }
+
+private:
+ void registerMatcher(StringRef MatcherName, MatcherCreateCallback *Callback);
+ ConstructorMap Constructors;
+};
+
+void RegistryMaps::registerMatcher(StringRef MatcherName,
+ MatcherCreateCallback *Callback) {
+ Constructors[MatcherName] = Callback;
+}
+
+#define REGISTER_MATCHER(name) \
+ registerMatcher(#name, internal::makeMatcherAutoMarshall( \
+ ::clang::ast_matchers::name, #name));
+
+/// \brief Generate a registry map with all the known matchers.
+RegistryMaps::RegistryMaps() {
+ // TODO: This list is not complete. It only has non-overloaded matchers,
+ // which are the simplest to add to the system. Overloaded matchers require
+ // more supporting code that was omitted from the first revision for
+ // simplicitly of code review.
+
+ REGISTER_MATCHER(binaryOperator);
+ REGISTER_MATCHER(bindTemporaryExpr);
+ REGISTER_MATCHER(boolLiteral);
+ REGISTER_MATCHER(callExpr);
+ REGISTER_MATCHER(characterLiteral);
+ REGISTER_MATCHER(compoundStmt);
+ REGISTER_MATCHER(conditionalOperator);
+ REGISTER_MATCHER(constCastExpr);
+ REGISTER_MATCHER(constructExpr);
+ REGISTER_MATCHER(constructorDecl);
+ REGISTER_MATCHER(declRefExpr);
+ REGISTER_MATCHER(declStmt);
+ REGISTER_MATCHER(defaultArgExpr);
+ REGISTER_MATCHER(doStmt);
+ REGISTER_MATCHER(dynamicCastExpr);
+ REGISTER_MATCHER(explicitCastExpr);
+ REGISTER_MATCHER(expr);
+ REGISTER_MATCHER(fieldDecl);
+ REGISTER_MATCHER(forStmt);
+ REGISTER_MATCHER(functionDecl);
+ REGISTER_MATCHER(hasAnyParameter);
+ REGISTER_MATCHER(hasAnySubstatement);
+ REGISTER_MATCHER(hasConditionVariableStatement);
+ REGISTER_MATCHER(hasDestinationType);
+ REGISTER_MATCHER(hasEitherOperand);
+ REGISTER_MATCHER(hasFalseExpression);
+ REGISTER_MATCHER(hasImplicitDestinationType);
+ REGISTER_MATCHER(hasInitializer);
+ REGISTER_MATCHER(hasLHS);
+ REGISTER_MATCHER(hasName);
+ REGISTER_MATCHER(hasObjectExpression);
+ REGISTER_MATCHER(hasRHS);
+ REGISTER_MATCHER(hasSourceExpression);
+ REGISTER_MATCHER(hasTrueExpression);
+ REGISTER_MATCHER(hasUnaryOperand);
+ REGISTER_MATCHER(ifStmt);
+ REGISTER_MATCHER(implicitCastExpr);
+ REGISTER_MATCHER(integerLiteral);
+ REGISTER_MATCHER(isArrow);
+ REGISTER_MATCHER(isConstQualified);
+ REGISTER_MATCHER(isImplicit);
+ REGISTER_MATCHER(member);
+ REGISTER_MATCHER(memberExpr);
+ REGISTER_MATCHER(methodDecl);
+ REGISTER_MATCHER(namedDecl);
+ REGISTER_MATCHER(newExpr);
+ REGISTER_MATCHER(ofClass);
+ REGISTER_MATCHER(on);
+ REGISTER_MATCHER(onImplicitObjectArgument);
+ REGISTER_MATCHER(operatorCallExpr);
+ REGISTER_MATCHER(recordDecl);
+ REGISTER_MATCHER(reinterpretCastExpr);
+ REGISTER_MATCHER(staticCastExpr);
+ REGISTER_MATCHER(stmt);
+ REGISTER_MATCHER(stringLiteral);
+ REGISTER_MATCHER(switchCase);
+ REGISTER_MATCHER(to);
+ REGISTER_MATCHER(unaryOperator);
+ REGISTER_MATCHER(varDecl);
+ REGISTER_MATCHER(whileStmt);
+}
+
+RegistryMaps::~RegistryMaps() {
+ for (ConstructorMap::iterator it = Constructors.begin(),
+ end = Constructors.end();
+ it != end; ++it) {
+ delete it->second;
+ }
+}
+
+static llvm::ManagedStatic<RegistryMaps> RegistryData;
+
+} // anonymous namespace
+
+// static
+DynTypedMatcher *Registry::constructMatcher(StringRef MatcherName,
+ const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error) {
+ ConstructorMap::const_iterator it =
+ RegistryData->constructors().find(MatcherName);
+ if (it == RegistryData->constructors().end()) {
+ Error->pushErrorFrame(NameRange, Error->ET_RegistryNotFound)
+ << MatcherName;
+ return NULL;
+ }
+
+ return it->second->run(NameRange, Args, Error);
+}
+
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
--- /dev/null
+//===--- VariantValue.cpp - Polymorphic value type -*- C++ -*-===/
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Polymorphic value type.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+
+VariantValue::VariantValue(const VariantValue &Other) : Type(VT_Nothing) {
+ *this = Other;
+}
+
+VariantValue::VariantValue(const DynTypedMatcher &Matcher) : Type(VT_Nothing) {
+ setMatcher(Matcher);
+}
+
+VariantValue::VariantValue(const std::string &String) : Type(VT_Nothing) {
+ setString(String);
+}
+
+VariantValue::~VariantValue() { reset(); }
+
+VariantValue &VariantValue::operator=(const VariantValue &Other) {
+ if (this == &Other) return *this;
+ reset();
+ switch (Other.Type) {
+ case VT_String:
+ setString(Other.getString());
+ break;
+ case VT_Matcher:
+ setMatcher(Other.getMatcher());
+ break;
+ case VT_Nothing:
+ Type = VT_Nothing;
+ break;
+ }
+ return *this;
+}
+
+void VariantValue::reset() {
+ switch (Type) {
+ case VT_String:
+ delete Value.String;
+ break;
+ case VT_Matcher:
+ delete Value.Matcher;
+ break;
+ // Cases that do nothing.
+ case VT_Nothing:
+ break;
+ }
+ Type = VT_Nothing;
+}
+
+bool VariantValue::isString() const {
+ return Type == VT_String;
+}
+
+const std::string &VariantValue::getString() const {
+ assert(isString());
+ return *Value.String;
+}
+
+void VariantValue::setString(const std::string &NewValue) {
+ reset();
+ Type = VT_String;
+ Value.String = new std::string(NewValue);
+}
+
+bool VariantValue::isMatcher() const {
+ return Type == VT_Matcher;
+}
+
+const DynTypedMatcher &VariantValue::getMatcher() const {
+ assert(isMatcher());
+ return *Value.Matcher;
+}
+
+void VariantValue::setMatcher(const DynTypedMatcher &NewValue) {
+ reset();
+ Type = VT_Matcher;
+ Value.Matcher = NewValue.clone();
+}
+
+void VariantValue::takeMatcher(DynTypedMatcher *NewValue) {
+ reset();
+ Type = VT_Matcher;
+ Value.Matcher = NewValue;
+}
+
+} // end namespace dynamic
+} // end namespace ast_matchers
+} // end namespace clang
CLANG_LEVEL := ../..
LIBRARYNAME := clangASTMatchers
+PARALLEL_DIRS = Dynamic
+
include $(CLANG_LEVEL)/Makefile
return matchesConditionally(Code, AMatcher, false, "-std=c++11");
}
+inline testing::AssertionResult
+matchesConditionallyDynamic(const std::string &Code,
+ const internal::DynTypedMatcher &AMatcher,
+ bool ExpectMatch, llvm::StringRef CompileArg) {
+ bool Found = false;
+ MatchFinder Finder;
+ Finder.addDynamicMatcher(AMatcher, new VerifyMatch(0, &Found));
+ OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ // Some tests use typeof, which is a gnu extension.
+ std::vector<std::string> Args(1, CompileArg);
+ if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) {
+ 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();
+}
+
+inline testing::AssertionResult
+matchesDynamic(const std::string &Code,
+ const internal::DynTypedMatcher &AMatcher) {
+ return matchesConditionallyDynamic(Code, AMatcher, true, "-std=c++11");
+}
+
+inline testing::AssertionResult
+notMatchesDynamic(const std::string &Code,
+ const internal::DynTypedMatcher &AMatcher) {
+ return matchesConditionallyDynamic(Code, AMatcher, false, "-std=c++11");
+}
+
template <typename T>
testing::AssertionResult
matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher,
target_link_libraries(ASTMatchersTests
gtest gtest_main clangASTMatchers clangTooling)
+
+add_subdirectory(Dynamic)
--- /dev/null
+add_clang_unittest(DynamicASTMatchersTests
+ VariantValueTest.cpp
+ ParserTest.cpp
+ RegistryTest.cpp)
+
+target_link_libraries(DynamicASTMatchersTests
+ gtest gtest_main clangASTMatchers clangDynamicASTMatchers clangTooling)
--- /dev/null
+##===- unittests/ASTMatchers/Dynamic/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 = ../../..
+TESTNAME = DynamicASTMatchers
+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 \
+ clangDynamicASTMatchers.a
+
+include $(CLANG_LEVEL)/unittests/Makefile
--- /dev/null
+//===- unittest/ASTMatchers/Dynamic/ParserTest.cpp - Parser unit tests -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-------------------------------------------------------------------===//
+
+#include <string>
+#include <vector>
+
+#include "../ASTMatchersTest.h"
+#include "clang/ASTMatchers/Dynamic/Parser.h"
+#include "clang/ASTMatchers/Dynamic/Registry.h"
+#include "gtest/gtest.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+namespace {
+
+class DummyDynTypedMatcher : public DynTypedMatcher {
+public:
+ DummyDynTypedMatcher(uint64_t ID) : ID(ID) {}
+
+ typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder;
+ typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
+ virtual bool matches(const ast_type_traits::DynTypedNode DynNode,
+ ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return false;
+ }
+
+ /// \brief Makes a copy of this matcher object.
+ virtual DynTypedMatcher *clone() const {
+ return new DummyDynTypedMatcher(ID);
+ }
+
+ /// \brief Returns a unique ID for the matcher.
+ virtual uint64_t getID() const { return ID; }
+
+private:
+ uint64_t ID;
+};
+
+class MockSema : public Parser::Sema {
+public:
+ virtual ~MockSema() {}
+
+ uint64_t expectMatcher(StringRef MatcherName) {
+ uint64_t ID = ExpectedMatchers.size() + 1;
+ ExpectedMatchers[MatcherName] = ID;
+ return ID;
+ }
+
+ void parse(StringRef Code) {
+ Diagnostics Error;
+ VariantValue Value;
+ Parser::parseExpression(Code, this, &Value, &Error);
+ Values.push_back(Value);
+ Errors.push_back(Error.ToStringFull());
+ }
+
+ DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
+ const SourceRange &NameRange,
+ ArrayRef<ParserValue> Args,
+ Diagnostics *Error) {
+ MatcherInfo ToStore = { MatcherName, NameRange, Args };
+ Matchers.push_back(ToStore);
+ return new DummyDynTypedMatcher(ExpectedMatchers[MatcherName]);
+ }
+
+ struct MatcherInfo {
+ StringRef MatcherName;
+ SourceRange NameRange;
+ std::vector<ParserValue> Args;
+ };
+
+ std::vector<std::string> Errors;
+ std::vector<VariantValue> Values;
+ std::vector<MatcherInfo> Matchers;
+ llvm::StringMap<uint64_t> ExpectedMatchers;
+};
+
+TEST(ParserTest, ParseString) {
+ MockSema Sema;
+ Sema.parse("\"Foo\"");
+ Sema.parse("\"\"");
+ Sema.parse("\"Baz");
+ EXPECT_EQ(3ULL, Sema.Values.size());
+ EXPECT_EQ("Foo", Sema.Values[0].getString());
+ EXPECT_EQ("", Sema.Values[1].getString());
+ EXPECT_EQ("1:1: Error parsing string token: <\"Baz>", Sema.Errors[2]);
+}
+
+bool matchesRange(const SourceRange &Range, unsigned StartLine,
+ unsigned EndLine, unsigned StartColumn, unsigned EndColumn) {
+ EXPECT_EQ(StartLine, Range.Start.Line);
+ EXPECT_EQ(EndLine, Range.End.Line);
+ EXPECT_EQ(StartColumn, Range.Start.Column);
+ EXPECT_EQ(EndColumn, Range.End.Column);
+ return Range.Start.Line == StartLine && Range.End.Line == EndLine &&
+ Range.Start.Column == StartColumn && Range.End.Column == EndColumn;
+}
+
+TEST(ParserTest, ParseMatcher) {
+ MockSema Sema;
+ const uint64_t ExpectedFoo = Sema.expectMatcher("Foo");
+ const uint64_t ExpectedBar = Sema.expectMatcher("Bar");
+ const uint64_t ExpectedBaz = Sema.expectMatcher("Baz");
+ Sema.parse(" Foo ( Bar (), Baz( \n \"B A,Z\") ) ");
+ for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) {
+ EXPECT_EQ("", Sema.Errors[i]);
+ }
+
+ EXPECT_EQ(1ULL, Sema.Values.size());
+ EXPECT_EQ(ExpectedFoo, Sema.Values[0].getMatcher().getID());
+
+ EXPECT_EQ(3ULL, Sema.Matchers.size());
+ const MockSema::MatcherInfo Bar = Sema.Matchers[0];
+ EXPECT_EQ("Bar", Bar.MatcherName);
+ EXPECT_TRUE(matchesRange(Bar.NameRange, 1, 1, 8, 14));
+ EXPECT_EQ(0ULL, Bar.Args.size());
+
+ const MockSema::MatcherInfo Baz = Sema.Matchers[1];
+ EXPECT_EQ("Baz", Baz.MatcherName);
+ EXPECT_TRUE(matchesRange(Baz.NameRange, 1, 2, 16, 10));
+ EXPECT_EQ(1ULL, Baz.Args.size());
+ EXPECT_EQ("B A,Z", Baz.Args[0].Value.getString());
+
+ const MockSema::MatcherInfo Foo = Sema.Matchers[2];
+ EXPECT_EQ("Foo", Foo.MatcherName);
+ EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12));
+ EXPECT_EQ(2ULL, Foo.Args.size());
+ EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID());
+ EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID());
+}
+
+using ast_matchers::internal::Matcher;
+
+TEST(ParserTest, FullParserTest) {
+ OwningPtr<DynTypedMatcher> Matcher(Parser::parseMatcherExpression(
+ "hasInitializer(binaryOperator(hasLHS(integerLiteral())))", NULL));
+ EXPECT_TRUE(matchesDynamic("int x = 1 + false;", *Matcher));
+ EXPECT_FALSE(matchesDynamic("int x = true + 1;", *Matcher));
+
+ Diagnostics Error;
+ EXPECT_TRUE(Parser::parseMatcherExpression(
+ "hasInitializer(\n binaryOperator(hasLHS(\"A\")))", &Error) == NULL);
+ EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n"
+ "2:5: Error parsing argument 1 for matcher binaryOperator.\n"
+ "2:20: Error building matcher hasLHS.\n"
+ "2:27: Incorrect type on function hasLHS for arg 1.",
+ Error.ToStringFull());
+}
+
+std::string ParseWithError(StringRef Code) {
+ Diagnostics Error;
+ VariantValue Value;
+ Parser::parseExpression(Code, &Value, &Error);
+ return Error.ToStringFull();
+}
+
+std::string ParseMatcherWithError(StringRef Code) {
+ Diagnostics Error;
+ Parser::parseMatcherExpression(Code, &Error);
+ return Error.ToStringFull();
+}
+
+TEST(ParserTest, Errors) {
+ EXPECT_EQ(
+ "1:5: Error parsing matcher. Found token <123> while looking for '('.",
+ ParseWithError("Foo 123"));
+ EXPECT_EQ(
+ "1:9: Error parsing matcher. Found token <123> while looking for ','.",
+ ParseWithError("Foo(\"A\" 123)"));
+ EXPECT_EQ(
+ "1:4: Error parsing matcher. Found end-of-code while looking for ')'.",
+ ParseWithError("Foo("));
+ EXPECT_EQ("1:1: End of code found while looking for token.",
+ ParseWithError(""));
+ EXPECT_EQ("Input value is not a matcher expression.",
+ ParseMatcherWithError("\"A\""));
+ EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n"
+ "1:5: Invalid token <(> found when looking for a value.",
+ ParseWithError("Foo(("));
+}
+
+} // end anonymous namespace
+} // end namespace dynamic
+} // end namespace ast_matchers
+} // end namespace clang
--- /dev/null
+//===- unittest/ASTMatchers/Dynamic/RegistryTest.cpp - Registry unit tests -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===//
+
+#include <vector>
+
+#include "../ASTMatchersTest.h"
+#include "clang/ASTMatchers/Dynamic/Registry.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+namespace {
+
+using ast_matchers::internal::Matcher;
+
+DynTypedMatcher *constructMatcher(StringRef MatcherName, Diagnostics *Error) {
+ const std::vector<ParserValue> Args;
+ return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
+}
+
+DynTypedMatcher *constructMatcher(StringRef MatcherName,
+ const VariantValue &Arg1,
+ Diagnostics *Error) {
+ std::vector<ParserValue> Args(1);
+ Args[0].Value = Arg1;
+ return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
+}
+
+DynTypedMatcher *constructMatcher(StringRef MatcherName,
+ const VariantValue &Arg1,
+ const VariantValue &Arg2,
+ Diagnostics *Error) {
+ std::vector<ParserValue> Args(2);
+ Args[0].Value = Arg1;
+ Args[1].Value = Arg2;
+ return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error);
+}
+
+TEST(RegistryTest, CanConstructNoArgs) {
+ OwningPtr<DynTypedMatcher> IsArrowValue(constructMatcher("isArrow", NULL));
+ OwningPtr<DynTypedMatcher> BoolValue(constructMatcher("boolLiteral", NULL));
+
+ const std::string ClassSnippet = "struct Foo { int x; };\n"
+ "Foo *foo = new Foo;\n"
+ "int i = foo->x;\n";
+ const std::string BoolSnippet = "bool Foo = true;\n";
+
+ EXPECT_TRUE(matchesDynamic(ClassSnippet, *IsArrowValue));
+ EXPECT_TRUE(matchesDynamic(BoolSnippet, *BoolValue));
+ EXPECT_FALSE(matchesDynamic(ClassSnippet, *BoolValue));
+ EXPECT_FALSE(matchesDynamic(BoolSnippet, *IsArrowValue));
+}
+
+TEST(RegistryTest, ConstructWithSimpleArgs) {
+ OwningPtr<DynTypedMatcher> Value(
+ constructMatcher("hasName", std::string("X"), NULL));
+ EXPECT_TRUE(matchesDynamic("class X {};", *Value));
+ EXPECT_FALSE(matchesDynamic("int x;", *Value));
+}
+
+TEST(RegistryTest, ConstructWithMatcherArgs) {
+ OwningPtr<DynTypedMatcher> HasInitializerSimple(
+ constructMatcher("hasInitializer", stmt(), NULL));
+ OwningPtr<DynTypedMatcher> HasInitializerComplex(
+ constructMatcher("hasInitializer", callExpr(), NULL));
+
+ std::string code = "int i;";
+ EXPECT_FALSE(matchesDynamic(code, *HasInitializerSimple));
+ EXPECT_FALSE(matchesDynamic(code, *HasInitializerComplex));
+
+ code = "int i = 1;";
+ EXPECT_TRUE(matchesDynamic(code, *HasInitializerSimple));
+ EXPECT_FALSE(matchesDynamic(code, *HasInitializerComplex));
+
+ code = "int y(); int i = y();";
+ EXPECT_TRUE(matchesDynamic(code, *HasInitializerSimple));
+ EXPECT_TRUE(matchesDynamic(code, *HasInitializerComplex));
+}
+
+TEST(RegistryTest, Errors) {
+ // Incorrect argument count.
+ OwningPtr<Diagnostics> Error(new Diagnostics());
+ EXPECT_TRUE(NULL == constructMatcher("hasInitializer", Error.get()));
+ EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)",
+ Error->ToString());
+ Error.reset(new Diagnostics());
+ EXPECT_TRUE(NULL == constructMatcher("isArrow", std::string(), Error.get()));
+ EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)",
+ Error->ToString());
+
+ // Bad argument type
+ Error.reset(new Diagnostics());
+ EXPECT_TRUE(NULL == constructMatcher("ofClass", std::string(), Error.get()));
+ EXPECT_EQ("Incorrect type on function ofClass for arg 1.", Error->ToString());
+ Error.reset(new Diagnostics());
+ EXPECT_TRUE(NULL == constructMatcher("recordDecl", recordDecl(),
+ ::std::string(), Error.get()));
+ EXPECT_EQ("Incorrect type on function recordDecl for arg 2.",
+ Error->ToString());
+}
+
+} // end anonymous namespace
+} // end namespace dynamic
+} // end namespace ast_matchers
+} // end namespace clang
--- /dev/null
+//===- unittest/ASTMatchers/Dynamic/VariantValueTest.cpp - VariantValue unit tests -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------------===//
+
+#include "../ASTMatchersTest.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+namespace {
+
+using ast_matchers::internal::DynTypedMatcher;
+using ast_matchers::internal::Matcher;
+
+TEST(VariantValueTest, String) {
+ const ::std::string kString = "string";
+ VariantValue Value = kString;
+
+ EXPECT_TRUE(Value.isString());
+ EXPECT_EQ(kString, Value.getString());
+
+ EXPECT_FALSE(Value.isMatcher());
+ EXPECT_FALSE(Value.isTypedMatcher<clang::Decl>());
+ EXPECT_FALSE(Value.isTypedMatcher<clang::UnaryOperator>());
+}
+
+TEST(VariantValueTest, DynTypedMatcher) {
+ VariantValue Value = stmt();
+
+ EXPECT_FALSE(Value.isString());
+
+ EXPECT_TRUE(Value.isMatcher());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+
+ // Conversion to any type of matcher works.
+ // If they are not compatible it would just return a matcher that matches
+ // nothing. We test this below.
+ Value = recordDecl();
+ EXPECT_TRUE(Value.isMatcher());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+
+ Value = unaryOperator();
+ EXPECT_TRUE(Value.isMatcher());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::Stmt>());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+}
+
+TEST(VariantValueTest, Assignment) {
+ VariantValue Value = std::string("A");
+ EXPECT_TRUE(Value.isString());
+ EXPECT_EQ("A", Value.getString());
+ EXPECT_FALSE(Value.isMatcher());
+
+ Value = recordDecl();
+ EXPECT_FALSE(Value.isString());
+ EXPECT_TRUE(Value.isMatcher());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>());
+ EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>());
+
+ Value = VariantValue();
+ EXPECT_FALSE(Value.isString());
+ EXPECT_FALSE(Value.isMatcher());
+}
+
+TEST(GeneicValueTest, Matcher) {
+ EXPECT_TRUE(matchesDynamic(
+ "class X {};", VariantValue(recordDecl(hasName("X"))).getMatcher()));
+ EXPECT_TRUE(matchesDynamic(
+ "int x;", VariantValue(varDecl()).getTypedMatcher<clang::Decl>()));
+ EXPECT_TRUE(matchesDynamic("int foo() { return 1 + 1; }",
+ VariantValue(functionDecl()).getMatcher()));
+ // Going through the wrong Matcher<T> will fail to match, even if the
+ // underlying matcher is correct.
+ EXPECT_FALSE(matchesDynamic(
+ "int x;", VariantValue(varDecl()).getTypedMatcher<clang::Stmt>()));
+
+ EXPECT_FALSE(
+ matchesDynamic("int x;", VariantValue(functionDecl()).getMatcher()));
+ EXPECT_FALSE(matchesDynamic(
+ "int foo() { return 1 + 1; }",
+ VariantValue(declRefExpr()).getTypedMatcher<clang::DeclRefExpr>()));
+}
+
+} // end anonymous namespace
+} // end namespace dynamic
+} // end namespace ast_matchers
+} // end namespace clang
CLANG_LEVEL = ../..
+PARALLEL_DIRS = Dynamic
+
TESTNAME = ASTMatchers
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc