Hi pcc,
Add new 'let' command to bind arbitrary values into constants.
These constants can then be used in the matcher expressions.
http://reviews.llvm.org/D3383
Files:
clang-query/Query.cpp
clang-query/Query.h
clang-query/QueryParser.cpp
clang-query/QueryParser.h
clang-query/QuerySession.h
clang-query/tool/ClangQuery.cpp
unittests/clang-query/QueryEngineTest.cpp
unittests/clang-query/QueryParserTest.cpp
Index: clang-query/Query.cpp
===================================================================
--- clang-query/Query.cpp
+++ clang-query/Query.cpp
@@ -54,7 +54,7 @@
}
};
-}
+} // namespace
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
unsigned MatchCount = 0;
@@ -124,6 +124,15 @@
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;
Index: clang-query/Query.h
===================================================================
--- clang-query/Query.h
+++ clang-query/Query.h
@@ -30,7 +30,8 @@
QK_Help,
QK_Match,
QK_SetBool,
- QK_SetOutputKind
+ QK_SetOutputKind,
+ QK_Let,
};
class QuerySession;
@@ -86,6 +87,17 @@
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> {
Index: clang-query/QueryParser.cpp
===================================================================
--- clang-query/QueryParser.cpp
+++ clang-query/QueryParser.cpp
@@ -132,30 +132,68 @@
return Q;
}
+namespace {
+
enum ParsedQueryKind {
PQK_Invalid,
PQK_NoOp,
PQK_Help,
PQK_Match,
- PQK_Set
+ PQK_Set,
+ PQK_Let,
};
enum ParsedQueryVariable {
PQV_Invalid,
PQV_Output,
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("match", PQK_Match)
.Case("set", PQK_Set)
+ .Case("let", PQK_Let)
.Default(PQK_Invalid);
+ QuerySessionSema S(QS);
+
switch (QKind) {
case PQK_NoOp:
return new NoOpQuery;
@@ -165,29 +203,35 @@
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();
+ return completeMatcherExpression();
} else {
Diagnostics Diag;
- Optional<DynTypedMatcher> Matcher =
- Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
+ Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
+ StringRef(Begin, End - Begin), &S, &Diag);
if (!Matcher) {
- std::string ErrStr;
- llvm::raw_string_ostream OS(ErrStr);
- Diag.printToStreamFull(OS);
- return new InvalidQuery(OS.str());
+ return makeInvalidQueryFromDiagnostics(Diag);
}
return new MatchQuery(*Matcher);
}
}
+ case PQK_Let: {
+ StringRef Name = lexWord();
+
+ if (CompletionPos) {
+ return completeMatcherExpression();
+ } else {
+ 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_Set: {
StringRef VarStr;
ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
@@ -221,13 +265,13 @@
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();
Index: clang-query/QueryParser.h
===================================================================
--- clang-query/QueryParser.h
+++ clang-query/QueryParser.h
@@ -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,27 +25,29 @@
/// 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();
template <typename T> struct LexOrCompleteWord;
template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
QueryRef parseSetBool(bool QuerySession::*Var);
QueryRef parseSetOutputKind();
+ QueryRef completeMatcherExpression();
QueryRef endQuery(QueryRef Q);
@@ -59,6 +62,8 @@
const char *CompletionPos;
std::vector<llvm::LineEditor::Completion> Completions;
+
+ const QuerySession &QS;
};
} // namespace query
Index: clang-query/QuerySession.h
===================================================================
--- clang-query/QuerySession.h
+++ clang-query/QuerySession.h
@@ -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 @@
llvm::ArrayRef<ASTUnit *> ASTs;
OutputKind OutKind;
bool BindRoot;
+ llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
};
} // namespace query
Index: clang-query/tool/ClangQuery.cpp
===================================================================
--- clang-query/tool/ClangQuery.cpp
+++ clang-query/tool/ClangQuery.cpp
@@ -60,6 +60,12 @@
cl::desc("<source0> [... <sourceN>]"),
cl::OneOrMore);
+const QuerySession *GlobalQS;
+static std::vector<llvm::LineEditor::Completion> complete(StringRef Line,
+ size_t Pos) {
+ return QueryParser::complete(Line, Pos, *GlobalQS);
+}
+
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal();
cl::ParseCommandLineOptions(argc, argv);
@@ -90,12 +96,13 @@
return 1;
QuerySession QS(ASTs);
+ GlobalQS = &QS;
if (!Commands.empty()) {
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 +119,16 @@
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(complete);
while (llvm::Optional<std::string> Line = LE.readLine()) {
- QueryRef Q = QueryParser::parse(*Line);
+ QueryRef Q = QueryParser::parse(*Line, QS);
Q->run(llvm::outs(), QS);
}
}
Index: unittests/clang-query/QueryEngineTest.cpp
===================================================================
--- unittests/clang-query/QueryEngineTest.cpp
+++ unittests/clang-query/QueryEngineTest.cpp
@@ -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::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());
+class QueryEngineTest : public ::testing::Test {
+protected:
+ QueryEngineTest() {}
- ASTUnit *ASTs[] = { FooAST.get(), BarAST.get() };
+ 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,22 @@
EXPECT_EQ("Not a valid top-level matcher.\n", OS.str());
}
+
+TEST_F(QueryEngineTest, LetAndMatch) {
+ QueryRef LetString = QueryParser::parse("let x \"foo1\"", S);
+ EXPECT_TRUE(LetString->run(OS, S));
+ EXPECT_EQ("", OS.str());
+ Str.clear();
+
+ QueryRef LetMatcher = QueryParser::parse("let y hasName(x)", S);
+ EXPECT_TRUE(LetMatcher->run(OS, S));
+ EXPECT_EQ("", OS.str());
+ Str.clear();
+
+ QueryRef Match = QueryParser::parse("match functionDecl(y)", S);
+ EXPECT_TRUE(Match->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();
+}
Index: unittests/clang-query/QueryParserTest.cpp
===================================================================
--- unittests/clang-query/QueryParserTest.cpp
+++ unittests/clang-query/QueryParserTest.cpp
@@ -16,95 +16,119 @@
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, Let) {
+ 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());
+}
+
+TEST_F(QueryParserTest, Complete) {
std::vector<llvm::LineEditor::Completion> Comps =
- QueryParser::complete("", 0);
- ASSERT_EQ(3u, Comps.size());
+ QueryParser::complete("", 0, QS);
+ ASSERT_EQ(4u, 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[3].TypedText);
+ EXPECT_EQ("let", Comps[3].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>...)",
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits