r183144 - Add support for .bind("foo") expressions on the dynamic matchers.

Samuel Benzaquen sbenza at google.com
Mon Jun 3 12:31:09 PDT 2013


Author: sbenza
Date: Mon Jun  3 14:31:08 2013
New Revision: 183144

URL: http://llvm.org/viewvc/llvm-project?rev=183144&view=rev
Log:
Add support for .bind("foo") expressions on the dynamic matchers.

Summary: Add support on the parser, registry, and DynTypedMatcher for binding IDs dynamically.

Reviewers: klimek

CC: cfe-commits, revane

Differential Revision: http://llvm-reviews.chandlerc.com/D911

Modified:
    cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
    cfe/trunk/include/clang/ASTMatchers/Dynamic/Diagnostics.h
    cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h
    cfe/trunk/include/clang/ASTMatchers/Dynamic/Registry.h
    cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
    cfe/trunk/lib/ASTMatchers/Dynamic/Diagnostics.cpp
    cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp
    cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
    cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp

Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h Mon Jun  3 14:31:08 2013
@@ -232,7 +232,7 @@ private:
 /// on the actual node, or return false if it is not convertible.
 class DynTypedMatcher {
 public:
-  virtual ~DynTypedMatcher() {}
+  virtual ~DynTypedMatcher();
 
   /// \brief Returns true if the matcher matches the given \c DynNode.
   virtual bool matches(const ast_type_traits::DynTypedNode DynNode,
@@ -244,6 +244,11 @@ public:
 
   /// \brief Returns a unique ID for the matcher.
   virtual uint64_t getID() const = 0;
+
+  /// \brief Bind the specified \p ID to the matcher.
+  /// \return A new matcher with the \p ID bound to it if this matcher supports
+  ///   binding. Otherwise, returns NULL. Returns NULL by default.
+  virtual DynTypedMatcher* tryBind(StringRef ID) const;
 };
 
 /// \brief Wrapper of a MatcherInterface<T> *that allows copying.
@@ -806,6 +811,16 @@ public:
   Matcher<T> bind(StringRef ID) const {
     return Matcher<T>(new IdMatcher<T>(ID, *this));
   }
+
+  /// \brief Makes a copy of this matcher object.
+  virtual BindableMatcher<T>* clone() const {
+    return new BindableMatcher<T>(*this);
+  }
+
+  /// \brief Bind the specified \c ID to the matcher.
+  virtual Matcher<T>* tryBind(StringRef ID) const {
+    return new Matcher<T>(bind(ID));
+  }
 };
 
 /// \brief Matches nodes of type T that have child nodes of type ChildT for

Modified: cfe/trunk/include/clang/ASTMatchers/Dynamic/Diagnostics.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/Dynamic/Diagnostics.h?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/Dynamic/Diagnostics.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/Dynamic/Diagnostics.h Mon Jun  3 14:31:08 2013
@@ -57,6 +57,7 @@ class Diagnostics {
     ET_RegistryNotFound = 1,
     ET_RegistryWrongArgCount = 2,
     ET_RegistryWrongArgType = 3,
+    ET_RegistryNotBindable = 4,
 
     ET_ParserStringError = 100,
     ET_ParserMatcherArgFailure = 101,
@@ -66,7 +67,9 @@ class Diagnostics {
     ET_ParserNoComma = 105,
     ET_ParserNoCode = 106,
     ET_ParserNotAMatcher = 107,
-    ET_ParserInvalidToken = 108
+    ET_ParserInvalidToken = 108,
+    ET_ParserMalformedBindExpr = 109,
+    ET_ParserTrailingCode = 110
   };
 
   /// \brief Helper stream class.

Modified: cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/Dynamic/Parser.h Mon Jun  3 14:31:08 2013
@@ -20,7 +20,8 @@
 /// Grammar for the expressions supported:
 /// <Expression>        := <StringLiteral> | <MatcherExpression>
 /// <StringLiteral>     := "quoted string"
-/// <MatcherExpression> := <MatcherName>(<ArgumentList>)
+/// <MatcherExpression> := <MatcherName>(<ArgumentList>) |
+///                        <MatcherName>(<ArgumentList>).bind(<StringLiteral>)
 /// <MatcherName>       := [a-zA-Z]+
 /// <ArgumentList>      := <Expression> | <Expression>,<ArgumentList>
 /// \endcode
@@ -66,15 +67,18 @@ public:
     /// \param NameRange The location of the name in the matcher source.
     ///   Useful for error reporting.
     ///
+    /// \param BindID The ID to use to bind the matcher, or a null \c StringRef
+    ///   if no ID is specified.
+    ///
     /// \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;
+    virtual DynTypedMatcher *actOnMatcherExpression(
+        StringRef MatcherName, const SourceRange &NameRange, StringRef BindID,
+        ArrayRef<ParserValue> Args, Diagnostics *Error) = 0;
   };
 
   /// \brief Parse a matcher expression, creating matchers from the registry.

Modified: cfe/trunk/include/clang/ASTMatchers/Dynamic/Registry.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/Dynamic/Registry.h?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/Dynamic/Registry.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/Dynamic/Registry.h Mon Jun  3 14:31:08 2013
@@ -52,6 +52,18 @@ public:
                                            ArrayRef<ParserValue> Args,
                                            Diagnostics *Error);
 
+  /// \brief Construct a matcher from the registry and bind it.
+  ///
+  /// Similar the \c constructMatcher() above, but it then tries to bind the
+  /// matcher to the specified \c BindID.
+  /// If the matcher is not bindable, it sets an error in \c Error and returns
+  /// \c NULL.
+  static DynTypedMatcher *constructBoundMatcher(StringRef MatcherName,
+                                                const SourceRange &NameRange,
+                                                StringRef BindID,
+                                                ArrayRef<ParserValue> Args,
+                                                Diagnostics *Error);
+
 private:
   Registry() LLVM_DELETED_FUNCTION;
 };

Modified: cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp Mon Jun  3 14:31:08 2013
@@ -82,6 +82,10 @@ BoundNodesTree BoundNodesTreeBuilder::bu
   return BoundNodesTree(Bindings, RecursiveBindings);
 }
 
+DynTypedMatcher::~DynTypedMatcher() {}
+
+DynTypedMatcher *DynTypedMatcher::tryBind(StringRef ID) const { return NULL; }
+
 } // end namespace internal
 } // end namespace ast_matchers
 } // end namespace clang

