brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.6 KiB · e289469 Raw
304 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8//9//  Provides MatchVerifier, a base class to implement gtest matchers that10//  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 has14//  the expected source location or source range.15//16//===----------------------------------------------------------------------===//17 18#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H19#define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H20 21#include "clang/AST/ASTContext.h"22#include "clang/ASTMatchers/ASTMatchFinder.h"23#include "clang/ASTMatchers/ASTMatchers.h"24#include "clang/Testing/CommandLineArgs.h"25#include "clang/Tooling/Tooling.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::string> Args;39    return match(Code, AMatcher, Args, Lang_CXX03);40  }41 42  template <typename MatcherType>43  testing::AssertionResult match(const std::string &Code,44                                 const MatcherType &AMatcher, TestLanguage L) {45    std::vector<std::string> Args;46    return match(Code, AMatcher, Args, L);47  }48 49  template <typename MatcherType>50  testing::AssertionResult51  match(const std::string &Code, const MatcherType &AMatcher,52        std::vector<std::string> &Args, TestLanguage L);53 54  template <typename MatcherType>55  testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher);56 57protected:58  void run(const MatchFinder::MatchResult &Result) override;59  virtual void verify(const MatchFinder::MatchResult &Result,60                      const NodeType &Node) {}61 62  void setFailure(const Twine &Result) {63    Verified = false;64    VerifyResult = Result.str();65  }66 67  void setSuccess() {68    Verified = true;69  }70 71private:72  bool Verified;73  std::string VerifyResult;74};75 76/// \brief Runs a matcher over some code, and returns the result of the77/// verifier for the matched node.78template <typename NodeType>79template <typename MatcherType>80testing::AssertionResult81MatchVerifier<NodeType>::match(const std::string &Code,82                               const MatcherType &AMatcher,83                               std::vector<std::string> &Args, TestLanguage 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#define TESTLANGUAGE(lang, version, std_flag, version_index)                   \92  case Lang_##lang##version:                                                   \93    Args.push_back("-std=" #std_flag);                                         \94    FileName = getFilenameForTesting(Lang_##lang##version);                    \95    break;96#include "clang/Testing/TestLanguage.def"97 98  case Lang_OpenCL:99    Args.push_back("-cl-no-stdinc");100    FileName = "input.cl";101    break;102  case Lang_OBJC:103    Args.push_back("-fobjc-nonfragile-abi");104    FileName = "input.m";105    break;106  case Lang_OBJCXX:107    FileName = "input.mm";108    break;109  }110 111  // Default to failure in case callback is never called112  setFailure("Could not find match");113  if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))114    return testing::AssertionFailure() << "Parsing error";115  if (!Verified)116    return testing::AssertionFailure() << VerifyResult;117  return testing::AssertionSuccess();118}119 120/// \brief Runs a matcher over some AST, and returns the result of the121/// verifier for the matched node.122template <typename NodeType> template <typename MatcherType>123testing::AssertionResult MatchVerifier<NodeType>::match(124    const Decl *D, const MatcherType &AMatcher) {125  MatchFinder Finder;126  Finder.addMatcher(AMatcher.bind(""), this);127 128  setFailure("Could not find match");129  Finder.match(*D, D->getASTContext());130 131  if (!Verified)132    return testing::AssertionFailure() << VerifyResult;133  return testing::AssertionSuccess();134}135 136template <typename NodeType>137void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {138  const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");139  if (!Node) {140    setFailure("Matched node has wrong type");141  } else {142    // Callback has been called, default to success.143    setSuccess();144    verify(Result, *Node);145  }146}147 148template <>149inline void150MatchVerifier<DynTypedNode>::run(const MatchFinder::MatchResult &Result) {151  BoundNodes::IDToNodeMap M = Result.Nodes.getMap();152  BoundNodes::IDToNodeMap::const_iterator I = M.find("");153  if (I == M.end()) {154    setFailure("Node was not bound");155  } else {156    // Callback has been called, default to success.157    setSuccess();158    verify(Result, I->second);159  }160}161 162/// \brief Verify whether a node has the correct source location.163///164/// By default, Node.getSourceLocation() is checked. This can be changed165/// by overriding getLocation().166template <typename NodeType>167class LocationVerifier : public MatchVerifier<NodeType> {168public:169  void expectLocation(unsigned Line, unsigned Column) {170    ExpectLine = Line;171    ExpectColumn = Column;172  }173 174protected:175  void verify(const MatchFinder::MatchResult &Result,176              const NodeType &Node) override {177    SourceLocation Loc = getLocation(Node);178    unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);179    unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);180    if (Line != ExpectLine || Column != ExpectColumn) {181      std::string MsgStr;182      llvm::raw_string_ostream Msg(MsgStr);183      Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn184          << ">, found <";185      Loc.print(Msg, *Result.SourceManager);186      Msg << '>';187      this->setFailure(MsgStr);188    }189  }190 191  virtual SourceLocation getLocation(const NodeType &Node) {192    return Node.getLocation();193  }194 195private:196  unsigned ExpectLine, ExpectColumn;197};198 199/// \brief Verify whether a node has the correct source range.200///201/// By default, Node.getSourceRange() is checked. This can be changed202/// by overriding getRange().203template <typename NodeType>204class RangeVerifier : public MatchVerifier<NodeType> {205public:206  void expectRange(unsigned BeginLine, unsigned BeginColumn,207                   unsigned EndLine, unsigned EndColumn) {208    ExpectBeginLine = BeginLine;209    ExpectBeginColumn = BeginColumn;210    ExpectEndLine = EndLine;211    ExpectEndColumn = EndColumn;212  }213 214protected:215  void verify(const MatchFinder::MatchResult &Result,216              const NodeType &Node) override {217    SourceRange R = getRange(Node);218    SourceLocation Begin = R.getBegin();219    SourceLocation End = R.getEnd();220    unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);221    unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);222    unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);223    unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);224    if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||225        EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {226      std::string MsgStr;227      llvm::raw_string_ostream Msg(MsgStr);228      Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn229          << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";230      Begin.print(Msg, *Result.SourceManager);231      Msg << '-';232      End.print(Msg, *Result.SourceManager);233      Msg << '>';234      this->setFailure(MsgStr);235    }236  }237 238  virtual SourceRange getRange(const NodeType &Node) {239    return Node.getSourceRange();240  }241 242private:243  unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;244};245 246/// \brief Verify whether a node's dump contains a given substring.247class DumpVerifier : public MatchVerifier<DynTypedNode> {248public:249  void expectSubstring(const std::string &Str) {250    ExpectSubstring = Str;251  }252 253protected:254  void verify(const MatchFinder::MatchResult &Result,255              const DynTypedNode &Node) override {256    std::string DumpStr;257    llvm::raw_string_ostream Dump(DumpStr);258    Node.dump(Dump, *Result.Context);259 260    if (DumpStr.find(ExpectSubstring) == std::string::npos) {261      std::string MsgStr;262      llvm::raw_string_ostream Msg(MsgStr);263      Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"264          << DumpStr << '>';265      this->setFailure(MsgStr);266    }267  }268 269private:270  std::string ExpectSubstring;271};272 273/// \brief Verify whether a node's pretty print matches a given string.274class PrintVerifier : public MatchVerifier<DynTypedNode> {275public:276  void expectString(const std::string &Str) {277    ExpectString = Str;278  }279 280protected:281  void verify(const MatchFinder::MatchResult &Result,282              const DynTypedNode &Node) override {283    std::string PrintStr;284    llvm::raw_string_ostream Print(PrintStr);285    Node.print(Print, Result.Context->getPrintingPolicy());286 287    if (PrintStr != ExpectString) {288      std::string MsgStr;289      llvm::raw_string_ostream Msg(MsgStr);290      Msg << "Expected pretty print <" << ExpectString << ">, found <"291          << PrintStr << '>';292      this->setFailure(MsgStr);293    }294  }295 296private:297  std::string ExpectString;298};299 300} // end namespace ast_matchers301} // end namespace clang302 303#endif304