From f7f295f321fd434e1e542844a71f538a56f2f8fb Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 14 May 2013 09:13:00 +0000 Subject: [PATCH] First revision of the dynamic ASTMatcher library. This library supports all the features of the compile-time based ASTMatcher library, but allows the user to specify and construct the matchers at runtime. It contains the following modules: - A variant type, to be used by the matcher factory. - A registry, where the matchers are indexed by name and have a factory method with a generic signature. - A simple matcher expression parser, that can be used to convert a matcher expression string into actual matchers that can be used with the AST at runtime. Many features where omitted from this first revision to simplify this code review. The main ideas are still represented in this change and it already has support working use cases. Things that are missing: - Support for polymorphic matchers. These requires supporting code in the registry, the marshallers and the variant type. - Support for numbers, char and bool arguments to the matchers. This requires supporting code in the parser and the variant type. - A command line program putting everything together and providing an already functional tool. Patch by Samuel Benzaquen. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181768 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ASTMatchers/ASTMatchFinder.h | 11 + .../clang/ASTMatchers/ASTMatchersInternal.h | 6 + .../clang/ASTMatchers/Dynamic/Diagnostics.h | 109 ++++++ include/clang/ASTMatchers/Dynamic/Parser.h | 143 ++++++++ include/clang/ASTMatchers/Dynamic/Registry.h | 63 ++++ .../clang/ASTMatchers/Dynamic/VariantValue.h | 125 +++++++ lib/ASTMatchers/ASTMatchFinder.cpp | 8 + lib/ASTMatchers/CMakeLists.txt | 2 + lib/ASTMatchers/Dynamic/CMakeLists.txt | 12 + lib/ASTMatchers/Dynamic/Diagnostics.cpp | 113 ++++++ lib/ASTMatchers/Dynamic/Makefile | 13 + lib/ASTMatchers/Dynamic/Marshallers.h | 223 ++++++++++++ lib/ASTMatchers/Dynamic/Parser.cpp | 332 ++++++++++++++++++ lib/ASTMatchers/Dynamic/Registry.cpp | 153 ++++++++ lib/ASTMatchers/Dynamic/VariantValue.cpp | 105 ++++++ lib/ASTMatchers/Makefile | 2 + unittests/ASTMatchers/ASTMatchersTest.h | 35 ++ unittests/ASTMatchers/CMakeLists.txt | 2 + unittests/ASTMatchers/Dynamic/CMakeLists.txt | 7 + unittests/ASTMatchers/Dynamic/Makefile | 18 + unittests/ASTMatchers/Dynamic/ParserTest.cpp | 194 ++++++++++ .../ASTMatchers/Dynamic/RegistryTest.cpp | 112 ++++++ .../ASTMatchers/Dynamic/VariantValueTest.cpp | 97 +++++ unittests/ASTMatchers/Makefile | 2 + 24 files changed, 1887 insertions(+) create mode 100644 include/clang/ASTMatchers/Dynamic/Diagnostics.h create mode 100644 include/clang/ASTMatchers/Dynamic/Parser.h create mode 100644 include/clang/ASTMatchers/Dynamic/Registry.h create mode 100644 include/clang/ASTMatchers/Dynamic/VariantValue.h create mode 100644 lib/ASTMatchers/Dynamic/CMakeLists.txt create mode 100644 lib/ASTMatchers/Dynamic/Diagnostics.cpp create mode 100644 lib/ASTMatchers/Dynamic/Makefile create mode 100644 lib/ASTMatchers/Dynamic/Marshallers.h create mode 100644 lib/ASTMatchers/Dynamic/Parser.cpp create mode 100644 lib/ASTMatchers/Dynamic/Registry.cpp create mode 100644 lib/ASTMatchers/Dynamic/VariantValue.cpp create mode 100644 unittests/ASTMatchers/Dynamic/CMakeLists.txt create mode 100644 unittests/ASTMatchers/Dynamic/Makefile create mode 100644 unittests/ASTMatchers/Dynamic/ParserTest.cpp create mode 100644 unittests/ASTMatchers/Dynamic/RegistryTest.cpp create mode 100644 unittests/ASTMatchers/Dynamic/VariantValueTest.cpp diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h index 870a39b391..be58afffd1 100644 --- a/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -131,6 +131,17 @@ public: 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(); diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index 30691ad8f9..bc4ddce36f 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -239,6 +239,9 @@ public: 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; }; @@ -301,6 +304,9 @@ public: return matches(*Node, Finder, Builder); } + /// \brief Makes a copy of this matcher object. + virtual Matcher *clone() const { return new Matcher(*this); } + /// \brief Allows the conversion of a \c Matcher to a \c /// Matcher. /// diff --git a/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/include/clang/ASTMatchers/Dynamic/Diagnostics.h new file mode 100644 index 0000000000..417bc67967 --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/Diagnostics.h @@ -0,0 +1,109 @@ +//===--- 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 +#include + +#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 ArgStream &operator<<(const T &Arg) { + return operator<<(Twine(Arg)); + } + ArgStream &operator<<(const Twine &Arg); + std::vector *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 Args; + + std::string ToString() const; + }; + ArrayRef 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 Frames; +}; + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_DIAGNOSTICS_H diff --git a/include/clang/ASTMatchers/Dynamic/Parser.h b/include/clang/ASTMatchers/Dynamic/Parser.h new file mode 100644 index 0000000000..f981c6055e --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/Parser.h @@ -0,0 +1,143 @@ +//===--- 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: +/// := | +/// := "quoted string" +/// := () +/// := [a-zA-Z]+ +/// := | , +/// \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 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 diff --git a/include/clang/ASTMatchers/Dynamic/Registry.h b/include/clang/ASTMatchers/Dynamic/Registry.h new file mode 100644 index 0000000000..b092ed8fe1 --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/Registry.h @@ -0,0 +1,63 @@ +//===--- 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 Args, + Diagnostics *Error); + +private: + Registry() LLVM_DELETED_FUNCTION; +}; + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_REGISTRY_H diff --git a/include/clang/ASTMatchers/Dynamic/VariantValue.h b/include/clang/ASTMatchers/Dynamic/VariantValue.h new file mode 100644 index 0000000000..8dc4238757 --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -0,0 +1,125 @@ +//===--- 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 +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 is/get functions. + template + bool isTypedMatcher() const { + // TODO: Add some logic to test if T is actually valid for the underlying + // type of the matcher. + return isMatcher(); + } + + template + ast_matchers::internal::Matcher getTypedMatcher() const { + return ast_matchers::internal::makeMatcher( + new DerivedTypeMatcher(getMatcher())); + } + +private: + void reset(); + + /// \brief Matcher bridge between a Matcher and a generic DynTypedMatcher. + template + class DerivedTypeMatcher : + public ast_matchers::internal::MatcherInterface { + 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 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 diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index 6ebd736e3c..eccc9cd4d3 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -744,6 +744,14 @@ void MatchFinder::addMatcher(const TypeLocMatcher &NodeMatch, 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); } diff --git a/lib/ASTMatchers/CMakeLists.txt b/lib/ASTMatchers/CMakeLists.txt index 86560d61c9..4a390a8fc3 100644 --- a/lib/ASTMatchers/CMakeLists.txt +++ b/lib/ASTMatchers/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(Dynamic) + set(LLVM_LINK_COMPONENTS support) add_clang_library(clangASTMatchers diff --git a/lib/ASTMatchers/Dynamic/CMakeLists.txt b/lib/ASTMatchers/Dynamic/CMakeLists.txt new file mode 100644 index 0000000000..ba920a4f67 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/CMakeLists.txt @@ -0,0 +1,12 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangDynamicASTMatchers + Diagnostics.cpp + VariantValue.cpp + Parser.cpp + Registry.cpp + ) + +add_dependencies(clangDynamicASTMatchers + clangASTMatchers + ) diff --git a/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/lib/ASTMatchers/Dynamic/Diagnostics.cpp new file mode 100644 index 0000000000..fb3cac370f --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Diagnostics.cpp @@ -0,0 +1,113 @@ +//===--- 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 ""; + } + llvm_unreachable("Unknown ErrorType value."); +} + +std::string FormatErrorString(StringRef FormatString, + ArrayRef Args) { + std::string Out; + while (!FormatString.empty()) { + std::pair 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 += ""; + } + } + } + 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 diff --git a/lib/ASTMatchers/Dynamic/Makefile b/lib/ASTMatchers/Dynamic/Makefile new file mode 100644 index 0000000000..a57d752229 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Makefile @@ -0,0 +1,13 @@ +##===- 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 diff --git a/lib/ASTMatchers/Dynamic/Marshallers.h b/lib/ASTMatchers/Dynamic/Marshallers.h new file mode 100644 index 0000000000..e75a175738 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Marshallers.h @@ -0,0 +1,223 @@ +//===--- 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 +#include +#include + +#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 struct ArgTypeTraits; +template struct ArgTypeTraits : public ArgTypeTraits { +}; + +template <> struct ArgTypeTraits { + static bool is(const VariantValue &Value) { return Value.isString(); } + static const std::string &get(const VariantValue &Value) { + return Value.getString(); + } +}; + +template struct ArgTypeTraits > { + static bool is(const VariantValue &Value) { return Value.isMatcher(); } + static ast_matchers::internal::Matcher get(const VariantValue &Value) { + return Value.getTypedMatcher(); + } + +}; + +/// \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 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 +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 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 +MatcherCreateCallback * +createMarshallerCallback(MarshallerType Marshaller, FuncType Func, + StringRef MatcherName) { + return new FixedArgCountMatcherCreateCallback( + 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::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 +struct remove_const_ref : + public llvm::remove_const::type> { +}; + +/// \brief 0-arg marshaller function. +template +DynTypedMatcher *matcherMarshall0(ReturnType (*Func)(), StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) { + CHECK_ARG_COUNT(0); + return Func().clone(); +} + +/// \brief 1-arg marshaller function. +template +DynTypedMatcher *matcherMarshall1(ReturnType (*Func)(InArgType1), + StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) { + typedef typename remove_const_ref::type ArgType1; + CHECK_ARG_COUNT(1); + CHECK_ARG_TYPE(0, ArgType1); + return Func(ArgTypeTraits::get(Args[0].Value)).clone(); +} + +/// \brief Variadic marshaller function. +template +class VariadicMatcherCreateCallback : public MatcherCreateCallback { +public: + explicit VariadicMatcherCreateCallback(StringRef MatcherName) + : MatcherName(MatcherName.str()) {} + + typedef ast_matchers::internal::Matcher DerivedMatcherType; + + DynTypedMatcher *run(const SourceRange &NameRange, ArrayRef Args, + Diagnostics *Error) const { + std::list References; + std::vector InnerArgs(Args.size()); + for (size_t i = 0, e = Args.size(); i != e; ++i) { + CHECK_ARG_TYPE(i, DerivedMatcherType); + References.push_back( + ArgTypeTraits::get(Args[i].Value)); + InnerArgs[i] = &References.back(); + } + return ast_matchers::internal::makeDynCastAllOfComposite( + ArrayRef(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 +MatcherCreateCallback *makeMatcherAutoMarshall(ReturnType (*Func)(), + StringRef MatcherName) { + return createMarshallerCallback(matcherMarshall0, Func, + MatcherName); +} + +/// \brief 1-arg overload +template +MatcherCreateCallback *makeMatcherAutoMarshall(ReturnType (*Func)(ArgType1), + StringRef MatcherName) { + return createMarshallerCallback(matcherMarshall1, Func, + MatcherName); +} + +/// \brief Variadic overload. +template +MatcherCreateCallback *makeMatcherAutoMarshall( + ast_matchers::internal::VariadicAllOfMatcher Func, + StringRef MatcherName) { + return new VariadicMatcherCreateCallback( + MatcherName); +} + +template +MatcherCreateCallback * +makeMatcherAutoMarshall(ast_matchers::internal::VariadicDynCastAllOfMatcher< + BaseType, MatcherType> Func, + StringRef MatcherName) { + return new VariadicMatcherCreateCallback(MatcherName); +} + +} // namespace internal +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_MARSHALLERS_H diff --git a/lib/ASTMatchers/Dynamic/Parser.cpp b/lib/ASTMatchers/Dynamic/Parser.cpp new file mode 100644 index 0000000000..1678820da0 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Parser.cpp @@ -0,0 +1,332 @@ +//===--- 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 +#include + +#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 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 +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 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 diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp new file mode 100644 index 0000000000..53e90f1c77 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Registry.cpp @@ -0,0 +1,153 @@ +//===--- 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 + +#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 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 RegistryData; + +} // anonymous namespace + +// static +DynTypedMatcher *Registry::constructMatcher(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef 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 diff --git a/lib/ASTMatchers/Dynamic/VariantValue.cpp b/lib/ASTMatchers/Dynamic/VariantValue.cpp new file mode 100644 index 0000000000..e310fbfc58 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/VariantValue.cpp @@ -0,0 +1,105 @@ +//===--- 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 diff --git a/lib/ASTMatchers/Makefile b/lib/ASTMatchers/Makefile index 76d82719a1..3ee9ccb580 100644 --- a/lib/ASTMatchers/Makefile +++ b/lib/ASTMatchers/Makefile @@ -10,4 +10,6 @@ CLANG_LEVEL := ../.. LIBRARYNAME := clangASTMatchers +PARALLEL_DIRS = Dynamic + include $(CLANG_LEVEL)/Makefile diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 5fed85bb30..05258f7fe2 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -84,6 +84,41 @@ testing::AssertionResult notMatches(const std::string &Code, 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 Factory(newFrontendActionFactory(&Finder)); + // Some tests use typeof, which is a gnu extension. + std::vector 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 testing::AssertionResult matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, diff --git a/unittests/ASTMatchers/CMakeLists.txt b/unittests/ASTMatchers/CMakeLists.txt index 91feaac4d9..862c6a0fd9 100644 --- a/unittests/ASTMatchers/CMakeLists.txt +++ b/unittests/ASTMatchers/CMakeLists.txt @@ -11,3 +11,5 @@ add_clang_unittest(ASTMatchersTests target_link_libraries(ASTMatchersTests gtest gtest_main clangASTMatchers clangTooling) + +add_subdirectory(Dynamic) diff --git a/unittests/ASTMatchers/Dynamic/CMakeLists.txt b/unittests/ASTMatchers/Dynamic/CMakeLists.txt new file mode 100644 index 0000000000..eb9fa549e1 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/CMakeLists.txt @@ -0,0 +1,7 @@ +add_clang_unittest(DynamicASTMatchersTests + VariantValueTest.cpp + ParserTest.cpp + RegistryTest.cpp) + +target_link_libraries(DynamicASTMatchersTests + gtest gtest_main clangASTMatchers clangDynamicASTMatchers clangTooling) diff --git a/unittests/ASTMatchers/Dynamic/Makefile b/unittests/ASTMatchers/Dynamic/Makefile new file mode 100644 index 0000000000..082a4c04c1 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/Makefile @@ -0,0 +1,18 @@ +##===- 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 diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp new file mode 100644 index 0000000000..41f522856d --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -0,0 +1,194 @@ +//===- 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 +#include + +#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 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 Args; + }; + + std::vector Errors; + std::vector Values; + std::vector Matchers; + llvm::StringMap 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 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 diff --git a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp new file mode 100644 index 0000000000..64af120193 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -0,0 +1,112 @@ +//===- 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 + +#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 Args; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +DynTypedMatcher *constructMatcher(StringRef MatcherName, + const VariantValue &Arg1, + Diagnostics *Error) { + std::vector 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 Args(2); + Args[0].Value = Arg1; + Args[1].Value = Arg2; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +TEST(RegistryTest, CanConstructNoArgs) { + OwningPtr IsArrowValue(constructMatcher("isArrow", NULL)); + OwningPtr 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 Value( + constructMatcher("hasName", std::string("X"), NULL)); + EXPECT_TRUE(matchesDynamic("class X {};", *Value)); + EXPECT_FALSE(matchesDynamic("int x;", *Value)); +} + +TEST(RegistryTest, ConstructWithMatcherArgs) { + OwningPtr HasInitializerSimple( + constructMatcher("hasInitializer", stmt(), NULL)); + OwningPtr 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 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 diff --git a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp new file mode 100644 index 0000000000..6c202e52fa --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -0,0 +1,97 @@ +//===- 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()); + EXPECT_FALSE(Value.isTypedMatcher()); +} + +TEST(VariantValueTest, DynTypedMatcher) { + VariantValue Value = stmt(); + + EXPECT_FALSE(Value.isString()); + + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + + // 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()); + EXPECT_TRUE(Value.isTypedMatcher()); + + Value = unaryOperator(); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); +} + +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()); + EXPECT_TRUE(Value.isTypedMatcher()); + + 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())); + EXPECT_TRUE(matchesDynamic("int foo() { return 1 + 1; }", + VariantValue(functionDecl()).getMatcher())); + // Going through the wrong Matcher will fail to match, even if the + // underlying matcher is correct. + EXPECT_FALSE(matchesDynamic( + "int x;", VariantValue(varDecl()).getTypedMatcher())); + + EXPECT_FALSE( + matchesDynamic("int x;", VariantValue(functionDecl()).getMatcher())); + EXPECT_FALSE(matchesDynamic( + "int foo() { return 1 + 1; }", + VariantValue(declRefExpr()).getTypedMatcher())); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/ASTMatchers/Makefile b/unittests/ASTMatchers/Makefile index 2abe6eeea5..7ff42f850c 100644 --- a/unittests/ASTMatchers/Makefile +++ b/unittests/ASTMatchers/Makefile @@ -9,6 +9,8 @@ CLANG_LEVEL = ../.. +PARALLEL_DIRS = Dynamic + TESTNAME = ASTMatchers include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc -- 2.40.0