Skip to content

Commit 1f6066c

Browse files
committed
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 llvm-svn: 206984
1 parent 5a7c364 commit 1f6066c

File tree

8 files changed

+237
-78
lines changed

8 files changed

+237
-78
lines changed

clang-tools-extra/clang-query/Query.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ struct CollectBoundNodes : MatchFinder::MatchCallback {
5454
}
5555
};
5656

57-
}
57+
} // namespace
5858

5959
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
6060
unsigned MatchCount = 0;
@@ -124,6 +124,15 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
124124
return true;
125125
}
126126

127+
bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
128+
if (Value) {
129+
QS.NamedValues[Name] = Value;
130+
} else {
131+
QS.NamedValues.erase(Name);
132+
}
133+
return true;
134+
}
135+
127136
#ifndef _MSC_VER
128137
const QueryKind SetQueryKind<bool>::value;
129138
const QueryKind SetQueryKind<OutputKind>::value;

clang-tools-extra/clang-query/Query.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ enum QueryKind {
2828
QK_Invalid,
2929
QK_NoOp,
3030
QK_Help,
31+
QK_Let,
3132
QK_Match,
3233
QK_SetBool,
33-
QK_SetOutputKind
34+
QK_SetOutputKind,
3435
};
3536

3637
class QuerySession;
@@ -86,6 +87,17 @@ struct MatchQuery : Query {
8687
static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
8788
};
8889

90+
struct LetQuery : Query {
91+
LetQuery(StringRef Name, const ast_matchers::dynamic::VariantValue &Value)
92+
: Query(QK_Let), Name(Name), Value(Value) {}
93+
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
94+
95+
std::string Name;
96+
ast_matchers::dynamic::VariantValue Value;
97+
98+
static bool classof(const Query *Q) { return Q->Kind == QK_Let; }
99+
};
100+
89101
template <typename T> struct SetQueryKind {};
90102

91103
template <> struct SetQueryKind<bool> {

clang-tools-extra/clang-query/QueryParser.cpp

+83-27
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,16 @@ QueryRef QueryParser::endQuery(QueryRef Q) {
132132
return Q;
133133
}
134134

135+
namespace {
136+
135137
enum ParsedQueryKind {
136138
PQK_Invalid,
137139
PQK_NoOp,
138140
PQK_Help,
141+
PQK_Let,
139142
PQK_Match,
140-
PQK_Set
143+
PQK_Set,
144+
PQK_Unlet,
141145
};
142146

143147
enum ParsedQueryVariable {
@@ -146,46 +150,89 @@ enum ParsedQueryVariable {
146150
PQV_BindRoot
147151
};
148152

153+
QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
154+
std::string ErrStr;
155+
llvm::raw_string_ostream OS(ErrStr);
156+
Diag.printToStreamFull(OS);
157+
return new InvalidQuery(OS.str());
158+
}
159+
160+
class QuerySessionSema : public Parser::RegistrySema {
161+
public:
162+
QuerySessionSema(const QuerySession &QS) : QS(QS) {}
163+
164+
ast_matchers::dynamic::VariantValue getNamedValue(StringRef Name) override {
165+
return QS.NamedValues.lookup(Name);
166+
}
167+
168+
private:
169+
const QuerySession &QS;
170+
};
171+
172+
} // namespace
173+
174+
QueryRef QueryParser::completeMatcherExpression() {
175+
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
176+
StringRef(Begin, End - Begin), CompletionPos - Begin);
177+
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
178+
E = Comps.end();
179+
I != E; ++I) {
180+
Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
181+
}
182+
return QueryRef();
183+
}
184+
149185
QueryRef QueryParser::doParse() {
150186
StringRef CommandStr;
151187
ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
152188
.Case("", PQK_NoOp)
153189
.Case("help", PQK_Help)
154190
.Case("m", PQK_Match, /*IsCompletion=*/false)
191+
.Case("let", PQK_Let)
155192
.Case("match", PQK_Match)
156193
.Case("set", PQK_Set)
194+
.Case("unlet", PQK_Unlet)
157195
.Default(PQK_Invalid);
158196

197+
QuerySessionSema S(QS);
198+
159199
switch (QKind) {
160200
case PQK_NoOp:
161201
return new NoOpQuery;
162202

163203
case PQK_Help:
164204
return endQuery(new HelpQuery);
165205

206+
case PQK_Let: {
207+
StringRef Name = lexWord();
208+
209+
if (Name.empty())
210+
return new InvalidQuery("expected variable name");
211+
212+
if (CompletionPos)
213+
return completeMatcherExpression();
214+
215+
Diagnostics Diag;
216+
ast_matchers::dynamic::VariantValue Value;
217+
if (!Parser::parseExpression(StringRef(Begin, End - Begin), &S, &Value,
218+
&Diag)) {
219+
return makeInvalidQueryFromDiagnostics(Diag);
220+
}
221+
222+
return new LetQuery(Name, Value);
223+
}
224+
166225
case PQK_Match: {
167-
if (CompletionPos) {
168-
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
169-
StringRef(Begin, End - Begin), CompletionPos - Begin);
170-
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
171-
E = Comps.end();
172-
I != E; ++I) {
173-
Completions.push_back(
174-
LineEditor::Completion(I->TypedText, I->MatcherDecl));
175-
}
176-
return QueryRef();
177-
} else {
178-
Diagnostics Diag;
179-
Optional<DynTypedMatcher> Matcher =
180-
Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
181-
if (!Matcher) {
182-
std::string ErrStr;
183-
llvm::raw_string_ostream OS(ErrStr);
184-
Diag.printToStreamFull(OS);
185-
return new InvalidQuery(OS.str());
186-
}
187-
return new MatchQuery(*Matcher);
226+
if (CompletionPos)
227+
return completeMatcherExpression();
228+
229+
Diagnostics Diag;
230+
Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
231+
StringRef(Begin, End - Begin), &S, &Diag);
232+
if (!Matcher) {
233+
return makeInvalidQueryFromDiagnostics(Diag);
188234
}
235+
return new MatchQuery(*Matcher);
189236
}
190237

