]> granicus.if.org Git - clang/commitdiff
Add support for named values in the parser.
authorSamuel Benzaquen <sbenza@google.com>
Wed, 2 Apr 2014 13:11:45 +0000 (13:11 +0000)
committerSamuel Benzaquen <sbenza@google.com>
Wed, 2 Apr 2014 13:11:45 +0000 (13:11 +0000)
Summary:
Add support for named values in the parser.
This allows injection of arbitrary constants using a custom Sema object.
Completions are not supported right now.

Will be used by clang_query to support the 'let' command.
Usage example:
  clang_query> let unique_ptr recordDecl(hasName("unique_ptr"))
  clang_query> match varDecl(hasType(unique_ptr))

Reviewers: klimek, pcc

CC: cfe-commits, klimek
Differential Revision: http://llvm-reviews.chandlerc.com/D3229

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@205419 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/ASTMatchers/Dynamic/Parser.h
include/clang/ASTMatchers/Dynamic/VariantValue.h
lib/ASTMatchers/Dynamic/Parser.cpp
unittests/ASTMatchers/Dynamic/ParserTest.cpp
unittests/ASTMatchers/Dynamic/VariantValueTest.cpp

index 5901495bddb11bc8982fc83b67997416968bd1a9..896b07010ca4050ce25bc60ebd4829f7d4b792c8 100644 (file)
 ///
 /// \code
 /// Grammar for the expressions supported:
-/// <Expression>        := <Literal> | <MatcherExpression>
+/// <Expression>        := <Literal> | <NamedValue> | <MatcherExpression>
 /// <Literal>           := <StringLiteral> | <Unsigned>
 /// <StringLiteral>     := "quoted string"
 /// <Unsigned>          := [0-9]+
-/// <MatcherExpression> := <MatcherName>(<ArgumentList>) |
-///                        <MatcherName>(<ArgumentList>).bind(<StringLiteral>)
-/// <MatcherName>       := [a-zA-Z]+
+/// <NamedValue>        := <Identifier>
+/// <MatcherExpression> := <Identifier>(<ArgumentList>) |
+///                        <Identifier>(<ArgumentList>).bind(<StringLiteral>)
+/// <Identifier>        := [a-zA-Z]+
 /// <ArgumentList>      := <Expression> | <Expression>,<ArgumentList>
 /// \endcode
 ///
@@ -62,6 +63,19 @@ public:
   public:
     virtual ~Sema();
 
+    /// \brief Lookup a value by name.
+    ///
+    /// This can be used in the Sema layer to declare known constants or to
+    /// allow to split an expression in pieces.
+    ///
+    /// \param Name The name of the value to lookup.
+    ///
+    /// \return The named value. It could be any type that VariantValue
+    ///   supports. A 'nothing' value means that the name is not recognized.
+    virtual VariantValue getNamedValue(StringRef Name) {
+      return VariantValue();
+    }
+
     /// \brief Process a matcher expression.
     ///
     /// All the arguments passed here have already been processed.
@@ -100,6 +114,21 @@ public:
                       Diagnostics *Error) = 0;
   };
 
+  /// \brief Sema implementation that uses the matcher registry to process the
+  ///   tokens.
+  class RegistrySema : public Parser::Sema {
+   public:
+    virtual ~RegistrySema();
+    llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName,
+                                                  const SourceRange &NameRange,
+                                                  Diagnostics *Error) override;
+    VariantMatcher actOnMatcherExpression(MatcherCtor Ctor,
+                                          const SourceRange &NameRange,
+                                          StringRef BindID,
+                                          ArrayRef<ParserValue> Args,
+                                          Diagnostics *Error) override;
+  };
+
   /// \brief Parse a matcher expression, creating matchers from the registry.
   ///
   /// This overload creates matchers calling directly into the registry. If the
