[clang-tools-extra] r206984 - Add new 'let' command to bind arbitrary values into constants.

Samuel Benzaquen sbenza at google.com
Wed Apr 23 07:04:53 PDT 2014


Author: sbenza
Date: Wed Apr 23 09:04:52 2014
New Revision: 206984

URL: http://llvm.org/viewvc/llvm-project?rev=206984&view=rev
Log:
Add new 'let' command to bind arbitrary values into constants.

Summary:
Add new 'let' command to bind arbitrary values into constants.
These constants can then be used in the matcher expressions.

Reviewers: pcc

CC: cfe-commits

Differential Revision: http://reviews.llvm.org/D3383

Modified:
    clang-tools-extra/trunk/clang-query/Query.cpp
    clang-tools-extra/trunk/clang-query/Query.h
    clang-tools-extra/trunk/clang-query/QueryParser.cpp
    clang-tools-extra/trunk/clang-query/QueryParser.h
    clang-tools-extra/trunk/clang-query/QuerySession.h
    clang-tools-extra/trunk/clang-query/tool/ClangQuery.cpp
    clang-tools-extra/trunk/unittests/clang-query/QueryEngineTest.cpp
    clang-tools-extra/trunk/unittests/clang-query/QueryParserTest.cpp

Modified: clang-tools-extra/trunk/clang-query/Query.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-query/Query.cpp?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-query/Query.cpp (original)
+++ clang-tools-extra/trunk/clang-query/Query.cpp Wed Apr 23 09:04:52 2014
@@ -54,7 +54,7 @@ struct CollectBoundNodes : MatchFinder::
   }
 };
 
-}
+}  // namespace
 
 bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
   unsigned MatchCount = 0;
@@ -124,6 +124,15 @@ bool MatchQuery::run(llvm::raw_ostream &
   return true;
 }
 
+bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+  if (Value) {
+    QS.NamedValues[Name] = Value;
+  } else {
+    QS.NamedValues.erase(Name);
+  }
+  return true;
+}
+
 #ifndef _MSC_VER
 const QueryKind SetQueryKind<bool>::value;
 const QueryKind SetQueryKind<OutputKind>::value;

Modified: clang-tools-extra/trunk/clang-query/Query.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-query/Query.h?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-query/Query.h (original)
+++ clang-tools-extra/trunk/clang-query/Query.h Wed Apr 23 09:04:52 2014
@@ -28,9 +28,10 @@ enum QueryKind {
   QK_Invalid,
   QK_NoOp,
   QK_Help,
+  QK_Let,
   QK_Match,
   QK_SetBool,
-  QK_SetOutputKind
+  QK_SetOutputKind,
 };
 
 class QuerySession;
@@ -86,6 +87,17 @@ struct MatchQuery : Query {
   static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
 };
 
+struct LetQuery : Query {
+  LetQuery(StringRef Name, const ast_matchers::dynamic::VariantValue &Value)
+      : Query(QK_Let), Name(Name), Value(Value) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+  std::string Name;
+  ast_matchers::dynamic::VariantValue Value;
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_Let; }
+};
+
 template <typename T> struct SetQueryKind {};
 
 template <> struct SetQueryKind<bool> {

Modified: clang-tools-extra/trunk/clang-query/QueryParser.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-query/QueryParser.cpp?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-query/QueryParser.cpp (original)
+++ clang-tools-extra/trunk/clang-query/QueryParser.cpp Wed Apr 23 09:04:52 2014
@@ -132,12 +132,16 @@ QueryRef QueryParser::endQuery(QueryRef
   return Q;
 }
 
+namespace {
+
 enum ParsedQueryKind {
   PQK_Invalid,
   PQK_NoOp,
   PQK_Help,
+  PQK_Let,
   PQK_Match,
-  PQK_Set
+  PQK_Set,
+  PQK_Unlet,
 };
 
 enum ParsedQueryVariable {
@@ -146,16 +150,52 @@ enum ParsedQueryVariable {
   PQV_BindRoot
 };
 
+QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
+  std::string ErrStr;
+  llvm::raw_string_ostream OS(ErrStr);
+  Diag.printToStreamFull(OS);
+  return new InvalidQuery(OS.str());
+}
+
+class QuerySessionSema : public Parser::RegistrySema {
+public:
+  QuerySessionSema(const QuerySession &QS) : QS(QS) {}
+
+  ast_matchers::dynamic::VariantValue getNamedValue(StringRef Name) override {
+    return QS.NamedValues.lookup(Name);
+  }
+
+private:
+  const QuerySession &QS;
+};
+
+}  // namespace
+
+QueryRef QueryParser::completeMatcherExpression() {
+  std::vector<MatcherCompletion> Comps = Parser::completeExpression(
+      StringRef(Begin, End - Begin), CompletionPos - Begin);
+  for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
+                                                E = Comps.end();
+       I != E; ++I) {
+    Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
+  }
+  return QueryRef();
+}
+
 QueryRef QueryParser::doParse() {
   StringRef CommandStr;
   ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
                               .Case("", PQK_NoOp)
                               .Case("help", PQK_Help)
                               .Case("m", PQK_Match, /*IsCompletion=*/false)
+                              .Case("let", PQK_Let)
                               .Case("match", PQK_Match)
                               .Case("set", PQK_Set)
+                              .Case("unlet", PQK_Unlet)
                               .Default(PQK_Invalid);
 
+  QuerySessionSema S(QS);
+
   switch (QKind) {
   case PQK_NoOp:
     return new NoOpQuery;
@@ -163,29 +203,36 @@ QueryRef QueryParser::doParse() {
   case PQK_Help:
     return endQuery(new HelpQuery);
 
+  case PQK_Let: {
+    StringRef Name = lexWord();
+
+    if (Name.empty())
+      return new InvalidQuery("expected variable name");
+
+    if (CompletionPos)
+      return completeMatcherExpression();
+
+    Diagnostics Diag;
+    ast_matchers::dynamic::VariantValue Value;
+    if (!Parser::parseExpression(StringRef(Begin, End - Begin), &S, &Value,
+                                 &Diag)) {
+      return makeInvalidQueryFromDiagnostics(Diag);
+    }
+
+    return new LetQuery(Name, Value);
+  }
+
   case PQK_Match: {
-    if (CompletionPos) {
-      std::vector<MatcherCompletion> Comps = Parser::completeExpression(
-          StringRef(Begin, End - Begin), CompletionPos - Begin);
-      for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
-                                                    E = Comps.end();
-           I != E; ++I) {
-        Completions.push_back(
-            LineEditor::Completion(I->TypedText, I->MatcherDecl));
-      }
-      return QueryRef();
-    } else {
-      Diagnostics Diag;
-      Optional<DynTypedMatcher> Matcher =
-          Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
-      if (!Matcher) {
-        std::string ErrStr;
-        llvm::raw_string_ostream OS(ErrStr);
-        Diag.printToStreamFull(OS);
-        return new InvalidQuery(OS.str());
-      }
-      return new MatchQuery(*Matcher);
+    if (CompletionPos)
+      return completeMatcherExpression();
+
+    Diagnostics Diag;
+    Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
+        StringRef(Begin, End - Begin), &S, &Diag);
+    if (!Matcher) {
+      return makeInvalidQueryFromDiagnostics(Diag);
     }
+    return new MatchQuery(*Matcher);
   }
 
   case PQK_Set: {
@@ -214,6 +261,15 @@ QueryRef QueryParser::doParse() {
     return endQuery(Q);
   }
 
+  case PQK_Unlet: {
+    StringRef Name = lexWord();
+
+    if (Name.empty())
+      return new InvalidQuery("expected variable name");
+
+    return endQuery(new LetQuery(Name, {}));
+  }
+
   case PQK_Invalid:
     return new InvalidQuery("unknown command: " + CommandStr);
   }
@@ -221,13 +277,13 @@ QueryRef QueryParser::doParse() {
   llvm_unreachable("Invalid query kind");
 }
 
-QueryRef QueryParser::parse(StringRef Line) {
-  return QueryParser(Line).doParse();
+QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
+  return QueryParser(Line, QS).doParse();
 }
 
-std::vector<LineEditor::Completion> QueryParser::complete(StringRef Line,
-                                                          size_t Pos) {
-  QueryParser P(Line);
+std::vector<LineEditor::Completion>
+QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
+  QueryParser P(Line, QS);
   P.CompletionPos = Line.data() + Pos;
 
   P.doParse();

Modified: clang-tools-extra/trunk/clang-query/QueryParser.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-query/QueryParser.h?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-query/QueryParser.h (original)
+++ clang-tools-extra/trunk/clang-query/QueryParser.h Wed Apr 23 09:04:52 2014
@@ -11,6 +11,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
 
 #include "Query.h"
+#include "QuerySession.h"
 #include "llvm/LineEditor/LineEditor.h"
 #include <stddef.h>
 
@@ -24,19 +25,20 @@ public:
   /// Parse \a Line as a query.
   ///
   /// \return A QueryRef representing the query, which may be an InvalidQuery.
-  static QueryRef parse(StringRef Line);
+  static QueryRef parse(StringRef Line, const QuerySession &QS);
 
   /// Compute a list of completions for \a Line assuming a cursor at
   /// \param Pos characters past the start of \a Line, ordered from most
   /// likely to least likely.
   ///
   /// \return A vector of completions for \a Line.
-  static std::vector<llvm::LineEditor::Completion> complete(StringRef Line,
-                                                            size_t Pos);
+  static std::vector<llvm::LineEditor::Completion>
+  complete(StringRef Line, size_t Pos, const QuerySession &QS);
 
 private:
-  QueryParser(StringRef Line)
-      : Begin(Line.data()), End(Line.data() + Line.size()), CompletionPos(0) {}
+  QueryParser(StringRef Line, const QuerySession &QS)
+      : Begin(Line.data()), End(Line.data() + Line.size()), CompletionPos(0),
+        QS(QS) {}
 
   StringRef lexWord();
 
@@ -45,6 +47,7 @@ private:
 
   QueryRef parseSetBool(bool QuerySession::*Var);
   QueryRef parseSetOutputKind();
+  QueryRef completeMatcherExpression();
 
   QueryRef endQuery(QueryRef Q);
 
@@ -59,6 +62,8 @@ private:
 
   const char *CompletionPos;
   std::vector<llvm::LineEditor::Completion> Completions;
+
+  const QuerySession &QS;
 };
 
 } // namespace query

Modified: clang-tools-extra/trunk/clang-query/QuerySession.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-query/QuerySession.h?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-query/QuerySession.h (original)
+++ clang-tools-extra/trunk/clang-query/QuerySession.h Wed Apr 23 09:04:52 2014
@@ -11,7 +11,9 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
 
 #include "Query.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
 
 namespace clang {
 
@@ -28,6 +30,7 @@ public:
   llvm::ArrayRef<ASTUnit *> ASTs;
   OutputKind OutKind;
   bool BindRoot;
+  llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
 };
 
 } // namespace query