191238
case PQK_Set: {
@@ -214,20 +261,29 @@ QueryRef QueryParser::doParse() {
214261
return endQuery(Q);
215262
}
216263

264+
case PQK_Unlet: {
265+
StringRef Name = lexWord();
266+
267+
if (Name.empty())
268+
return new InvalidQuery("expected variable name");
269+
270+
return endQuery(new LetQuery(Name, {}));
271+
}
272+
217273
case PQK_Invalid:
218274
return new InvalidQuery("unknown command: " + CommandStr);
219275
}
220276

221277
llvm_unreachable("Invalid query kind");
222278
}
223279

224-
QueryRef QueryParser::parse(StringRef Line) {
225-
return QueryParser(Line).doParse();
280+
QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
281+
return QueryParser(Line, QS).doParse();
226282
}
227283

228-
std::vector<LineEditor::Completion> QueryParser::complete(StringRef Line,
229-
size_t Pos) {
230-
QueryParser P(Line);
284+
std::vector<LineEditor::Completion>
285+
QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
286+
QueryParser P(Line, QS);
231287
P.CompletionPos = Line.data() + Pos;
232288

233289
P.doParse();

clang-tools-extra/clang-query/QueryParser.h

+10-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
1212

1313
#include "Query.h"
14+
#include "QuerySession.h"
1415
#include "llvm/LineEditor/LineEditor.h"
1516
#include <stddef.h>
1617

@@ -24,19 +25,20 @@ class QueryParser {
2425
/// Parse \a Line as a query.
2526
///
2627
/// \return A QueryRef representing the query, which may be an InvalidQuery.
27-
static QueryRef parse(StringRef Line);
28+
static QueryRef parse(StringRef Line, const QuerySession &QS);
2829

2930
/// Compute a list of completions for \a Line assuming a cursor at
3031
/// \param Pos characters past the start of \a Line, ordered from most
3132
/// likely to least likely.
3233
///
3334
/// \return A vector of completions for \a Line.
34-
static std::vector<llvm::LineEditor::Completion> complete(StringRef Line,
35-
size_t Pos);
35+
static std::vector<llvm::LineEditor::Completion>
36+
complete(StringRef Line, size_t Pos, const QuerySession &QS);
3637

3738
private:
38-
QueryParser(StringRef Line)
39-
: Begin(Line.data()), End(Line.data() + Line.size()), CompletionPos(0) {}
39+
QueryParser(StringRef Line, const QuerySession &QS)
40+
: Begin(Line.data()), End(Line.data() + Line.size()), CompletionPos(0),
41+
QS(QS) {}
4042

4143
StringRef lexWord();
4244

@@ -45,6 +47,7 @@ class QueryParser {
4547

4648
QueryRef parseSetBool(bool QuerySession::*Var);
4749
QueryRef parseSetOutputKind();
50+
QueryRef completeMatcherExpression();
4851

4952
QueryRef endQuery(QueryRef Q);
5053

@@ -59,6 +62,8 @@ class QueryParser {
5962

6063
const char *CompletionPos;
6164
std::vector<llvm::LineEditor::Completion> Completions;
65+
66+
const QuerySession &QS;
6267
};
6368

6469
} // namespace query

clang-tools-extra/clang-query/QuerySession.h

+3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
1212

1313
#include "Query.h"
14+
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
1415
#include "llvm/ADT/ArrayRef.h"
16+
#include "llvm/ADT/StringMap.h"
1517

1618
namespace clang {
1719

@@ -28,6 +30,7 @@ class QuerySession {
2830
llvm::ArrayRef<ASTUnit *> ASTs;
2931
OutputKind OutKind;
3032
bool BindRoot;
33+
llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
3134
};
3235

3336
} // namespace query

clang-tools-extra/clang-query/tool/ClangQuery.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ int main(int argc, const char **argv) {
9595
for (cl::list<std::string>::iterator I = Commands.begin(),
9696
E = Commands.end();
9797
I != E; ++I) {
98-
QueryRef Q = QueryParser::parse(I->c_str());
98+
QueryRef Q = QueryParser::parse(I->c_str(), QS);
9999
if (!Q->run(llvm::outs(), QS))
100100
return 1;
101101
}
@@ -112,16 +112,18 @@ int main(int argc, const char **argv) {
112112
std::string Line;
113113
std::getline(Input, Line);
114114

115-
QueryRef Q = QueryParser::parse(Line.c_str());
115+
QueryRef Q = QueryParser::parse(Line.c_str(), QS);
116116
if (!Q->run(llvm::outs(), QS))
117117
return 1;
118118
}
119119
}
120120
} else {
121121
LineEditor LE("clang-query");
122-
LE.setListCompleter(QueryParser::complete);
122+
LE.setListCompleter([&QS](StringRef Line, size_t Pos) {
123+
return QueryParser::complete(Line, Pos, QS);
124+
});
123125
while (llvm::Optional<std::string> Line = LE.readLine()) {
124-
QueryRef Q = QueryParser::parse(*Line);
126+
QueryRef Q = QueryParser::parse(*Line, QS);
125127
Q->run(llvm::outs(), QS);
126128
}
127129
}

clang-tools-extra/unittests/clang-query/QueryEngineTest.cpp

+38-10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//===----------------------------------------------------------------------===//
99

1010
#include "Query.h"
11+
#include "QueryParser.h"
1112
#include "QuerySession.h"
1213
#include "clang/ASTMatchers/ASTMatchers.h"
1314
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
@@ -24,20 +25,22 @@ using namespace clang::ast_matchers::dynamic;
2425
using namespace clang::query;
2526
using namespace clang::tooling;
2627

27-
TEST(Query, Basic) {
28-
std::unique_ptr<ASTUnit> FooAST(
29-
buildASTFromCode("void foo1(void) {}\nvoid foo2(void) {}", "foo.cc"));
30-
ASSERT_TRUE(FooAST.get());
31-
std::unique_ptr<ASTUnit> BarAST(
32-
buildASTFromCode("void bar1(void) {}\nvoid bar2(void) {}", "bar.cc"));
33-
ASSERT_TRUE(BarAST.get());
28+
class QueryEngineTest : public ::testing::Test {
29+
protected:
30+
QueryEngineTest() {}
3431

35-
ASTUnit *ASTs[] = { FooAST.get(), BarAST.get() };
32+
std::unique_ptr<ASTUnit> FooAST{
33+
buildASTFromCode("void foo1(void) {}\nvoid foo2(void) {}", "foo.cc")};
34+
std::unique_ptr<ASTUnit> BarAST{
35+
buildASTFromCode("void bar1(void) {}\nvoid bar2(void) {}", "bar.cc")};
36+
ASTUnit *ASTs[2]{FooAST.get(), BarAST.get()};
37+
QuerySession S{ASTs};
3638

3739
std::string Str;
38-
llvm::raw_string_ostream OS(Str);
39-
QuerySession S(ASTs);
40+
llvm::raw_string_ostream OS{Str};
41+
};
4042

43+
TEST_F(QueryEngineTest, Basic) {
4144
DynTypedMatcher FnMatcher = functionDecl();
4245
DynTypedMatcher FooMatcher = functionDecl(hasName("foo1"));
4346

@@ -108,3 +111,28 @@ TEST(Query, Basic) {
108111

109112
EXPECT_EQ("Not a valid top-level matcher.\n", OS.str());
110113
}
114+
115+
TEST_F(QueryEngineTest, LetAndMatch) {
116+
EXPECT_TRUE(QueryParser::parse("let x \"foo1\"", S)->run(OS, S));
117+
EXPECT_EQ("", OS.str());
118+
Str.clear();
119+
120+
EXPECT_TRUE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
121+
EXPECT_EQ("", OS.str());
122+
Str.clear();
123+
124+
EXPECT_TRUE(QueryParser::parse("match functionDecl(y)", S)->run(OS, S));
125+
EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
126+
std::string::npos);
127+
EXPECT_TRUE(OS.str().find("1 match.") != std::string::npos);
128+
Str.clear();
129+
130+
EXPECT_TRUE(QueryParser::parse("unlet x", S)->run(OS, S));
131+
EXPECT_EQ("", OS.str());
132+
Str.clear();
133+
134+
EXPECT_FALSE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
135+
EXPECT_EQ("1:2: Error parsing argument 1 for matcher hasName.\n"
136+
"1:10: Value not found: x\n", OS.str());
137+
Str.clear();
138+
}

0 commit comments

Comments
 (0)