index c6853572ec8cf90706cbc0ac3681cc4121767c24..cd7395b4157457ac4578cdeaf267415df105c533 100644 (file)
@@ -78,7 +78,8 @@ public:
   /// \brief Clones the provided matchers.
   ///
   /// They should be the result of a polymorphic matcher.
-  static VariantMatcher PolymorphicMatcher(std::vector<DynTypedMatcher> Matchers);
+  static VariantMatcher
+  PolymorphicMatcher(std::vector<DynTypedMatcher> Matchers);
 
   /// \brief Creates a 'variadic' operator matcher.
   ///
@@ -208,6 +209,9 @@ public:
   VariantValue(const std::string &String);
   VariantValue(const VariantMatcher &Matchers);
 
+  /// \brief Returns true iff this is an empty value.
+  bool isNothing() const { return Type == VT_Nothing; }
+
   /// \brief Unsigned value functions.
   bool isUnsigned() const;
   unsigned getUnsigned() const;
index 24ffdcda51b89ee3a5d44ac9de801f3c18e376a0..bc6f73ca45214d9ad00608eff66840b0a60de7c3 100644 (file)
@@ -424,8 +424,18 @@ bool Parser::parseExpressionImpl(VariantValue *Value) {
     *Value = Tokenizer->consumeNextToken().Value;
     return true;
 
-  case TokenInfo::TK_Ident:
+  case TokenInfo::TK_Ident: {
+    // Identifier could be a name known by Sema as a named value.
+    const VariantValue NamedValue =
+        S->getNamedValue(Tokenizer->peekNextToken().Text);
+    if (!NamedValue.isNothing()) {
+      Tokenizer->consumeNextToken();  // Actually consume it.
+      *Value = NamedValue;
+      return true;
+    }
+    // Fallback to full matcher parsing.
     return parseMatcherExpressionImpl(Value);
+  }
 
   case TokenInfo::TK_CodeCompletion:
     addExpressionCompletions();
@@ -457,27 +467,23 @@ Parser::Parser(CodeTokenizer *Tokenizer, Sema *S,
                Diagnostics *Error)
     : Tokenizer(Tokenizer), S(S), Error(Error) {}
 
-class RegistrySema : public Parser::Sema {
-public:
-  virtual ~RegistrySema() {}
-  llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName,
-                                                const SourceRange &NameRange,
-                                                Diagnostics *Error) {
-    return Registry::lookupMatcherCtor(MatcherName, NameRange, Error);
-  }
-  VariantMatcher actOnMatcherExpression(MatcherCtor Ctor,
-                                        const SourceRange &NameRange,
-                                        StringRef BindID,
-                                        ArrayRef<ParserValue> Args,
-                                        Diagnostics *Error) {
-    if (BindID.empty()) {
-      return Registry::constructMatcher(Ctor, NameRange, Args, Error);
-    } else {
-      return Registry::constructBoundMatcher(Ctor, NameRange, BindID, Args,
-                                             Error);
-    }
+Parser::RegistrySema::~RegistrySema() {}
+
+llvm::Optional<MatcherCtor> Parser::RegistrySema::lookupMatcherCtor(
+    StringRef MatcherName, const SourceRange &NameRange, Diagnostics *Error) {
+  return Registry::lookupMatcherCtor(MatcherName, NameRange, Error);
+}
+
+VariantMatcher Parser::RegistrySema::actOnMatcherExpression(
+    MatcherCtor Ctor, const SourceRange &NameRange, StringRef BindID,
+    ArrayRef<ParserValue> Args, Diagnostics *Error) {
+  if (BindID.empty()) {
+    return Registry::constructMatcher(Ctor, NameRange, Args, Error);
+  } else {
+    return Registry::constructBoundMatcher(Ctor, NameRange, BindID, Args,
+                                           Error);
   }
-};
+}
 
 bool Parser::parseExpression(StringRef Code, VariantValue *Value,
                              Diagnostics *Error) {
index cdf4f92db656f592578da326a640519153593325..fdfacf181885c1e9548c598c0312c49edfa977a7 100644 (file)
@@ -175,6 +175,29 @@ TEST(ParserTest, FullParserTest) {
   EXPECT_TRUE(matches("void f(int a, int x);", M));
   EXPECT_FALSE(matches("void f(int x, int a);", M));
 
+  // Test named values.
+  struct NamedSema : public Parser::RegistrySema {
+   public:
+    virtual VariantValue getNamedValue(StringRef Name) {
+      if (Name == "nameX")
+        return std::string("x");
+      if (Name == "param0")
+        return VariantMatcher::SingleMatcher(hasParameter(0, hasName("a")));
+      return VariantValue();
+    }
+  };
+  NamedSema Sema;
+  llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues(
+      Parser::parseMatcherExpression(
+          "functionDecl(param0, hasParameter(1, hasName(nameX)))", &Sema,
+          &Error));
+  EXPECT_EQ("", Error.toStringFull());
+  M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>();
+
+  EXPECT_TRUE(matches("void f(int a, int x);", M));
+  EXPECT_FALSE(matches("void f(int x, int a);", M));
+
+
   EXPECT_TRUE(!Parser::parseMatcherExpression(
                    "hasInitializer(\n    binaryOperator(hasLHS(\"A\")))",
                    &Error).hasValue());
index e62a4645f7581d93a96a30786a804f6fa1a02a24..8d6e767f550fb0d282811643460c3183904cca33 100644 (file)
@@ -26,6 +26,7 @@ TEST(VariantValueTest, Unsigned) {
   EXPECT_TRUE(Value.isUnsigned());
   EXPECT_EQ(kUnsigned, Value.getUnsigned());
 
+  EXPECT_FALSE(Value.isNothing());
   EXPECT_FALSE(Value.isString());
   EXPECT_FALSE(Value.isMatcher());
 }
@@ -38,6 +39,7 @@ TEST(VariantValueTest, String) {
   EXPECT_EQ(kString, Value.getString());
   EXPECT_EQ("String", Value.getTypeAsString());
 
+  EXPECT_FALSE(Value.isNothing());
   EXPECT_FALSE(Value.isUnsigned());
   EXPECT_FALSE(Value.isMatcher());
 }
@@ -45,6 +47,7 @@ TEST(VariantValueTest, String) {
 TEST(VariantValueTest, DynTypedMatcher) {
   VariantValue Value = VariantMatcher::SingleMatcher(stmt());
 
+  EXPECT_FALSE(Value.isNothing());
   EXPECT_FALSE(Value.isUnsigned());
   EXPECT_FALSE(Value.isString());
 
@@ -74,11 +77,13 @@ TEST(VariantValueTest, Assignment) {
   VariantValue Value = std::string("A");
   EXPECT_TRUE(Value.isString());
   EXPECT_EQ("A", Value.getString());
+  EXPECT_FALSE(Value.isNothing());
   EXPECT_FALSE(Value.isUnsigned());
   EXPECT_FALSE(Value.isMatcher());
   EXPECT_EQ("String", Value.getTypeAsString());
 
   Value = VariantMatcher::SingleMatcher(recordDecl());
+  EXPECT_FALSE(Value.isNothing());
   EXPECT_FALSE(Value.isUnsigned());
   EXPECT_FALSE(Value.isString());
   EXPECT_TRUE(Value.isMatcher());
@@ -89,10 +94,12 @@ TEST(VariantValueTest, Assignment) {
   Value = 17;
   EXPECT_TRUE(Value.isUnsigned());
   EXPECT_EQ(17U, Value.getUnsigned());
+  EXPECT_FALSE(Value.isNothing());
   EXPECT_FALSE(Value.isMatcher());
   EXPECT_FALSE(Value.isString());
 
   Value = VariantValue();
+  EXPECT_TRUE(Value.isNothing());
   EXPECT_FALSE(Value.isUnsigned());
   EXPECT_FALSE(Value.isString());
   EXPECT_FALSE(Value.isMatcher());