blob: b20c1ac6e9ac58431c848ef60dc26c2c39eb3b79 [file] [log] [blame]
Manuel Klimekf7f295f2013-05-14 09:13:00 +00001//===- unittest/ASTMatchers/Dynamic/ParserTest.cpp - Parser unit tests -===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===-------------------------------------------------------------------===//
9
10#include <string>
11#include <vector>
12
13#include "../ASTMatchersTest.h"
14#include "clang/ASTMatchers/Dynamic/Parser.h"
15#include "clang/ASTMatchers/Dynamic/Registry.h"
16#include "gtest/gtest.h"
17#include "llvm/ADT/StringMap.h"
18
19namespace clang {
20namespace ast_matchers {
21namespace dynamic {
22namespace {
23
24class DummyDynTypedMatcher : public DynTypedMatcher {
25public:
26 DummyDynTypedMatcher(uint64_t ID) : ID(ID) {}
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000027 DummyDynTypedMatcher(uint64_t ID, StringRef BoundID)
28 : ID(ID), BoundID(BoundID) {}
Manuel Klimekf7f295f2013-05-14 09:13:00 +000029
30 typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder;
31 typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder;
32 virtual bool matches(const ast_type_traits::DynTypedNode DynNode,
33 ASTMatchFinder *Finder,
34 BoundNodesTreeBuilder *Builder) const {
35 return false;
36 }
37
38 /// \brief Makes a copy of this matcher object.
39 virtual DynTypedMatcher *clone() const {
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000040 return new DummyDynTypedMatcher(*this);
Manuel Klimekf7f295f2013-05-14 09:13:00 +000041 }
42
43 /// \brief Returns a unique ID for the matcher.
44 virtual uint64_t getID() const { return ID; }
45
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000046 virtual DynTypedMatcher* tryBind(StringRef BoundID) const {
47 return new DummyDynTypedMatcher(ID, BoundID);
48 }
49
50 StringRef boundID() const { return BoundID; }
51
Manuel Klimekf7f295f2013-05-14 09:13:00 +000052private:
53 uint64_t ID;
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000054 std::string BoundID;
Manuel Klimekf7f295f2013-05-14 09:13:00 +000055};
56
57class MockSema : public Parser::Sema {
58public:
59 virtual ~MockSema() {}
60
61 uint64_t expectMatcher(StringRef MatcherName) {
62 uint64_t ID = ExpectedMatchers.size() + 1;
63 ExpectedMatchers[MatcherName] = ID;
64 return ID;
65 }
66
67 void parse(StringRef Code) {
68 Diagnostics Error;
69 VariantValue Value;
70 Parser::parseExpression(Code, this, &Value, &Error);
71 Values.push_back(Value);
72 Errors.push_back(Error.ToStringFull());
73 }
74
75 DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName,
76 const SourceRange &NameRange,
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000077 StringRef BindID,
Manuel Klimekf7f295f2013-05-14 09:13:00 +000078 ArrayRef<ParserValue> Args,
79 Diagnostics *Error) {
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000080 MatcherInfo ToStore = { MatcherName, NameRange, Args, BindID };
Manuel Klimekf7f295f2013-05-14 09:13:00 +000081 Matchers.push_back(ToStore);
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000082 DummyDynTypedMatcher Matcher(ExpectedMatchers[MatcherName]);
83 return Matcher.tryBind(BindID);
Manuel Klimekf7f295f2013-05-14 09:13:00 +000084 }
85
86 struct MatcherInfo {
87 StringRef MatcherName;
88 SourceRange NameRange;
89 std::vector<ParserValue> Args;
Samuel Benzaquen4f37d922013-06-03 19:31:08 +000090 std::string BoundID;
Manuel Klimekf7f295f2013-05-14 09:13:00 +000091 };
92
93 std::vector<std::string> Errors;
94 std::vector<VariantValue> Values;
95 std::vector<MatcherInfo> Matchers;
96 llvm::StringMap<uint64_t> ExpectedMatchers;
97};
98
Samuel Benzaquen7a337af2013-06-04 15:46:22 +000099TEST(ParserTest, ParseUnsigned) {
100 MockSema Sema;
101 Sema.parse("0");
102 Sema.parse("123");
103 Sema.parse("0x1f");
104 Sema.parse("12345678901");
105 Sema.parse("1a1");
106 EXPECT_EQ(5U, Sema.Values.size());
107 EXPECT_EQ(0U, Sema.Values[0].getUnsigned());
108 EXPECT_EQ(123U, Sema.Values[1].getUnsigned());
109 EXPECT_EQ(31U, Sema.Values[2].getUnsigned());
110 EXPECT_EQ("1:1: Error parsing unsigned token: <12345678901>", Sema.Errors[3]);
111 EXPECT_EQ("1:1: Error parsing unsigned token: <1a1>", Sema.Errors[4]);
112}
113
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000114TEST(ParserTest, ParseString) {
115 MockSema Sema;
116 Sema.parse("\"Foo\"");
117 Sema.parse("\"\"");
118 Sema.parse("\"Baz");
119 EXPECT_EQ(3ULL, Sema.Values.size());
120 EXPECT_EQ("Foo", Sema.Values[0].getString());
121 EXPECT_EQ("", Sema.Values[1].getString());
122 EXPECT_EQ("1:1: Error parsing string token: <\"Baz>", Sema.Errors[2]);
123}
124
125bool matchesRange(const SourceRange &Range, unsigned StartLine,
126 unsigned EndLine, unsigned StartColumn, unsigned EndColumn) {
127 EXPECT_EQ(StartLine, Range.Start.Line);
128 EXPECT_EQ(EndLine, Range.End.Line);
129 EXPECT_EQ(StartColumn, Range.Start.Column);
130 EXPECT_EQ(EndColumn, Range.End.Column);
131 return Range.Start.Line == StartLine && Range.End.Line == EndLine &&
132 Range.Start.Column == StartColumn && Range.End.Column == EndColumn;
133}
134
135TEST(ParserTest, ParseMatcher) {
136 MockSema Sema;
137 const uint64_t ExpectedFoo = Sema.expectMatcher("Foo");
138 const uint64_t ExpectedBar = Sema.expectMatcher("Bar");
139 const uint64_t ExpectedBaz = Sema.expectMatcher("Baz");
Samuel Benzaquen7a337af2013-06-04 15:46:22 +0000140 Sema.parse(" Foo ( Bar ( 17), Baz( \n \"B A,Z\") ) .bind( \"Yo!\") ");
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000141 for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) {
142 EXPECT_EQ("", Sema.Errors[i]);
143 }
144
145 EXPECT_EQ(1ULL, Sema.Values.size());
146 EXPECT_EQ(ExpectedFoo, Sema.Values[0].getMatcher().getID());
Samuel Benzaquen4f37d922013-06-03 19:31:08 +0000147 EXPECT_EQ("Yo!", static_cast<const DummyDynTypedMatcher &>(
148 Sema.Values[0].getMatcher()).boundID());
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000149
150 EXPECT_EQ(3ULL, Sema.Matchers.size());
151 const MockSema::MatcherInfo Bar = Sema.Matchers[0];
152 EXPECT_EQ("Bar", Bar.MatcherName);
Samuel Benzaquen7a337af2013-06-04 15:46:22 +0000153 EXPECT_TRUE(matchesRange(Bar.NameRange, 1, 1, 8, 17));
154 EXPECT_EQ(1ULL, Bar.Args.size());
155 EXPECT_EQ(17U, Bar.Args[0].Value.getUnsigned());
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000156
157 const MockSema::MatcherInfo Baz = Sema.Matchers[1];
158 EXPECT_EQ("Baz", Baz.MatcherName);
Samuel Benzaquen7a337af2013-06-04 15:46:22 +0000159 EXPECT_TRUE(matchesRange(Baz.NameRange, 1, 2, 19, 10));
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000160 EXPECT_EQ(1ULL, Baz.Args.size());
161 EXPECT_EQ("B A,Z", Baz.Args[0].Value.getString());
162
163 const MockSema::MatcherInfo Foo = Sema.Matchers[2];
164 EXPECT_EQ("Foo", Foo.MatcherName);
165 EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12));
166 EXPECT_EQ(2ULL, Foo.Args.size());
167 EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID());
168 EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID());
Samuel Benzaquen4f37d922013-06-03 19:31:08 +0000169 EXPECT_EQ("Yo!", Foo.BoundID);
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000170}
171
172using ast_matchers::internal::Matcher;
173
174TEST(ParserTest, FullParserTest) {
175 OwningPtr<DynTypedMatcher> Matcher(Parser::parseMatcherExpression(
176 "hasInitializer(binaryOperator(hasLHS(integerLiteral())))", NULL));
177 EXPECT_TRUE(matchesDynamic("int x = 1 + false;", *Matcher));
178 EXPECT_FALSE(matchesDynamic("int x = true + 1;", *Matcher));
179
Samuel Benzaquen7a337af2013-06-04 15:46:22 +0000180 Matcher.reset(
181 Parser::parseMatcherExpression("hasParameter(1, hasName(\"x\"))", NULL));
182 EXPECT_TRUE(matchesDynamic("void f(int a, int x);", *Matcher));
183 EXPECT_FALSE(matchesDynamic("void f(int x, int a);", *Matcher));
184
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000185 Diagnostics Error;
186 EXPECT_TRUE(Parser::parseMatcherExpression(
187 "hasInitializer(\n binaryOperator(hasLHS(\"A\")))", &Error) == NULL);
188 EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n"
189 "2:5: Error parsing argument 1 for matcher binaryOperator.\n"
190 "2:20: Error building matcher hasLHS.\n"
191 "2:27: Incorrect type on function hasLHS for arg 1.",
192 Error.ToStringFull());
193}
194
195std::string ParseWithError(StringRef Code) {
196 Diagnostics Error;
197 VariantValue Value;
198 Parser::parseExpression(Code, &Value, &Error);
199 return Error.ToStringFull();
200}
201
202std::string ParseMatcherWithError(StringRef Code) {
203 Diagnostics Error;
204 Parser::parseMatcherExpression(Code, &Error);
205 return Error.ToStringFull();
206}
207
208TEST(ParserTest, Errors) {
209 EXPECT_EQ(
210 "1:5: Error parsing matcher. Found token <123> while looking for '('.",
211 ParseWithError("Foo 123"));
212 EXPECT_EQ(
213 "1:9: Error parsing matcher. Found token <123> while looking for ','.",
214 ParseWithError("Foo(\"A\" 123)"));
215 EXPECT_EQ(
216 "1:4: Error parsing matcher. Found end-of-code while looking for ')'.",
217 ParseWithError("Foo("));
218 EXPECT_EQ("1:1: End of code found while looking for token.",
219 ParseWithError(""));
220 EXPECT_EQ("Input value is not a matcher expression.",
221 ParseMatcherWithError("\"A\""));
222 EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n"
223 "1:5: Invalid token <(> found when looking for a value.",
224 ParseWithError("Foo(("));
Samuel Benzaquen4f37d922013-06-03 19:31:08 +0000225 EXPECT_EQ("1:7: Expected end of code.", ParseWithError("expr()a"));
226 EXPECT_EQ("1:11: Malformed bind() expression.",
227 ParseWithError("isArrow().biind"));
228 EXPECT_EQ("1:15: Malformed bind() expression.",
229 ParseWithError("isArrow().bind"));
230 EXPECT_EQ("1:16: Malformed bind() expression.",
231 ParseWithError("isArrow().bind(foo"));
232 EXPECT_EQ("1:21: Malformed bind() expression.",
233 ParseWithError("isArrow().bind(\"foo\""));
234 EXPECT_EQ("1:1: Error building matcher isArrow.\n"
235 "1:1: Matcher does not support binding.",
236 ParseWithError("isArrow().bind(\"foo\")"));
Manuel Klimekf7f295f2013-05-14 09:13:00 +0000237}
238
239} // end anonymous namespace
240} // end namespace dynamic
241} // end namespace ast_matchers
242} // end namespace clang