Modified: cfe/trunk/lib/ASTMatchers/Dynamic/Diagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/Dynamic/Diagnostics.cpp?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/Dynamic/Diagnostics.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Diagnostics.cpp Mon Jun  3 14:31:08 2013
@@ -37,6 +37,8 @@ StringRef ErrorTypeToString(Diagnostics:
     return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
   case Diagnostics::ET_RegistryWrongArgType:
     return "Incorrect type on function $0 for arg $1.";
+  case Diagnostics::ET_RegistryNotBindable:
+    return "Matcher does not support binding.";
 
   case Diagnostics::ET_ParserStringError:
     return "Error parsing string token: <$0>";
@@ -56,6 +58,10 @@ StringRef ErrorTypeToString(Diagnostics:
     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_ParserMalformedBindExpr:
+    return "Malformed bind() expression.";
+  case Diagnostics::ET_ParserTrailingCode:
+    return "Expected end of code.";
 
   case Diagnostics::ET_None:
     return "<N/A>";

Modified: cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Parser.cpp Mon Jun  3 14:31:08 2013
@@ -32,12 +32,16 @@ struct Parser::TokenInfo {
     TK_OpenParen = 1,
     TK_CloseParen = 2,
     TK_Comma = 3,
-    TK_Literal = 4,
-    TK_Ident = 5,
-    TK_InvalidChar = 6,
-    TK_Error = 7
+    TK_Period = 4,
+    TK_Literal = 5,
+    TK_Ident = 6,
+    TK_InvalidChar = 7,
+    TK_Error = 8
   };
 
+  /// \brief Some known identifiers.
+  static const char* const ID_Bind;
+
   TokenInfo() : Text(), Kind(TK_Eof), Range(), Value() {}
 
   StringRef Text;
@@ -46,6 +50,8 @@ struct Parser::TokenInfo {
   VariantValue Value;
 };
 
+const char* const Parser::TokenInfo::ID_Bind = "bind";
+
 /// \brief Simple tokenizer for the parser.
 class Parser::CodeTokenizer {
 public:
@@ -84,6 +90,11 @@ private:
       Result.Text = Code.substr(0, 1);
       Code = Code.drop_front();
       break;
+    case '.':
+      Result.Kind = TokenInfo::TK_Period;
+      Result.Text = Code.substr(0, 1);
+      Code = Code.drop_front();
+      break;
     case '(':
       Result.Kind = TokenInfo::TK_OpenParen;
       Result.Text = Code.substr(0, 1);
@@ -234,11 +245,43 @@ bool Parser::parseMatcherExpressionImpl(
     return false;
   }
 
+  std::string BindID;
+  if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) {
+    // Parse .bind("foo")
+    Tokenizer->consumeNextToken();  // consume the period.
+    const TokenInfo BindToken = Tokenizer->consumeNextToken();
+    const TokenInfo OpenToken = Tokenizer->consumeNextToken();
+    const TokenInfo IDToken = Tokenizer->consumeNextToken();
+    const TokenInfo CloseToken = Tokenizer->consumeNextToken();
+
+    // TODO: We could use different error codes for each/some to be more
+    //       explicit about the syntax error.
+    if (BindToken.Kind != TokenInfo::TK_Ident ||
+        BindToken.Text != TokenInfo::ID_Bind) {
+      Error->pushErrorFrame(BindToken.Range, Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    if (OpenToken.Kind != TokenInfo::TK_OpenParen) {
+      Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    if (IDToken.Kind != TokenInfo::TK_Literal || !IDToken.Value.isString()) {
+      Error->pushErrorFrame(IDToken.Range, Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    if (CloseToken.Kind != TokenInfo::TK_CloseParen) {
+      Error->pushErrorFrame(CloseToken.Range,
+                            Error->ET_ParserMalformedBindExpr);
+      return false;
+    }
+    BindID = IDToken.Value.getString();
+  }
+
   // Merge the start and end infos.
   SourceRange MatcherRange = NameToken.Range;
   MatcherRange.End = EndToken.Range.End;
-  DynTypedMatcher *Result =
-      S->actOnMatcherExpression(NameToken.Text, MatcherRange, Args, Error);
+  DynTypedMatcher *Result = S->actOnMatcherExpression(
+      NameToken.Text, MatcherRange, BindID, Args, Error);
   if (Result == NULL) {
     Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure)
         << NameToken.Text;
@@ -271,6 +314,7 @@ bool Parser::parseExpressionImpl(Variant
   case TokenInfo::TK_OpenParen:
   case TokenInfo::TK_CloseParen:
   case TokenInfo::TK_Comma:
+  case TokenInfo::TK_Period:
   case TokenInfo::TK_InvalidChar:
     const TokenInfo Token = Tokenizer->consumeNextToken();
     Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken)
@@ -290,9 +334,15 @@ public:
   virtual ~RegistrySema() {}
   DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
                                           const SourceRange &NameRange,
+                                          StringRef BindID,
                                           ArrayRef<ParserValue> Args,
                                           Diagnostics *Error) {
-    return Registry::constructMatcher(MatcherName, NameRange, Args, Error);
+    if (BindID.empty()) {
+      return Registry::constructMatcher(MatcherName, NameRange, Args, Error);
+    } else {
+      return Registry::constructBoundMatcher(MatcherName, NameRange, BindID,
+                                             Args, Error);
+    }
   }
 };
 
@@ -305,7 +355,13 @@ bool Parser::parseExpression(StringRef C
 bool Parser::parseExpression(StringRef Code, Sema *S,
                              VariantValue *Value, Diagnostics *Error) {
   CodeTokenizer Tokenizer(Code, Error);
-  return Parser(&Tokenizer, S, Error).parseExpressionImpl(Value);
+  if (!Parser(&Tokenizer, S, Error).parseExpressionImpl(Value)) return false;
+  if (Tokenizer.peekNextToken().Kind != TokenInfo::TK_Eof) {
+    Error->pushErrorFrame(Tokenizer.peekNextToken().Range,
+                          Error->ET_ParserTrailingCode);
+    return false;
+  }
+  return true;
 }
 
 DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code,

Modified: cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp Mon Jun  3 14:31:08 2013
@@ -148,6 +148,23 @@ DynTypedMatcher *Registry::constructMatc
   return it->second->run(NameRange, Args, Error);
 }
 
+// static
+DynTypedMatcher *Registry::constructBoundMatcher(StringRef MatcherName,
+                                                 const SourceRange &NameRange,
+                                                 StringRef BindID,
+                                                 ArrayRef<ParserValue> Args,
+                                                 Diagnostics *Error) {
+  OwningPtr<DynTypedMatcher> Out(
+      constructMatcher(MatcherName, NameRange, Args, Error));
+  if (!Out) return NULL;
+  DynTypedMatcher *Bound = Out->tryBind(BindID);
+  if (!Bound) {
+    Error->pushErrorFrame(NameRange, Error->ET_RegistryNotBindable);
+    return NULL;
+  }
+  return Bound;
+}
+
 }  // namespace dynamic
 }  // namespace ast_matchers
 }  // namespace clang

Modified: cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp?rev=183144&r1=183143&r2=183144&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp (original)
+++ cfe/trunk/unittests/ASTMatchers/Dynamic/ParserTest.cpp Mon Jun  3 14:31:08 2013
@@ -24,6 +24,8 @@ namespace {
 class DummyDynTypedMatcher : public DynTypedMatcher {
 public:
   DummyDynTypedMatcher(uint64_t ID) : ID(ID) {}
+  DummyDynTypedMatcher(uint64_t ID, StringRef BoundID)
+      : ID(ID), BoundID(BoundID) {}
 
   typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder;
   typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
@@ -35,14 +37,21 @@ public:
 
   /// \brief Makes a copy of this matcher object.
   virtual DynTypedMatcher *clone() const {
-    return new DummyDynTypedMatcher(ID);
+    return new DummyDynTypedMatcher(*this);
   }
 
   /// \brief Returns a unique ID for the matcher.
   virtual uint64_t getID() const { return ID; }
 
+  virtual DynTypedMatcher* tryBind(StringRef BoundID) const {
+    return new DummyDynTypedMatcher(ID, BoundID);
+  }
+
+  StringRef boundID() const { return BoundID; }
+
 private:
   uint64_t ID;
+  std::string BoundID;
 };
 
 class MockSema : public Parser::Sema {
@@ -65,17 +74,20 @@ public:
 
   DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
                                           const SourceRange &NameRange,
+                                          StringRef BindID,
                                           ArrayRef<ParserValue> Args,
                                           Diagnostics *Error) {
-    MatcherInfo ToStore = { MatcherName, NameRange, Args };
+    MatcherInfo ToStore = { MatcherName, NameRange, Args, BindID };
     Matchers.push_back(ToStore);
-    return new DummyDynTypedMatcher(ExpectedMatchers[MatcherName]);
+    DummyDynTypedMatcher Matcher(ExpectedMatchers[MatcherName]);
+    return Matcher.tryBind(BindID);
   }
 
   struct MatcherInfo {
     StringRef MatcherName;
     SourceRange NameRange;
     std::vector<ParserValue> Args;
+    std::string BoundID;
   };
 
   std::vector<std::string> Errors;
@@ -110,13 +122,15 @@ TEST(ParserTest, ParseMatcher) {
   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\") )  ");
+  Sema.parse(" Foo ( Bar (), Baz( \n \"B A,Z\") ) .bind( \"Yo!\") ");
   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("Yo!", static_cast<const DummyDynTypedMatcher &>(
+                       Sema.Values[0].getMatcher()).boundID());
 
   EXPECT_EQ(3ULL, Sema.Matchers.size());
   const MockSema::MatcherInfo Bar = Sema.Matchers[0];
@@ -136,6 +150,7 @@ TEST(ParserTest, ParseMatcher) {
   EXPECT_EQ(2ULL, Foo.Args.size());
   EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID());
   EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID());
+  EXPECT_EQ("Yo!", Foo.BoundID);
 }
 
 using ast_matchers::internal::Matcher;
@@ -186,6 +201,18 @@ TEST(ParserTest, Errors) {
   EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n"
             "1:5: Invalid token <(> found when looking for a value.",
             ParseWithError("Foo(("));
+  EXPECT_EQ("1:7: Expected end of code.", ParseWithError("expr()a"));
+  EXPECT_EQ("1:11: Malformed bind() expression.",
+            ParseWithError("isArrow().biind"));
+  EXPECT_EQ("1:15: Malformed bind() expression.",
+            ParseWithError("isArrow().bind"));
+  EXPECT_EQ("1:16: Malformed bind() expression.",
+            ParseWithError("isArrow().bind(foo"));
+  EXPECT_EQ("1:21: Malformed bind() expression.",
+            ParseWithError("isArrow().bind(\"foo\""));
+  EXPECT_EQ("1:1: Error building matcher isArrow.\n"
+            "1:1: Matcher does not support binding.",
+            ParseWithError("isArrow().bind(\"foo\")"));
 }
 
 }  // end anonymous namespace





More information about the cfe-commits mailing list