Clang Project

clang_source_code/unittests/AST/MatchVerifier.h
1//===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9//  Provides MatchVerifier, a base class to implement gtest matchers that
10//  verify things that can be matched on the AST.
11//
12//  Also implements matchers based on MatchVerifier:
13//  LocationVerifier and RangeVerifier to verify whether a matched node has
14//  the expected source location or source range.
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
28namespace clang {
29namespace ast_matchers {
30
31/// \brief Base class for verifying some property of nodes found by a matcher.
32template <typename NodeType>
33class MatchVerifier : public MatchFinder::MatchCallback {
34public:
35  template <typename MatcherType>
36  testing::AssertionResult match(const std::string &Code,
37                                 const MatcherType &AMatcher) {
38    std::vector<std::stringArgs;
39    return match(CodeAMatcherArgsLang_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::stringArgs;
47    return match(CodeAMatcherArgsL);
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
59protected:
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
73private:
74  bool Verified;
75  std::string VerifyResult;
76};
77
78/// \brief Runs a matcher over some code, and returns the result of the
79/// verifier for the matched node.
80template <typename NodeType> template <typename MatcherType>
81testing::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::FrontendActionFactoryFactory(
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  // Default to failure in case callback is never called
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/// \brief Runs a matcher over some AST, and returns the result of the
129/// verifier for the matched node.
130template <typename NodeType> template <typename MatcherType>
131testing::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(*DD->getASTContext());
138
139  if (!Verified)
140    return testing::AssertionFailure() << VerifyResult;
141  return testing::AssertionSuccess();
142}
143
144template <typename NodeType>
145void 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    // Callback has been called, default to success.
151    setSuccess();
152    verify(Result, *Node);
153  }
154}
155
156template <>
157inline 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    // Callback has been called, default to success.
165    setSuccess();
166    verify(Result, I->second);
167  }
168}
169
170/// \brief Verify whether a node has the correct source location.
171///
172/// By default, Node.getSourceLocation() is checked. This can be changed
173/// by overriding getLocation().
174template <typename NodeType>
175class LocationVerifier : public MatchVerifier<NodeType> {
176public:
177  void expectLocation(unsigned Line, unsigned Column) {
178    ExpectLine = Line;
179    ExpectColumn = Column;
180  }
181
182protected:
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
203private:
204  unsigned ExpectLine, ExpectColumn;
205};
206
207/// \brief Verify whether a node has the correct source range.
208///
209/// By default, Node.getSourceRange() is checked. This can be changed
210/// by overriding getRange().
211template <typename NodeType>
212class RangeVerifier : public MatchVerifier<NodeType> {
213public:
214  void expectRange(unsigned BeginLineunsigned BeginColumn,
215                   unsigned EndLineunsigned EndColumn) {
216    ExpectBeginLine = BeginLine;
217    ExpectBeginColumn = BeginColumn;
218    ExpectEndLine = EndLine;
219    ExpectEndColumn = EndColumn;
220  }
221
222protected:
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
250private:
251  unsigned ExpectBeginLineExpectBeginColumnExpectEndLineExpectEndColumn;
252};
253
254/// \brief Verify whether a node's dump contains a given substring.
255class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
256public:
257  void expectSubstring(const std::string &Str) {
258    ExpectSubstring = Str;
259  }
260
261protected:
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
277private:
278  std::string ExpectSubstring;
279};
280
281/// \brief Verify whether a node's pretty print matches a given string.
282class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
283public:
284  void expectString(const std::string &Str) {
285    ExpectString = Str;
286  }
287
288protected:
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
304private:
305  std::string ExpectString;
306};
307
308// end namespace ast_matchers
309// end namespace clang
310
311#endif
312
clang::ast_matchers::MatchVerifier::match
clang::ast_matchers::MatchVerifier::match
clang::ast_matchers::MatchVerifier::match
clang::ast_matchers::MatchVerifier::match
clang::ast_matchers::MatchVerifier::run
clang::ast_matchers::MatchVerifier::verify
clang::ast_matchers::MatchVerifier::setFailure
clang::ast_matchers::MatchVerifier::setSuccess
clang::ast_matchers::MatchVerifier::Verified
clang::ast_matchers::MatchVerifier::VerifyResult
clang::ast_matchers::MatchVerifier::match
clang::ast_matchers::MatchVerifier::match
clang::ast_matchers::MatchVerifier::run
clang::ast_matchers::RangeVerifier::expectRange
clang::ast_matchers::RangeVerifier::verify
clang::ast_matchers::RangeVerifier::getRange
clang::ast_matchers::RangeVerifier::ExpectBeginLine
clang::ast_matchers::RangeVerifier::ExpectBeginColumn
clang::ast_matchers::RangeVerifier::ExpectEndLine
clang::ast_matchers::RangeVerifier::ExpectEndColumn
clang::ast_matchers::DumpVerifier::expectSubstring
clang::ast_matchers::DumpVerifier::verify
clang::ast_matchers::DumpVerifier::ExpectSubstring
clang::ast_matchers::PrintVerifier::expectString
clang::ast_matchers::PrintVerifier::verify
clang::ast_matchers::PrintVerifier::ExpectString