Modified: clang-tools-extra/trunk/clang-query/tool/ClangQuery.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-query/tool/ClangQuery.cpp?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-query/tool/ClangQuery.cpp (original)
+++ clang-tools-extra/trunk/clang-query/tool/ClangQuery.cpp Wed Apr 23 09:04:52 2014
@@ -95,7 +95,7 @@ int main(int argc, const char **argv) {
     for (cl::list<std::string>::iterator I = Commands.begin(),
                                          E = Commands.end();
          I != E; ++I) {
-      QueryRef Q = QueryParser::parse(I->c_str());
+      QueryRef Q = QueryParser::parse(I->c_str(), QS);
       if (!Q->run(llvm::outs(), QS))
         return 1;
     }
@@ -112,16 +112,18 @@ int main(int argc, const char **argv) {
         std::string Line;
         std::getline(Input, Line);
 
-        QueryRef Q = QueryParser::parse(Line.c_str());
+        QueryRef Q = QueryParser::parse(Line.c_str(), QS);
         if (!Q->run(llvm::outs(), QS))
           return 1;
       }
     }
   } else {
     LineEditor LE("clang-query");
-    LE.setListCompleter(QueryParser::complete);
+    LE.setListCompleter([&QS](StringRef Line, size_t Pos) {
+      return QueryParser::complete(Line, Pos, QS);
+    });
     while (llvm::Optional<std::string> Line = LE.readLine()) {
-      QueryRef Q = QueryParser::parse(*Line);
+      QueryRef Q = QueryParser::parse(*Line, QS);
       Q->run(llvm::outs(), QS);
     }
   }

Modified: clang-tools-extra/trunk/unittests/clang-query/QueryEngineTest.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-query/QueryEngineTest.cpp?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clang-query/QueryEngineTest.cpp (original)
+++ clang-tools-extra/trunk/unittests/clang-query/QueryEngineTest.cpp Wed Apr 23 09:04:52 2014
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Query.h"
+#include "QueryParser.h"
 #include "QuerySession.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/ASTMatchers/Dynamic/VariantValue.h"
@@ -24,20 +25,22 @@ using namespace clang::ast_matchers::dyn
 using namespace clang::query;
 using namespace clang::tooling;
 
