1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | #ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H |
19 | #define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H |
20 | |
21 | #include "clang/AST/ASTContext.h" |
22 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
23 | #include "clang/ASTMatchers/ASTMatchers.h" |
24 | #include "clang/Tooling/Tooling.h" |
25 | #include "Language.h" |
26 | #include "gtest/gtest.h" |
27 | |
28 | namespace clang { |
29 | namespace ast_matchers { |
30 | |
31 | |
32 | template <typename NodeType> |
33 | class MatchVerifier : public MatchFinder::MatchCallback { |
34 | public: |
35 | template <typename MatcherType> |
36 | testing::AssertionResult match(const std::string &Code, |
37 | const MatcherType &AMatcher) { |
38 | std::vector<std::string> Args; |
39 | return match(Code, AMatcher, Args, Lang_CXX); |
40 | } |
41 | |
42 | template <typename MatcherType> |
43 | testing::AssertionResult match(const std::string &Code, |
44 | const MatcherType &AMatcher, |
45 | Language L) { |
46 | std::vector<std::string> Args; |
47 | return match(Code, AMatcher, Args, L); |
48 | } |
49 | |
50 | template <typename MatcherType> |
51 | testing::AssertionResult match(const std::string &Code, |
52 | const MatcherType &AMatcher, |
53 | std::vector<std::string>& Args, |
54 | Language L); |
55 | |
56 | template <typename MatcherType> |
57 | testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher); |
58 | |
59 | protected: |
60 | void run(const MatchFinder::MatchResult &Result) override; |
61 | virtual void verify(const MatchFinder::MatchResult &Result, |
62 | const NodeType &Node) {} |
63 | |
64 | void setFailure(const Twine &Result) { |
65 | Verified = false; |
66 | VerifyResult = Result.str(); |
67 | } |
68 | |
69 | void setSuccess() { |
70 | Verified = true; |
71 | } |
72 | |
73 | private: |
74 | bool Verified; |
75 | std::string VerifyResult; |
76 | }; |
77 | |
78 | |
79 | |
80 | template <typename NodeType> template <typename MatcherType> |
81 | testing::AssertionResult MatchVerifier<NodeType>::match( |
82 | const std::string &Code, const MatcherType &AMatcher, |
83 | std::vector<std::string>& Args, Language L) { |
84 | MatchFinder Finder; |
85 | Finder.addMatcher(AMatcher.bind(""), this); |
86 | std::unique_ptr<tooling::FrontendActionFactory> Factory( |
87 | tooling::newFrontendActionFactory(&Finder)); |
88 | |
89 | StringRef FileName; |
90 | switch (L) { |
91 | case Lang_C: |
92 | Args.push_back("-std=c99"); |
93 | FileName = "input.c"; |
94 | break; |
95 | case Lang_C89: |
96 | Args.push_back("-std=c89"); |
97 | FileName = "input.c"; |
98 | break; |
99 | case Lang_CXX: |
100 | Args.push_back("-std=c++98"); |
101 | FileName = "input.cc"; |
102 | break; |
103 | case Lang_CXX11: |
104 | Args.push_back("-std=c++11"); |
105 | FileName = "input.cc"; |
106 | break; |
107 | case Lang_CXX14: |
108 | Args.push_back("-std=c++14"); |
109 | FileName = "input.cc"; |
110 | break; |
111 | case Lang_OpenCL: |
112 | FileName = "input.cl"; |
113 | break; |
114 | case Lang_OBJCXX: |
115 | FileName = "input.mm"; |
116 | break; |
117 | } |
118 | |
119 | |
120 | setFailure("Could not find match"); |
121 | if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) |
122 | return testing::AssertionFailure() << "Parsing error"; |
123 | if (!Verified) |
124 | return testing::AssertionFailure() << VerifyResult; |
125 | return testing::AssertionSuccess(); |
126 | } |
127 | |
128 | |
129 | |
130 | template <typename NodeType> template <typename MatcherType> |
131 | testing::AssertionResult MatchVerifier<NodeType>::match( |
132 | const Decl *D, const MatcherType &AMatcher) { |
133 | MatchFinder Finder; |
134 | Finder.addMatcher(AMatcher.bind(""), this); |
135 | |
136 | setFailure("Could not find match"); |
137 | Finder.match(*D, D->getASTContext()); |
138 | |
139 | if (!Verified) |
140 | return testing::AssertionFailure() << VerifyResult; |
141 | return testing::AssertionSuccess(); |
142 | } |
143 | |
144 | template <typename NodeType> |
145 | void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) { |
146 | const NodeType *Node = Result.Nodes.getNodeAs<NodeType>(""); |
147 | if (!Node) { |
148 | setFailure("Matched node has wrong type"); |
149 | } else { |
150 | |
151 | setSuccess(); |
152 | verify(Result, *Node); |
153 | } |
154 | } |
155 | |
156 | template <> |
157 | inline void MatchVerifier<ast_type_traits::DynTypedNode>::run( |
158 | const MatchFinder::MatchResult &Result) { |
159 | BoundNodes::IDToNodeMap M = Result.Nodes.getMap(); |
160 | BoundNodes::IDToNodeMap::const_iterator I = M.find(""); |
161 | if (I == M.end()) { |
162 | setFailure("Node was not bound"); |
163 | } else { |
164 | |
165 | setSuccess(); |
166 | verify(Result, I->second); |
167 | } |
168 | } |
169 | |
170 | |
171 | |
172 | |
173 | |
174 | template <typename NodeType> |
175 | class LocationVerifier : public MatchVerifier<NodeType> { |
176 | public: |
177 | void expectLocation(unsigned Line, unsigned Column) { |
178 | ExpectLine = Line; |
179 | ExpectColumn = Column; |
180 | } |
181 | |
182 | protected: |
183 | void verify(const MatchFinder::MatchResult &Result, |
184 | const NodeType &Node) override { |
185 | SourceLocation Loc = getLocation(Node); |
186 | unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc); |
187 | unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc); |
188 | if (Line != ExpectLine || Column != ExpectColumn) { |
189 | std::string MsgStr; |
190 | llvm::raw_string_ostream Msg(MsgStr); |
191 | Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn |
192 | << ">, found <"; |
193 | Loc.print(Msg, *Result.SourceManager); |
194 | Msg << '>'; |
195 | this->setFailure(Msg.str()); |
196 | } |
197 | } |
198 | |
199 | virtual SourceLocation getLocation(const NodeType &Node) { |
200 | return Node.getLocation(); |
201 | } |
202 | |
203 | private: |
204 | unsigned ExpectLine, ExpectColumn; |
205 | }; |
206 | |
207 | |
208 | |
209 | |
210 | |
211 | template <typename NodeType> |
212 | class RangeVerifier : public MatchVerifier<NodeType> { |
213 | public: |
214 | void expectRange(unsigned BeginLine, unsigned BeginColumn, |
215 | unsigned EndLine, unsigned EndColumn) { |
216 | ExpectBeginLine = BeginLine; |
217 | ExpectBeginColumn = BeginColumn; |
218 | ExpectEndLine = EndLine; |
219 | ExpectEndColumn = EndColumn; |
220 | } |
221 | |
222 | protected: |
223 | void verify(const MatchFinder::MatchResult &Result, |
224 | const NodeType &Node) override { |
225 | SourceRange R = getRange(Node); |
226 | SourceLocation Begin = R.getBegin(); |
227 | SourceLocation End = R.getEnd(); |
228 | unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin); |
229 | unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin); |
230 | unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End); |
231 | unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End); |
232 | if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn || |
233 | EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) { |
234 | std::string MsgStr; |
235 | llvm::raw_string_ostream Msg(MsgStr); |
236 | Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn |
237 | << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <"; |
238 | Begin.print(Msg, *Result.SourceManager); |
239 | Msg << '-'; |
240 | End.print(Msg, *Result.SourceManager); |
241 | Msg << '>'; |
242 | this->setFailure(Msg.str()); |
243 | } |
244 | } |
245 | |
246 | virtual SourceRange getRange(const NodeType &Node) { |
247 | return Node.getSourceRange(); |
248 | } |
249 | |
250 | private: |
251 | unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn; |
252 | }; |
253 | |
254 | |
255 | class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> { |
256 | public: |
257 | void expectSubstring(const std::string &Str) { |
258 | ExpectSubstring = Str; |
259 | } |
260 | |
261 | protected: |
262 | void verify(const MatchFinder::MatchResult &Result, |
263 | const ast_type_traits::DynTypedNode &Node) override { |
264 | std::string DumpStr; |
265 | llvm::raw_string_ostream Dump(DumpStr); |
266 | Node.dump(Dump, *Result.SourceManager); |
267 | |
268 | if (Dump.str().find(ExpectSubstring) == std::string::npos) { |
269 | std::string MsgStr; |
270 | llvm::raw_string_ostream Msg(MsgStr); |
271 | Msg << "Expected dump substring <" << ExpectSubstring << ">, found <" |
272 | << Dump.str() << '>'; |
273 | this->setFailure(Msg.str()); |
274 | } |
275 | } |
276 | |
277 | private: |
278 | std::string ExpectSubstring; |
279 | }; |
280 | |
281 | |
282 | class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> { |
283 | public: |
284 | void expectString(const std::string &Str) { |
285 | ExpectString = Str; |
286 | } |
287 | |
288 | protected: |
289 | void verify(const MatchFinder::MatchResult &Result, |
290 | const ast_type_traits::DynTypedNode &Node) override { |
291 | std::string PrintStr; |
292 | llvm::raw_string_ostream Print(PrintStr); |
293 | Node.print(Print, Result.Context->getPrintingPolicy()); |
294 | |
295 | if (Print.str() != ExpectString) { |
296 | std::string MsgStr; |
297 | llvm::raw_string_ostream Msg(MsgStr); |
298 | Msg << "Expected pretty print <" << ExpectString << ">, found <" |
299 | << Print.str() << '>'; |
300 | this->setFailure(Msg.str()); |
301 | } |
302 | } |
303 | |
304 | private: |
305 | std::string ExpectString; |
306 | }; |
307 | |
308 | } |
309 | } |
310 | |
311 | #endif |
312 | |