-TEST(Query, Basic) {
-  std::unique_ptr<ASTUnit> FooAST(
-      buildASTFromCode("void foo1(void) {}\nvoid foo2(void) {}", "foo.cc"));
-  ASSERT_TRUE(FooAST.get());
-  std::unique_ptr<ASTUnit> BarAST(
-      buildASTFromCode("void bar1(void) {}\nvoid bar2(void) {}", "bar.cc"));
-  ASSERT_TRUE(BarAST.get());
-
-  ASTUnit *ASTs[] = { FooAST.get(), BarAST.get() };
+class QueryEngineTest : public ::testing::Test {
+protected:
+  QueryEngineTest() {}
+
+  std::unique_ptr<ASTUnit> FooAST{
+      buildASTFromCode("void foo1(void) {}\nvoid foo2(void) {}", "foo.cc")};
+  std::unique_ptr<ASTUnit> BarAST{
+      buildASTFromCode("void bar1(void) {}\nvoid bar2(void) {}", "bar.cc")};
+  ASTUnit *ASTs[2]{FooAST.get(), BarAST.get()};
+  QuerySession S{ASTs};
 
   std::string Str;
-  llvm::raw_string_ostream OS(Str);
-  QuerySession S(ASTs);
+  llvm::raw_string_ostream OS{Str};
+};
 
+TEST_F(QueryEngineTest, Basic) {
   DynTypedMatcher FnMatcher = functionDecl();
   DynTypedMatcher FooMatcher = functionDecl(hasName("foo1"));
 
@@ -108,3 +111,28 @@ TEST(Query, Basic) {
 
   EXPECT_EQ("Not a valid top-level matcher.\n", OS.str());
 }
+
+TEST_F(QueryEngineTest, LetAndMatch) {
+  EXPECT_TRUE(QueryParser::parse("let x \"foo1\"", S)->run(OS, S));
+  EXPECT_EQ("", OS.str());
+  Str.clear();
+
+  EXPECT_TRUE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
+  EXPECT_EQ("", OS.str());
+  Str.clear();
+
+  EXPECT_TRUE(QueryParser::parse("match functionDecl(y)", S)->run(OS, S));
+  EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("1 match.") != std::string::npos);
+  Str.clear();
+
+  EXPECT_TRUE(QueryParser::parse("unlet x", S)->run(OS, S));
+  EXPECT_EQ("", OS.str());
+  Str.clear();
+
+  EXPECT_FALSE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
+  EXPECT_EQ("1:2: Error parsing argument 1 for matcher hasName.\n"
+            "1:10: Value not found: x\n", OS.str());
+  Str.clear();
+}

Modified: clang-tools-extra/trunk/unittests/clang-query/QueryParserTest.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-query/QueryParserTest.cpp?rev=206984&r1=206983&r2=206984&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clang-query/QueryParserTest.cpp (original)
+++ clang-tools-extra/trunk/unittests/clang-query/QueryParserTest.cpp Wed Apr 23 09:04:52 2014
@@ -16,95 +16,139 @@
 using namespace clang;
 using namespace clang::query;
 
-TEST(QueryParser, NoOp) {
-  QueryRef Q = QueryParser::parse("");
+class QueryParserTest : public ::testing::Test {
+protected:
+  QueryParserTest() {}
+  QueryRef parse(StringRef Code) { return QueryParser::parse(Code, QS); }
+
+  QuerySession QS{llvm::ArrayRef<ASTUnit*>()};
+};
+
+TEST_F(QueryParserTest, NoOp) {
+  QueryRef Q = parse("");
   EXPECT_TRUE(isa<NoOpQuery>(Q));
 
-  Q = QueryParser::parse("\n");
+  Q = parse("\n");
   EXPECT_TRUE(isa<NoOpQuery>(Q));
 }
 
-TEST(QueryParser, Invalid) {
-  QueryRef Q = QueryParser::parse("foo");
+TEST_F(QueryParserTest, Invalid) {
+  QueryRef Q = parse("foo");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("unknown command: foo", cast<InvalidQuery>(Q)->ErrStr);
 }
 
-TEST(QueryParser, Help) {
-  QueryRef Q = QueryParser::parse("help");
+TEST_F(QueryParserTest, Help) {
+  QueryRef Q = parse("help");
   ASSERT_TRUE(isa<HelpQuery>(Q));
 
-  Q = QueryParser::parse("help me");
+  Q = parse("help me");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
 }
 
-TEST(QueryParser, Set) {
-  QueryRef Q = QueryParser::parse("set");
+TEST_F(QueryParserTest, Set) {
+  QueryRef Q = parse("set");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
 
-  Q = QueryParser::parse("set foo bar");
+  Q = parse("set foo bar");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);
 
-  Q = QueryParser::parse("set output");
+  Q = parse("set output");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("expected 'diag', 'print' or 'dump', got ''",
             cast<InvalidQuery>(Q)->ErrStr);
 
-  Q = QueryParser::parse("set bind-root true foo");
+  Q = parse("set bind-root true foo");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);
 
-  Q = QueryParser::parse("set output foo");
+  Q = parse("set output foo");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("expected 'diag', 'print' or 'dump', got 'foo'",
             cast<InvalidQuery>(Q)->ErrStr);
 
-  Q = QueryParser::parse("set output dump");
+  Q = parse("set output dump");
   ASSERT_TRUE(isa<SetQuery<OutputKind> >(Q));
   EXPECT_EQ(&QuerySession::OutKind, cast<SetQuery<OutputKind> >(Q)->Var);
   EXPECT_EQ(OK_Dump, cast<SetQuery<OutputKind> >(Q)->Value);
 
-  Q = QueryParser::parse("set bind-root foo");
+  Q = parse("set bind-root foo");
   ASSERT_TRUE(isa<InvalidQuery>(Q));
   EXPECT_EQ("expected 'true' or 'false', got 'foo'",
             cast<InvalidQuery>(Q)->ErrStr);
 
-  Q = QueryParser::parse("set bind-root true");
+  Q = parse("set bind-root true");
   ASSERT_TRUE(isa<SetQuery<bool> >(Q));
   EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);
   EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);
 }
 
-TEST(QueryParser, Match) {
-  QueryRef Q = QueryParser::parse("match decl()");
+TEST_F(QueryParserTest, Match) {
+  QueryRef Q = parse("match decl()");
   ASSERT_TRUE(isa<MatchQuery>(Q));
   EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());
 
-  Q = QueryParser::parse("m stmt()");
+  Q = parse("m stmt()");
   ASSERT_TRUE(isa<MatchQuery>(Q));
   EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());
 }
 
-TEST(QueryParser, Complete) {
+TEST_F(QueryParserTest, LetUnlet) {
+  QueryRef Q = parse("let foo decl()");
+  ASSERT_TRUE(isa<LetQuery>(Q));
+  EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);
+  EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());
+  EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());
+
+  Q = parse("let bar \"str\"");
+  ASSERT_TRUE(isa<LetQuery>(Q));
+  EXPECT_EQ("bar", cast<LetQuery>(Q)->Name);
+  EXPECT_TRUE(cast<LetQuery>(Q)->Value.isString());
+  EXPECT_EQ("str", cast<LetQuery>(Q)->Value.getString());
+
+  Q = parse("let");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("unlet x");
+  ASSERT_TRUE(isa<LetQuery>(Q));
+  EXPECT_EQ("x", cast<LetQuery>(Q)->Name);
+  EXPECT_FALSE(cast<LetQuery>(Q)->Value.hasValue());
+
+  Q = parse("unlet");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("unlet x bad_data");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("unexpected extra input: ' bad_data'",
+            cast<InvalidQuery>(Q)->ErrStr);
+}
+
+TEST_F(QueryParserTest, Complete) {
   std::vector<llvm::LineEditor::Completion> Comps =
-      QueryParser::complete("", 0);
-  ASSERT_EQ(3u, Comps.size());
+      QueryParser::complete("", 0, QS);
+  ASSERT_EQ(5u, Comps.size());
   EXPECT_EQ("help ", Comps[0].TypedText);
   EXPECT_EQ("help", Comps[0].DisplayText);
-  EXPECT_EQ("match ", Comps[1].TypedText);
-  EXPECT_EQ("match", Comps[1].DisplayText);
-  EXPECT_EQ("set ", Comps[2].TypedText);
-  EXPECT_EQ("set", Comps[2].DisplayText);
+  EXPECT_EQ("let ", Comps[1].TypedText);
+  EXPECT_EQ("let", Comps[1].DisplayText);
+  EXPECT_EQ("match ", Comps[2].TypedText);
+  EXPECT_EQ("match", Comps[2].DisplayText);
+  EXPECT_EQ("set ", Comps[3].TypedText);
+  EXPECT_EQ("set", Comps[3].DisplayText);
+  EXPECT_EQ("unlet ", Comps[4].TypedText);
+  EXPECT_EQ("unlet", Comps[4].DisplayText);
 
-  Comps = QueryParser::complete("set o", 5);
+  Comps = QueryParser::complete("set o", 5, QS);
   ASSERT_EQ(1u, Comps.size());
   EXPECT_EQ("utput ", Comps[0].TypedText);
   EXPECT_EQ("output", Comps[0].DisplayText);
 
-  Comps = QueryParser::complete("match while", 11);
+  Comps = QueryParser::complete("match while", 11, QS);
   ASSERT_EQ(1u, Comps.size());
   EXPECT_EQ("Stmt(", Comps[0].TypedText);
   EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",





More information about the cfe-commits mailing list